summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortb <>2024-10-30 18:16:34 +0000
committertb <>2024-10-30 18:16:34 +0000
commitf953655e6861bda2cfd07fa49af0ecd20b4507c2 (patch)
tree4305dbdabff19dba57f0176ca9be0a859323868b /src
parent8c30fb7d85e73088455141bfbd758b3a43c931df (diff)
downloadopenbsd-f953655e6861bda2cfd07fa49af0ecd20b4507c2.tar.gz
openbsd-f953655e6861bda2cfd07fa49af0ecd20b4507c2.tar.bz2
openbsd-f953655e6861bda2cfd07fa49af0ecd20b4507c2.zip
Move the GFp-specific point <-> octets functions to ec_convert.c
discussed with jsing
Diffstat (limited to 'src')
-rw-r--r--src/lib/libcrypto/ec/ec_convert.c296
-rw-r--r--src/lib/libcrypto/ec/ecp_oct.c296
2 files changed, 296 insertions, 296 deletions
diff --git a/src/lib/libcrypto/ec/ec_convert.c b/src/lib/libcrypto/ec/ec_convert.c
index fd0182f420..123cf90ef9 100644
--- a/src/lib/libcrypto/ec/ec_convert.c
+++ b/src/lib/libcrypto/ec/ec_convert.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ec_convert.c,v 1.1 2024/10/30 18:14:49 tb Exp $ */ 1/* $OpenBSD: ec_convert.c,v 1.2 2024/10/30 18:16:34 tb Exp $ */
2/* 2/*
3 * Originally written by Bodo Moeller for the OpenSSL project. 3 * Originally written by Bodo Moeller for the OpenSSL project.
4 */ 4 */
@@ -69,6 +69,300 @@
69#include "asn1_local.h" 69#include "asn1_local.h"
70#include "ec_local.h" 70#include "ec_local.h"
71 71
72/*
73 * Only the last three bits of the leading octet of a point should be set.
74 * Bits 3 and 2 encode the conversion form for all points except the point
75 * at infinity. In compressed and hybrid form bit 1 indicates if the even
76 * or the odd solution of the quadratic equation for y should be used.
77 *
78 * The public point_conversion_t enum lacks the point at infinity, so we
79 * ignore it except at the API boundary.
80 */
81
82#define EC_OCT_YBIT 0x01
83
84#define EC_OCT_POINT_AT_INFINITY 0x00
85#define EC_OCT_POINT_COMPRESSED 0x02
86#define EC_OCT_POINT_UNCOMPRESSED 0x04
87#define EC_OCT_POINT_HYBRID 0x06
88#define EC_OCT_POINT_CONVERSION_MASK 0x06
89
90static int
91ec_oct_conversion_form_is_valid(uint8_t form)
92{
93 return (form & EC_OCT_POINT_CONVERSION_MASK) == form;
94}
95
96static int
97ec_oct_check_hybrid_ybit_is_consistent(uint8_t form, int ybit, const BIGNUM *y)
98{
99 if (form == EC_OCT_POINT_HYBRID && ybit != BN_is_odd(y)) {
100 ECerror(EC_R_INVALID_ENCODING);
101 return 0;
102 }
103
104 return 1;
105}
106
107/* Nonzero y-bit only makes sense with compressed or hybrid encoding. */
108static int
109ec_oct_nonzero_ybit_allowed(uint8_t form)
110{
111 return form == EC_OCT_POINT_COMPRESSED || form == EC_OCT_POINT_HYBRID;
112}
113
114static int
115ec_oct_add_leading_octet_cbb(CBB *cbb, uint8_t form, int ybit)
116{
117 if (ec_oct_nonzero_ybit_allowed(form) && ybit != 0)
118 form |= EC_OCT_YBIT;
119
120 return CBB_add_u8(cbb, form);
121}
122
123static int
124ec_oct_get_leading_octet_cbs(CBS *cbs, uint8_t *out_form, int *out_ybit)
125{
126 uint8_t octet;
127
128 if (!CBS_get_u8(cbs, &octet)) {
129 ECerror(EC_R_BUFFER_TOO_SMALL);
130 return 0;
131 }
132
133 *out_ybit = octet & EC_OCT_YBIT;
134 *out_form = octet & ~EC_OCT_YBIT;
135
136 if (!ec_oct_conversion_form_is_valid(*out_form)) {
137 ECerror(EC_R_INVALID_ENCODING);
138 return 0;
139 }
140
141 if (*out_ybit != 0 && !ec_oct_nonzero_ybit_allowed(*out_form)) {
142 ECerror(EC_R_INVALID_ENCODING);
143 return 0;
144 }
145
146 return 1;
147}
148
149static int
150ec_oct_encoded_length(const EC_GROUP *group, uint8_t form, size_t *out_len)
151{
152 switch (form) {
153 case EC_OCT_POINT_AT_INFINITY:
154 *out_len = 1;
155 return 1;
156 case EC_OCT_POINT_COMPRESSED:
157 *out_len = 1 + BN_num_bytes(&group->field);
158 return 1;
159 case EC_OCT_POINT_UNCOMPRESSED:
160 case EC_OCT_POINT_HYBRID:
161 *out_len = 1 + 2 * BN_num_bytes(&group->field);
162 return 1;
163 default:
164 return 0;
165 }
166}
167
168static int
169ec_oct_field_element_is_valid(const EC_GROUP *group, const BIGNUM *bn)
170{
171 /* Ensure bn is in the range [0, field). */
172 return !BN_is_negative(bn) && BN_cmp(&group->field, bn) > 0;
173}
174
175static int
176ec_oct_add_field_element_cbb(CBB *cbb, const EC_GROUP *group, const BIGNUM *bn)
177{
178 uint8_t *buf = NULL;
179 int buf_len = BN_num_bytes(&group->field);
180
181 if (!ec_oct_field_element_is_valid(group, bn)) {
182 ECerror(EC_R_BIGNUM_OUT_OF_RANGE);
183 return 0;
184 }
185 if (!CBB_add_space(cbb, &buf, buf_len)) {
186 ECerror(ERR_R_MALLOC_FAILURE);
187 return 0;
188 }
189 if (BN_bn2binpad(bn, buf, buf_len) != buf_len) {
190 ECerror(ERR_R_MALLOC_FAILURE);
191 return 0;
192 }
193
194 return 1;
195}
196
197static int
198ec_oct_get_field_element_cbs(CBS *cbs, const EC_GROUP *group, BIGNUM *bn)
199{
200 CBS field_element;
201
202 if (!CBS_get_bytes(cbs, &field_element, BN_num_bytes(&group->field))) {
203 ECerror(EC_R_INVALID_ENCODING);
204 return 0;
205 }
206 if (!BN_bin2bn(CBS_data(&field_element), CBS_len(&field_element), bn)) {
207 ECerror(ERR_R_MALLOC_FAILURE);
208 return 0;
209 }
210 if (!ec_oct_field_element_is_valid(group, bn)) {
211 ECerror(EC_R_BIGNUM_OUT_OF_RANGE);
212 return 0;
213 }
214
215 return 1;
216}
217
218size_t
219ec_GFp_simple_point2oct(const EC_GROUP *group, const EC_POINT *point,
220 point_conversion_form_t conversion_form, unsigned char *buf, size_t len,
221 BN_CTX *ctx)
222{
223 CBB cbb;
224 uint8_t form;
225 BIGNUM *x, *y;
226 size_t encoded_length;
227 size_t ret = 0;
228
229 if (conversion_form > UINT8_MAX) {
230 ECerror(EC_R_INVALID_FORM);
231 return 0;
232 }
233
234 form = conversion_form;
235
236 /*
237 * Established behavior is to reject a request for the form 0 for the
238 * point at infinity even if it is valid.
239 */
240 if (form == 0 || !ec_oct_conversion_form_is_valid(form)) {
241 ECerror(EC_R_INVALID_FORM);
242 return 0;
243 }
244
245 if (EC_POINT_is_at_infinity(group, point))
246 form = EC_OCT_POINT_AT_INFINITY;
247
248 if (!ec_oct_encoded_length(group, form, &encoded_length)) {
249 ECerror(EC_R_INVALID_FORM);
250 return 0;
251 }
252
253 if (buf == NULL)
254 return encoded_length;
255
256 if (len < encoded_length) {
257 ECerror(EC_R_BUFFER_TOO_SMALL);
258 return 0;
259 }
260
261 BN_CTX_start(ctx);
262 if (!CBB_init_fixed(&cbb, buf, len))
263 goto err;
264
265 if (form == EC_OCT_POINT_AT_INFINITY) {
266 if (!ec_oct_add_leading_octet_cbb(&cbb, form, 0))
267 goto err;
268
269 goto done;
270 }
271
272 if ((x = BN_CTX_get(ctx)) == NULL)
273 goto err;
274 if ((y = BN_CTX_get(ctx)) == NULL)
275 goto err;
276 if (!EC_POINT_get_affine_coordinates(group, point, x, y, ctx))
277 goto err;
278
279 if (!ec_oct_add_leading_octet_cbb(&cbb, form, BN_is_odd(y)))
280 goto err;
281
282 if (form == EC_OCT_POINT_COMPRESSED) {
283 if (!ec_oct_add_field_element_cbb(&cbb, group, x))
284 goto err;
285 } else {
286 if (!ec_oct_add_field_element_cbb(&cbb, group, x))
287 goto err;
288 if (!ec_oct_add_field_element_cbb(&cbb, group, y))
289 goto err;
290 }
291
292 done:
293 if (!CBB_finish(&cbb, NULL, &ret))
294 goto err;
295
296 if (ret != encoded_length) {
297 ret = 0;
298 goto err;
299 }
300
301 err:
302 CBB_cleanup(&cbb);
303 BN_CTX_end(ctx);
304
305 return ret;
306}
307
308int
309ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
310 const unsigned char *buf, size_t len, BN_CTX *ctx)
311{
312 CBS cbs;
313 uint8_t form;
314 int ybit;
315 BIGNUM *x, *y;
316 int ret = 0;
317
318 BN_CTX_start(ctx);
319 CBS_init(&cbs, buf, len);
320
321 if (!ec_oct_get_leading_octet_cbs(&cbs, &form, &ybit))
322 goto err;
323
324 if (form == EC_OCT_POINT_AT_INFINITY) {
325 if (!EC_POINT_set_to_infinity(group, point))
326 goto err;
327
328 goto done;
329 }
330
331 if ((x = BN_CTX_get(ctx)) == NULL)
332 goto err;
333 if ((y = BN_CTX_get(ctx)) == NULL)
334 goto err;
335
336 if (form == EC_OCT_POINT_COMPRESSED) {
337 if (!ec_oct_get_field_element_cbs(&cbs, group, x))
338 goto err;
339 if (!EC_POINT_set_compressed_coordinates(group, point, x, ybit, ctx))
340 goto err;
341 } else {
342 if (!ec_oct_get_field_element_cbs(&cbs, group, x))
343 goto err;
344 if (!ec_oct_get_field_element_cbs(&cbs, group, y))
345 goto err;
346 if (!ec_oct_check_hybrid_ybit_is_consistent(form, ybit, y))
347 goto err;
348 if (!EC_POINT_set_affine_coordinates(group, point, x, y, ctx))
349 goto err;
350 }
351
352 done:
353 if (CBS_len(&cbs) > 0) {
354 ECerror(EC_R_INVALID_ENCODING);
355 goto err;
356 }
357
358 ret = 1;
359
360 err:
361 BN_CTX_end(ctx);
362
363 return ret;
364}
365
72int 366int
73ec_point_to_octets(const EC_GROUP *group, const EC_POINT *point, int form, 367ec_point_to_octets(const EC_GROUP *group, const EC_POINT *point, int form,
74 unsigned char **out_buf, size_t *out_len, BN_CTX *ctx) 368 unsigned char **out_buf, size_t *out_len, BN_CTX *ctx)
diff --git a/src/lib/libcrypto/ec/ecp_oct.c b/src/lib/libcrypto/ec/ecp_oct.c
index ce63bfadc4..0f4d8cb551 100644
--- a/src/lib/libcrypto/ec/ecp_oct.c
+++ b/src/lib/libcrypto/ec/ecp_oct.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ecp_oct.c,v 1.29 2024/10/25 18:06:42 tb Exp $ */ 1/* $OpenBSD: ecp_oct.c,v 1.30 2024/10/30 18:16:34 tb Exp $ */
2/* Includes code written by Lenka Fibikova <fibikova@exp-math.uni-essen.de> 2/* Includes code written by Lenka Fibikova <fibikova@exp-math.uni-essen.de>
3 * for the OpenSSL project. 3 * for the OpenSSL project.
4 * Includes code written by Bodo Moeller for the OpenSSL project. 4 * Includes code written by Bodo Moeller for the OpenSSL project.
@@ -184,297 +184,3 @@ ec_GFp_simple_set_compressed_coordinates(const EC_GROUP *group,
184 184
185 return ret; 185 return ret;
186} 186}
187
188/*
189 * Only the last three bits of the leading octet of a point should be set.
190 * Bits 3 and 2 encode the conversion form for all points except the point
191 * at infinity. In compressed and hybrid form bit 1 indicates if the even
192 * or the odd solution of the quadratic equation for y should be used.
193 *
194 * The public point_conversion_t enum lacks the point at infinity, so we
195 * ignore it except at the API boundary.
196 */
197
198#define EC_OCT_YBIT 0x01
199
200#define EC_OCT_POINT_AT_INFINITY 0x00
201#define EC_OCT_POINT_COMPRESSED 0x02
202#define EC_OCT_POINT_UNCOMPRESSED 0x04
203#define EC_OCT_POINT_HYBRID 0x06
204#define EC_OCT_POINT_CONVERSION_MASK 0x06
205
206static int
207ec_oct_conversion_form_is_valid(uint8_t form)
208{
209 return (form & EC_OCT_POINT_CONVERSION_MASK) == form;
210}
211
212static int
213ec_oct_check_hybrid_ybit_is_consistent(uint8_t form, int ybit, const BIGNUM *y)
214{
215 if (form == EC_OCT_POINT_HYBRID && ybit != BN_is_odd(y)) {
216 ECerror(EC_R_INVALID_ENCODING);
217 return 0;
218 }
219
220 return 1;
221}
222
223/* Nonzero y-bit only makes sense with compressed or hybrid encoding. */
224static int
225ec_oct_nonzero_ybit_allowed(uint8_t form)
226{
227 return form == EC_OCT_POINT_COMPRESSED || form == EC_OCT_POINT_HYBRID;
228}
229
230static int
231ec_oct_add_leading_octet_cbb(CBB *cbb, uint8_t form, int ybit)
232{
233 if (ec_oct_nonzero_ybit_allowed(form) && ybit != 0)
234 form |= EC_OCT_YBIT;
235
236 return CBB_add_u8(cbb, form);
237}
238
239static int
240ec_oct_get_leading_octet_cbs(CBS *cbs, uint8_t *out_form, int *out_ybit)
241{
242 uint8_t octet;
243
244 if (!CBS_get_u8(cbs, &octet)) {
245 ECerror(EC_R_BUFFER_TOO_SMALL);
246 return 0;
247 }
248
249 *out_ybit = octet & EC_OCT_YBIT;
250 *out_form = octet & ~EC_OCT_YBIT;
251
252 if (!ec_oct_conversion_form_is_valid(*out_form)) {
253 ECerror(EC_R_INVALID_ENCODING);
254 return 0;
255 }
256
257 if (*out_ybit != 0 && !ec_oct_nonzero_ybit_allowed(*out_form)) {
258 ECerror(EC_R_INVALID_ENCODING);
259 return 0;
260 }
261
262 return 1;
263}
264
265static int
266ec_oct_encoded_length(const EC_GROUP *group, uint8_t form, size_t *out_len)
267{
268 switch (form) {
269 case EC_OCT_POINT_AT_INFINITY:
270 *out_len = 1;
271 return 1;
272 case EC_OCT_POINT_COMPRESSED:
273 *out_len = 1 + BN_num_bytes(&group->field);
274 return 1;
275 case EC_OCT_POINT_UNCOMPRESSED:
276 case EC_OCT_POINT_HYBRID:
277 *out_len = 1 + 2 * BN_num_bytes(&group->field);
278 return 1;
279 default:
280 return 0;
281 }
282}
283
284static int
285ec_oct_field_element_is_valid(const EC_GROUP *group, const BIGNUM *bn)
286{
287 /* Ensure bn is in the range [0, field). */
288 return !BN_is_negative(bn) && BN_cmp(&group->field, bn) > 0;
289}
290
291static int
292ec_oct_add_field_element_cbb(CBB *cbb, const EC_GROUP *group, const BIGNUM *bn)
293{
294 uint8_t *buf = NULL;
295 int buf_len = BN_num_bytes(&group->field);
296
297 if (!ec_oct_field_element_is_valid(group, bn)) {
298 ECerror(EC_R_BIGNUM_OUT_OF_RANGE);
299 return 0;
300 }
301 if (!CBB_add_space(cbb, &buf, buf_len)) {
302 ECerror(ERR_R_MALLOC_FAILURE);
303 return 0;
304 }
305 if (BN_bn2binpad(bn, buf, buf_len) != buf_len) {
306 ECerror(ERR_R_MALLOC_FAILURE);
307 return 0;
308 }
309
310 return 1;
311}
312
313static int
314ec_oct_get_field_element_cbs(CBS *cbs, const EC_GROUP *group, BIGNUM *bn)
315{
316 CBS field_element;
317
318 if (!CBS_get_bytes(cbs, &field_element, BN_num_bytes(&group->field))) {
319 ECerror(EC_R_INVALID_ENCODING);
320 return 0;
321 }
322 if (!BN_bin2bn(CBS_data(&field_element), CBS_len(&field_element), bn)) {
323 ECerror(ERR_R_MALLOC_FAILURE);
324 return 0;
325 }
326 if (!ec_oct_field_element_is_valid(group, bn)) {
327 ECerror(EC_R_BIGNUM_OUT_OF_RANGE);
328 return 0;
329 }
330
331 return 1;
332}
333
334size_t
335ec_GFp_simple_point2oct(const EC_GROUP *group, const EC_POINT *point,
336 point_conversion_form_t conversion_form, unsigned char *buf, size_t len,
337 BN_CTX *ctx)
338{
339 CBB cbb;
340 uint8_t form;
341 BIGNUM *x, *y;
342 size_t encoded_length;
343 size_t ret = 0;
344
345 if (conversion_form > UINT8_MAX) {
346 ECerror(EC_R_INVALID_FORM);
347 return 0;
348 }
349
350 form = conversion_form;
351
352 /*
353 * Established behavior is to reject a request for the form 0 for the
354 * point at infinity even if it is valid.
355 */
356 if (form == 0 || !ec_oct_conversion_form_is_valid(form)) {
357 ECerror(EC_R_INVALID_FORM);
358 return 0;
359 }
360
361 if (EC_POINT_is_at_infinity(group, point))
362 form = EC_OCT_POINT_AT_INFINITY;
363
364 if (!ec_oct_encoded_length(group, form, &encoded_length)) {
365 ECerror(EC_R_INVALID_FORM);
366 return 0;
367 }
368
369 if (buf == NULL)
370 return encoded_length;
371
372 if (len < encoded_length) {
373 ECerror(EC_R_BUFFER_TOO_SMALL);
374 return 0;
375 }
376
377 BN_CTX_start(ctx);
378 if (!CBB_init_fixed(&cbb, buf, len))
379 goto err;
380
381 if (form == EC_OCT_POINT_AT_INFINITY) {
382 if (!ec_oct_add_leading_octet_cbb(&cbb, form, 0))
383 goto err;
384
385 goto done;
386 }
387
388 if ((x = BN_CTX_get(ctx)) == NULL)
389 goto err;
390 if ((y = BN_CTX_get(ctx)) == NULL)
391 goto err;
392 if (!EC_POINT_get_affine_coordinates(group, point, x, y, ctx))
393 goto err;
394
395 if (!ec_oct_add_leading_octet_cbb(&cbb, form, BN_is_odd(y)))
396 goto err;
397
398 if (form == EC_OCT_POINT_COMPRESSED) {
399 if (!ec_oct_add_field_element_cbb(&cbb, group, x))
400 goto err;
401 } else {
402 if (!ec_oct_add_field_element_cbb(&cbb, group, x))
403 goto err;
404 if (!ec_oct_add_field_element_cbb(&cbb, group, y))
405 goto err;
406 }
407
408 done:
409 if (!CBB_finish(&cbb, NULL, &ret))
410 goto err;
411
412 if (ret != encoded_length) {
413 ret = 0;
414 goto err;
415 }
416
417 err:
418 CBB_cleanup(&cbb);
419 BN_CTX_end(ctx);
420
421 return ret;
422}
423
424int
425ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
426 const unsigned char *buf, size_t len, BN_CTX *ctx)
427{
428 CBS cbs;
429 uint8_t form;
430 int ybit;
431 BIGNUM *x, *y;
432 int ret = 0;
433
434 BN_CTX_start(ctx);
435 CBS_init(&cbs, buf, len);
436
437 if (!ec_oct_get_leading_octet_cbs(&cbs, &form, &ybit))
438 goto err;
439
440 if (form == EC_OCT_POINT_AT_INFINITY) {
441 if (!EC_POINT_set_to_infinity(group, point))
442 goto err;
443
444 goto done;
445 }
446
447 if ((x = BN_CTX_get(ctx)) == NULL)
448 goto err;
449 if ((y = BN_CTX_get(ctx)) == NULL)
450 goto err;
451
452 if (form == EC_OCT_POINT_COMPRESSED) {
453 if (!ec_oct_get_field_element_cbs(&cbs, group, x))
454 goto err;
455 if (!EC_POINT_set_compressed_coordinates(group, point, x, ybit, ctx))
456 goto err;
457 } else {
458 if (!ec_oct_get_field_element_cbs(&cbs, group, x))
459 goto err;
460 if (!ec_oct_get_field_element_cbs(&cbs, group, y))
461 goto err;
462 if (!ec_oct_check_hybrid_ybit_is_consistent(form, ybit, y))
463 goto err;
464 if (!EC_POINT_set_affine_coordinates(group, point, x, y, ctx))
465 goto err;
466 }
467
468 done:
469 if (CBS_len(&cbs) > 0) {
470 ECerror(EC_R_INVALID_ENCODING);
471 goto err;
472 }
473
474 ret = 1;
475
476 err:
477 BN_CTX_end(ctx);
478
479 return ret;
480}