diff options
author | tb <> | 2025-06-14 07:50:37 +0000 |
---|---|---|
committer | tb <> | 2025-06-14 07:50:37 +0000 |
commit | e5a554b79b2bc282794430de6918d535b52eb82c (patch) | |
tree | a2d392b89037077b9834b578d0ed32d512467972 /src | |
parent | 591a81a1eb45c279f1a5b696396c259b999cd61d (diff) | |
download | openbsd-e5a554b79b2bc282794430de6918d535b52eb82c.tar.gz openbsd-e5a554b79b2bc282794430de6918d535b52eb82c.tar.bz2 openbsd-e5a554b79b2bc282794430de6918d535b52eb82c.zip |
ec_asn1_test: for curves that have a seed, validate the parameters a and b
per X9.62, F.3.4.b. This ensures that the table entries in ec_curves.c for
the NIST curves P-224, P-256, P-384, and P-521 are internally consistent
and in particular that the seed is correct.
Diffstat (limited to 'src')
-rw-r--r-- | src/regress/lib/libcrypto/ec/ec_asn1_test.c | 197 |
1 files changed, 195 insertions, 2 deletions
diff --git a/src/regress/lib/libcrypto/ec/ec_asn1_test.c b/src/regress/lib/libcrypto/ec/ec_asn1_test.c index d9501922de..a4066ae732 100644 --- a/src/regress/lib/libcrypto/ec/ec_asn1_test.c +++ b/src/regress/lib/libcrypto/ec/ec_asn1_test.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ec_asn1_test.c,v 1.33 2025/05/04 05:00:03 tb Exp $ */ | 1 | /* $OpenBSD: ec_asn1_test.c,v 1.34 2025/06/14 07:50:37 tb Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2017, 2021 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2017, 2021 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> | 4 | * Copyright (c) 2024 Theo Buehler <tb@openbsd.org> |
@@ -20,9 +20,10 @@ | |||
20 | #include <string.h> | 20 | #include <string.h> |
21 | 21 | ||
22 | #include <openssl/bio.h> | 22 | #include <openssl/bio.h> |
23 | #include <openssl/ec.h> | ||
24 | #include <openssl/err.h> | 23 | #include <openssl/err.h> |
24 | #include <openssl/ec.h> | ||
25 | #include <openssl/objects.h> | 25 | #include <openssl/objects.h> |
26 | #include <openssl/sha.h> | ||
26 | 27 | ||
27 | #include "ec_local.h" | 28 | #include "ec_local.h" |
28 | 29 | ||
@@ -2348,6 +2349,197 @@ ec_group_check_private_keys(void) | |||
2348 | return failed; | 2349 | return failed; |
2349 | } | 2350 | } |
2350 | 2351 | ||
2352 | static void | ||
2353 | ec_group_sha1_bignum(BIGNUM *out, const BIGNUM *in) | ||
2354 | { | ||
2355 | char md[SHA_DIGEST_LENGTH]; | ||
2356 | unsigned char *bin; | ||
2357 | size_t bin_len; | ||
2358 | |||
2359 | if (BN_num_bytes(in) <= 0) | ||
2360 | errx(1, "%s: invalid bignum", __func__); | ||
2361 | |||
2362 | bin_len = BN_num_bytes(in); | ||
2363 | if ((bin = calloc(1, bin_len)) == NULL) | ||
2364 | err(1, "calloc"); | ||
2365 | if (BN_bn2bin(in, bin) <= 0) | ||
2366 | errx(1, "BN_bn2bin"); | ||
2367 | |||
2368 | SHA1(bin, bin_len, md); | ||
2369 | free(bin); | ||
2370 | |||
2371 | if (BN_bin2bn(md, sizeof(md), out) == NULL) | ||
2372 | errx(1, "BN_bin2bn"); | ||
2373 | } | ||
2374 | |||
2375 | static int | ||
2376 | ec_group_check_seed(const EC_builtin_curve *curve, BN_CTX *ctx) | ||
2377 | { | ||
2378 | EC_GROUP *group = NULL; | ||
2379 | BIGNUM *p, *a, *b, *pow2, *r, *seed_bn, *w; | ||
2380 | const unsigned char *seed; | ||
2381 | size_t seed_len; | ||
2382 | int i, g, h, s, t; | ||
2383 | int failed = 1; | ||
2384 | |||
2385 | if ((group = EC_GROUP_new_by_curve_name(curve->nid)) == NULL) | ||
2386 | errx(1, "EC_GROUP_new_by_curve_name"); | ||
2387 | |||
2388 | BN_CTX_start(ctx); | ||
2389 | |||
2390 | if ((p = BN_CTX_get(ctx)) == NULL) | ||
2391 | errx(1, "p = BN_CTX_get()"); | ||
2392 | if ((a = BN_CTX_get(ctx)) == NULL) | ||
2393 | errx(1, "a = BN_CTX_get()"); | ||
2394 | if ((b = BN_CTX_get(ctx)) == NULL) | ||
2395 | errx(1, "b = BN_CTX_get()"); | ||
2396 | if ((r = BN_CTX_get(ctx)) == NULL) | ||
2397 | errx(1, "r = BN_CTX_get()"); | ||
2398 | if ((pow2 = BN_CTX_get(ctx)) == NULL) | ||
2399 | errx(1, "pow2 = BN_CTX_get()"); | ||
2400 | if ((seed_bn = BN_CTX_get(ctx)) == NULL) | ||
2401 | errx(1, "seed_bn = BN_CTX_get()"); | ||
2402 | if ((w = BN_CTX_get(ctx)) == NULL) | ||
2403 | errx(1, "w = BN_CTX_get()"); | ||
2404 | |||
2405 | /* | ||
2406 | * If the curve has a seed, verify that its parameters a and b have | ||
2407 | * been selected using that seed, loosely following X9.62, F.3.4.b. | ||
2408 | * Otherwise there's nothing to do. | ||
2409 | */ | ||
2410 | if ((seed = EC_GROUP_get0_seed(group)) == NULL) | ||
2411 | goto done; | ||
2412 | seed_len = EC_GROUP_get_seed_len(group); | ||
2413 | |||
2414 | /* | ||
2415 | * This isn't a requirement but happens to be the case for NIST | ||
2416 | * curves - the only built-in curves that have a seed. | ||
2417 | */ | ||
2418 | if (seed_len != SHA_DIGEST_LENGTH) { | ||
2419 | fprintf(stderr, "%s FAIL: unexpected seed length. " | ||
2420 | "want %d, got %zu\n", __func__, SHA_DIGEST_LENGTH, seed_len); | ||
2421 | goto err; | ||
2422 | } | ||
2423 | |||
2424 | /* Seed length in bits, per F.3.3.b. */ | ||
2425 | g = 8 * seed_len; | ||
2426 | |||
2427 | /* | ||
2428 | * Prepare to build the verifiably random element r of GFp by | ||
2429 | * concatenating the SHA-1 of modifications of the seed as a number. | ||
2430 | */ | ||
2431 | if (BN_bin2bn(seed, seed_len, seed_bn) == NULL) | ||
2432 | errx(1, "BN_bin2bn"); | ||
2433 | |||
2434 | if (!EC_GROUP_get_curve(group, p, a, b, ctx)) | ||
2435 | errx(1, "EC_GROUP_get_curve"); | ||
2436 | |||
2437 | t = BN_num_bits(p); /* bit length needed. */ | ||
2438 | s = (t - 1) / 160; /* number of SHA-1 fitting in bit length. */ | ||
2439 | h = t - 160 * s; /* remaining number of bits in r. */ | ||
2440 | |||
2441 | /* | ||
2442 | * Steps 1 - 3: compute hash of the seed and take h - 1 rightmost bits. | ||
2443 | */ | ||
2444 | |||
2445 | ec_group_sha1_bignum(r, seed_bn); | ||
2446 | BN_zero(pow2); | ||
2447 | if (!BN_set_bit(pow2, h - 1)) | ||
2448 | errx(1, "BN_set_bit"); | ||
2449 | if (!BN_mod(r, r, pow2, ctx)) | ||
2450 | errx(1, "BN_nnmod"); | ||
2451 | |||
2452 | /* | ||
2453 | * Steps 4 - 6: for i from 1 to s do Wi = SHA-1(SEED + i mod 2^g), | ||
2454 | * With W0 = r as already computed, let r = W0 || W1 || ... || Ws. | ||
2455 | */ | ||
2456 | |||
2457 | BN_zero(pow2); | ||
2458 | if (!BN_set_bit(pow2, g)) | ||
2459 | errx(1, "BN_set_bit"); | ||
2460 | |||
2461 | for (i = 0; i < s; i++) { | ||
2462 | /* | ||
2463 | * This is a bit silly since the seed isn't going to have all | ||
2464 | * its bits set, so BN_add_word(seed_bn, 1) would do, but for | ||
2465 | * the sake of correctness... | ||
2466 | */ | ||
2467 | if (!BN_mod_add(seed_bn, seed_bn, BN_value_one(), pow2, ctx)) | ||
2468 | errx(1, "BN_mod_add"); | ||
2469 | |||
2470 | ec_group_sha1_bignum(w, seed_bn); | ||
2471 | |||
2472 | if (!BN_lshift(r, r, 8 * SHA_DIGEST_LENGTH)) | ||
2473 | errx(1, "BN_lshift"); | ||
2474 | if (!BN_add(r, r, w)) | ||
2475 | errx(1, "BN_add"); | ||
2476 | } | ||
2477 | |||
2478 | /* | ||
2479 | * Step 7: check that r * b^2 == a^3 (mod p) | ||
2480 | */ | ||
2481 | |||
2482 | /* Compute r = r * b^2 (mod p). */ | ||
2483 | if (!BN_mod_sqr(b, b, p, ctx)) | ||
2484 | errx(1, "BN_mod_sqr"); | ||
2485 | if (!BN_mod_mul(r, r, b, p, ctx)) | ||
2486 | errx(1, "BN_mod_mul"); | ||
2487 | |||
2488 | /* Compute a = a^3 (mod p). */ | ||
2489 | if (!BN_mod_sqr(b, a, p, ctx)) | ||
2490 | errx(1, "BN_mod_sqr"); | ||
2491 | if (!BN_mod_mul(a, a, b, p, ctx)) | ||
2492 | errx(1, "BN_mod_mul"); | ||
2493 | |||
2494 | /* | ||
2495 | * XXX - this assumes that a, b, p >= 0, so the results are in [0, p). | ||
2496 | * This is currently enforced in the EC code. | ||
2497 | */ | ||
2498 | if (BN_cmp(r, a) != 0) { | ||
2499 | fprintf(stderr, "FAIL: %s verification failed for %s\nr * b^2:\t", | ||
2500 | __func__, curve->comment); | ||
2501 | BN_print_fp(stderr, r); | ||
2502 | fprintf(stderr, "\na^3:\t\t"); | ||
2503 | BN_print_fp(stderr, a); | ||
2504 | fprintf(stderr, "\n"); | ||
2505 | goto err; | ||
2506 | } | ||
2507 | |||
2508 | done: | ||
2509 | failed = 0; | ||
2510 | |||
2511 | err: | ||
2512 | BN_CTX_end(ctx); | ||
2513 | EC_GROUP_free(group); | ||
2514 | |||
2515 | return failed; | ||
2516 | } | ||
2517 | |||
2518 | static int | ||
2519 | ec_group_check_seeds(void) | ||
2520 | { | ||
2521 | BN_CTX *ctx = NULL; | ||
2522 | EC_builtin_curve *all_curves = NULL; | ||
2523 | size_t curve_id, ncurves; | ||
2524 | int failed = 0; | ||
2525 | |||
2526 | if ((ctx = BN_CTX_new()) == NULL) | ||
2527 | errx(1, "BN_CTX_new"); | ||
2528 | |||
2529 | ncurves = EC_get_builtin_curves(NULL, 0); | ||
2530 | if ((all_curves = calloc(ncurves, sizeof(*all_curves))) == NULL) | ||
2531 | err(1, "calloc builtin curves"); | ||
2532 | EC_get_builtin_curves(all_curves, ncurves); | ||
2533 | |||
2534 | for (curve_id = 0; curve_id < ncurves; curve_id++) | ||
2535 | failed |= ec_group_check_seed(&all_curves[curve_id], ctx); | ||
2536 | |||
2537 | free(all_curves); | ||
2538 | BN_CTX_free(ctx); | ||
2539 | |||
2540 | return failed; | ||
2541 | } | ||
2542 | |||
2351 | int | 2543 | int |
2352 | main(int argc, char **argv) | 2544 | main(int argc, char **argv) |
2353 | { | 2545 | { |
@@ -2359,6 +2551,7 @@ main(int argc, char **argv) | |||
2359 | failed |= ec_group_roundtrip_builtin_curves(); | 2551 | failed |= ec_group_roundtrip_builtin_curves(); |
2360 | failed |= ec_group_non_builtin_curves(); | 2552 | failed |= ec_group_non_builtin_curves(); |
2361 | failed |= ec_group_check_private_keys(); | 2553 | failed |= ec_group_check_private_keys(); |
2554 | failed |= ec_group_check_seeds(); | ||
2362 | 2555 | ||
2363 | return failed; | 2556 | return failed; |
2364 | } | 2557 | } |