xref: /openbsd-src/regress/lib/libcrypto/ec/ec_point_conversion.c (revision ee657da96042b93a982179ba98da54c4a537cfbf)
1 /*	$OpenBSD: ec_point_conversion.c,v 1.17 2024/10/23 15:06:46 tb Exp $ */
2 /*
3  * Copyright (c) 2021 Theo Buehler <tb@openbsd.org>
4  * Copyright (c) 2021 Joel Sing <jsing@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <err.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include <openssl/bn.h>
25 #include <openssl/ec.h>
26 #include <openssl/objects.h>
27 
28 int forms[] = {
29 	POINT_CONVERSION_COMPRESSED,
30 	POINT_CONVERSION_UNCOMPRESSED,
31 	POINT_CONVERSION_HYBRID,
32 };
33 
34 static const size_t N_FORMS = sizeof(forms) / sizeof(forms[0]);
35 #define N_RANDOM_POINTS 10
36 
37 static const char *
38 form2str(int form)
39 {
40 	switch (form) {
41 	case POINT_CONVERSION_COMPRESSED:
42 		return "compressed form";
43 	case POINT_CONVERSION_UNCOMPRESSED:
44 		return "uncompressed form";
45 	case POINT_CONVERSION_HYBRID:
46 		return "hybrid form";
47 	default:
48 		return "unknown form";
49 	}
50 }
51 
52 static void
53 hexdump(const unsigned char *buf, size_t len)
54 {
55 	size_t i;
56 
57 	for (i = 1; i <= len; i++)
58 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
59 	if (len % 8)
60 		fprintf(stderr, "\n");
61 }
62 
63 static int
64 roundtrip(EC_GROUP *group, EC_POINT *point, int form, BIGNUM *x, BIGNUM *y)
65 {
66 	BIGNUM *x_out = NULL, *y_out = NULL;
67 	size_t len;
68 	uint8_t *buf = NULL;
69 	int failed = 1;
70 
71 	if ((len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
72 		errx(1, "point2oct");
73 	if ((buf = malloc(len)) == NULL)
74 		errx(1, "malloc");
75 	if (EC_POINT_point2oct(group, point, form, buf, len, NULL) != len)
76 		errx(1, "point2oct");
77 
78 	if (!EC_POINT_oct2point(group, point, buf, len, NULL))
79 		errx(1, "%s oct2point", form2str(form));
80 
81 	if ((x_out = BN_new()) == NULL)
82 		errx(1, "new x_out");
83 	if ((y_out = BN_new()) == NULL)
84 		errx(1, "new y_out");
85 
86 	if (!EC_POINT_get_affine_coordinates(group, point, x_out, y_out, NULL))
87 		errx(1, "get affine");
88 
89 	if (BN_cmp(x, x_out) != 0) {
90 		warnx("%s: x", form2str(form));
91 		goto err;
92 	}
93 	if (BN_cmp(y, y_out) != 0) {
94 		warnx("%s: y", form2str(form));
95 		goto err;
96 	}
97 
98 	failed = 0;
99 
100  err:
101 	if (failed)
102 		hexdump(buf, len);
103 
104 	free(buf);
105 	BN_free(x_out);
106 	BN_free(y_out);
107 
108 	return failed;
109 }
110 
111 /* XXX This only tests multiples of the generator for now... */
112 static int
113 test_random_points_on_curve(EC_builtin_curve *curve)
114 {
115 	EC_GROUP *group;
116 	BIGNUM *order = NULL;
117 	BIGNUM *random;
118 	BIGNUM *x, *y;
119 	size_t i, j;
120 	int failed = 0;
121 
122 	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
123 		errx(1, "EC_GROUP_new_by_curve_name(%s)",
124 		    OBJ_nid2sn(curve->nid));
125 
126 	if ((order = BN_new()) == NULL)
127 		errx(1, "BN_new order");
128 	if ((random = BN_new()) == NULL)
129 		errx(1, "BN_new random");
130 	if ((x = BN_new()) == NULL)
131 		errx(1, "BN_new x");
132 	if ((y = BN_new()) == NULL)
133 		errx(1, "BN_new y");
134 
135 	if (!EC_GROUP_get_order(group, order, NULL))
136 		errx(1, "EC_group_get_order");
137 
138 	for (i = 0; i < N_RANDOM_POINTS; i++) {
139 		EC_POINT *random_point;
140 
141 		do {
142 			if (!BN_rand_range(random, order))
143 				errx(1, "BN_rand_range");
144 		} while (BN_is_zero(random));
145 
146 		if ((random_point = EC_POINT_new(group)) == NULL)
147 			errx(1, "EC_POINT_new");
148 
149 		if (!EC_POINT_mul(group, random_point, random, NULL, NULL, NULL))
150 			errx(1, "EC_POINT_mul");
151 
152 		if (EC_POINT_is_at_infinity(group, random_point)) {
153 			EC_POINT_free(random_point);
154 
155 			warnx("info: got infinity");
156 			fprintf(stderr, "random = ");
157 			BN_print_fp(stderr, random);
158 			fprintf(stderr, "\n");
159 
160 			continue;
161 		}
162 
163 		if (!EC_POINT_get_affine_coordinates(group, random_point,
164 		    x, y, NULL))
165 			errx(1, "EC_POINT_get_affine_coordinates");
166 
167 		for (j = 0; j < N_FORMS; j++)
168 			failed |= roundtrip(group, random_point, forms[j], x, y);
169 
170 		EC_POINT_free(random_point);
171 	}
172 
173 	BN_free(order);
174 	BN_free(random);
175 	BN_free(x);
176 	BN_free(y);
177 	EC_GROUP_free(group);
178 
179 	return failed;
180 }
181 
182 static int
183 test_random_points(void)
184 {
185 	EC_builtin_curve *all_curves = NULL;
186 	size_t ncurves = 0;
187 	size_t curve_id;
188 	int failed = 0;
189 
190 	ncurves = EC_get_builtin_curves(NULL, 0);
191 	if ((all_curves = calloc(ncurves, sizeof(EC_builtin_curve))) == NULL)
192 		err(1, "calloc builtin curves");
193 	EC_get_builtin_curves(all_curves, ncurves);
194 
195 	for (curve_id = 0; curve_id < ncurves; curve_id++)
196 		failed |= test_random_points_on_curve(&all_curves[curve_id]);
197 
198 	fprintf(stderr, "%s %s\n", __func__, failed ? ": FAILED" : "");
199 
200 	free(all_curves);
201 	return failed;
202 }
203 
204 static const struct point_conversion {
205 	const char *description;
206 	int nid;
207 	uint8_t octets[256];
208 	size_t octets_len;
209 	int valid;
210 	int point_at_infinity;
211 } point_conversions[] = {
212 	/* XXX - now that sect571 is no longer tested, add another test? */
213 	{
214 		.description = "point at infinity on secp256r1",
215 		.nid = NID_X9_62_prime256v1,
216 		.octets = { 0x00 },
217 		.octets_len = 1,
218 		.valid = 1,
219 		.point_at_infinity = 1,
220 	},
221 	{
222 		.description = "point at infinity on secp256r1 (flipped y_bit)",
223 		.nid = NID_X9_62_prime256v1,
224 		.octets = { 0x01 },
225 		.octets_len = 1,
226 		.valid = 0,
227 		.point_at_infinity = 1,
228 	},
229 	{
230 		.description = "zero x compressed point on secp256r1",
231 		.nid = NID_X9_62_prime256v1,
232 		.octets = {
233 			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 			0x00,
238 		},
239 		.octets_len = 33,
240 		.valid = 1,
241 	},
242 	{
243 		.description =
244 		    "zero x compressed point on secp256r1 (flipped y_bit)",
245 		.nid = NID_X9_62_prime256v1,
246 		.octets = {
247 			0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 			0x00,
252 		},
253 		.octets_len = 33,
254 		.valid = 1,
255 	},
256 	{
257 		.description = "generic compressed point on secp256r1",
258 		.nid = NID_X9_62_prime256v1,
259 		.octets = {
260 			0x03, 0xa3, 0x96, 0xa0, 0x42, 0x73, 0x1a, 0x8b,
261 			0x90, 0xd8, 0xcb, 0xae, 0xda, 0x1b, 0x23, 0x11,
262 			0x77, 0x5f, 0x6a, 0x4c, 0xb4, 0x57, 0xbf, 0xe0,
263 			0x65, 0xd4, 0x09, 0x11, 0x5f, 0x54, 0xe4, 0xee,
264 			0xdd,
265 		},
266 		.octets_len = 33,
267 		.valid = 1,
268 	},
269 	{
270 		.description =
271 		    "generic compressed point on secp256r1 (flipped y_bit)",
272 		.nid = NID_X9_62_prime256v1,
273 		.octets = {
274 			0x02, 0xa3, 0x96, 0xa0, 0x42, 0x73, 0x1a, 0x8b,
275 			0x90, 0xd8, 0xcb, 0xae, 0xda, 0x1b, 0x23, 0x11,
276 			0x77, 0x5f, 0x6a, 0x4c, 0xb4, 0x57, 0xbf, 0xe0,
277 			0x65, 0xd4, 0x09, 0x11, 0x5f, 0x54, 0xe4, 0xee,
278 			0xdd,
279 		},
280 		.octets_len = 33,
281 		.valid = 1,
282 	},
283 	{
284 		.description = "zero x uncompressed point #1 on secp256r1",
285 		.nid = NID_X9_62_prime256v1,
286 		.octets = {
287 			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
289 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
292 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
293 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
294 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
295 			0xf4,
296 		},
297 		.octets_len = 65,
298 		.valid = 1,
299 	},
300 	{
301 		.description =
302 		    "zero x uncompressed point #1 on secp256r1 (flipped y_bit)",
303 		.nid = NID_X9_62_prime256v1,
304 		.octets = {
305 			0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
310 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
311 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
312 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
313 			0xf4,
314 		},
315 		.octets_len = 65,
316 		.valid = 0,
317 	},
318 	{
319 		.description = "zero x uncompressed point #2 on secp256r1",
320 		.nid = NID_X9_62_prime256v1,
321 		.octets = {
322 			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
327 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
328 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
329 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
330 			0x0b,
331 		},
332 		.octets_len = 65,
333 		.valid = 1,
334 	},
335 	{
336 		.description =
337 		    "zero x uncompressed point #2 on secp256r1 (flipped y_bit)",
338 		.nid = NID_X9_62_prime256v1,
339 		.octets = {
340 			0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
345 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
346 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
347 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
348 			0x0b,
349 		},
350 		.octets_len = 65,
351 		.valid = 0,
352 	},
353 	{
354 		.description = "generic uncompressed point on secp256r1",
355 		.nid = NID_X9_62_prime256v1,
356 		.octets = {
357 			0x04, 0x23, 0xe5, 0x85, 0xa5, 0x4b, 0xda, 0x34,
358 			0x7e, 0xe5, 0x65, 0x53, 0x7f, 0x3b, 0xce, 0xe4,
359 			0x54, 0xd8, 0xa4, 0x5a, 0x53, 0x4b, 0xb0, 0x4c,
360 			0xb9, 0x31, 0x09, 0x29, 0xa2, 0x03, 0x4c, 0x73,
361 			0x20, 0xd2, 0xc6, 0x17, 0xca, 0xe3, 0xcf, 0xc2,
362 			0xd8, 0x31, 0xfe, 0xf1, 0x7c, 0x6f, 0x9d, 0x7a,
363 			0x01, 0x7c, 0x34, 0x65, 0x42, 0x05, 0xaf, 0xcc,
364 			0x04, 0xa3, 0x2f, 0x44, 0x14, 0xbe, 0xd8, 0xc2,
365 			0x03,
366 		},
367 		.octets_len = 65,
368 		.valid = 1,
369 	},
370 	{
371 		.description =
372 		    "generic uncompressed point on secp256r1 (flipped y_bit)",
373 		.nid = NID_X9_62_prime256v1,
374 		.octets = {
375 			0x05, 0x23, 0xe5, 0x85, 0xa5, 0x4b, 0xda, 0x34,
376 			0x7e, 0xe5, 0x65, 0x53, 0x7f, 0x3b, 0xce, 0xe4,
377 			0x54, 0xd8, 0xa4, 0x5a, 0x53, 0x4b, 0xb0, 0x4c,
378 			0xb9, 0x31, 0x09, 0x29, 0xa2, 0x03, 0x4c, 0x73,
379 			0x20, 0xd2, 0xc6, 0x17, 0xca, 0xe3, 0xcf, 0xc2,
380 			0xd8, 0x31, 0xfe, 0xf1, 0x7c, 0x6f, 0x9d, 0x7a,
381 			0x01, 0x7c, 0x34, 0x65, 0x42, 0x05, 0xaf, 0xcc,
382 			0x04, 0xa3, 0x2f, 0x44, 0x14, 0xbe, 0xd8, 0xc2,
383 			0x03,
384 		},
385 		.octets_len = 65,
386 		.valid = 0,
387 	},
388 	{
389 		.description = "zero x hybrid point #1 on secp256r1",
390 		.nid = NID_X9_62_prime256v1,
391 		.octets = {
392 			0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
394 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
397 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
398 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
399 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
400 			0xf4,
401 		},
402 		.octets_len = 65,
403 		.valid = 1,
404 	},
405 	{
406 		.description =
407 		    "zero x hybrid point #1 on secp256r1 (flipped y_bit)",
408 		.nid = NID_X9_62_prime256v1,
409 		.octets = {
410 			0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
411 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
415 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
416 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
417 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
418 			0xf4,
419 		},
420 		.octets_len = 65,
421 		.valid = 0,
422 	},
423 	{
424 		.description = "zero x hybrid point #2 on secp256r1",
425 		.nid = NID_X9_62_prime256v1,
426 		.octets = {
427 			0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
432 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
433 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
434 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
435 			0x0b,
436 		},
437 		.octets_len = 65,
438 		.valid = 1,
439 	},
440 	{
441 		.description =
442 		    "zero x hybrid point #2 on secp256r1 (flipped y_bit)",
443 		.nid = NID_X9_62_prime256v1,
444 		.octets = {
445 			0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
447 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
449 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
450 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
451 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
452 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
453 			0x0b,
454 		},
455 		.octets_len = 65,
456 		.valid = 0,
457 	},
458 	{
459 		.description = "generic hybrid point on secp256r1",
460 		.nid = NID_X9_62_prime256v1,
461 		.octets = {
462 			0x07, 0x38, 0xb2, 0x98, 0x38, 0x21, 0x6b, 0xec,
463 			0x87, 0xcf, 0x50, 0xbb, 0x65, 0x11, 0x96, 0x63,
464 			0xf3, 0x90, 0x64, 0xc3, 0x5c, 0x59, 0xa5, 0x6f,
465 			0xaf, 0x56, 0x2a, 0x0c, 0xc0, 0x3a, 0x9b, 0x92,
466 			0x85, 0x95, 0x54, 0xf3, 0x08, 0x0f, 0x78, 0x59,
467 			0xa2, 0x44, 0x2f, 0x19, 0x5d, 0xd5, 0xcd, 0xf6,
468 			0xa5, 0xbe, 0x2f, 0x83, 0x70, 0x94, 0xf5, 0xcd,
469 			0x8c, 0x40, 0x7f, 0xd8, 0x97, 0x92, 0x14, 0xf7,
470 			0xc5,
471 		},
472 		.octets_len = 65,
473 		.valid = 1,
474 	},
475 	{
476 		.description =
477 		    "generic hybrid point on secp256r1 (flipped y_bit)",
478 		.nid = NID_X9_62_prime256v1,
479 		.octets = {
480 			0x06, 0x38, 0xb2, 0x98, 0x38, 0x21, 0x6b, 0xec,
481 			0x87, 0xcf, 0x50, 0xbb, 0x65, 0x11, 0x96, 0x63,
482 			0xf3, 0x90, 0x64, 0xc3, 0x5c, 0x59, 0xa5, 0x6f,
483 			0xaf, 0x56, 0x2a, 0x0c, 0xc0, 0x3a, 0x9b, 0x92,
484 			0x85, 0x95, 0x54, 0xf3, 0x08, 0x0f, 0x78, 0x59,
485 			0xa2, 0x44, 0x2f, 0x19, 0x5d, 0xd5, 0xcd, 0xf6,
486 			0xa5, 0xbe, 0x2f, 0x83, 0x70, 0x94, 0xf5, 0xcd,
487 			0x8c, 0x40, 0x7f, 0xd8, 0x97, 0x92, 0x14, 0xf7,
488 			0xc5,
489 		},
490 		.octets_len = 65,
491 		.valid = 0,
492 	},
493 };
494 
495 static const size_t N_POINT_CONVERSIONS =
496     sizeof(point_conversions) / sizeof(point_conversions[0]);
497 
498 static int
499 check_point_at_infinity(const EC_GROUP *group, const EC_POINT *point,
500     const struct point_conversion *test)
501 {
502 	const uint8_t conversion_forms[4] = { 0x00, 0x02, 0x04, 0x06, };
503 	uint8_t buf[1];
504 	uint8_t form;
505 	size_t i, ret;
506 	int failed = 0;
507 
508 	/* The form for the point at infinity is expected to fail. */
509 	form = conversion_forms[0];
510 
511 	ret = EC_POINT_point2oct(group, point, form, buf, sizeof(buf), NULL);
512 	if (ret != 0) {
513 		fprintf(stderr, "FAIL: %s: expected encoding with form 0x%02x "
514 		    "to fail, got %zu\n", test->description, form, ret);
515 		failed |= 1;
516 	}
517 
518 	/* For all other forms we expect the zero octet. */
519 	for (i = 1; i < sizeof(conversion_forms); i++) {
520 		form = conversion_forms[i];
521 
522 		ret = EC_POINT_point2oct(group, point, form, buf, sizeof(buf), NULL);
523 		if (ret != 1) {
524 			fprintf(stderr, "FAIL: %s: expected success, got %zu\n",
525 			    test->description, ret);
526 			failed |= 1;
527 			continue;
528 		}
529 
530 		if (memcmp(buf, test->octets, test->octets_len) != 0) {
531 			fprintf(stderr, "FAIL: %s: want 0x%02x, got 0x%02x\n",
532 			    test->description, test->octets[0], buf[0]);
533 			failed |= 1;
534 			continue;
535 		}
536 	}
537 
538 	return failed;
539 }
540 
541 static int
542 point_conversion_form_y_bit(const struct point_conversion *test)
543 {
544 	EC_GROUP *group = NULL;
545 	EC_POINT *point = NULL;
546 	int ret;
547 	int failed = 0;
548 
549 	if ((group = EC_GROUP_new_by_curve_name(test->nid)) == NULL)
550 		errx(1, "group");
551 	if ((point = EC_POINT_new(group)) == NULL)
552 		errx(1, "point");
553 
554 	ret = EC_POINT_oct2point(group, point, test->octets, test->octets_len,
555 	    NULL);
556 	if (ret != test->valid) {
557 		fprintf(stderr, "%s want %d got %d\n", test->description,
558 		    test->valid, ret);
559 		failed |= 1;
560 	}
561 
562 	if (test->valid && test->point_at_infinity) {
563 		failed |= check_point_at_infinity(group, point, test);
564 	} else if (test->valid) {
565 		uint8_t buf[256];
566 		uint8_t form = test->octets[0] & 0x06;
567 		size_t len;
568 
569 		len = EC_POINT_point2oct(group, point, form, buf, sizeof(buf), NULL);
570 
571 		if (len != test->octets_len) {
572 			fprintf(stderr, "%s: EC_POINT_point2oct: want %zu, got %zu\n",
573 			    test->description, test->octets_len, len);
574 			failed |= 1;
575 			goto failed;
576 		}
577 		if (memcmp(test->octets, buf, len) != 0) {
578 			fprintf(stderr, "%s: unexpected encoding\nwant:\n",
579 			    test->description);
580 			hexdump(test->octets, test->octets_len);
581 			fprintf(stderr, "\ngot:\n");
582 			hexdump(buf, len);
583 			failed |= 1;
584 			goto failed;
585 		}
586 	}
587 
588  failed:
589 	EC_GROUP_free(group);
590 	EC_POINT_free(point);
591 
592 	return failed;
593 }
594 
595 static int
596 test_point_conversions(void)
597 {
598 	size_t i;
599 	int failed = 0;
600 
601 	for (i = 0; i < N_POINT_CONVERSIONS; i++)
602 		failed |= point_conversion_form_y_bit(&point_conversions[i]);
603 
604 	fprintf(stderr, "%s %s\n", __func__, failed ? ": FAILED" : "");
605 
606 	return failed;
607 }
608 
609 int
610 main(int argc, char **argv)
611 {
612 	int failed = 0;
613 
614 	failed |= test_random_points();
615 	failed |= test_point_conversions();
616 
617 	return failed;
618 }
619