diff options
Diffstat (limited to 'lib/util/secport.c')
-rw-r--r-- | lib/util/secport.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/lib/util/secport.c b/lib/util/secport.c index 8594072df..fb5223d64 100644 --- a/lib/util/secport.c +++ b/lib/util/secport.c @@ -800,3 +800,80 @@ NSS_SecureMemcmpZero(const void *mem, size_t n) /* 0 <= r < 256, so -r has bit 8 set when r != 0 */ return 1 & (-r >> 8); } + +/* + * A "value barrier" prevents the compiler from making optimizations based on + * the value that a variable takes. + * + * Standard C does not have value barriers, so C implementations of them are + * compiler-specific and are not guaranteed to be effective. Thus, the value + * barriers here are a best-effort, defense-in-depth, strategy. They are not a + * substitute for standard constant-time programming discipline. + * + * Some implementations have a performance penalty, so value barriers should + * be used sparingly. + */ +static inline int +value_barrier_int(int x) +{ +#if defined(__GNUC__) || defined(__clang__) + /* This inline assembly trick from Chandler Carruth's CppCon 2015 talk + * generates no instructions. + * + * "+r"(x) means that x will be mapped to a register that is both an input + * and an output to the assembly routine (""). The compiler will not + * inspect the assembly routine itself, so it cannot assume anything about + * the value of x after this line. + */ + __asm__("" + : "+r"(x) + : /* no other inputs */); + return x; +#else + /* If the compiler does not support the inline assembly trick above, we can + * put x in `volatile` storage and read it out again. This will generate + * explict store and load instructions, and possibly more depending on the + * target. + */ + volatile int y = x; + return y; +#endif +} + +/* + * A branch-free implementation of + * if (!b) { + * memmove(dest, src0, n); + * } else { + * memmove(dest, src1, n); + * } + * + * The memmove is performed with src0 if `b == 0` and with src1 + * otherwise. + * + * As with memmove, the selected src can overlap dest. + * + * Each of dest, src0, and src1 must point to an allocated buffer + * of at least n bytes. + */ +void +NSS_SecureSelect(void *dest, const void *src0, const void *src1, size_t n, unsigned char b) + +{ + // This value barrier makes it safe for the compiler to inline + // NSS_SecureSelect into a routine where it could otherwise infer something + // about the value of b, e.g. that b is 0/1 valued. + int w = value_barrier_int(b); + + // 0 <= b < 256, and int is at least 16 bits, so -w has bits 8-15 + // set when w != 0. + unsigned char mask = 0xff & (-w >> 8); + + for (size_t i = 0; i < n; ++i) { + unsigned char s0i = ((unsigned char *)src0)[i]; + unsigned char s1i = ((unsigned char *)src1)[i]; + // if mask == 0 this simplifies to s0 ^ 0 + // if mask == -1 this simplifies to s0 ^ s0 ^ s1 + ((unsigned char *)dest)[i] = s0i ^ (mask & (s0i ^ s1i)); + } +} |