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