diff --git a/library/constant_time.c b/library/constant_time.c index c60ec84550..d4bd331d2e 100644 --- a/library/constant_time.c +++ b/library/constant_time.c @@ -129,48 +129,18 @@ int mbedtls_ct_memcmp(const void *a, void mbedtls_ct_memmove_left(void *start, size_t total, size_t offset) { - /* In case of inlining, ensure that code generated is independent of the value of offset - * (e.g., if the compiler knows that offset == 0, it might be able to optimise this function - * to a no-op). */ - size_t hidden_offset = mbedtls_ct_compiler_opaque(offset); - - /* During this loop, j will take every value from [0..total) exactly once, - * regardless of the value of hidden_offset (it only changes the initial - * value for j). - * - * For this reason, when testing, it is safe to mark hidden_offset as non-secret. - * This prevents the const-flow checkers from generating a false-positive. - */ - TEST_CF_PUBLIC(&hidden_offset, sizeof(hidden_offset)); - - /* Iterate over the array, reading each byte once and writing each byte once. */ + volatile unsigned char *buf = start; for (size_t i = 0; i < total; i++) { - /* Each iteration, read one byte, and write it to start[i]. - * - * The source address will either be the "true" source address, if it's in the range - * where data is getting moved, or (if the source address is off the end of the - * array), it will wrap back to the start. - * - * If the source address is out of range, mask it to zero. - */ - - // The offset that we will read from (if in range) - size_t j = i + hidden_offset; - - // Is the address off the end of the array? - mbedtls_ct_condition_t not_dummy = mbedtls_ct_bool_lt(j, total); - - // Bring read address into range - j = j % total; - - // Read a byte - uint8_t b = ((uint8_t *) start)[j]; - - // Set it to zero if it's out of range - b = mbedtls_ct_uint_if0(not_dummy, b); - - // Write the byte to start[i] - ((uint8_t *) start)[i] = b; + mbedtls_ct_condition_t no_op = mbedtls_ct_bool_gt(total - offset, i); + /* The first `total - offset` passes are a no-op. The last + * `offset` passes shift the data one byte to the left and + * zero out the last byte. */ + for (size_t n = 0; n < total - 1; n++) { + unsigned char current = buf[n]; + unsigned char next = buf[n+1]; + buf[n] = mbedtls_ct_uint_if(no_op, current, next); + } + buf[total-1] = mbedtls_ct_uint_if0(no_op, buf[total-1]); } }