Merge pull request #7579 from daverodgman/safer-ct-asm

Arm assembly implementation of constant time primitives
This commit is contained in:
Dave Rodgman 2023-08-28 08:26:29 +00:00 committed by GitHub
commit d395590597
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 170 additions and 4 deletions

View file

@ -150,8 +150,13 @@ void mbedtls_ct_memcpy_if(mbedtls_ct_condition_t condition,
const unsigned char *src2, const unsigned char *src2,
size_t len) size_t len)
{ {
#if defined(MBEDTLS_CT_SIZE_64)
const uint64_t mask = (uint64_t) condition;
const uint64_t not_mask = (uint64_t) ~mbedtls_ct_compiler_opaque(condition);
#else
const uint32_t mask = (uint32_t) condition; const uint32_t mask = (uint32_t) condition;
const uint32_t not_mask = (uint32_t) ~mbedtls_ct_compiler_opaque(condition); const uint32_t not_mask = (uint32_t) ~mbedtls_ct_compiler_opaque(condition);
#endif
/* If src2 is NULL, setup src2 so that we read from the destination address. /* If src2 is NULL, setup src2 so that we read from the destination address.
* *
@ -165,11 +170,19 @@ void mbedtls_ct_memcpy_if(mbedtls_ct_condition_t condition,
/* dest[i] = c1 == c2 ? src[i] : dest[i] */ /* dest[i] = c1 == c2 ? src[i] : dest[i] */
size_t i = 0; size_t i = 0;
#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) #if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS)
#if defined(MBEDTLS_CT_SIZE_64)
for (; (i + 8) <= len; i += 8) {
uint64_t a = mbedtls_get_unaligned_uint64(src1 + i) & mask;
uint64_t b = mbedtls_get_unaligned_uint64(src2 + i) & not_mask;
mbedtls_put_unaligned_uint64(dest + i, a | b);
}
#else
for (; (i + 4) <= len; i += 4) { for (; (i + 4) <= len; i += 4) {
uint32_t a = mbedtls_get_unaligned_uint32(src1 + i) & mask; uint32_t a = mbedtls_get_unaligned_uint32(src1 + i) & mask;
uint32_t b = mbedtls_get_unaligned_uint32(src2 + i) & not_mask; uint32_t b = mbedtls_get_unaligned_uint32(src2 + i) & not_mask;
mbedtls_put_unaligned_uint32(dest + i, a | b); mbedtls_put_unaligned_uint32(dest + i, a | b);
} }
#endif /* defined(MBEDTLS_CT_SIZE_64) */
#endif /* MBEDTLS_EFFICIENT_UNALIGNED_ACCESS */ #endif /* MBEDTLS_EFFICIENT_UNALIGNED_ACCESS */
for (; i < len; i++) { for (; i < len; i++) {
dest[i] = (src1[i] & mask) | (src2[i] & not_mask); dest[i] = (src1[i] & mask) | (src2[i] & not_mask);

View file

@ -48,8 +48,14 @@
#pragma GCC diagnostic ignored "-Wredundant-decls" #pragma GCC diagnostic ignored "-Wredundant-decls"
#endif #endif
/* Disable asm under Memsan because it confuses Memsan and generates false errors */ /* Disable asm under Memsan because it confuses Memsan and generates false errors.
#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) *
* We also disable under Valgrind by default, because it's more useful
* for Valgrind to test the plain C implementation. MBEDTLS_TEST_CONSTANT_FLOW_ASM //no-check-names
* may be set to permit building asm under Valgrind.
*/
#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) || \
(defined(MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND) && !defined(MBEDTLS_TEST_CONSTANT_FLOW_ASM)) //no-check-names
#define MBEDTLS_CT_NO_ASM #define MBEDTLS_CT_NO_ASM
#elif defined(__has_feature) #elif defined(__has_feature)
#if __has_feature(memory_sanitizer) #if __has_feature(memory_sanitizer)
@ -109,6 +115,28 @@ static inline mbedtls_ct_uint_t mbedtls_ct_compiler_opaque(mbedtls_ct_uint_t x)
#endif #endif
} }
/*
* Selecting unified syntax is needed for gcc, and harmless on clang.
*
* This is needed because on Thumb 1, condition flags are always set, so
* e.g. "negs" is supported but "neg" is not (on Thumb 2, both exist).
*
* Under Thumb 1 unified syntax, only the "negs" form is accepted, and
* under divided syntax, only the "neg" form is accepted. clang only
* supports unified syntax.
*
* On Thumb 2 and Arm, both compilers are happy with the "s" suffix,
* although we don't actually care about setting the flags.
*
* For gcc, restore divided syntax afterwards - otherwise old versions of gcc
* seem to apply unified syntax globally, which breaks other asm code.
*/
#if !defined(__clang__)
#define RESTORE_ASM_SYNTAX ".syntax divided \n\t"
#else
#define RESTORE_ASM_SYNTAX
#endif
/* Convert a number into a condition in constant time. */ /* Convert a number into a condition in constant time. */
static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x) static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x)
{ {
@ -120,6 +148,34 @@ static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x)
* Otherwise, we define a plain C fallback which (in May 2023) does not get optimised into * Otherwise, we define a plain C fallback which (in May 2023) does not get optimised into
* conditional instructions or branches by trunk clang, gcc, or MSVC v19. * conditional instructions or branches by trunk clang, gcc, or MSVC v19.
*/ */
#if defined(MBEDTLS_CT_AARCH64_ASM) && (defined(MBEDTLS_CT_SIZE_32) || defined(MBEDTLS_CT_SIZE_64))
mbedtls_ct_uint_t s;
asm volatile ("neg %x[s], %x[x] \n\t"
"orr %x[x], %x[s], %x[x] \n\t"
"asr %x[x], %x[x], 63"
:
[s] "=&r" (s),
[x] "+&r" (x)
:
:
);
return (mbedtls_ct_condition_t) x;
#elif defined(MBEDTLS_CT_ARM_ASM) && defined(MBEDTLS_CT_SIZE_32)
uint32_t s;
asm volatile (".syntax unified \n\t"
"negs %[s], %[x] \n\t"
"orrs %[x], %[x], %[s] \n\t"
"asrs %[x], %[x], #31 \n\t"
RESTORE_ASM_SYNTAX
:
[s] "=&l" (s),
[x] "+&l" (x)
:
:
"cc" /* clobbers flag bits */
);
return (mbedtls_ct_condition_t) x;
#else
const mbedtls_ct_uint_t xo = mbedtls_ct_compiler_opaque(x); const mbedtls_ct_uint_t xo = mbedtls_ct_compiler_opaque(x);
#if defined(_MSC_VER) #if defined(_MSC_VER)
/* MSVC has a warning about unary minus on unsigned, but this is /* MSVC has a warning about unary minus on unsigned, but this is
@ -127,24 +183,98 @@ static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x)
#pragma warning( push ) #pragma warning( push )
#pragma warning( disable : 4146 ) #pragma warning( disable : 4146 )
#endif #endif
return (mbedtls_ct_condition_t) (((mbedtls_ct_int_t) ((-xo) | -(xo >> 1))) >> // y is negative (i.e., top bit set) iff x is non-zero
(MBEDTLS_CT_SIZE - 1)); mbedtls_ct_int_t y = (-xo) | -(xo >> 1);
// extract only the sign bit of y so that y == 1 (if x is non-zero) or 0 (if x is zero)
y = (((mbedtls_ct_uint_t) y) >> (MBEDTLS_CT_SIZE - 1));
// -y has all bits set (if x is non-zero), or all bits clear (if x is zero)
return (mbedtls_ct_condition_t) (-y);
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning( pop ) #pragma warning( pop )
#endif #endif
#endif
} }
static inline mbedtls_ct_uint_t mbedtls_ct_if(mbedtls_ct_condition_t condition, static inline mbedtls_ct_uint_t mbedtls_ct_if(mbedtls_ct_condition_t condition,
mbedtls_ct_uint_t if1, mbedtls_ct_uint_t if1,
mbedtls_ct_uint_t if0) mbedtls_ct_uint_t if0)
{ {
#if defined(MBEDTLS_CT_AARCH64_ASM) && (defined(MBEDTLS_CT_SIZE_32) || defined(MBEDTLS_CT_SIZE_64))
asm volatile ("and %x[if1], %x[if1], %x[condition] \n\t"
"mvn %x[condition], %x[condition] \n\t"
"and %x[condition], %x[condition], %x[if0] \n\t"
"orr %x[condition], %x[if1], %x[condition]"
:
[condition] "+&r" (condition),
[if1] "+&r" (if1)
:
[if0] "r" (if0)
:
);
return (mbedtls_ct_uint_t) condition;
#elif defined(MBEDTLS_CT_ARM_ASM) && defined(MBEDTLS_CT_SIZE_32)
asm volatile (".syntax unified \n\t"
"ands %[if1], %[if1], %[condition] \n\t"
"mvns %[condition], %[condition] \n\t"
"ands %[condition], %[condition], %[if0] \n\t"
"orrs %[condition], %[if1], %[condition] \n\t"
RESTORE_ASM_SYNTAX
:
[condition] "+&l" (condition),
[if1] "+&l" (if1)
:
[if0] "l" (if0)
:
"cc"
);
return (mbedtls_ct_uint_t) condition;
#else
mbedtls_ct_condition_t not_cond = mbedtls_ct_condition_t not_cond =
(mbedtls_ct_condition_t) (~mbedtls_ct_compiler_opaque(condition)); (mbedtls_ct_condition_t) (~mbedtls_ct_compiler_opaque(condition));
return (mbedtls_ct_uint_t) ((condition & if1) | (not_cond & if0)); return (mbedtls_ct_uint_t) ((condition & if1) | (not_cond & if0));
#endif
} }
static inline mbedtls_ct_condition_t mbedtls_ct_uint_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y) static inline mbedtls_ct_condition_t mbedtls_ct_uint_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y)
{ {
#if defined(MBEDTLS_CT_AARCH64_ASM) && (defined(MBEDTLS_CT_SIZE_32) || defined(MBEDTLS_CT_SIZE_64))
uint64_t s1;
asm volatile ("eor %x[s1], %x[y], %x[x] \n\t"
"sub %x[x], %x[x], %x[y] \n\t"
"bic %x[x], %x[x], %x[s1] \n\t"
"and %x[s1], %x[s1], %x[y] \n\t"
"orr %x[s1], %x[x], %x[s1] \n\t"
"asr %x[x], %x[s1], 63"
: [s1] "=&r" (s1), [x] "+&r" (x)
: [y] "r" (y)
:
);
return (mbedtls_ct_condition_t) x;
#elif defined(MBEDTLS_CT_ARM_ASM) && defined(MBEDTLS_CT_SIZE_32)
uint32_t s1;
asm volatile (
".syntax unified \n\t"
#if defined(__thumb__) && !defined(__thumb2__)
"movs %[s1], %[x] \n\t"
"eors %[s1], %[s1], %[y] \n\t"
#else
"eors %[s1], %[x], %[y] \n\t"
#endif
"subs %[x], %[x], %[y] \n\t"
"bics %[x], %[x], %[s1] \n\t"
"ands %[y], %[s1], %[y] \n\t"
"orrs %[x], %[x], %[y] \n\t"
"asrs %[x], %[x], #31 \n\t"
RESTORE_ASM_SYNTAX
: [s1] "=&l" (s1), [x] "+&l" (x), [y] "+&l" (y)
:
:
"cc"
);
return (mbedtls_ct_condition_t) x;
#else
/* Ensure that the compiler cannot optimise the following operations over x and y, /* Ensure that the compiler cannot optimise the following operations over x and y,
* even if it knows the value of x and y. * even if it knows the value of x and y.
*/ */
@ -173,6 +303,7 @@ static inline mbedtls_ct_condition_t mbedtls_ct_uint_lt(mbedtls_ct_uint_t x, mbe
// Convert to a condition (i.e., all bits set iff non-zero) // Convert to a condition (i.e., all bits set iff non-zero)
return mbedtls_ct_bool(ret); return mbedtls_ct_bool(ret);
#endif
} }
static inline mbedtls_ct_condition_t mbedtls_ct_uint_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y) static inline mbedtls_ct_condition_t mbedtls_ct_uint_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y)

