From 32c67bd12691f00889df32fec839471a5b78c2df Mon Sep 17 00:00:00 2001 From: tb Date: Wed, 21 Apr 2021 20:15:08 +0000 Subject: [PATCH] Add a test that roundtrips a bunch of points on all builtin curves 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 | 25 +- .../lib/libcrypto/ec/ec_point_conversion.c | 249 ++++++++++++++++++ 2 files changed, 268 insertions(+), 6 deletions(-) create mode 100644 regress/lib/libcrypto/ec/ec_point_conversion.c diff --git a/regress/lib/libcrypto/ec/Makefile b/regress/lib/libcrypto/ec/Makefile index 76b552fa231..394ac51226f 100644 --- a/regress/lib/libcrypto/ec/Makefile +++ b/regress/lib/libcrypto/ec/Makefile @@ -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 diff --git a/regress/lib/libcrypto/ec/ec_point_conversion.c b/regress/lib/libcrypto/ec/ec_point_conversion.c new file mode 100644 index 00000000000..97ba70034de --- /dev/null +++ b/regress/lib/libcrypto/ec/ec_point_conversion.c @@ -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 + * Copyright (c) 2021 Joel Sing + * + * 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 +#include +#include + +#include +#include + +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; +} -- 2.20.1