Add a test that roundtrips a bunch of points on all builtin curves
authortb <tb@openbsd.org>
Wed, 21 Apr 2021 20:15:08 +0000 (20:15 +0000)
committertb <tb@openbsd.org>
Wed, 21 Apr 2021 20:15:08 +0000 (20:15 +0000)
via point2oct and oct2point and that checks the corner case in hybrid
encoding that was fixed in ec2_oct.c r1.13.

regress/lib/libcrypto/ec/Makefile
regress/lib/libcrypto/ec/ec_point_conversion.c [new file with mode: 0644]

index 76b552f..394ac51 100644 (file)
@@ -1,9 +1,22 @@
-#      $OpenBSD: Makefile,v 1.4 2021/04/20 17:09:45 tb Exp $
+#      $OpenBSD: Makefile,v 1.5 2021/04/21 20:15:08 tb Exp $
 
-PROG=  ectest
-LDADD= ${CRYPTO_INT}
-DPADD= ${LIBCRYPTO}
-WARNINGS=      Yes
-CFLAGS+=       -DLIBRESSL_INTERNAL -Werror
+PROGS +=               ectest
+PROGS +=               ec_point_conversion
+
+.for t in ${PROGS}
+REGRESS_TARGETS +=     run-$t
+.endfor
+
+LDADD =                        ${CRYPTO_INT}
+DPADD =                        ${LIBCRYPTO}
+WARNINGS =             Yes
+CFLAGS +=              -DLIBRESSL_INTERNAL -Wall -Wundef -Werror
+
+CLEANFILES +=          ${PROGS}
+
+.for t in ${PROGS}
+run-$t: $t
+       ./$t
+.endfor
 
 .include <bsd.regress.mk>
