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 | } |