View file

@ -85,12 +85,14 @@ typedef ptrdiff_t mbedtls_ct_int_t;
typedef uint64_t mbedtls_ct_condition_t; typedef uint64_t mbedtls_ct_condition_t;
typedef uint64_t mbedtls_ct_uint_t; typedef uint64_t mbedtls_ct_uint_t;
typedef int64_t mbedtls_ct_int_t; typedef int64_t mbedtls_ct_int_t;
#define MBEDTLS_CT_SIZE_64
#define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT64_MAX)) #define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT64_MAX))
#else #else
/* Pointer size <= 32-bit, and no 64-bit MPIs */ /* Pointer size <= 32-bit, and no 64-bit MPIs */
typedef uint32_t mbedtls_ct_condition_t; typedef uint32_t mbedtls_ct_condition_t;
typedef uint32_t mbedtls_ct_uint_t; typedef uint32_t mbedtls_ct_uint_t;
typedef int32_t mbedtls_ct_int_t; typedef int32_t mbedtls_ct_int_t;
#define MBEDTLS_CT_SIZE_32
#define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT32_MAX)) #define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT32_MAX))
#endif #endif
#define MBEDTLS_CT_FALSE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(0)) #define MBEDTLS_CT_FALSE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(0))

View file

@ -1872,6 +1872,16 @@ skip_suites_without_constant_flow () {
export SKIP_TEST_SUITES export SKIP_TEST_SUITES
} }
skip_all_except_given_suite () {
# Skip all but the given test suite
SKIP_TEST_SUITES=$(
ls -1 tests/suites/test_suite_*.function |
grep -v $1.function |
sed 's/tests.suites.test_suite_//; s/\.function$//' |
tr '\n' ,)
export SKIP_TEST_SUITES
}
component_test_memsan_constant_flow () { component_test_memsan_constant_flow () {
# This tests both (1) accesses to undefined memory, and (2) branches or # This tests both (1) accesses to undefined memory, and (2) branches or
# memory access depending on secret values. To distinguish between those: # memory access depending on secret values. To distinguish between those:
@ -1931,6 +1941,16 @@ component_test_valgrind_constant_flow () {
# details are left in Testing/<date>/DynamicAnalysis.xml # details are left in Testing/<date>/DynamicAnalysis.xml
msg "test: some suites (full minus MBEDTLS_USE_PSA_CRYPTO, valgrind + constant flow)" msg "test: some suites (full minus MBEDTLS_USE_PSA_CRYPTO, valgrind + constant flow)"
make memcheck make memcheck
# Test asm path in constant time module - by default, it will test the plain C
# path under Valgrind or Memsan. Running only the constant_time tests is fast (<1s)
msg "test: valgrind asm constant_time"
scripts/config.py --force set MBEDTLS_TEST_CONSTANT_FLOW_ASM
skip_all_except_given_suite test_suite_constant_time
cmake -D CMAKE_BUILD_TYPE:String=Release .
make clean
make
make memcheck
} }
component_test_valgrind_constant_flow_psa () { component_test_valgrind_constant_flow_psa () {