diff --git a/regress/lib/libcrypto/ec/ec_point_conversion.c b/regress/lib/libcrypto/ec/ec_point_conversion.c
new file mode 100644 (file)
index 0000000..97ba700
--- /dev/null
@@ -0,0 +1,249 @@
+/*     $OpenBSD: ec_point_conversion.c,v 1.1 2021/04/21 20:15:08 tb Exp $ */
+/*
+ * Copyright (c) 2021 Theo Buehler <tb@openbsd.org>
+ * Copyright (c) 2021 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 <stdio.h>
+#include <stdlib.h>
+
+#include <openssl/ec.h>
+#include <openssl/objects.h>
+
+int bn_rand_interval(BIGNUM *, const BIGNUM *, const BIGNUM *);
+
+int forms[] = {
+       POINT_CONVERSION_COMPRESSED,
+       POINT_CONVERSION_UNCOMPRESSED,
+       POINT_CONVERSION_HYBRID,
+};
+
+static const size_t N_FORMS = sizeof(forms) / sizeof(forms[0]);
+#define N_RANDOM_POINTS 10
+
+static const char *
+form2str(int form)
+{
+       switch (form) {
+       case POINT_CONVERSION_COMPRESSED:
+               return "compressed form";
+       case POINT_CONVERSION_UNCOMPRESSED:
+               return "uncompressed form";
+       case POINT_CONVERSION_HYBRID:
+               return "hybrid form";
+       default:
+               return "unknown form";
+       }
+}
+
+static void
+hexdump(const unsigned char *buf, size_t len)
+{
+       size_t i;
+
+       for (i = 1; i <= len; i++)
+               fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
+
+       if (len % 8)
+               fprintf(stderr, "\n");
+}
+
+static int
+roundtrip(EC_GROUP *group, EC_POINT *point, int form, BIGNUM *x, BIGNUM *y)
+{
+       BIGNUM *x_out = NULL, *y_out = NULL;
+       size_t len;
+       uint8_t *buf = NULL;
+       int failed = 1;
+
+       if ((len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
+               errx(1, "point2oct");
+       if ((buf = malloc(len)) == NULL)
+               errx(1, "malloc");
+       if (EC_POINT_point2oct(group, point, form, buf, len, NULL) != len)
+               errx(1, "point2oct");
+
+       if (!EC_POINT_oct2point(group, point, buf, len, NULL))
+               errx(1, "%s oct2point", form2str(form));
+
+       if ((x_out = BN_new()) == NULL)
+               errx(1, "new x_out");
+       if ((y_out = BN_new()) == NULL)
+               errx(1, "new y_out");
+
+       if (!EC_POINT_get_affine_coordinates(group, point, x_out, y_out, NULL))
+               errx(1, "get affine");
+
+       if (BN_cmp(x, x_out) != 0) {
+               warnx("%s: x", form2str(form));
+               goto err;
+       }
+       if (BN_cmp(y, y_out) != 0) {
+               warnx("%s: y", form2str(form));
+               goto err;
+       }
+
+       failed = 0;
+
+ err:
+       if (failed)
+               hexdump(buf, len);
+
+       free(buf);
+       BN_free(x_out);
+       BN_free(y_out);
+
+       return failed;
+}
+
+static int
+hybrid_corner_case(void)
+{
+       BIGNUM *x = NULL, *y = NULL;
+       EC_GROUP *group;
+       EC_POINT *point;
+       size_t i;
+       int failed = 0;
+
+       if (!BN_hex2bn(&x, "0"))
+               errx(1, "BN_hex2bn x");
+       if (!BN_hex2bn(&y, "01"))
+               errx(1, "BN_hex2bn y");
+
+       if ((group = EC_GROUP_new_by_curve_name(NID_sect571k1)) == NULL)
+               errx(1, "group");
+       if ((point = EC_POINT_new(group)) == NULL)
+               errx(1, "point");
+
+       if (!EC_POINT_set_affine_coordinates(group, point, x, y, NULL))
+               errx(1, "set affine");
+
+       for (i = 0; i < N_FORMS; i++)
+               failed |= roundtrip(group, point, forms[i], x, y);
+
+       fprintf(stderr, "%s: %s\n", __func__, failed ? "FAILED" : "SUCCESS");
+
+       EC_GROUP_free(group);
+       EC_POINT_free(point);
+       BN_free(x);
+       BN_free(y);
+
+       return failed;
+}
+
+/* XXX This only tests multiples of the generator for now... */
+static int
+test_random_points_on_curve(EC_builtin_curve *curve)
+{
+       EC_GROUP *group;
+       BIGNUM *order = NULL;
+       BIGNUM *random;
+       BIGNUM *x, *y;
+       size_t i, j;
+       int failed = 0;
+
+       fprintf(stderr, "%s\n", OBJ_nid2sn(curve->nid));
+       if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
+               errx(1, "EC_GROUP_new_by_curve_name");
+
+       if ((order = BN_new()) == NULL)
+               errx(1, "BN_new order");
+       if ((random = BN_new()) == NULL)
+               errx(1, "BN_new random");
+       if ((x = BN_new()) == NULL)
+               errx(1, "BN_new x");
+       if ((y = BN_new()) == NULL)
+               errx(1, "BN_new y");
+
+       if (!EC_GROUP_get_order(group, order, NULL))
+               errx(1, "EC_group_get_order");
+
+       for (i = 0; i < N_RANDOM_POINTS; i++) {
+               EC_POINT *random_point;
+
+               if (!bn_rand_interval(random, BN_value_one(), order))
+                       errx(1, "bn_rand_interval");
+
+               if ((random_point = EC_POINT_new(group)) == NULL)
+                       errx(1, "EC_POINT_new");
+
+               if (!EC_POINT_mul(group, random_point, random, NULL, NULL, NULL))
+                       errx(1, "EC_POINT_mul");
+
+               if (EC_POINT_is_at_infinity(group, random_point)) {
+                       EC_POINT_free(random_point);
+
+                       warnx("info: got infinity");
+                       fprintf(stderr, "random = ");
+                       BN_print_fp(stderr, random);
+                       fprintf(stderr, "\n");
+
+                       continue;
+               }
+
+               if (!EC_POINT_get_affine_coordinates(group, random_point,
+                   x, y, NULL))
+                       errx(1, "EC_POINT_get_affine_coordinates");
+
+               for (j = 0; j < N_FORMS; j++)
+                       failed |= roundtrip(group, random_point, forms[j], x, y);
+
+               EC_POINT_free(random_point);
+       }
+
+       BN_free(order);
+       BN_free(random);
+       BN_free(x);
+       BN_free(y);
+       EC_GROUP_free(group);
+
+       return failed;
+}
+
+static int
+test_random_points(void)
+{
+       EC_builtin_curve *all_curves = NULL;
+       size_t ncurves = 0;
+       size_t curve_id;
+       int failed = 0;
+
+       ncurves = EC_get_builtin_curves(NULL, 0);
+       if ((all_curves = calloc(ncurves, sizeof(EC_builtin_curve))) == NULL)
+               err(1, "calloc builtin curves");
+       EC_get_builtin_curves(all_curves, ncurves);
+
+       for (curve_id = 0; curve_id < ncurves; curve_id++)
+               test_random_points_on_curve(&all_curves[curve_id]);
+
+       fprintf(stderr, "%s: %s\n", __func__, failed ? "FAILED" : "SUCCESS");
+
+       free(all_curves);
+       return failed;
+}
+
+int
+main(int argc, char **argv)
+{
+       int failed = 0;
+
+       failed |= test_random_points();
+       failed |= hybrid_corner_case();
+
+       fprintf(stderr, "%s\n", failed ? "FAILED" : "SUCCESS");
+
+       return failed;
+}