1 /*	$OpenBSD: ec_point_conversion.c,v 1.1 2021/04/21 20:15:08 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/ec.h>
24 #include <openssl/objects.h>
25 
26 int bn_rand_interval(BIGNUM *, const BIGNUM *, const BIGNUM *);
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 
60 	if (len % 8)
61 		fprintf(stderr, "\n");
62 }
63 
64 static int
65 roundtrip(EC_GROUP *group, EC_POINT *point, int form, BIGNUM *x, BIGNUM *y)
66 {
67 	BIGNUM *x_out = NULL, *y_out = NULL;
68 	size_t len;
69 	uint8_t *buf = NULL;
70 	int failed = 1;
71 
72 	if ((len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL)) == 0)
73 		errx(1, "point2oct");
74 	if ((buf = malloc(len)) == NULL)
75 		errx(1, "malloc");
76 	if (EC_POINT_point2oct(group, point, form, buf, len, NULL) != len)
77 		errx(1, "point2oct");
78 
79 	if (!EC_POINT_oct2point(group, point, buf, len, NULL))
80 		errx(1, "%s oct2point", form2str(form));
81 
82 	if ((x_out = BN_new()) == NULL)
83 		errx(1, "new x_out");
84 	if ((y_out = BN_new()) == NULL)
85 		errx(1, "new y_out");
86 
87 	if (!EC_POINT_get_affine_coordinates(group, point, x_out, y_out, NULL))
88 		errx(1, "get affine");
89 
90 	if (BN_cmp(x, x_out) != 0) {
91 		warnx("%s: x", form2str(form));
92 		goto err;
93 	}
94 	if (BN_cmp(y, y_out) != 0) {
95 		warnx("%s: y", form2str(form));
96 		goto err;
97 	}
98 
99 	failed = 0;
100 
101  err:
102 	if (failed)
103 		hexdump(buf, len);
104 
105 	free(buf);
106 	BN_free(x_out);
107 	BN_free(y_out);
108 
109 	return failed;
110 }
111 
112 static int
113 hybrid_corner_case(void)
114 {
115 	BIGNUM *x = NULL, *y = NULL;
116 	EC_GROUP *group;
117 	EC_POINT *point;
118 	size_t i;
119 	int failed = 0;
120 
121 	if (!BN_hex2bn(&x, "0"))
122 		errx(1, "BN_hex2bn x");
123 	if (!BN_hex2bn(&y, "01"))
124 		errx(1, "BN_hex2bn y");
125 
126 	if ((group = EC_GROUP_new_by_curve_name(NID_sect571k1)) == NULL)
127 		errx(1, "group");
128 	if ((point = EC_POINT_new(group)) == NULL)
129 		errx(1, "point");
130 
131 	if (!EC_POINT_set_affine_coordinates(group, point, x, y, NULL))
132 		errx(1, "set affine");
133 
134 	for (i = 0; i < N_FORMS; i++)
135 		failed |= roundtrip(group, point, forms[i], x, y);
136 
137 	fprintf(stderr, "%s: %s\n", __func__, failed ? "FAILED" : "SUCCESS");
138 
139 	EC_GROUP_free(group);
140 	EC_POINT_free(point);
141 	BN_free(x);
142 	BN_free(y);
143 
144 	return failed;
145 }
146 
147 /* XXX This only tests multiples of the generator for now... */
148 static int
149 test_random_points_on_curve(EC_builtin_curve *curve)
150 {
151 	EC_GROUP *group;
152 	BIGNUM *order = NULL;
153 	BIGNUM *random;
154 	BIGNUM *x, *y;
155 	size_t i, j;
156 	int failed = 0;
157 
158 	fprintf(stderr, "%s\n", OBJ_nid2sn(curve->nid));
159 	if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL)
160 		errx(1, "EC_GROUP_new_by_curve_name");
161 
162 	if ((order = BN_new()) == NULL)
163 		errx(1, "BN_new order");
164 	if ((random = BN_new()) == NULL)
165 		errx(1, "BN_new random");
166 	if ((x = BN_new()) == NULL)
167 		errx(1, "BN_new x");
168 	if ((y = BN_new()) == NULL)
169 		errx(1, "BN_new y");
170 
171 	if (!EC_GROUP_get_order(group, order, NULL))
172 		errx(1, "EC_group_get_order");
173 
174 	for (i = 0; i < N_RANDOM_POINTS; i++) {
175 		EC_POINT *random_point;
176 
177 		if (!bn_rand_interval(random, BN_value_one(), order))
178 			errx(1, "bn_rand_interval");
179 
180 		if ((random_point = EC_POINT_new(group)) == NULL)
181 			errx(1, "EC_POINT_new");
182 
183 		if (!EC_POINT_mul(group, random_point, random, NULL, NULL, NULL))
184 			errx(1, "EC_POINT_mul");
185 
186 		if (EC_POINT_is_at_infinity(group, random_point)) {
187 			EC_POINT_free(random_point);
188 
189 			warnx("info: got infinity");
190 			fprintf(stderr, "random = ");
191 			BN_print_fp(stderr, random);
192 			fprintf(stderr, "\n");
193 
194 			continue;
195 		}
196 
197 		if (!EC_POINT_get_affine_coordinates(group, random_point,
198 		    x, y, NULL))
199 			errx(1, "EC_POINT_get_affine_coordinates");
200 
201 		for (j = 0; j < N_FORMS; j++)
202 			failed |= roundtrip(group, random_point, forms[j], x, y);
203 
204 		EC_POINT_free(random_point);
205 	}
206 
207 	BN_free(order);
208 	BN_free(random);
209 	BN_free(x);
210 	BN_free(y);
211 	EC_GROUP_free(group);
212 
213 	return failed;
214 }
215 
216 static int
217 test_random_points(void)
218 {
219 	EC_builtin_curve *all_curves = NULL;
220 	size_t ncurves = 0;
221 	size_t curve_id;
222 	int failed = 0;
223 
224 	ncurves = EC_get_builtin_curves(NULL, 0);
225 	if ((all_curves = calloc(ncurves, sizeof(EC_builtin_curve))) == NULL)
226 		err(1, "calloc builtin curves");
227 	EC_get_builtin_curves(all_curves, ncurves);
228 
229 	for (curve_id = 0; curve_id < ncurves; curve_id++)
230 		test_random_points_on_curve(&all_curves[curve_id]);
231 
232 	fprintf(stderr, "%s: %s\n", __func__, failed ? "FAILED" : "SUCCESS");
233 
234 	free(all_curves);
235 	return failed;
236 }
237 
238 int
239 main(int argc, char **argv)
240 {
241 	int failed = 0;
242 
243 	failed |= test_random_points();
244 	failed |= hybrid_corner_case();
245 
246 	fprintf(stderr, "%s\n", failed ? "FAILED" : "SUCCESS");
247 
248 	return failed;
249 }
250