Add regress coverage for BN_{add,sub,mul,div,mod}_word().
authorjsing <jsing@openbsd.org>
Sat, 11 Mar 2023 14:04:21 +0000 (14:04 +0000)
committerjsing <jsing@openbsd.org>
Sat, 11 Mar 2023 14:04:21 +0000 (14:04 +0000)
This also provides some indirect coverage for BN_hex2bn(), BN_bn2hex() and
BN_get_word().

Two of these tests are currently failing and will be fixed shortly.

regress/lib/libcrypto/bn/Makefile
regress/lib/libcrypto/bn/bn_word.c [new file with mode: 0644]

index 2852b2e..bcdfe3e 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.21 2023/01/31 05:13:28 jsing Exp $
+#      $OpenBSD: Makefile,v 1.22 2023/03/11 14:04:21 jsing Exp $
 
 PROGS +=       bn_add_sub
 PROGS +=       bn_cmp
@@ -15,6 +15,7 @@ PROGS +=      bn_shift
 PROGS +=       bn_test
 PROGS +=       bn_to_string
 PROGS +=       bn_unit
+PROGS +=       bn_word
 
 STATIC_LINK += bn_isqrt
 STATIC_LINK += bn_mod_exp
diff --git a/regress/lib/libcrypto/bn/bn_word.c b/regress/lib/libcrypto/bn/bn_word.c
new file mode 100644 (file)
index 0000000..0a543ad
--- /dev/null
@@ -0,0 +1,617 @@
+/* $OpenBSD: bn_word.c,v 1.1 2023/03/11 14:04:21 jsing Exp $ */
+/*
+ * Copyright (c) 2023 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+
+struct bn_word_test {
+       const char *in_hex;
+       BN_ULONG in_word;
+       BN_ULONG mod_word;
+       BN_ULONG out_word;
+       const char *out_hex;
+       int out_is_negative;
+};
+
+static int
+check_bn_word_test(const char *op_name, const BIGNUM *bn,
+    const struct bn_word_test *bwt)
+{
+       char *out_hex = NULL;
+       BN_ULONG out_word;
+       int failed = 1;
+
+       if ((out_word = BN_get_word(bn)) != bwt->out_word) {
+               fprintf(stderr, "FAIL %s: Got word %lx, want %lx\n",
+                   op_name, (unsigned long)out_word,
+                   (unsigned long)bwt->out_word);
+               goto failure;
+       }
+
+       if (BN_is_negative(bn) != bwt->out_is_negative) {
+               fprintf(stderr, "FAIL %s: Got is negative %d, want %d\n",
+                   op_name, BN_is_negative(bn), bwt->out_is_negative);
+               goto failure;
+       }
+
+       if ((out_hex = BN_bn2hex(bn)) == NULL)
+               errx(1, "BN_bn2hex() failed\n");
+
+       if (strcmp(out_hex, bwt->out_hex) != 0) {
+               fprintf(stderr, "FAIL %s: Got hex %s, want %s\n",
+                   op_name, out_hex, bwt->out_hex);
+               goto failure;
+       }
+
+       if (BN_is_zero(bn) && BN_is_negative(bn) != 0) {
+               fprintf(stderr, "FAIL %s: Got negative zero\n", op_name);
+               goto failure;
+       }
+
+       failed = 0;
+
+ failure:
+       free(out_hex);
+
+       return failed;
+}
+
+static int
+test_bn_word(int (*bn_word_op)(BIGNUM *, BN_ULONG), const char *op_name,
+    const struct bn_word_test *bwts, size_t num_tests)
+{
+       const struct bn_word_test *bwt;
+       BIGNUM *bn;
+       size_t i;
+       int failed = 0;
+
+       if ((bn = BN_new()) == NULL)
+               errx(1, "BN_new() failed\n");
+
+       for (i = 0; i < num_tests; i++) {
+               bwt = &bwts[i];
+
+               if (!BN_hex2bn(&bn, bwt->in_hex)) {
+                       fprintf(stderr, "FAIL: BN_hex2bn(\"%s\") failed\n",
+                           bwt->in_hex);
+                       failed = 1;
+                       continue;
+               }
+
+               if (!bn_word_op(bn, bwt->in_word)) {
+                       fprintf(stderr, "FAIL: %s(%lx) failed\n", op_name,
+                            (unsigned long)bwt->in_word);
+                       failed = 1;
+                       continue;
+               }
+
+               failed |= check_bn_word_test(op_name, bn, bwt);
+       }
+
+       BN_free(bn);
+
+       return failed;
+}
+
+static const struct bn_word_test bn_add_word_tests[] = {
+       {
+               .in_hex = "1",
+               .in_word = 0,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "0",
+               .in_word = 1,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "1",
+               .in_word = 1,
+               .out_word = 2,
+               .out_hex = "02",
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 2,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 1,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "-3",
+               .in_word = 2,
+               .out_word = 1,
+               .out_hex = "-01",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "1",
+               .in_word = 0xfffffffeUL,
+               .out_word = 0xffffffffUL,
+               .out_hex = "FFFFFFFF",
+       },
+       {
+               .in_hex = "FFFFFFFFFFFFFFFF",
+               .in_word = 1,
+               .out_word = BN_MASK2,
+               .out_hex = "010000000000000000",
+       },
+};
+
+#define N_BN_ADD_WORD_TESTS \
+    (sizeof(bn_add_word_tests) / sizeof(bn_add_word_tests[0]))
+
+static int
+test_bn_add_word(void)
+{
+       return test_bn_word(BN_add_word, "BN_add_word", bn_add_word_tests,
+           N_BN_ADD_WORD_TESTS);
+}
+
+static const struct bn_word_test bn_sub_word_tests[] = {
+       {
+               .in_hex = "1",
+               .in_word = 0,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "0",
+               .in_word = 1,
+               .out_word = 1,
+               .out_hex = "-01",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "1",
+               .in_word = 1,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "2",
+               .in_word = 1,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 2,
+               .out_word = 3,
+               .out_hex = "-03",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "1",
+               .in_word = 1,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "3",
+               .in_word = 2,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "-3",
+               .in_word = 2,
+               .out_word = 5,
+               .out_hex = "-05",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 0xfffffffeUL,
+               .out_word = 0xffffffffUL,
+               .out_hex = "-FFFFFFFF",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "010000000000000000",
+               .in_word = 1,
+               .out_word = BN_MASK2,
+               .out_hex = "FFFFFFFFFFFFFFFF",
+       },
+};
+
+#define N_BN_SUB_WORD_TESTS \
+    (sizeof(bn_sub_word_tests) / sizeof(bn_sub_word_tests[0]))
+
+static int
+test_bn_sub_word(void)
+{
+       return test_bn_word(BN_sub_word, "BN_sub_word", bn_sub_word_tests,
+           N_BN_SUB_WORD_TESTS);
+}
+
+static const struct bn_word_test bn_mul_word_tests[] = {
+       {
+               .in_hex = "1",
+               .in_word = 0,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "0",
+               .in_word = 1,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "1",
+               .in_word = 1,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 0,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 1,
+               .out_word = 1,
+               .out_hex = "-01",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "-3",
+               .in_word = 2,
+               .out_word = 6,
+               .out_hex = "-06",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "1",
+               .in_word = 0xfffffffeUL,
+               .out_word = 0xfffffffeUL,
+               .out_hex = "FFFFFFFE",
+       },
+       {
+               .in_hex = "010000000000000000",
+               .in_word = 2,
+               .out_word = BN_MASK2,
+               .out_hex = "020000000000000000",
+       },
+};
+
+#define N_BN_MUL_WORD_TESTS \
+    (sizeof(bn_mul_word_tests) / sizeof(bn_mul_word_tests[0]))
+
+static int
+test_bn_mul_word(void)
+{
+       return test_bn_word(BN_mul_word, "BN_mul_word", bn_mul_word_tests,
+           N_BN_MUL_WORD_TESTS);
+}
+
+static const struct bn_word_test bn_div_word_tests[] = {
+       {
+               .in_hex = "1",
+               .in_word = 0,
+               .mod_word = BN_MASK2,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "0",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "4",
+               .in_word = 2,
+               .mod_word = 0,
+               .out_word = 2,
+               .out_hex = "02",
+       },
+       {
+               .in_hex = "7",
+               .in_word = 3,
+               .mod_word = 1,
+               .out_word = 2,
+               .out_hex = "02",
+       },
+       {
+               .in_hex = "1",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "-2",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 2,
+               .out_hex = "-02",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 2,
+               .mod_word = 1,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "-3",
+               .in_word = 2,
+               .mod_word = 1,
+               .out_word = 1,
+               .out_hex = "-01",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "1",
+               .in_word = 0xffffffffUL,
+               .mod_word = 1,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "FFFFFFFF",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 0xffffffffUL,
+               .out_hex = "FFFFFFFF",
+       },
+       {
+               .in_hex = "FFFFFFFE",
+               .in_word = 0xffffffffUL,
+               .mod_word = 0xfffffffeUL,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "FFFFFFFFFFFFFFFF",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = BN_MASK2,
+               .out_hex = "FFFFFFFFFFFFFFFF",
+       },
+       {
+               .in_hex = "FFFFFFFF",
+               .in_word = 0xff,
+               .mod_word = 0,
+               .out_word = 0x1010101UL,
+               .out_hex = "01010101",
+       },
+       {
+               .in_hex = "FFFFFFFF",
+               .in_word = 0x10,
+               .mod_word = 0xf,
+               .out_word = 0xfffffffUL,
+               .out_hex = "0FFFFFFF",
+       },
+};
+
+#define N_BN_DIV_WORD_TESTS \
+    (sizeof(bn_div_word_tests) / sizeof(bn_div_word_tests[0]))
+
+static int
+test_bn_div_word(void)
+{
+       const char *op_name = "BN_div_word";
+       const struct bn_word_test *bwt;
+       BN_ULONG mod_word;
+       BIGNUM *bn;
+       size_t i;
+       int failed = 0;
+
+       if ((bn = BN_new()) == NULL)
+               errx(1, "BN_new() failed\n");
+
+       for (i = 0; i < N_BN_DIV_WORD_TESTS; i++) {
+               bwt = &bn_div_word_tests[i];
+
+               if (!BN_hex2bn(&bn, bwt->in_hex)) {
+                       fprintf(stderr, "FAIL: BN_hex2bn(\"%s\") failed\n",
+                           bwt->in_hex);
+                       failed = 1;
+                       continue;
+               }
+
+               if ((mod_word = BN_div_word(bn, bwt->in_word)) != bwt->mod_word) {
+                       fprintf(stderr, "FAIL %s: Got mod word %lx, want %lx\n",
+                           op_name, (unsigned long)mod_word,
+                           (unsigned long)bwt->mod_word);
+                       failed = 1;
+                       continue;
+               }
+
+               failed |= check_bn_word_test(op_name, bn, bwt);
+       }
+
+       BN_free(bn);
+
+       return failed;
+}
+
+static const struct bn_word_test bn_mod_word_tests[] = {
+       {
+               .in_hex = "1",
+               .in_word = 0,
+               .mod_word = BN_MASK2,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "0",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 0,
+               .out_hex = "0",
+       },
+       {
+               .in_hex = "4",
+               .in_word = 2,
+               .mod_word = 0,
+               .out_word = 4,
+               .out_hex = "04",
+       },
+       {
+               .in_hex = "7",
+               .in_word = 3,
+               .mod_word = 1,
+               .out_word = 7,
+               .out_hex = "07",
+       },
+       {
+               .in_hex = "1",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "-2",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 2,
+               .out_hex = "-02",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "-1",
+               .in_word = 2,
+               .mod_word = 1,
+               .out_word = 1,
+               .out_hex = "-01",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "-3",
+               .in_word = 2,
+               .mod_word = 1,
+               .out_word = 3,
+               .out_hex = "-03",
+               .out_is_negative = 1,
+       },
+       {
+               .in_hex = "1",
+               .in_word = 0xffffffffUL,
+               .mod_word = 1,
+               .out_word = 1,
+               .out_hex = "01",
+       },
+       {
+               .in_hex = "FFFFFFFF",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = 0xffffffffUL,
+               .out_hex = "FFFFFFFF",
+       },
+       {
+               .in_hex = "FFFFFFFE",
+               .in_word = 0xffffffffUL,
+               .mod_word = 0xfffffffeUL,
+               .out_word = 0xfffffffeUL,
+               .out_hex = "FFFFFFFE",
+       },
+       {
+               .in_hex = "FFFFFFFFFFFFFFFF",
+               .in_word = 1,
+               .mod_word = 0,
+               .out_word = BN_MASK2,
+               .out_hex = "FFFFFFFFFFFFFFFF",
+       },
+       {
+               .in_hex = "FFFFFFFF",
+               .in_word = 0xff,
+               .mod_word = 0,
+               .out_word = 0xffffffff,
+               .out_hex = "FFFFFFFF",
+       },
+       {
+               .in_hex = "FFFFFFFF",
+               .in_word = 0x10,
+               .mod_word = 0xf,
+               .out_word = 0xffffffffUL,
+               .out_hex = "FFFFFFFF",
+       },
+};
+
+#define N_BN_MOD_WORD_TESTS \
+    (sizeof(bn_mod_word_tests) / sizeof(bn_mod_word_tests[0]))
+
+static int
+test_bn_mod_word(void)
+{
+       const char *op_name = "BN_mod_word";
+       const struct bn_word_test *bwt;
+       BN_ULONG mod_word;
+       BIGNUM *bn;
+       size_t i;
+       int failed = 0;
+
+       if ((bn = BN_new()) == NULL)
+               errx(1, "BN_new() failed\n");
+
+       for (i = 0; i < N_BN_MOD_WORD_TESTS; i++) {
+               bwt = &bn_mod_word_tests[i];
+
+               if (!BN_hex2bn(&bn, bwt->in_hex)) {
+                       fprintf(stderr, "FAIL: BN_hex2bn(\"%s\") failed\n",
+                           bwt->in_hex);
+                       failed = 1;
+                       continue;
+               }
+
+               if ((mod_word = BN_mod_word(bn, bwt->in_word)) != bwt->mod_word) {
+                       fprintf(stderr, "FAIL %s: Got mod word %lx, want %lx\n",
+                           op_name, (unsigned long)mod_word,
+                           (unsigned long)bwt->mod_word);
+                       failed = 1;
+                       continue;
+               }
+
+               failed |= check_bn_word_test(op_name, bn, bwt);
+       }
+
+       BN_free(bn);
+
+       return failed;
+}
+
+int
+main(int argc, char **argv)
+{
+       int failed = 0;
+
+       failed |= test_bn_add_word();
+       failed |= test_bn_sub_word();
+       failed |= test_bn_mul_word();
+       failed |= test_bn_div_word();
+       failed |= test_bn_mod_word();
+
+       return failed;
+}