diff options
-rw-r--r-- | src/lib/libcrypto/ec/ecp_oct.c | 153 |
1 files changed, 90 insertions, 63 deletions
diff --git a/src/lib/libcrypto/ec/ecp_oct.c b/src/lib/libcrypto/ec/ecp_oct.c index aa5a3906d1..0a66a5cd48 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.24 2024/10/22 21:08:49 tb Exp $ */ | 1 | /* $OpenBSD: ecp_oct.c,v 1.25 2024/10/22 21:10:45 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. |
@@ -217,6 +217,15 @@ ec_oct_nonzero_ybit_allowed(uint8_t form) | |||
217 | } | 217 | } |
218 | 218 | ||
219 | static int | 219 | static int |
220 | ec_oct_add_leading_octet_cbb(CBB *cbb, uint8_t form, int ybit) | ||
221 | { | ||
222 | if (ec_oct_nonzero_ybit_allowed(form) && ybit != 0) | ||
223 | form |= EC_OCT_YBIT; | ||
224 | |||
225 | return CBB_add_u8(cbb, form); | ||
226 | } | ||
227 | |||
228 | static int | ||
220 | ec_oct_get_leading_octet_cbs(CBS *cbs, uint8_t *out_form, int *out_ybit) | 229 | ec_oct_get_leading_octet_cbs(CBS *cbs, uint8_t *out_form, int *out_ybit) |
221 | { | 230 | { |
222 | uint8_t octet; | 231 | uint8_t octet; |
@@ -243,6 +252,25 @@ ec_oct_get_leading_octet_cbs(CBS *cbs, uint8_t *out_form, int *out_ybit) | |||
243 | } | 252 | } |
244 | 253 | ||
245 | static int | 254 | static int |
255 | ec_oct_encoded_length(const EC_GROUP *group, uint8_t form, size_t *out_len) | ||
256 | { | ||
257 | switch (form) { | ||
258 | case EC_OCT_POINT_AT_INFINITY: | ||
259 | *out_len = 1; | ||
260 | return 1; | ||
261 | case EC_OCT_POINT_COMPRESSED: | ||
262 | *out_len = 1 + BN_num_bytes(&group->field); | ||
263 | return 1; | ||
264 | case EC_OCT_POINT_UNCOMPRESSED: | ||
265 | case EC_OCT_POINT_HYBRID: | ||
266 | *out_len = 1 + 2 * BN_num_bytes(&group->field); | ||
267 | return 1; | ||
268 | default: | ||
269 | return 0; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | static int | ||
246 | ec_oct_field_element_is_valid(const EC_GROUP *group, const BIGNUM *bn) | 274 | ec_oct_field_element_is_valid(const EC_GROUP *group, const BIGNUM *bn) |
247 | { | 275 | { |
248 | /* Ensure bn is in the range [0, field). */ | 276 | /* Ensure bn is in the range [0, field). */ |
@@ -250,6 +278,28 @@ ec_oct_field_element_is_valid(const EC_GROUP *group, const BIGNUM *bn) | |||
250 | } | 278 | } |
251 | 279 | ||
252 | static int | 280 | static int |
281 | ec_oct_add_field_element_cbb(CBB *cbb, const EC_GROUP *group, const BIGNUM *bn) | ||
282 | { | ||
283 | uint8_t *buf = NULL; | ||
284 | int buf_len = BN_num_bytes(&group->field); | ||
285 | |||
286 | if (!ec_oct_field_element_is_valid(group, bn)) { | ||
287 | ECerror(EC_R_BIGNUM_OUT_OF_RANGE); | ||
288 | return 0; | ||
289 | } | ||
290 | if (!CBB_add_space(cbb, &buf, buf_len)) { | ||
291 | ECerror(ERR_R_MALLOC_FAILURE); | ||
292 | return 0; | ||
293 | } | ||
294 | if (BN_bn2binpad(bn, buf, buf_len) != buf_len) { | ||
295 | ECerror(ERR_R_MALLOC_FAILURE); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | return 1; | ||
300 | } | ||
301 | |||
302 | static int | ||
253 | ec_oct_get_field_element_cbs(CBS *cbs, const EC_GROUP *group, BIGNUM *bn) | 303 | ec_oct_get_field_element_cbs(CBS *cbs, const EC_GROUP *group, BIGNUM *bn) |
254 | { | 304 | { |
255 | CBS field_element; | 305 | CBS field_element; |
@@ -275,9 +325,10 @@ ec_GFp_simple_point2oct(const EC_GROUP *group, const EC_POINT *point, | |||
275 | point_conversion_form_t conversion_form, unsigned char *buf, size_t len, | 325 | point_conversion_form_t conversion_form, unsigned char *buf, size_t len, |
276 | BN_CTX *ctx) | 326 | BN_CTX *ctx) |
277 | { | 327 | { |
328 | CBB cbb; | ||
278 | uint8_t form; | 329 | uint8_t form; |
279 | BIGNUM *x, *y; | 330 | BIGNUM *x, *y; |
280 | size_t field_len, i, skip; | 331 | size_t encoded_length; |
281 | size_t ret = 0; | 332 | size_t ret = 0; |
282 | 333 | ||
283 | if (conversion_form > UINT8_MAX) { | 334 | if (conversion_form > UINT8_MAX) { |
@@ -296,82 +347,58 @@ ec_GFp_simple_point2oct(const EC_GROUP *group, const EC_POINT *point, | |||
296 | return 0; | 347 | return 0; |
297 | } | 348 | } |
298 | 349 | ||
299 | if (EC_POINT_is_at_infinity(group, point) > 0) { | 350 | if (EC_POINT_is_at_infinity(group, point)) |
300 | /* encodes to a single 0 octet */ | 351 | form = EC_OCT_POINT_AT_INFINITY; |
301 | if (buf != NULL) { | 352 | |
302 | if (len < 1) { | 353 | if (!ec_oct_encoded_length(group, form, &encoded_length)) { |
303 | ECerror(EC_R_BUFFER_TOO_SMALL); | 354 | ECerror(EC_R_INVALID_FORM); |
304 | return 0; | 355 | return 0; |
305 | } | ||
306 | buf[0] = 0; | ||
307 | } | ||
308 | return 1; | ||
309 | } | 356 | } |
310 | 357 | ||
311 | /* ret := required output buffer length */ | 358 | if (buf == NULL) |
312 | field_len = BN_num_bytes(&group->field); | 359 | return encoded_length; |
313 | ret = (form == POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len; | 360 | |
361 | if (len < encoded_length) { | ||
362 | ECerror(EC_R_BUFFER_TOO_SMALL); | ||
363 | return 0; | ||
364 | } | ||
314 | 365 | ||
366 | CBB_init_fixed(&cbb, buf, len); | ||
315 | BN_CTX_start(ctx); | 367 | BN_CTX_start(ctx); |
316 | 368 | ||
317 | /* if 'buf' is NULL, just return required length */ | 369 | if ((x = BN_CTX_get(ctx)) == NULL) |
318 | if (buf != NULL) { | 370 | goto err; |
319 | if (len < ret) { | 371 | if ((y = BN_CTX_get(ctx)) == NULL) |
320 | ECerror(EC_R_BUFFER_TOO_SMALL); | 372 | goto err; |
321 | goto err; | 373 | if (!EC_POINT_get_affine_coordinates(group, point, x, y, ctx)) |
322 | } | 374 | goto err; |
375 | |||
376 | if (!ec_oct_add_leading_octet_cbb(&cbb, form, BN_is_odd(y))) | ||
377 | goto err; | ||
323 | 378 | ||
324 | if ((x = BN_CTX_get(ctx)) == NULL) | 379 | if (form == EC_OCT_POINT_AT_INFINITY) { |
380 | /* Encoded in leading octet. */; | ||
381 | } else if (form == EC_OCT_POINT_COMPRESSED) { | ||
382 | if (!ec_oct_add_field_element_cbb(&cbb, group, x)) | ||
325 | goto err; | 383 | goto err; |
326 | if ((y = BN_CTX_get(ctx)) == NULL) | 384 | } else { |
385 | if (!ec_oct_add_field_element_cbb(&cbb, group, x)) | ||
327 | goto err; | 386 | goto err; |
328 | 387 | if (!ec_oct_add_field_element_cbb(&cbb, group, y)) | |
329 | if (!EC_POINT_get_affine_coordinates(group, point, x, y, ctx)) | ||
330 | goto err; | 388 | goto err; |
389 | } | ||
331 | 390 | ||
332 | if ((form == POINT_CONVERSION_COMPRESSED || form == POINT_CONVERSION_HYBRID) && BN_is_odd(y)) | 391 | if (!CBB_finish(&cbb, NULL, &ret)) |
333 | buf[0] = form + 1; | 392 | goto err; |
334 | else | ||
335 | buf[0] = form; | ||
336 | |||
337 | i = 1; | ||
338 | 393 | ||
339 | skip = field_len - BN_num_bytes(x); | 394 | if (ret != encoded_length) { |
340 | if (skip > field_len) { | 395 | ret = 0; |
341 | ECerror(ERR_R_INTERNAL_ERROR); | 396 | goto err; |
342 | goto err; | ||
343 | } | ||
344 | while (skip > 0) { | ||
345 | buf[i++] = 0; | ||
346 | skip--; | ||
347 | } | ||
348 | skip = BN_bn2bin(x, buf + i); | ||
349 | i += skip; | ||
350 | if (i != 1 + field_len) { | ||
351 | ECerror(ERR_R_INTERNAL_ERROR); | ||
352 | goto err; | ||
353 | } | ||
354 | if (form == POINT_CONVERSION_UNCOMPRESSED || form == POINT_CONVERSION_HYBRID) { | ||
355 | skip = field_len - BN_num_bytes(y); | ||
356 | if (skip > field_len) { | ||
357 | ECerror(ERR_R_INTERNAL_ERROR); | ||
358 | goto err; | ||
359 | } | ||
360 | while (skip > 0) { | ||
361 | buf[i++] = 0; | ||
362 | skip--; | ||
363 | } | ||
364 | skip = BN_bn2bin(y, buf + i); | ||
365 | i += skip; | ||
366 | } | ||
367 | if (i != ret) { | ||
368 | ECerror(ERR_R_INTERNAL_ERROR); | ||
369 | goto err; | ||
370 | } | ||
371 | } | 397 | } |
372 | 398 | ||
373 | err: | 399 | err: |
374 | BN_CTX_end(ctx); | 400 | BN_CTX_end(ctx); |
401 | CBB_cleanup(&cbb); | ||
375 | 402 | ||
376 | return ret; | 403 | return ret; |
377 | } | 404 | } |