summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2019-08-21 15:47:03 -0700
committerCommit Bot <commit-bot@chromium.org>2020-08-11 23:38:29 +0000
commit987bf0f02dba3df938eb72f805f02c89e5afe92c (patch)
treecd9fcbffbafc116099220c90edbcf8b52b8f681d
parent485e86a7df8a779abbf88c1ca2774bdd2af1b286 (diff)
downloadchrome-ec-987bf0f02dba3df938eb72f805f02c89e5afe92c.tar.gz
tpmtest: added more corner case, make it work again with OpenSSL 1.1
The TPM test directory has bitrotted and does not compile any more, leave alone pass tests. This patch updates the tests to match changed EC codebase: test/tpm_test/Makefile - look for include files in more directories test/tpm_test/bn_test.c - 1. add support for OpenSSL 1.1 where BIGNUM structure became opaque and require special functions to access it. 2. added backward compatibility layer for OpenSSL 1.0.2 3. fixed issues with OpenSSL memory allocations 4. added support to print details of failure 5. added more cases for modulo inverse testing 6. added testing for bn_div to increase branch coverage BRANCH=cr50 BUG=none TEST=./test/tpm_test (../../build/tpm_test/bn_test) now passes Change-Id: Ida5fb07277909977f78ad1199e7a0f3677aabdc3 Signed-off-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1764711 Reviewed-by: Andrey Pronin <apronin@chromium.org> Commit-Queue: Andrey Pronin <apronin@chromium.org> (cherry picked from commit fb1d26a58e5511d70f747e8b943096c22dead07c) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2223147 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> (cherry picked from commit 1da8e0dd19cd7e7c16712fbf0deb00c9f4ad5889) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2314110 Tested-by: Mary Ruthven <mruthven@chromium.org> Reviewed-by: Mary Ruthven <mruthven@chromium.org> Commit-Queue: Mary Ruthven <mruthven@chromium.org> (cherry picked from commit 257cadc0e13e4573c4f51d41f64183a32837e9ab) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2350272
-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;
}