summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/tpm_test/Makefile1
-rw-r--r--test/tpm_test/bn_test.c348
2 files changed, 315 insertions, 34 deletions
diff --git a/test/tpm_test/Makefile b/test/tpm_test/Makefile
index 8eefed5a3e..e45f32ce0b 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;
}