xref: /openbsd-src/regress/lib/libcrypto/ec/ec_point_conversion.c (revision ee657da96042b93a982179ba98da54c4a537cfbf)
1*ee657da9Stb /*	$OpenBSD: ec_point_conversion.c,v 1.17 2024/10/23 15:06:46 tb Exp $ */
232c67bd1Stb /*
332c67bd1Stb  * Copyright (c) 2021 Theo Buehler <tb@openbsd.org>
432c67bd1Stb  * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
532c67bd1Stb  *
632c67bd1Stb  * Permission to use, copy, modify, and distribute this software for any
732c67bd1Stb  * purpose with or without fee is hereby granted, provided that the above
832c67bd1Stb  * copyright notice and this permission notice appear in all copies.
932c67bd1Stb  *
1032c67bd1Stb  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132c67bd1Stb  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232c67bd1Stb  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332c67bd1Stb  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432c67bd1Stb  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532c67bd1Stb  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632c67bd1Stb  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732c67bd1Stb  */
1832c67bd1Stb 
1932c67bd1Stb #include <err.h>
2032c67bd1Stb #include <stdio.h>
2132c67bd1Stb #include <stdlib.h>
22850a9c24Stb #include <string.h>
2332c67bd1Stb 
247cba3b5bStb #include <openssl/bn.h>
2532c67bd1Stb #include <openssl/ec.h>
2632c67bd1Stb #include <openssl/objects.h>
2732c67bd1Stb 
2832c67bd1Stb int forms[] = {
2932c67bd1Stb 	POINT_CONVERSION_COMPRESSED,
3032c67bd1Stb 	POINT_CONVERSION_UNCOMPRESSED,
3132c67bd1Stb 	POINT_CONVERSION_HYBRID,
3232c67bd1Stb };
3332c67bd1Stb 
3432c67bd1Stb static const size_t N_FORMS = sizeof(forms) / sizeof(forms[0]);
3532c67bd1Stb #define N_RANDOM_POINTS 10
3632c67bd1Stb 
3732c67bd1Stb static const char *
3832c67bd1Stb form2str(int form)
3932c67bd1Stb {
4032c67bd1Stb 	switch (form) {
4132c67bd1Stb 	case POINT_CONVERSION_COMPRESSED:
4232c67bd1Stb 		return "compressed form";
4332c67bd1Stb 	case POINT_CONVERSION_UNCOMPRESSED:
4432c67bd1Stb 		return "uncompressed form";
4532c67bd1Stb 	case POINT_CONVERSION_HYBRID:
4632c67bd1Stb 		return "hybrid form";
4732c67bd1Stb 	default:
4832c67bd1Stb 		return "unknown form";
4932c67bd1Stb 	}
5032c67bd1Stb }
5132c67bd1Stb 
5232c67bd1Stb static void
5332c67bd1Stb hexdump(const unsigned char *buf, size_t len)
5432c67bd1Stb {
5532c67bd1Stb 	size_t i;
5632c67bd1Stb 
5732c67bd1Stb 	for (i = 1; i <= len; i++)
5832c67bd1Stb 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
5932c67bd1Stb 	if (len % 8)
6032c67bd1Stb 		fprintf(stderr, "\n");
6132c67bd1Stb }
6232c67bd1Stb 
6332c67bd1Stb static int
6432c67bd1Stb roundtrip(EC_GROUP *group, EC_POINT *point, int form, BIGNUM *x, BIGNUM *y)
6532c67bd1Stb {
6632c67bd1Stb 	BIGNUM *x_out = NULL, *y_out = NULL;
6732c67bd1Stb 	size_t len;
6832c67bd1Stb 	uint8_t *buf = NULL;
6932c67bd1Stb 	int failed = 1;
7032c67bd1Stb 
7132c67bd1Stb 	if ((len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
7232c67bd1Stb 		errx(1, "point2oct");
7332c67bd1Stb 	if ((buf = malloc(len)) == NULL)
7432c67bd1Stb 		errx(1, "malloc");
7532c67bd1Stb 	if (EC_POINT_point2oct(group, point, form, buf, len, NULL) != len)
7632c67bd1Stb 		errx(1, "point2oct");
7732c67bd1Stb 
7832c67bd1Stb 	if (!EC_POINT_oct2point(group, point, buf, len, NULL))
7932c67bd1Stb 		errx(1, "%s oct2point", form2str(form));
8032c67bd1Stb 
8132c67bd1Stb 	if ((x_out = BN_new()) == NULL)
8232c67bd1Stb 		errx(1, "new x_out");
8332c67bd1Stb 	if ((y_out = BN_new()) == NULL)
8432c67bd1Stb 		errx(1, "new y_out");
8532c67bd1Stb 
8632c67bd1Stb 	if (!EC_POINT_get_affine_coordinates(group, point, x_out, y_out, NULL))
8732c67bd1Stb 		errx(1, "get affine");
8832c67bd1Stb 
8932c67bd1Stb 	if (BN_cmp(x, x_out) != 0) {
9032c67bd1Stb 		warnx("%s: x", form2str(form));
9132c67bd1Stb 		goto err;
9232c67bd1Stb 	}
9332c67bd1Stb 	if (BN_cmp(y, y_out) != 0) {
9432c67bd1Stb 		warnx("%s: y", form2str(form));
9532c67bd1Stb 		goto err;
9632c67bd1Stb 	}
9732c67bd1Stb 
9832c67bd1Stb 	failed = 0;
9932c67bd1Stb 
10032c67bd1Stb  err:
10132c67bd1Stb 	if (failed)
10232c67bd1Stb 		hexdump(buf, len);
10332c67bd1Stb 
10432c67bd1Stb 	free(buf);
10532c67bd1Stb 	BN_free(x_out);
10632c67bd1Stb 	BN_free(y_out);
10732c67bd1Stb 
10832c67bd1Stb 	return failed;
10932c67bd1Stb }
11032c67bd1Stb 
11132c67bd1Stb /* XXX This only tests multiples of the generator for now... */
11232c67bd1Stb static int
11332c67bd1Stb test_random_points_on_curve(EC_builtin_curve *curve)
11432c67bd1Stb {
11532c67bd1Stb 	EC_GROUP *group;
11632c67bd1Stb 	BIGNUM *order = NULL;
11732c67bd1Stb 	BIGNUM *random;
11832c67bd1Stb 	BIGNUM *x, *y;
11932c67bd1Stb 	size_t i, j;
12032c67bd1Stb 	int failed = 0;
12132c67bd1Stb 
12232c67bd1Stb 	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
123c9d1fabeStb 		errx(1, "EC_GROUP_new_by_curve_name(%s)",
124c9d1fabeStb 		    OBJ_nid2sn(curve->nid));
12532c67bd1Stb 
12632c67bd1Stb 	if ((order = BN_new()) == NULL)
12732c67bd1Stb 		errx(1, "BN_new order");
12832c67bd1Stb 	if ((random = BN_new()) == NULL)
12932c67bd1Stb 		errx(1, "BN_new random");
13032c67bd1Stb 	if ((x = BN_new()) == NULL)
13132c67bd1Stb 		errx(1, "BN_new x");
13232c67bd1Stb 	if ((y = BN_new()) == NULL)
13332c67bd1Stb 		errx(1, "BN_new y");
13432c67bd1Stb 
13532c67bd1Stb 	if (!EC_GROUP_get_order(group, order, NULL))
13632c67bd1Stb 		errx(1, "EC_group_get_order");
13732c67bd1Stb 
13832c67bd1Stb 	for (i = 0; i < N_RANDOM_POINTS; i++) {
13932c67bd1Stb 		EC_POINT *random_point;
14032c67bd1Stb 
1414bec30b6Stb 		do {
1424bec30b6Stb 			if (!BN_rand_range(random, order))
1434bec30b6Stb 				errx(1, "BN_rand_range");
1444bec30b6Stb 		} while (BN_is_zero(random));
14532c67bd1Stb 
14632c67bd1Stb 		if ((random_point = EC_POINT_new(group)) == NULL)
14732c67bd1Stb 			errx(1, "EC_POINT_new");
14832c67bd1Stb 
14932c67bd1Stb 		if (!EC_POINT_mul(group, random_point, random, NULL, NULL, NULL))
15032c67bd1Stb 			errx(1, "EC_POINT_mul");
15132c67bd1Stb 
15232c67bd1Stb 		if (EC_POINT_is_at_infinity(group, random_point)) {
15332c67bd1Stb 			EC_POINT_free(random_point);
15432c67bd1Stb 
15532c67bd1Stb 			warnx("info: got infinity");
15632c67bd1Stb 			fprintf(stderr, "random = ");
15732c67bd1Stb 			BN_print_fp(stderr, random);
15832c67bd1Stb 			fprintf(stderr, "\n");
15932c67bd1Stb 
16032c67bd1Stb 			continue;
16132c67bd1Stb 		}
16232c67bd1Stb 
16332c67bd1Stb 		if (!EC_POINT_get_affine_coordinates(group, random_point,
16432c67bd1Stb 		    x, y, NULL))
16532c67bd1Stb 			errx(1, "EC_POINT_get_affine_coordinates");
16632c67bd1Stb 
16732c67bd1Stb 		for (j = 0; j < N_FORMS; j++)
16832c67bd1Stb 			failed |= roundtrip(group, random_point, forms[j], x, y);
16932c67bd1Stb 
17032c67bd1Stb 		EC_POINT_free(random_point);
17132c67bd1Stb 	}
17232c67bd1Stb 
17332c67bd1Stb 	BN_free(order);
17432c67bd1Stb 	BN_free(random);
17532c67bd1Stb 	BN_free(x);
17632c67bd1Stb 	BN_free(y);
17732c67bd1Stb 	EC_GROUP_free(group);
17832c67bd1Stb 
17932c67bd1Stb 	return failed;
18032c67bd1Stb }
18132c67bd1Stb 
18232c67bd1Stb static int
18332c67bd1Stb test_random_points(void)
18432c67bd1Stb {
18532c67bd1Stb 	EC_builtin_curve *all_curves = NULL;
18632c67bd1Stb 	size_t ncurves = 0;
18732c67bd1Stb 	size_t curve_id;
18832c67bd1Stb 	int failed = 0;
18932c67bd1Stb 
19032c67bd1Stb 	ncurves = EC_get_builtin_curves(NULL, 0);
19132c67bd1Stb 	if ((all_curves = calloc(ncurves, sizeof(EC_builtin_curve))) == NULL)
19232c67bd1Stb 		err(1, "calloc builtin curves");
19332c67bd1Stb 	EC_get_builtin_curves(all_curves, ncurves);
19432c67bd1Stb 
19532c67bd1Stb 	for (curve_id = 0; curve_id < ncurves; curve_id++)
19678a67f35Stb 		failed |= test_random_points_on_curve(&all_curves[curve_id]);
19732c67bd1Stb 
198f49eea4eStb 	fprintf(stderr, "%s %s\n", __func__, failed ? ": FAILED" : "");
19932c67bd1Stb 
20032c67bd1Stb 	free(all_curves);
20132c67bd1Stb 	return failed;
20232c67bd1Stb }
20332c67bd1Stb 
204ac1153bcStb static const struct point_conversion {
205ac1153bcStb 	const char *description;
206ac1153bcStb 	int nid;
207ac1153bcStb 	uint8_t octets[256];
208850a9c24Stb 	size_t octets_len;
209ac1153bcStb 	int valid;
210850a9c24Stb 	int point_at_infinity;
211ac1153bcStb } point_conversions[] = {
212fa4ae9b2Stb 	/* XXX - now that sect571 is no longer tested, add another test? */
213b04110faStb 	{
214b04110faStb 		.description = "point at infinity on secp256r1",
215b04110faStb 		.nid = NID_X9_62_prime256v1,
216b04110faStb 		.octets = { 0x00 },
217b04110faStb 		.octets_len = 1,
218b04110faStb 		.valid = 1,
219850a9c24Stb 		.point_at_infinity = 1,
220b04110faStb 	},
221b04110faStb 	{
222b04110faStb 		.description = "point at infinity on secp256r1 (flipped y_bit)",
223b04110faStb 		.nid = NID_X9_62_prime256v1,
224b04110faStb 		.octets = { 0x01 },
225b04110faStb 		.octets_len = 1,
226b04110faStb 		.valid = 0,
227850a9c24Stb 		.point_at_infinity = 1,
228b04110faStb 	},
229b04110faStb 	{
230b04110faStb 		.description = "zero x compressed point on secp256r1",
231b04110faStb 		.nid = NID_X9_62_prime256v1,
232b04110faStb 		.octets = {
233b04110faStb 			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237b04110faStb 			0x00,
238b04110faStb 		},
239b04110faStb 		.octets_len = 33,
240b04110faStb 		.valid = 1,
241b04110faStb 	},
242b04110faStb 	{
243b04110faStb 		.description =
244b04110faStb 		    "zero x compressed point on secp256r1 (flipped y_bit)",
245b04110faStb 		.nid = NID_X9_62_prime256v1,
246b04110faStb 		.octets = {
247b04110faStb 			0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251b04110faStb 			0x00,
252b04110faStb 		},
253b04110faStb 		.octets_len = 33,
254b04110faStb 		.valid = 1,
255b04110faStb 	},
256b04110faStb 	{
257b04110faStb 		.description = "generic compressed point on secp256r1",
258b04110faStb 		.nid = NID_X9_62_prime256v1,
259b04110faStb 		.octets = {
260b04110faStb 			0x03, 0xa3, 0x96, 0xa0, 0x42, 0x73, 0x1a, 0x8b,
261b04110faStb 			0x90, 0xd8, 0xcb, 0xae, 0xda, 0x1b, 0x23, 0x11,
262b04110faStb 			0x77, 0x5f, 0x6a, 0x4c, 0xb4, 0x57, 0xbf, 0xe0,
263b04110faStb 			0x65, 0xd4, 0x09, 0x11, 0x5f, 0x54, 0xe4, 0xee,
264b04110faStb 			0xdd,
265b04110faStb 		},
266b04110faStb 		.octets_len = 33,
267b04110faStb 		.valid = 1,
268b04110faStb 	},
269b04110faStb 	{
270b04110faStb 		.description =
271b04110faStb 		    "generic compressed point on secp256r1 (flipped y_bit)",
272b04110faStb 		.nid = NID_X9_62_prime256v1,
273b04110faStb 		.octets = {
274b04110faStb 			0x02, 0xa3, 0x96, 0xa0, 0x42, 0x73, 0x1a, 0x8b,
275b04110faStb 			0x90, 0xd8, 0xcb, 0xae, 0xda, 0x1b, 0x23, 0x11,
276b04110faStb 			0x77, 0x5f, 0x6a, 0x4c, 0xb4, 0x57, 0xbf, 0xe0,
277b04110faStb 			0x65, 0xd4, 0x09, 0x11, 0x5f, 0x54, 0xe4, 0xee,
278b04110faStb 			0xdd,
279b04110faStb 		},
280b04110faStb 		.octets_len = 33,
281b04110faStb 		.valid = 1,
282b04110faStb 	},
283b04110faStb 	{
284b04110faStb 		.description = "zero x uncompressed point #1 on secp256r1",
285b04110faStb 		.nid = NID_X9_62_prime256v1,
286b04110faStb 		.octets = {
287b04110faStb 			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
289b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291b04110faStb 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
292b04110faStb 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
293b04110faStb 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
294b04110faStb 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
295b04110faStb 			0xf4,
296b04110faStb 		},
297b04110faStb 		.octets_len = 65,
298b04110faStb 		.valid = 1,
299b04110faStb 	},
300b04110faStb 	{
301b04110faStb 		.description =
302b04110faStb 		    "zero x uncompressed point #1 on secp256r1 (flipped y_bit)",
303b04110faStb 		.nid = NID_X9_62_prime256v1,
304b04110faStb 		.octets = {
305b04110faStb 			0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309b04110faStb 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
310b04110faStb 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
311b04110faStb 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
312b04110faStb 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
313b04110faStb 			0xf4,
314b04110faStb 		},
315b04110faStb 		.octets_len = 65,
316b04110faStb 		.valid = 0,
317b04110faStb 	},
318b04110faStb 	{
319b04110faStb 		.description = "zero x uncompressed point #2 on secp256r1",
320b04110faStb 		.nid = NID_X9_62_prime256v1,
321b04110faStb 		.octets = {
322b04110faStb 			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326b04110faStb 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
327b04110faStb 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
328b04110faStb 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
329b04110faStb 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
330b04110faStb 			0x0b,
331b04110faStb 		},
332b04110faStb 		.octets_len = 65,
333b04110faStb 		.valid = 1,
334b04110faStb 	},
335b04110faStb 	{
336b04110faStb 		.description =
337b04110faStb 		    "zero x uncompressed point #2 on secp256r1 (flipped y_bit)",
338b04110faStb 		.nid = NID_X9_62_prime256v1,
339b04110faStb 		.octets = {
340b04110faStb 			0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344b04110faStb 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
345b04110faStb 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
346b04110faStb 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
347b04110faStb 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
348b04110faStb 			0x0b,
349b04110faStb 		},
350b04110faStb 		.octets_len = 65,
351b04110faStb 		.valid = 0,
352b04110faStb 	},
353b04110faStb 	{
354b04110faStb 		.description = "generic uncompressed point on secp256r1",
355b04110faStb 		.nid = NID_X9_62_prime256v1,
356b04110faStb 		.octets = {
357b04110faStb 			0x04, 0x23, 0xe5, 0x85, 0xa5, 0x4b, 0xda, 0x34,
358b04110faStb 			0x7e, 0xe5, 0x65, 0x53, 0x7f, 0x3b, 0xce, 0xe4,
359b04110faStb 			0x54, 0xd8, 0xa4, 0x5a, 0x53, 0x4b, 0xb0, 0x4c,
360b04110faStb 			0xb9, 0x31, 0x09, 0x29, 0xa2, 0x03, 0x4c, 0x73,
361b04110faStb 			0x20, 0xd2, 0xc6, 0x17, 0xca, 0xe3, 0xcf, 0xc2,
362b04110faStb 			0xd8, 0x31, 0xfe, 0xf1, 0x7c, 0x6f, 0x9d, 0x7a,
363b04110faStb 			0x01, 0x7c, 0x34, 0x65, 0x42, 0x05, 0xaf, 0xcc,
364b04110faStb 			0x04, 0xa3, 0x2f, 0x44, 0x14, 0xbe, 0xd8, 0xc2,
365b04110faStb 			0x03,
366b04110faStb 		},
367b04110faStb 		.octets_len = 65,
368b04110faStb 		.valid = 1,
369b04110faStb 	},
370b04110faStb 	{
371b04110faStb 		.description =
372b04110faStb 		    "generic uncompressed point on secp256r1 (flipped y_bit)",
373b04110faStb 		.nid = NID_X9_62_prime256v1,
374b04110faStb 		.octets = {
375b04110faStb 			0x05, 0x23, 0xe5, 0x85, 0xa5, 0x4b, 0xda, 0x34,
376b04110faStb 			0x7e, 0xe5, 0x65, 0x53, 0x7f, 0x3b, 0xce, 0xe4,
377b04110faStb 			0x54, 0xd8, 0xa4, 0x5a, 0x53, 0x4b, 0xb0, 0x4c,
378b04110faStb 			0xb9, 0x31, 0x09, 0x29, 0xa2, 0x03, 0x4c, 0x73,
379b04110faStb 			0x20, 0xd2, 0xc6, 0x17, 0xca, 0xe3, 0xcf, 0xc2,
380b04110faStb 			0xd8, 0x31, 0xfe, 0xf1, 0x7c, 0x6f, 0x9d, 0x7a,
381b04110faStb 			0x01, 0x7c, 0x34, 0x65, 0x42, 0x05, 0xaf, 0xcc,
382b04110faStb 			0x04, 0xa3, 0x2f, 0x44, 0x14, 0xbe, 0xd8, 0xc2,
383b04110faStb 			0x03,
384b04110faStb 		},
385b04110faStb 		.octets_len = 65,
386b04110faStb 		.valid = 0,
387b04110faStb 	},
388b04110faStb 	{
389b04110faStb 		.description = "zero x hybrid point #1 on secp256r1",
390b04110faStb 		.nid = NID_X9_62_prime256v1,
391b04110faStb 		.octets = {
392b04110faStb 			0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
394b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396b04110faStb 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
397b04110faStb 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
398b04110faStb 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
399b04110faStb 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
400b04110faStb 			0xf4,
401b04110faStb 		},
402b04110faStb 		.octets_len = 65,
403b04110faStb 		.valid = 1,
404b04110faStb 	},
405b04110faStb 	{
406b04110faStb 		.description =
407b04110faStb 		    "zero x hybrid point #1 on secp256r1 (flipped y_bit)",
408b04110faStb 		.nid = NID_X9_62_prime256v1,
409b04110faStb 		.octets = {
410b04110faStb 			0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
411b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414b04110faStb 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
415b04110faStb 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
416b04110faStb 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
417b04110faStb 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
418b04110faStb 			0xf4,
419b04110faStb 		},
420b04110faStb 		.octets_len = 65,
421b04110faStb 		.valid = 0,
422b04110faStb 	},
423b04110faStb 	{
424b04110faStb 		.description = "zero x hybrid point #2 on secp256r1",
425b04110faStb 		.nid = NID_X9_62_prime256v1,
426b04110faStb 		.octets = {
427b04110faStb 			0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431b04110faStb 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
432b04110faStb 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
433b04110faStb 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
434b04110faStb 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
435b04110faStb 			0x0b,
436b04110faStb 		},
437b04110faStb 		.octets_len = 65,
438b04110faStb 		.valid = 1,
439b04110faStb 	},
440b04110faStb 	{
441b04110faStb 		.description =
442b04110faStb 		    "zero x hybrid point #2 on secp256r1 (flipped y_bit)",
443b04110faStb 		.nid = NID_X9_62_prime256v1,
444b04110faStb 		.octets = {
445b04110faStb 			0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
447b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448b04110faStb 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
449b04110faStb 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
450b04110faStb 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
451b04110faStb 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
452b04110faStb 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
453b04110faStb 			0x0b,
454b04110faStb 		},
455b04110faStb 		.octets_len = 65,
456b04110faStb 		.valid = 0,
457b04110faStb 	},
458b04110faStb 	{
459b04110faStb 		.description = "generic hybrid point on secp256r1",
460b04110faStb 		.nid = NID_X9_62_prime256v1,
461b04110faStb 		.octets = {
462b04110faStb 			0x07, 0x38, 0xb2, 0x98, 0x38, 0x21, 0x6b, 0xec,
463b04110faStb 			0x87, 0xcf, 0x50, 0xbb, 0x65, 0x11, 0x96, 0x63,
464b04110faStb 			0xf3, 0x90, 0x64, 0xc3, 0x5c, 0x59, 0xa5, 0x6f,
465b04110faStb 			0xaf, 0x56, 0x2a, 0x0c, 0xc0, 0x3a, 0x9b, 0x92,
466b04110faStb 			0x85, 0x95, 0x54, 0xf3, 0x08, 0x0f, 0x78, 0x59,
467b04110faStb 			0xa2, 0x44, 0x2f, 0x19, 0x5d, 0xd5, 0xcd, 0xf6,
468b04110faStb 			0xa5, 0xbe, 0x2f, 0x83, 0x70, 0x94, 0xf5, 0xcd,
469b04110faStb 			0x8c, 0x40, 0x7f, 0xd8, 0x97, 0x92, 0x14, 0xf7,
470b04110faStb 			0xc5,
471b04110faStb 		},
472b04110faStb 		.octets_len = 65,
473b04110faStb 		.valid = 1,
474b04110faStb 	},
475b04110faStb 	{
4767b1db04cStb 		.description =
4777b1db04cStb 		    "generic hybrid point on secp256r1 (flipped y_bit)",
478b04110faStb 		.nid = NID_X9_62_prime256v1,
479b04110faStb 		.octets = {
480b04110faStb 			0x06, 0x38, 0xb2, 0x98, 0x38, 0x21, 0x6b, 0xec,
481b04110faStb 			0x87, 0xcf, 0x50, 0xbb, 0x65, 0x11, 0x96, 0x63,
482b04110faStb 			0xf3, 0x90, 0x64, 0xc3, 0x5c, 0x59, 0xa5, 0x6f,
483b04110faStb 			0xaf, 0x56, 0x2a, 0x0c, 0xc0, 0x3a, 0x9b, 0x92,
484b04110faStb 			0x85, 0x95, 0x54, 0xf3, 0x08, 0x0f, 0x78, 0x59,
485b04110faStb 			0xa2, 0x44, 0x2f, 0x19, 0x5d, 0xd5, 0xcd, 0xf6,
486b04110faStb 			0xa5, 0xbe, 0x2f, 0x83, 0x70, 0x94, 0xf5, 0xcd,
487b04110faStb 			0x8c, 0x40, 0x7f, 0xd8, 0x97, 0x92, 0x14, 0xf7,
488b04110faStb 			0xc5,
489b04110faStb 		},
490b04110faStb 		.octets_len = 65,
491b04110faStb 		.valid = 0,
492b04110faStb 	},
493ac1153bcStb };
494ac1153bcStb 
495ac1153bcStb static const size_t N_POINT_CONVERSIONS =
496ac1153bcStb     sizeof(point_conversions) / sizeof(point_conversions[0]);
497ac1153bcStb 
498ac1153bcStb static int
499850a9c24Stb check_point_at_infinity(const EC_GROUP *group, const EC_POINT *point,
500850a9c24Stb     const struct point_conversion *test)
501850a9c24Stb {
502850a9c24Stb 	const uint8_t conversion_forms[4] = { 0x00, 0x02, 0x04, 0x06, };
503850a9c24Stb 	uint8_t buf[1];
504850a9c24Stb 	uint8_t form;
505850a9c24Stb 	size_t i, ret;
506850a9c24Stb 	int failed = 0;
507850a9c24Stb 
508850a9c24Stb 	/* The form for the point at infinity is expected to fail. */
509850a9c24Stb 	form = conversion_forms[0];
510850a9c24Stb 
511850a9c24Stb 	ret = EC_POINT_point2oct(group, point, form, buf, sizeof(buf), NULL);
512850a9c24Stb 	if (ret != 0) {
513850a9c24Stb 		fprintf(stderr, "FAIL: %s: expected encoding with form 0x%02x "
514850a9c24Stb 		    "to fail, got %zu\n", test->description, form, ret);
515850a9c24Stb 		failed |= 1;
516850a9c24Stb 	}
517850a9c24Stb 
518850a9c24Stb 	/* For all other forms we expect the zero octet. */
519850a9c24Stb 	for (i = 1; i < sizeof(conversion_forms); i++) {
520850a9c24Stb 		form = conversion_forms[i];
521850a9c24Stb 
522850a9c24Stb 		ret = EC_POINT_point2oct(group, point, form, buf, sizeof(buf), NULL);
523850a9c24Stb 		if (ret != 1) {
524850a9c24Stb 			fprintf(stderr, "FAIL: %s: expected success, got %zu\n",
525850a9c24Stb 			    test->description, ret);
526850a9c24Stb 			failed |= 1;
527850a9c24Stb 			continue;
528850a9c24Stb 		}
529850a9c24Stb 
530850a9c24Stb 		if (memcmp(buf, test->octets, test->octets_len) != 0) {
531850a9c24Stb 			fprintf(stderr, "FAIL: %s: want 0x%02x, got 0x%02x\n",
532850a9c24Stb 			    test->description, test->octets[0], buf[0]);
533850a9c24Stb 			failed |= 1;
534850a9c24Stb 			continue;
535850a9c24Stb 		}
536850a9c24Stb 	}
537850a9c24Stb 
538850a9c24Stb 	return failed;
539850a9c24Stb }
540850a9c24Stb 
541850a9c24Stb static int
542ac1153bcStb point_conversion_form_y_bit(const struct point_conversion *test)
543ac1153bcStb {
544ac1153bcStb 	EC_GROUP *group = NULL;
545ac1153bcStb 	EC_POINT *point = NULL;
546ac1153bcStb 	int ret;
547ac1153bcStb 	int failed = 0;
548ac1153bcStb 
549ac1153bcStb 	if ((group = EC_GROUP_new_by_curve_name(test->nid)) == NULL)
550ac1153bcStb 		errx(1, "group");
551ac1153bcStb 	if ((point = EC_POINT_new(group)) == NULL)
552ac1153bcStb 		errx(1, "point");
553ac1153bcStb 
554ac1153bcStb 	ret = EC_POINT_oct2point(group, point, test->octets, test->octets_len,
555ac1153bcStb 	    NULL);
556ac1153bcStb 	if (ret != test->valid) {
557ac1153bcStb 		fprintf(stderr, "%s want %d got %d\n", test->description,
558ac1153bcStb 		    test->valid, ret);
559ac1153bcStb 		failed |= 1;
560ac1153bcStb 	}
561ac1153bcStb 
562*ee657da9Stb 	if (test->valid && test->point_at_infinity) {
563850a9c24Stb 		failed |= check_point_at_infinity(group, point, test);
564*ee657da9Stb 	} else if (test->valid) {
565850a9c24Stb 		uint8_t buf[256];
566850a9c24Stb 		uint8_t form = test->octets[0] & 0x06;
567850a9c24Stb 		size_t len;
568850a9c24Stb 
569850a9c24Stb 		len = EC_POINT_point2oct(group, point, form, buf, sizeof(buf), NULL);
570850a9c24Stb 
571850a9c24Stb 		if (len != test->octets_len) {
572850a9c24Stb 			fprintf(stderr, "%s: EC_POINT_point2oct: want %zu, got %zu\n",
573850a9c24Stb 			    test->description, test->octets_len, len);
574850a9c24Stb 			failed |= 1;
575850a9c24Stb 			goto failed;
576850a9c24Stb 		}
577850a9c24Stb 		if (memcmp(test->octets, buf, len) != 0) {
578850a9c24Stb 			fprintf(stderr, "%s: unexpected encoding\nwant:\n",
579850a9c24Stb 			    test->description);
580850a9c24Stb 			hexdump(test->octets, test->octets_len);
581850a9c24Stb 			fprintf(stderr, "\ngot:\n");
582850a9c24Stb 			hexdump(buf, len);
583850a9c24Stb 			failed |= 1;
584850a9c24Stb 			goto failed;
585850a9c24Stb 		}
586850a9c24Stb 	}
587850a9c24Stb 
588850a9c24Stb  failed:
589ac1153bcStb 	EC_GROUP_free(group);
590ac1153bcStb 	EC_POINT_free(point);
591ac1153bcStb 
592ac1153bcStb 	return failed;
593ac1153bcStb }
594ac1153bcStb 
595ac1153bcStb static int
596ac1153bcStb test_point_conversions(void)
597ac1153bcStb {
598ac1153bcStb 	size_t i;
599ac1153bcStb 	int failed = 0;
600ac1153bcStb 
601ac1153bcStb 	for (i = 0; i < N_POINT_CONVERSIONS; i++)
602ac1153bcStb 		failed |= point_conversion_form_y_bit(&point_conversions[i]);
603ac1153bcStb 
604f49eea4eStb 	fprintf(stderr, "%s %s\n", __func__, failed ? ": FAILED" : "");
605ac1153bcStb 
606ac1153bcStb 	return failed;
607ac1153bcStb }
608ac1153bcStb 
60932c67bd1Stb int
61032c67bd1Stb main(int argc, char **argv)
61132c67bd1Stb {
61232c67bd1Stb 	int failed = 0;
61332c67bd1Stb 
61432c67bd1Stb 	failed |= test_random_points();
615ac1153bcStb 	failed |= test_point_conversions();
61632c67bd1Stb 
61732c67bd1Stb 	return failed;
61832c67bd1Stb }
619