1 /*	$OpenBSD: ec_point_conversion.c,v 1.15 2024/01/18 16:49:40 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 
23 #include <openssl/bn.h>
24 #include <openssl/ec.h>
25 #include <openssl/objects.h>
26 
27 int forms[] = {
28 	POINT_CONVERSION_COMPRESSED,
29 	POINT_CONVERSION_UNCOMPRESSED,
30 	POINT_CONVERSION_HYBRID,
31 };
32 
33 static const size_t N_FORMS = sizeof(forms) / sizeof(forms[0]);
34 #define N_RANDOM_POINTS 10
35 
36 static const char *
37 form2str(int form)
38 {
39 	switch (form) {
40 	case POINT_CONVERSION_COMPRESSED:
41 		return "compressed form";
42 	case POINT_CONVERSION_UNCOMPRESSED:
43 		return "uncompressed form";
44 	case POINT_CONVERSION_HYBRID:
45 		return "hybrid form";
46 	default:
47 		return "unknown form";
48 	}
49 }
50 
51 static void
52 hexdump(const unsigned char *buf, size_t len)
53 {
54 	size_t i;
55 
56 	for (i = 1; i <= len; i++)
57 		fprintf(stderr, " 0x%02hhx,%s", buf[i - 1], i % 8 ? "" : "\n");
58 	if (len % 8)
59 		fprintf(stderr, "\n");
60 }
61 
62 static int
63 roundtrip(EC_GROUP *group, EC_POINT *point, int form, BIGNUM *x, BIGNUM *y)
64 {
65 	BIGNUM *x_out = NULL, *y_out = NULL;
66 	size_t len;
67 	uint8_t *buf = NULL;
68 	int failed = 1;
69 
70 	if ((len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
71 		errx(1, "point2oct");
72 	if ((buf = malloc(len)) == NULL)
73 		errx(1, "malloc");
74 	if (EC_POINT_point2oct(group, point, form, buf, len, NULL) != len)
75 		errx(1, "point2oct");
76 
77 	if (!EC_POINT_oct2point(group, point, buf, len, NULL))
78 		errx(1, "%s oct2point", form2str(form));
79 
80 	if ((x_out = BN_new()) == NULL)
81 		errx(1, "new x_out");
82 	if ((y_out = BN_new()) == NULL)
83 		errx(1, "new y_out");
84 
85 	if (!EC_POINT_get_affine_coordinates(group, point, x_out, y_out, NULL))
86 		errx(1, "get affine");
87 
88 	if (BN_cmp(x, x_out) != 0) {
89 		warnx("%s: x", form2str(form));
90 		goto err;
91 	}
92 	if (BN_cmp(y, y_out) != 0) {
93 		warnx("%s: y", form2str(form));
94 		goto err;
95 	}
96 
97 	failed = 0;
98 
99  err:
100 	if (failed)
101 		hexdump(buf, len);
102 
103 	free(buf);
104 	BN_free(x_out);
105 	BN_free(y_out);
106 
107 	return failed;
108 }
109 
110 /* XXX This only tests multiples of the generator for now... */
111 static int
112 test_random_points_on_curve(EC_builtin_curve *curve)
113 {
114 	EC_GROUP *group;
115 	BIGNUM *order = NULL;
116 	BIGNUM *random;
117 	BIGNUM *x, *y;
118 	size_t i, j;
119 	int failed = 0;
120 
121 	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
122 		errx(1, "EC_GROUP_new_by_curve_name(%s)",
123 		    OBJ_nid2sn(curve->nid));
124 
125 	if ((order = BN_new()) == NULL)
126 		errx(1, "BN_new order");
127 	if ((random = BN_new()) == NULL)
128 		errx(1, "BN_new random");
129 	if ((x = BN_new()) == NULL)
130 		errx(1, "BN_new x");
131 	if ((y = BN_new()) == NULL)
132 		errx(1, "BN_new y");
133 
134 	if (!EC_GROUP_get_order(group, order, NULL))
135 		errx(1, "EC_group_get_order");
136 
137 	for (i = 0; i < N_RANDOM_POINTS; i++) {
138 		EC_POINT *random_point;
139 
140 		do {
141 			if (!BN_rand_range(random, order))
142 				errx(1, "BN_rand_range");
143 		} while (BN_is_zero(random));
144 
145 		if ((random_point = EC_POINT_new(group)) == NULL)
146 			errx(1, "EC_POINT_new");
147 
148 		if (!EC_POINT_mul(group, random_point, random, NULL, NULL, NULL))
149 			errx(1, "EC_POINT_mul");
150 
151 		if (EC_POINT_is_at_infinity(group, random_point)) {
152 			EC_POINT_free(random_point);
153 
154 			warnx("info: got infinity");
155 			fprintf(stderr, "random = ");
156 			BN_print_fp(stderr, random);
157 			fprintf(stderr, "\n");
158 
159 			continue;
160 		}
161 
162 		if (!EC_POINT_get_affine_coordinates(group, random_point,
163 		    x, y, NULL))
164 			errx(1, "EC_POINT_get_affine_coordinates");
165 
166 		for (j = 0; j < N_FORMS; j++)
167 			failed |= roundtrip(group, random_point, forms[j], x, y);
168 
169 		EC_POINT_free(random_point);
170 	}
171 
172 	BN_free(order);
173 	BN_free(random);
174 	BN_free(x);
175 	BN_free(y);
176 	EC_GROUP_free(group);
177 
178 	return failed;
179 }
180 
181 static int
182 test_random_points(void)
183 {
184 	EC_builtin_curve *all_curves = NULL;
185 	size_t ncurves = 0;
186 	size_t curve_id;
187 	int failed = 0;
188 
189 	ncurves = EC_get_builtin_curves(NULL, 0);
190 	if ((all_curves = calloc(ncurves, sizeof(EC_builtin_curve))) == NULL)
191 		err(1, "calloc builtin curves");
192 	EC_get_builtin_curves(all_curves, ncurves);
193 
194 	for (curve_id = 0; curve_id < ncurves; curve_id++)
195 		failed |= test_random_points_on_curve(&all_curves[curve_id]);
196 
197 	fprintf(stderr, "%s %s\n", __func__, failed ? ": FAILED" : "");
198 
199 	free(all_curves);
200 	return failed;
201 }
202 
203 static const struct point_conversion {
204 	const char *description;
205 	int nid;
206 	uint8_t octets[256];
207 	uint8_t octets_len;
208 	int valid;
209 } point_conversions[] = {
210 	/* XXX - now that sect571 is no longer tested, add another test? */
211 	{
212 		.description = "point at infinity on secp256r1",
213 		.nid = NID_X9_62_prime256v1,
214 		.octets = { 0x00 },
215 		.octets_len = 1,
216 		.valid = 1,
217 	},
218 	{
219 		.description = "point at infinity on secp256r1 (flipped y_bit)",
220 		.nid = NID_X9_62_prime256v1,
221 		.octets = { 0x01 },
222 		.octets_len = 1,
223 		.valid = 0,
224 	},
225 	{
226 		.description = "zero x compressed point on secp256r1",
227 		.nid = NID_X9_62_prime256v1,
228 		.octets = {
229 			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 			0x00,
234 		},
235 		.octets_len = 33,
236 		.valid = 1,
237 	},
238 	{
239 		.description =
240 		    "zero x compressed point on secp256r1 (flipped y_bit)",
241 		.nid = NID_X9_62_prime256v1,
242 		.octets = {
243 			0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 			0x00,
248 		},
249 		.octets_len = 33,
250 		.valid = 1,
251 	},
252 	{
253 		.description = "generic compressed point on secp256r1",
254 		.nid = NID_X9_62_prime256v1,
255 		.octets = {
256 			0x03, 0xa3, 0x96, 0xa0, 0x42, 0x73, 0x1a, 0x8b,
257 			0x90, 0xd8, 0xcb, 0xae, 0xda, 0x1b, 0x23, 0x11,
258 			0x77, 0x5f, 0x6a, 0x4c, 0xb4, 0x57, 0xbf, 0xe0,
259 			0x65, 0xd4, 0x09, 0x11, 0x5f, 0x54, 0xe4, 0xee,
260 			0xdd,
261 		},
262 		.octets_len = 33,
263 		.valid = 1,
264 	},
265 	{
266 		.description =
267 		    "generic compressed point on secp256r1 (flipped y_bit)",
268 		.nid = NID_X9_62_prime256v1,
269 		.octets = {
270 			0x02, 0xa3, 0x96, 0xa0, 0x42, 0x73, 0x1a, 0x8b,
271 			0x90, 0xd8, 0xcb, 0xae, 0xda, 0x1b, 0x23, 0x11,
272 			0x77, 0x5f, 0x6a, 0x4c, 0xb4, 0x57, 0xbf, 0xe0,
273 			0x65, 0xd4, 0x09, 0x11, 0x5f, 0x54, 0xe4, 0xee,
274 			0xdd,
275 		},
276 		.octets_len = 33,
277 		.valid = 1,
278 	},
279 	{
280 		.description = "zero x uncompressed point #1 on secp256r1",
281 		.nid = NID_X9_62_prime256v1,
282 		.octets = {
283 			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
284 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
285 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
286 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
288 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
289 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
290 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
291 			0xf4,
292 		},
293 		.octets_len = 65,
294 		.valid = 1,
295 	},
296 	{
297 		.description =
298 		    "zero x uncompressed point #1 on secp256r1 (flipped y_bit)",
299 		.nid = NID_X9_62_prime256v1,
300 		.octets = {
301 			0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
306 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
307 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
308 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
309 			0xf4,
310 		},
311 		.octets_len = 65,
312 		.valid = 0,
313 	},
314 	{
315 		.description = "zero x uncompressed point #2 on secp256r1",
316 		.nid = NID_X9_62_prime256v1,
317 		.octets = {
318 			0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
323 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
324 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
325 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
326 			0x0b,
327 		},
328 		.octets_len = 65,
329 		.valid = 1,
330 	},
331 	{
332 		.description =
333 		    "zero x uncompressed point #2 on secp256r1 (flipped y_bit)",
334 		.nid = NID_X9_62_prime256v1,
335 		.octets = {
336 			0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
341 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
342 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
343 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
344 			0x0b,
345 		},
346 		.octets_len = 65,
347 		.valid = 0,
348 	},
349 	{
350 		.description = "generic uncompressed point on secp256r1",
351 		.nid = NID_X9_62_prime256v1,
352 		.octets = {
353 			0x04, 0x23, 0xe5, 0x85, 0xa5, 0x4b, 0xda, 0x34,
354 			0x7e, 0xe5, 0x65, 0x53, 0x7f, 0x3b, 0xce, 0xe4,
355 			0x54, 0xd8, 0xa4, 0x5a, 0x53, 0x4b, 0xb0, 0x4c,
356 			0xb9, 0x31, 0x09, 0x29, 0xa2, 0x03, 0x4c, 0x73,
357 			0x20, 0xd2, 0xc6, 0x17, 0xca, 0xe3, 0xcf, 0xc2,
358 			0xd8, 0x31, 0xfe, 0xf1, 0x7c, 0x6f, 0x9d, 0x7a,
359 			0x01, 0x7c, 0x34, 0x65, 0x42, 0x05, 0xaf, 0xcc,
360 			0x04, 0xa3, 0x2f, 0x44, 0x14, 0xbe, 0xd8, 0xc2,
361 			0x03,
362 		},
363 		.octets_len = 65,
364 		.valid = 1,
365 	},
366 	{
367 		.description =
368 		    "generic uncompressed point on secp256r1 (flipped y_bit)",
369 		.nid = NID_X9_62_prime256v1,
370 		.octets = {
371 			0x05, 0x23, 0xe5, 0x85, 0xa5, 0x4b, 0xda, 0x34,
372 			0x7e, 0xe5, 0x65, 0x53, 0x7f, 0x3b, 0xce, 0xe4,
373 			0x54, 0xd8, 0xa4, 0x5a, 0x53, 0x4b, 0xb0, 0x4c,
374 			0xb9, 0x31, 0x09, 0x29, 0xa2, 0x03, 0x4c, 0x73,
375 			0x20, 0xd2, 0xc6, 0x17, 0xca, 0xe3, 0xcf, 0xc2,
376 			0xd8, 0x31, 0xfe, 0xf1, 0x7c, 0x6f, 0x9d, 0x7a,
377 			0x01, 0x7c, 0x34, 0x65, 0x42, 0x05, 0xaf, 0xcc,
378 			0x04, 0xa3, 0x2f, 0x44, 0x14, 0xbe, 0xd8, 0xc2,
379 			0x03,
380 		},
381 		.octets_len = 65,
382 		.valid = 0,
383 	},
384 	{
385 		.description = "zero x hybrid point #1 on secp256r1",
386 		.nid = NID_X9_62_prime256v1,
387 		.octets = {
388 			0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
389 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
390 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
391 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
393 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
394 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
395 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
396 			0xf4,
397 		},
398 		.octets_len = 65,
399 		.valid = 1,
400 	},
401 	{
402 		.description =
403 		    "zero x hybrid point #1 on secp256r1 (flipped y_bit)",
404 		.nid = NID_X9_62_prime256v1,
405 		.octets = {
406 			0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
407 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
408 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
409 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
410 			0x00, 0x66, 0x48, 0x5c, 0x78, 0x0e, 0x2f, 0x83,
411 			0xd7, 0x24, 0x33, 0xbd, 0x5d, 0x84, 0xa0, 0x6b,
412 			0xb6, 0x54, 0x1c, 0x2a, 0xf3, 0x1d, 0xae, 0x87,
413 			0x17, 0x28, 0xbf, 0x85, 0x6a, 0x17, 0x4f, 0x93,
414 			0xf4,
415 		},
416 		.octets_len = 65,
417 		.valid = 0,
418 	},
419 	{
420 		.description = "zero x hybrid point #2 on secp256r1",
421 		.nid = NID_X9_62_prime256v1,
422 		.octets = {
423 			0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
424 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
425 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
428 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
429 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
430 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
431 			0x0b,
432 		},
433 		.octets_len = 65,
434 		.valid = 1,
435 	},
436 	{
437 		.description =
438 		    "zero x hybrid point #2 on secp256r1 (flipped y_bit)",
439 		.nid = NID_X9_62_prime256v1,
440 		.octets = {
441 			0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
442 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
444 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
445 			0x00, 0x99, 0xb7, 0xa3, 0x86, 0xf1, 0xd0, 0x7c,
446 			0x29, 0xdb, 0xcc, 0x42, 0xa2, 0x7b, 0x5f, 0x94,
447 			0x49, 0xab, 0xe3, 0xd5, 0x0d, 0xe2, 0x51, 0x78,
448 			0xe8, 0xd7, 0x40, 0x7a, 0x95, 0xe8, 0xb0, 0x6c,
449 			0x0b,
450 		},
451 		.octets_len = 65,
452 		.valid = 0,
453 	},
454 	{
455 		.description = "generic hybrid point on secp256r1",
456 		.nid = NID_X9_62_prime256v1,
457 		.octets = {
458 			0x07, 0x38, 0xb2, 0x98, 0x38, 0x21, 0x6b, 0xec,
459 			0x87, 0xcf, 0x50, 0xbb, 0x65, 0x11, 0x96, 0x63,
460 			0xf3, 0x90, 0x64, 0xc3, 0x5c, 0x59, 0xa5, 0x6f,
461 			0xaf, 0x56, 0x2a, 0x0c, 0xc0, 0x3a, 0x9b, 0x92,
462 			0x85, 0x95, 0x54, 0xf3, 0x08, 0x0f, 0x78, 0x59,
463 			0xa2, 0x44, 0x2f, 0x19, 0x5d, 0xd5, 0xcd, 0xf6,
464 			0xa5, 0xbe, 0x2f, 0x83, 0x70, 0x94, 0xf5, 0xcd,
465 			0x8c, 0x40, 0x7f, 0xd8, 0x97, 0x92, 0x14, 0xf7,
466 			0xc5,
467 		},
468 		.octets_len = 65,
469 		.valid = 1,
470 	},
471 	{
472 		.description =
473 		    "generic hybrid point on secp256r1 (flipped y_bit)",
474 		.nid = NID_X9_62_prime256v1,
475 		.octets = {
476 			0x06, 0x38, 0xb2, 0x98, 0x38, 0x21, 0x6b, 0xec,
477 			0x87, 0xcf, 0x50, 0xbb, 0x65, 0x11, 0x96, 0x63,
478 			0xf3, 0x90, 0x64, 0xc3, 0x5c, 0x59, 0xa5, 0x6f,
479 			0xaf, 0x56, 0x2a, 0x0c, 0xc0, 0x3a, 0x9b, 0x92,
480 			0x85, 0x95, 0x54, 0xf3, 0x08, 0x0f, 0x78, 0x59,
481 			0xa2, 0x44, 0x2f, 0x19, 0x5d, 0xd5, 0xcd, 0xf6,
482 			0xa5, 0xbe, 0x2f, 0x83, 0x70, 0x94, 0xf5, 0xcd,
483 			0x8c, 0x40, 0x7f, 0xd8, 0x97, 0x92, 0x14, 0xf7,
484 			0xc5,
485 		},
486 		.octets_len = 65,
487 		.valid = 0,
488 	},
489 };
490 
491 static const size_t N_POINT_CONVERSIONS =
492     sizeof(point_conversions) / sizeof(point_conversions[0]);
493 
494 static int
495 point_conversion_form_y_bit(const struct point_conversion *test)
496 {
497 	EC_GROUP *group = NULL;
498 	EC_POINT *point = NULL;
499 	int ret;
500 	int failed = 0;
501 
502 	if ((group = EC_GROUP_new_by_curve_name(test->nid)) == NULL)
503 		errx(1, "group");
504 	if ((point = EC_POINT_new(group)) == NULL)
505 		errx(1, "point");
506 
507 	ret = EC_POINT_oct2point(group, point, test->octets, test->octets_len,
508 	    NULL);
509 	if (ret != test->valid) {
510 		fprintf(stderr, "%s want %d got %d\n", test->description,
511 		    test->valid, ret);
512 		failed |= 1;
513 	}
514 
515 	EC_GROUP_free(group);
516 	EC_POINT_free(point);
517 
518 	return failed;
519 }
520 
521 static int
522 test_point_conversions(void)
523 {
524 	size_t i;
525 	int failed = 0;
526 
527 	for (i = 0; i < N_POINT_CONVERSIONS; i++)
528 		failed |= point_conversion_form_y_bit(&point_conversions[i]);
529 
530 	fprintf(stderr, "%s %s\n", __func__, failed ? ": FAILED" : "");
531 
532 	return failed;
533 }
534 
535 int
536 main(int argc, char **argv)
537 {
538 	int failed = 0;
539 
540 	failed |= test_random_points();
541 	failed |= test_point_conversions();
542 
543 	return failed;
544 }
545