From edb43bc290046bd22548bf69ae2fbeb453112e44 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 18 Dec 2024 14:18:26 +0900 Subject: [PATCH 11/19] fips,cipher: Implement FIPS service indicator for gcry_pk_hash_ API. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * src/visibility.c (gcry_pk_hash_sign): Initialize the indicator. (gcry_pk_hash_verify): Likewise. * tests/t-fips-service-ind.c (check_pk_hash_sign_verify): New. (main): Call check_pk_hash_sign_verify. * cipher/ecc-curves.c (_gcry_ecc_fill_in_curve): Don't reject, but mark non-compliance. * cipher/pubkey.c (prepare_datasexp_to_be_signed): Likewise. (_gcry_pk_sign_md, _gcry_pk_verify_md): Likewise. -- GnuPG-bug-id: 7338 Signed-off-by: NIIBE Yutaka Signed-off-by: Lucas Mülling --- cipher/ecc-curves.c | 2 +- cipher/pubkey.c | 20 ++-- src/visibility.c | 2 + tests/t-fips-service-ind.c | 209 +++++++++++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+), 11 deletions(-) diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c index 17fa5505..ddf9cbe1 100644 --- a/cipher/ecc-curves.c +++ b/cipher/ecc-curves.c @@ -645,7 +645,7 @@ _gcry_ecc_fill_in_curve (unsigned int nbits, const char *name, possible to bypass this check by specifying the curve parameters directly. */ if (fips_mode () && !domain_parms[idx].fips ) - return GPG_ERR_NOT_SUPPORTED; + fips_service_indicator_mark_non_compliant (); switch (domain_parms[idx].model) { diff --git a/cipher/pubkey.c b/cipher/pubkey.c index 214bd611..e2e54199 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -510,10 +510,7 @@ prepare_datasexp_to_be_signed (const char *tmpl, gcry_md_hd_t hd, algo = _gcry_md_get_algo (hd); if (fips_mode () && algo == GCRY_MD_SHA1) - { - _gcry_md_close (hd); - return GPG_ERR_DIGEST_ALGO; - } + fips_service_indicator_mark_non_compliant (); digest_name = _gcry_md_algo_name (algo); digest_size = (int)_gcry_md_get_algo_dlen (algo); @@ -535,12 +532,13 @@ prepare_datasexp_to_be_signed (const char *tmpl, gcry_md_hd_t hd, algo = _gcry_md_map_name (digest_name_supplied); xfree (digest_name_supplied); - if (algo == 0 - || (fips_mode () && algo == GCRY_MD_SHA1)) + if (algo == 0) { _gcry_md_close (hd); return GPG_ERR_DIGEST_ALGO; } + else if (fips_mode () && algo == GCRY_MD_SHA1) + fips_service_indicator_mark_non_compliant (); digest_size = (int)_gcry_md_get_algo_dlen (algo); digest = _gcry_md_read (hd, algo); @@ -613,10 +611,11 @@ _gcry_pk_sign_md (gcry_sexp_t *r_sig, const char *tmpl, gcry_md_hd_t hd_orig, if (rc) goto leave; + if (!spec->flags.fips && fips_mode ()) + fips_service_indicator_mark_non_compliant (); + if (spec->flags.disabled) rc = GPG_ERR_PUBKEY_ALGO; - else if (!spec->flags.fips && fips_mode ()) - rc = GPG_ERR_PUBKEY_ALGO; else if (spec->sign) rc = spec->sign (r_sig, s_data, keyparms); else @@ -690,10 +689,11 @@ _gcry_pk_verify_md (gcry_sexp_t s_sig, const char *tmpl, gcry_md_hd_t hd_orig, if (rc) goto leave; + if (!spec->flags.fips && fips_mode ()) + fips_service_indicator_mark_non_compliant (); + if (spec->flags.disabled) rc = GPG_ERR_PUBKEY_ALGO; - else if (!spec->flags.fips && fips_mode ()) - rc = GPG_ERR_PUBKEY_ALGO; else if (spec->verify) rc = spec->verify (s_sig, s_data, keyparms); else diff --git a/src/visibility.c b/src/visibility.c index c9d07f0b..d22c8b59 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -1056,6 +1056,7 @@ gcry_pk_hash_sign (gcry_sexp_t *result, const char *data_tmpl, gcry_sexp_t skey, *result = NULL; return gpg_error (fips_not_operational ()); } + fips_service_indicator_init (); return gpg_error (_gcry_pk_sign_md (result, data_tmpl, hd, skey, ctx)); } @@ -1073,6 +1074,7 @@ gcry_pk_hash_verify (gcry_sexp_t sigval, const char *data_tmpl, gcry_sexp_t pkey { if (!fips_is_operational ()) return gpg_error (fips_not_operational ()); + fips_service_indicator_init (); return gpg_error (_gcry_pk_verify_md (sigval, data_tmpl, hd, pkey, ctx)); } diff --git a/tests/t-fips-service-ind.c b/tests/t-fips-service-ind.c index 4b13436f..9a22d696 100644 --- a/tests/t-fips-service-ind.c +++ b/tests/t-fips-service-ind.c @@ -29,6 +29,7 @@ #define PGM "t-fips-service-ind" +#define NEED_HEX2BUFFER #include "t-common.h" static int in_fips_mode; #define MAX_DATA_LEN 1040 @@ -39,6 +40,213 @@ static int in_fips_mode; # include #endif +/* Check gcry_pk_hash_sign, gcry_pk_hash_verify API. */ +static void +check_pk_hash_sign_verify (void) +{ + static struct { + int md_algo; + const char *prvkey; + const char *pubkey; + const char *data_tmpl; + const char *k; + int expect_failure; + int expect_failure_hash; + } tv[] = { + { /* non-compliant hash */ + GCRY_MD_BLAKE2B_512, + "(private-key (ecc (curve nistp256)" + " (d #519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464#)))", + "(public-key (ecc (curve nistp256)" + " (q #041ccbe91c075fc7f4f033bfa248db8fccd3565de94bbfb12f3c59ff46c271bf83" + "ce4014c68811f9a21a1fdb2c0e6113e06db7ca93b7404e78dc7ccd5ca89a4ca9#)))", + "(data(flags raw)(hash %s %b)(label %b))", + "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de", + 1, 1 + }, + { /* non-compliant curve */ + GCRY_MD_SHA256, + "(private-key (ecc (curve secp256k1)" + " (d #c2cdf0a8b0a83b35ace53f097b5e6e6a0a1f2d40535eff1cf434f52a43d59d8f#)))", + + "(public-key (ecc (curve secp256k1)" + " (q #046fcc37ea5e9e09fec6c83e5fbd7a745e3eee81d16ebd861c9e66f55518c19798" + "4e9f113c07f875691df8afc1029496fc4cb9509b39dcd38f251a83359cc8b4f7#)))", + "(data(flags raw)(hash %s %b)(label %b))", + "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de", + 1, 0 + }, + { + GCRY_MD_SHA256, + "(private-key (ecc (curve nistp256)" + " (d #519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464#)))", + "(public-key (ecc (curve nistp256)" + " (q #041ccbe91c075fc7f4f033bfa248db8fccd3565de94bbfb12f3c59ff46c271bf83" + "ce4014c68811f9a21a1fdb2c0e6113e06db7ca93b7404e78dc7ccd5ca89a4ca9#)))", + "(data(flags raw)(hash %s %b)(label %b))", + "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de", + 0, 0 + } + }; + int tvidx; + gpg_error_t err; + gpg_err_code_t ec; + const char *msg = "Takerufuji Mikiya, who won the championship in March 2024"; + int msglen; + + msglen = strlen (msg); + for (tvidx=0; tvidx < DIM(tv); tvidx++) + { + gcry_md_hd_t hd = NULL; + gcry_sexp_t s_sk = NULL; + gcry_sexp_t s_pk = NULL; + void *buffer = NULL; + size_t buflen; + gcry_ctx_t ctx = NULL; + gcry_sexp_t s_sig= NULL; + + if (verbose) + info ("checking gcry_pk_hash_ test %d\n", tvidx); + + err = gcry_md_open (&hd, tv[tvidx].md_algo, 0); + if (err) + { + fail ("algo %d, gcry_md_open failed: %s\n", tv[tvidx].md_algo, + gpg_strerror (err)); + goto next; + } + + ec = gcry_get_fips_service_indicator (); + if (ec == GPG_ERR_INV_OP) + { + /* libgcrypt is old, no support of the FIPS service indicator. */ + fail ("gcry_pk_hash test %d unexpectedly failed to check the FIPS service indicator.\n", + tvidx); + goto next; + } + + if (in_fips_mode && !tv[tvidx].expect_failure_hash && ec) + { + /* Success with the FIPS service indicator == 0 expected, but != 0. */ + fail ("gcry_pk_hash test %d unexpectedly set the indicator in FIPS mode.\n", + tvidx); + goto next; + } + else if (in_fips_mode && tv[tvidx].expect_failure_hash && !ec) + { + /* Success with the FIPS service indicator != 0 expected, but == 0. */ + fail ("gcry_pk_hash test %d unexpectedly cleared the indicator in FIPS mode.\n", + tvidx); + goto next; + } + + err = gcry_sexp_build (&s_sk, NULL, tv[tvidx].prvkey); + if (err) + { + fail ("error building SEXP for test, %s: %s", + "sk", gpg_strerror (err)); + goto next; + } + + err = gcry_sexp_build (&s_pk, NULL, tv[tvidx].pubkey); + if (err) + { + fail ("error building SEXP for test, %s: %s", + "pk", gpg_strerror (err)); + goto next; + } + + if (!(buffer = hex2buffer (tv[tvidx].k, &buflen))) + { + fail ("error parsing for test, %s: %s", + "msg", "invalid hex string"); + goto next; + } + + err = gcry_pk_random_override_new (&ctx, buffer, buflen); + if (err) + { + fail ("error setting 'k' for test: %s", + gpg_strerror (err)); + goto next; + } + + gcry_md_write (hd, msg, msglen); + + err = gcry_pk_hash_sign (&s_sig, tv[tvidx].data_tmpl, s_sk, hd, ctx); + if (err) + { + fail ("gcry_pk_hash_sign failed: %s", gpg_strerror (err)); + goto next; + } + + ec = gcry_get_fips_service_indicator (); + if (ec == GPG_ERR_INV_OP) + { + /* libgcrypt is old, no support of the FIPS service indicator. */ + fail ("gcry_pk_hash test %d unexpectedly failed to check the FIPS service indicator.\n", + tvidx); + goto next; + } + + if (in_fips_mode && !tv[tvidx].expect_failure && ec) + { + /* Success with the FIPS service indicator == 0 expected, but != 0. */ + fail ("gcry_pk_hash test %d unexpectedly set the indicator in FIPS mode.\n", + tvidx); + goto next; + } + else if (in_fips_mode && tv[tvidx].expect_failure && !ec) + { + /* Success with the FIPS service indicator != 0 expected, but == 0. */ + fail ("gcry_pk_hash_sign test %d unexpectedly cleared the indicator in FIPS mode.\n", + tvidx); + goto next; + } + + err = gcry_pk_hash_verify (s_sig, tv[tvidx].data_tmpl, s_pk, hd, ctx); + if (err) + { + fail ("gcry_pk_hash_verify failed for test: %s", + gpg_strerror (err)); + goto next; + } + + ec = gcry_get_fips_service_indicator (); + if (ec == GPG_ERR_INV_OP) + { + /* libgcrypt is old, no support of the FIPS service indicator. */ + fail ("gcry_pk_hash test %d unexpectedly failed to check the FIPS service indicator.\n", + tvidx); + goto next; + } + + if (in_fips_mode && !tv[tvidx].expect_failure && ec) + { + /* Success with the FIPS service indicator == 0 expected, but != 0. */ + fail ("gcry_pk_hash test %d unexpectedly set the indicator in FIPS mode.\n", + tvidx); + goto next; + } + else if (in_fips_mode && tv[tvidx].expect_failure && !ec) + { + /* Success with the FIPS service indicator != 0 expected, but == 0. */ + fail ("gcry_pk_hash_verify test %d unexpectedly cleared the indicator in FIPS mode.\n", + tvidx); + goto next; + } + + next: + gcry_sexp_release (s_sig); + xfree (buffer); + gcry_ctx_release (ctx); + gcry_sexp_release (s_pk); + gcry_sexp_release (s_sk); + if (hd) + gcry_md_close (hd); + } +} + /* Check gcry_cipher_open, gcry_cipher_setkey, gcry_cipher_encrypt, gcry_cipher_decrypt, gcry_cipher_close API. */ static void @@ -936,6 +1144,7 @@ main (int argc, char **argv) check_md_o_w_r_c (); check_mac_o_w_r_c (); check_cipher_o_s_e_d_c (); + check_pk_hash_sign_verify (); return !!error_count; } -- 2.49.0