diff options
-rw-r--r-- | test/tpm_test/Makefile | 1 | ||||
-rw-r--r-- | test/tpm_test/bn_test.c | 348 |
2 files changed, 315 insertions, 34 deletions
diff --git a/test/tpm_test/Makefile b/test/tpm_test/Makefile index 806dc02d42..8b4e4c55e2 100644 --- a/test/tpm_test/Makefile +++ b/test/tpm_test/Makefile @@ -37,6 +37,7 @@ CFLAGS += -Itestlib CFLAGS += -DLIBFTDI1=1 CFLAGS += -c CFLAGS += -DCR50_NO_BN_ASM +CFLAGS += -I../../fuzz TARGET = ftdi_spi_tpm .PRECIOUS: $(obj)/ftdi_spi_tpm_wrap.c diff --git a/test/tpm_test/bn_test.c b/test/tpm_test/bn_test.c index 9daf1bae64..db06ee93d4 100644 --- a/test/tpm_test/bn_test.c +++ b/test/tpm_test/bn_test.c @@ -11,16 +11,160 @@ #include <openssl/bn.h> -static int test_bn_modinv_helper(const BIGNUM *E, BN_CTX *ctx) +/** + * Compatibility layer for OpenSSL 1.0.x. + * BN_bn2lebinpad and BN_lebin2bn were added in OpenSSL 1.1, to provide + * import/export functionality as BIGNUM struct became opaque. + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define BN_RAND_TOP_ANY -1 +#define BN_RAND_TOP_ONE 0 +#define BN_RAND_TOP_TWO 1 +#define BN_RAND_BOTTOM_ODD 1 +#define BN_RAND_BOTTOM_ANY 0 + +/* export BIGNUM as little-endian padded to tolen bytes binary */ +static int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen) { int i; - BIGNUM *MOD = BN_CTX_get(ctx); + BN_ULONG l; - for (i = 0; i < 1000; i++) { + bn_check_top(a); + i = BN_num_bytes(a); + if (tolen < i) + return -1; + /* Add trailing zeroes if necessary */ + if (tolen > i) + memset(to + i, 0, tolen - i); + to += i; + while (i--) { + l = a->d[i / BN_BYTES]; + to--; + *to = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff; + } + return tolen; +} + +/* import BIGNUM from little-endian binary of specified length */ +static BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret) +{ + unsigned int i, m; + unsigned int n; + BN_ULONG l; + BIGNUM *bn = NULL; + + if (ret == NULL) + ret = bn = BN_new(); + if (ret == NULL) + return (NULL); + bn_check_top(ret); + s += len; + /* Skip trailing zeroes. */ + for (; len > 0 && s[-1] == 0; s--, len--) + continue; + n = len; + if (n == 0) { + ret->top = 0; + return ret; + } + i = ((n - 1) / BN_BYTES) + 1; + m = ((n - 1) % (BN_BYTES)); + if (bn_wexpand(ret, (int)i) == NULL) { + BN_free(bn); + return NULL; + } + ret->top = i; + ret->neg = 0; + l = 0; + while (n--) { + s--; + l = (l << 8L) | *s; + if (m-- == 0) { + ret->d[--i] = l; + l = 0; + m = BN_BYTES - 1; + } + } + /* + * need to call this due to clear byte at top if avoiding + * having the top bit set (-ve number) + */ + bn_correct_top(ret); + return ret; +} +#endif + +#define MAX_BN_TEST_SIZE 2048 + +static char to_hexchar(unsigned char c) +{ + return (c < 10) ? c + '0' : c - 10 + 'A'; +} + +static void hex_print(FILE *fp, unsigned char *d, int size) +{ + char buf[MAX_BN_TEST_SIZE / 4 + 1]; + int i = 0; + + assert((size * 2) + 1 <= sizeof(buf)); + while (i < size) { + buf[i * 2] = to_hexchar((d[size - i - 1] >> 4) & 0xF); + buf[i * 2 + 1] = to_hexchar(d[size - i - 1] & 0xF); + i++; + }; + buf[size * 2] = 0; + fprintf(fp, buf); +} - uint32_t m_buf[64]; - uint32_t d_buf[64]; - uint32_t e_buf[32]; +static void dcrypto_print(FILE *fp, struct LITE_BIGNUM *d, int size) +{ + hex_print(fp, (unsigned char *)d->d, size); +} + +static int bn_dcrypto_cmpeq(const BIGNUM *b, struct LITE_BIGNUM *d) +{ + unsigned char buf[MAX_BN_TEST_SIZE / 8]; + int size = BN_num_bytes(b); + + assert(size <= sizeof(buf)); + BN_bn2lebinpad(b, buf, size); + return memcmp(d->d, buf, size); +} + +/* Convert OpenSSL BIGNUM to Dcrypto, assumes caller provides buffer */ +static void bn_to_dcrypto(const BIGNUM *b, struct LITE_BIGNUM *d, uint32_t *buf, + size_t bufsize) +{ + int bn_size = BN_num_bytes(b); + + assert(bn_size <= bufsize); + memset(buf, 0, bufsize); + /** + * OpenSSL 1.0 was only working for little-endian architectures (x86) + * and had direct access to BIGNUM structure, so DCRYPTO_bn_wrap which + * just sets a pointer to user provided buffer as source for + * LITE_BIGNUM could be applied to data in BIGNUM as is. + * In OpenSSL 1.1 BIGNUM became opaque, so we need to export binary + * to get data in little-endian format which used by DCRYPTO_*. + */ + BN_bn2lebinpad(b, (unsigned char *)buf, bn_size); + DCRYPTO_bn_wrap(d, buf, bufsize); +} + +static int test_bn_modinv_helper(const BIGNUM *E, BN_CTX *ctx, int mod_top, + int mod_bottom) +{ + int i, result = 0; + BIGNUM *MOD, *r; + + BN_CTX_start(ctx); + MOD = BN_CTX_get(ctx); + r = BN_CTX_get(ctx); + + for (i = 0; i < 1000; i++) { + uint32_t m_buf[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; + uint32_t d_buf[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; + uint32_t e_buf[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; int has_inverse; int test_inverse; @@ -28,24 +172,16 @@ static int test_bn_modinv_helper(const BIGNUM *E, BN_CTX *ctx) struct LITE_BIGNUM e; struct LITE_BIGNUM d; - BIGNUM *r = BN_CTX_get(ctx); - - memset(e_buf, 0, sizeof(e_buf)); - /* Top bit set, bottom bit clear. */ - BN_rand(MOD, 2048, 1, 0); + BN_rand(MOD, MAX_BN_TEST_SIZE, mod_top, mod_bottom); if (BN_mod_inverse(r, E, MOD, ctx)) has_inverse = 1; else has_inverse = 0; + bn_to_dcrypto(MOD, &m, m_buf, sizeof(m_buf)); + bn_to_dcrypto(E, &e, e_buf, sizeof(e_buf)); - DCRYPTO_bn_wrap(&m, m_buf, sizeof(m_buf)); - memcpy(m_buf, MOD->d, sizeof(m_buf)); - assert(BN_num_bytes(E) <= sizeof(e_buf)); - memcpy(e_buf, E->d, BN_num_bytes(E)); - - DCRYPTO_bn_wrap(&e, e_buf, sizeof(e_buf)); bn_init(&d, d_buf, sizeof(d_buf)); test_inverse = bn_modinv_vartime(&d, &e, &m); @@ -65,46 +201,71 @@ static int test_bn_modinv_helper(const BIGNUM *E, BN_CTX *ctx) fprintf(stderr, "M : "); BN_print_fp(stderr, MOD); fprintf(stderr, "\n"); - - return 1; + result = 1; + goto fail; } if (has_inverse) { - if (memcmp(d.d, r->d, BN_num_bytes(r)) != 0) { - fprintf(stderr, "memcmp fail\n"); - return 1; + if (bn_dcrypto_cmpeq(r, &d) != 0) { + fprintf(stderr, + "dcrypto bn_modinv_vartime fail\n"); + fprintf(stderr, "d : "); + BN_print_fp(stderr, r); + fprintf(stderr, "\n dd: "); + dcrypto_print(stderr, &d, BN_num_bytes(r)); + fprintf(stderr, "\n"); + + fprintf(stderr, "e : "); + BN_print_fp(stderr, E); + fprintf(stderr, "\n"); + + fprintf(stderr, "M : "); + BN_print_fp(stderr, MOD); + fprintf(stderr, "\n"); + + result = 1; + goto fail; } } - - BN_free(r); } - - return 0; +fail: + BN_CTX_end(ctx); + return result; } static int test_bn_modinv(void) { + BN_CTX *ctx; + BIGNUM *E; int result = 1; - BN_CTX *ctx = BN_CTX_new(); + ctx = BN_CTX_new(); BN_CTX_start(ctx); - BIGNUM *E = BN_CTX_get(ctx); + E = BN_CTX_get(ctx); + + BN_rand(E, 1024, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ODD); + /* Top bit set, bottom bit clear. */ + if (test_bn_modinv_helper(E, ctx, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) + goto fail; + + if (test_bn_modinv_helper(E, ctx, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ANY)) + goto fail; - BN_rand(E, 1024, 1, 1); - if (test_bn_modinv_helper(E, ctx)) + BN_rand(E, 32, BN_RAND_TOP_TWO, BN_RAND_BOTTOM_ODD); + if (test_bn_modinv_helper(E, ctx, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) goto fail; - BN_rand(E, 1024, 1, 0); - if (test_bn_modinv_helper(E, ctx)) + BN_rand(E, 17, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ODD); + if (test_bn_modinv_helper(E, ctx, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) goto fail; BN_set_word(E, 3); - if (test_bn_modinv_helper(E, ctx)) + if (test_bn_modinv_helper(E, ctx, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) goto fail; BN_set_word(E, 65537); - if (test_bn_modinv_helper(E, ctx)) + if (test_bn_modinv_helper(E, ctx, BN_RAND_TOP_ONE, BN_RAND_BOTTOM_ANY)) goto fail; result = 0; @@ -114,6 +275,124 @@ fail: return result; } +/* Build a BIGNUM with following template: + * 11111111111111110000001111111111111000000000000123455667 + * size - size in bits + * front_ones mid_ones_pos,mid_ones, rand_low + * front_ones - number of 1 bits in highest position + * mid_ones_pos - starting position of middle ones + * mid_ones - number of 1 bits in the middle + * rand_low - number of random low bits + */ +static BIGNUM *bn_gen(BIGNUM *out, int size, int front_ones, int mid_ones_pos, + int mid_ones, int rand_low) +{ + unsigned char n[MAX_BN_TEST_SIZE / 8] = {}; + + assert(size <= sizeof(n) * 8); + assert(front_ones < size); + assert(mid_ones_pos < (size - front_ones - 1)); + assert(mid_ones < (size - mid_ones_pos - 1)); + assert(rand_low < (size - mid_ones_pos - mid_ones - 1)); + /* generate little-endian representation */ + while (front_ones) { + n[(size - front_ones) / 8] |= 1 << ((size - front_ones) & 7); + front_ones--; + } + while (mid_ones) { + n[(mid_ones_pos - mid_ones) / 8] |= + 1 << ((mid_ones_pos - mid_ones) & 7); + mid_ones--; + } + while (rand_low) { + n[(rand_low - 1) / 8] |= (rand() & 1) << ((rand_low - 1) & 7); + rand_low--; + } + + return BN_lebin2bn(n, size / 8, out); +} + +static int test_bn_div(void) +{ + const int NSIZE = MAX_BN_TEST_SIZE; + const int PSIZE = MAX_BN_TEST_SIZE / 2; + BIGNUM *N, *P, *Q, *R; + BN_CTX *ctx; + int result = 0, total = 0, prev = 1; + int nf, nmps, nms, pf, pmps, pms; + struct LITE_BIGNUM p; + struct LITE_BIGNUM q; + struct LITE_BIGNUM n; + struct LITE_BIGNUM r; + + uint32_t p_buff[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; + uint32_t q_buff[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; + uint32_t n_buff[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; + uint32_t r_buff[MAX_BN_TEST_SIZE / LITE_BN_BITS2]; + + ctx = BN_CTX_new(); + BN_CTX_start(ctx); + N = BN_CTX_get(ctx); + P = BN_CTX_get(ctx); + Q = BN_CTX_get(ctx); + R = BN_CTX_get(ctx); + + for (nf = 1; nf <= NSIZE / 8; nf++) + for (nmps = NSIZE / 16; nmps < (NSIZE / 16) + 2; nmps++) + for (nms = NSIZE / 32; nms < (NSIZE / 32) + 2; nms++) { + N = bn_gen(N, NSIZE, nf, nmps, nms, (nmps - nms) / 2); + for (pf = 1; pf <= PSIZE / 4; pf++) + for (pmps = PSIZE / 16; pmps < (PSIZE / 16) + 2; pmps++) + for (pms = PSIZE / 32; pms < (PSIZE / 32) + 2; pms++) { + P = bn_gen(P, PSIZE, pf, pmps, pms, (pmps - pms) / 2); + total++; + bn_to_dcrypto(N, &n, n_buff, sizeof(n_buff)); + bn_to_dcrypto(P, &p, p_buff, sizeof(p_buff)); + DCRYPTO_bn_wrap(&q, q_buff, sizeof(q_buff)); + DCRYPTO_bn_wrap(&r, r_buff, sizeof(r_buff)); + + BN_div(Q, R, N, P, ctx); + DCRYPTO_bn_div(&q, &r, &n, &p); + + if ((bn_dcrypto_cmpeq(Q, &q) != 0) || + (bn_dcrypto_cmpeq(R, &r) != 0)) { + result++; + if (result > prev) { + /* print only 1 sample in 100000 */ + prev = result + 100000; + fprintf(stderr, "N : "); + BN_print_fp(stderr, N); + fprintf(stderr, "\n"); + fprintf(stderr, "P : "); + BN_print_fp(stderr, P); + fprintf(stderr, "\n"); + + fprintf(stderr, "Q : "); + BN_print_fp(stderr, Q); + fprintf(stderr, "\nQd: "); + dcrypto_print(stderr, &q, + BN_num_bytes(Q)); + fprintf(stderr, "\n"); + + fprintf(stderr, "R : "); + BN_print_fp(stderr, R); + fprintf(stderr, "\nRd: "); + dcrypto_print(stderr, &r, + BN_num_bytes(R)); + fprintf(stderr, "\n"); + } + } + } + } + if (result) + fprintf(stderr, "DCRYPTO_bn_div: total=%d, failures=%d\n", + total, result); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return result; +} + void *always_memset(void *s, int c, size_t n) { memset(s, c, n); @@ -127,6 +406,7 @@ void watchdog_reload(void) int main(void) { assert(test_bn_modinv() == 0); + assert(test_bn_div() == 0); fprintf(stderr, "PASS\n"); return 0; } |