diff --git a/SOURCES/libgcrypt-1.10.0-allow-short-salt.patch b/SOURCES/libgcrypt-1.10.0-allow-short-salt.patch index 46054cf..6800cf6 100644 --- a/SOURCES/libgcrypt-1.10.0-allow-short-salt.patch +++ b/SOURCES/libgcrypt-1.10.0-allow-short-salt.patch @@ -48,4 +48,30 @@ index c98247d8..aee5bffb 100644 -- 2.37.1 +commit 02718ade6ab5eee38169c2102097166770a2456d +Author: Jakub Jelen +Date: Thu Oct 20 16:33:11 2022 +0200 + visiblity: Check the HMAC key length in FIPS mode + + --- + * src/visibility.c (gcry_md_setkey): Check the HMAC key length in FIPS + mode also in the md_ API. + + Signed-off-by: Jakub Jelen + +diff --git a/src/visibility.c b/src/visibility.c +index 150b197d..73db3dea 100644 +--- a/src/visibility.c ++++ b/src/visibility.c +@@ -1357,6 +1357,10 @@ gcry_md_setkey (gcry_md_hd_t hd, const void *key, size_t keylen) + { + if (!fips_is_operational ()) + return gpg_error (fips_not_operational ()); ++ ++ if (fips_mode () && keylen < 14) ++ return GPG_ERR_INV_VALUE; ++ + return gpg_error (_gcry_md_setkey (hd, key, keylen)); + } + diff --git a/SOURCES/libgcrypt-1.10.0-fips-drbg.patch b/SOURCES/libgcrypt-1.10.0-fips-drbg.patch new file mode 100644 index 0000000..7b794e1 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-drbg.patch @@ -0,0 +1,85 @@ +From 45b80678109e5817b7cd15566a9d6c96b064b95f Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Wed, 1 Mar 2023 15:39:15 +0100 +Subject: [PATCH] random: Remove unused SHA384 DRBGs. + +* random/random-drbg.c (global): Remove unused SHA384-based defines. +(drbg_cores): Remove SHA384 configurations. +(drbg_sec_strength): Remove unused SHA384. +-- + +These are no longer allowed by FIPS and it looks like they were never +usable as they do not have any conversion from the string flags. + +GnuPG-bug-id: 6393 +Signed-off-by: Jakub Jelen +--- + random/random-drbg.c | 13 ++----------- + 1 file changed, 2 insertions(+), 11 deletions(-) + +diff --git a/random/random-drbg.c b/random/random-drbg.c +index f1cfe286..af49a5a5 100644 +--- a/random/random-drbg.c ++++ b/random/random-drbg.c +@@ -188,11 +188,9 @@ + #define DRBG_HASHSHA1 ((u32)1<<4) + #define DRBG_HASHSHA224 ((u32)1<<5) + #define DRBG_HASHSHA256 ((u32)1<<6) +-#define DRBG_HASHSHA384 ((u32)1<<7) + #define DRBG_HASHSHA512 ((u32)1<<8) + #define DRBG_HASH_MASK (DRBG_HASHSHA1 | DRBG_HASHSHA224 \ +- | DRBG_HASHSHA256 | DRBG_HASHSHA384 \ +- | DRBG_HASHSHA512) ++ | DRBG_HASHSHA256 | DRBG_HASHSHA512) + /* type modifiers (A.3)*/ + #define DRBG_HMAC ((u32)1<<12) + #define DRBG_SYM128 ((u32)1<<13) +@@ -211,23 +209,18 @@ + #define DRBG_NOPR_CTRAES256 (DRBG_CTRAES | DRBG_SYM256) + #define DRBG_PR_HASHSHA1 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA1) + #define DRBG_PR_HASHSHA256 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA256) +-#define DRBG_PR_HASHSHA384 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA384) + #define DRBG_PR_HASHSHA512 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA512) + #define DRBG_NOPR_HASHSHA1 (DRBG_HASHSHA1) + #define DRBG_NOPR_HASHSHA256 (DRBG_HASHSHA256) +-#define DRBG_NOPR_HASHSHA384 (DRBG_HASHSHA384) + #define DRBG_NOPR_HASHSHA512 (DRBG_HASHSHA512) + #define DRBG_PR_HMACSHA1 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA1 \ + | DRBG_HMAC) + #define DRBG_PR_HMACSHA256 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA256 \ + | DRBG_HMAC) +-#define DRBG_PR_HMACSHA384 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA384 \ +- | DRBG_HMAC) + #define DRBG_PR_HMACSHA512 (DRBG_PREDICTION_RESIST | DRBG_HASHSHA512 \ + | DRBG_HMAC) + #define DRBG_NOPR_HMACSHA1 (DRBG_HASHSHA1 | DRBG_HMAC) + #define DRBG_NOPR_HMACSHA256 (DRBG_HASHSHA256 | DRBG_HMAC) +-#define DRBG_NOPR_HMACSHA384 (DRBG_HASHSHA384 | DRBG_HMAC) + #define DRBG_NOPR_HMACSHA512 (DRBG_HASHSHA512 | DRBG_HMAC) + + +@@ -359,12 +352,10 @@ static const struct drbg_core_s drbg_cores[] = { + /* Hash DRBGs */ + {DRBG_HASHSHA1, 55, 20, GCRY_MD_SHA1}, + {DRBG_HASHSHA256, 55, 32, GCRY_MD_SHA256}, +- {DRBG_HASHSHA384, 111, 48, GCRY_MD_SHA384}, + {DRBG_HASHSHA512, 111, 64, GCRY_MD_SHA512}, + /* HMAC DRBGs */ + {DRBG_HASHSHA1 | DRBG_HMAC, 20, 20, GCRY_MD_SHA1}, + {DRBG_HASHSHA256 | DRBG_HMAC, 32, 32, GCRY_MD_SHA256}, +- {DRBG_HASHSHA384 | DRBG_HMAC, 48, 48, GCRY_MD_SHA384}, + {DRBG_HASHSHA512 | DRBG_HMAC, 64, 64, GCRY_MD_SHA512}, + /* block ciphers */ + {DRBG_CTRAES | DRBG_SYM128, 32, 16, GCRY_CIPHER_AES128}, +@@ -543,7 +534,7 @@ drbg_sec_strength (u32 flags) + else if (flags & DRBG_SYM192) + return 24; + else if ((flags & DRBG_SYM256) || (flags & DRBG_HASHSHA256) || +- (flags & DRBG_HASHSHA384) || (flags & DRBG_HASHSHA512)) ++ (flags & DRBG_HASHSHA512)) + return 32; + else + return 32; +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-indicator-md-hmac.patch b/SOURCES/libgcrypt-1.10.0-fips-indicator-md-hmac.patch new file mode 100644 index 0000000..a1c4e18 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-indicator-md-hmac.patch @@ -0,0 +1,277 @@ +From fd832687f36c1885d2388c55f7e8569184ba2593 Mon Sep 17 00:00:00 2001 +From: Tobias Heider +Date: Thu, 16 Feb 2023 03:20:48 +0100 +Subject: [PATCH] fips: Add explicit indicators for md and mac algorithms + +* src/fips.c (_gcry_fips_indicator_mac): New function indicating + non-approved mac algorithms + (_gcry_fips_indicator_md): new functions indicating non-approved + message digest algorithms +* src/g10lib.h (_gcry_fips_indicator_mac): new function + (_gcry_fips_indicator_md): ditto +* src/gcrypt.h.in (enum gcry_ctl_cmds): New symbols + GCRYCTL_FIPS_SERVICE_INDICATOR_MAC and + GCRYCTL_FIPS_SERVICE_INDICATOR_MD +* src/global.c (_gcry_vcontrol): Handle new FIPS indicators. +* doc/gcrypt.texi: Document the new option. +-- + +Signed-off-by: Tobias Heider +--- + doc/gcrypt.texi | 13 +++++++++++++ + src/fips.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ + src/g10lib.h | 2 ++ + src/gcrypt.h.in | 4 +++- + src/global.c | 14 ++++++++++++++ + 5 files changed, 83 insertions(+), 1 deletion(-) + +diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi +index e44c2f2e..462c5931 100644 +--- a/doc/gcrypt.texi ++++ b/doc/gcrypt.texi +@@ -992,6 +992,19 @@ certification. If the function is approved, this function returns + @code{GPG_ERR_NO_ERROR} (other restrictions might still apply). + Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. + ++@item GCRYCTL_FIPS_SERVICE_INDICATOR_MAC; Arguments: enum gcry_mac_algos ++ ++Check if the given MAC is approved under the current FIPS 140-3 ++certification. If the MAC is approved, this function returns ++@code{GPG_ERR_NO_ERROR}. Otherwise @code{GPG_ERR_NOT_SUPPORTED} ++is returned. ++ ++@item GCRYCTL_FIPS_SERVICE_INDICATOR_MD; Arguments: enum gcry_md_algos ++ ++Check if the given message digest algorithm is approved under the current ++FIPS 140-3 certification. If the algorithm is approved, this function returns ++@code{GPG_ERR_NO_ERROR}. Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. ++ + @end table + + @end deftypefun +diff --git a/src/fips.c b/src/fips.c +index 272aabae..8b3b3f04 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -377,6 +377,57 @@ _gcry_fips_indicator_cipher (va_list arg_ptr) + } + } + ++int ++_gcry_fips_indicator_mac (va_list arg_ptr) ++{ ++ enum gcry_mac_algos alg = va_arg (arg_ptr, enum gcry_mac_algos); ++ ++ switch (alg) ++ { ++ case GCRY_MAC_CMAC_AES: ++ case GCRY_MAC_HMAC_SHA1: ++ case GCRY_MAC_HMAC_SHA224: ++ case GCRY_MAC_HMAC_SHA256: ++ case GCRY_MAC_HMAC_SHA384: ++ case GCRY_MAC_HMAC_SHA512: ++ case GCRY_MAC_HMAC_SHA512_224: ++ case GCRY_MAC_HMAC_SHA512_256: ++ case GCRY_MAC_HMAC_SHA3_224: ++ case GCRY_MAC_HMAC_SHA3_256: ++ case GCRY_MAC_HMAC_SHA3_384: ++ case GCRY_MAC_HMAC_SHA3_512: ++ return GPG_ERR_NO_ERROR; ++ default: ++ return GPG_ERR_NOT_SUPPORTED; ++ } ++} ++ ++int ++_gcry_fips_indicator_md (va_list arg_ptr) ++{ ++ enum gcry_md_algos alg = va_arg (arg_ptr, enum gcry_md_algos); ++ ++ switch (alg) ++ { ++ case GCRY_MD_SHA1: ++ case GCRY_MD_SHA224: ++ case GCRY_MD_SHA256: ++ case GCRY_MD_SHA384: ++ case GCRY_MD_SHA512: ++ case GCRY_MD_SHA512_224: ++ case GCRY_MD_SHA512_256: ++ case GCRY_MD_SHA3_224: ++ case GCRY_MD_SHA3_256: ++ case GCRY_MD_SHA3_384: ++ case GCRY_MD_SHA3_512: ++ case GCRY_MD_SHAKE128: ++ case GCRY_MD_SHAKE256: ++ return GPG_ERR_NO_ERROR; ++ default: ++ return GPG_ERR_NOT_SUPPORTED; ++ } ++} ++ + int + _gcry_fips_indicator_kdf (va_list arg_ptr) + { +diff --git a/src/g10lib.h b/src/g10lib.h +index 6be0ab21..86337eed 100644 +--- a/src/g10lib.h ++++ b/src/g10lib.h +@@ -467,6 +467,8 @@ void _gcry_fips_signal_error (const char *srcfile, + #endif + + int _gcry_fips_indicator_cipher (va_list arg_ptr); ++int _gcry_fips_indicator_mac (va_list arg_ptr); ++int _gcry_fips_indicator_md (va_list arg_ptr); + int _gcry_fips_indicator_kdf (va_list arg_ptr); + int _gcry_fips_indicator_function (va_list arg_ptr); + +diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in +index aba22bfc..54080d46 100644 +--- a/src/gcrypt.h.in ++++ b/src/gcrypt.h.in +@@ -330,7 +330,9 @@ enum gcry_ctl_cmds + GCRYCTL_FIPS_SERVICE_INDICATOR_CIPHER = 81, + GCRYCTL_FIPS_SERVICE_INDICATOR_KDF = 82, + GCRYCTL_NO_FIPS_MODE = 83, +- GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION = 84 ++ GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION = 84, ++ GCRYCTL_FIPS_SERVICE_INDICATOR_MAC = 85, ++ GCRYCTL_FIPS_SERVICE_INDICATOR_MD = 86 + }; + + /* Perform various operations defined by CMD. */ +diff --git a/src/global.c b/src/global.c +index debf6194..d16d3709 100644 +--- a/src/global.c ++++ b/src/global.c +@@ -791,6 +791,20 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr) + rc = _gcry_fips_indicator_cipher (arg_ptr); + break; + ++ case GCRYCTL_FIPS_SERVICE_INDICATOR_MAC: ++ /* Get FIPS Service Indicator for a given message authentication code. ++ * Returns GPG_ERR_NO_ERROR if algorithm is allowed or ++ * GPG_ERR_NOT_SUPPORTED otherwise */ ++ rc = _gcry_fips_indicator_mac (arg_ptr); ++ break; ++ ++ case GCRYCTL_FIPS_SERVICE_INDICATOR_MD: ++ /* Get FIPS Service Indicator for a given message digest. Returns ++ * GPG_ERR_NO_ERROR if algorithm is allowed or GPG_ERR_NOT_SUPPORTED ++ * otherwise */ ++ rc = _gcry_fips_indicator_md (arg_ptr); ++ break; ++ + case GCRYCTL_FIPS_SERVICE_INDICATOR_KDF: + /* Get FIPS Service Indicator for a given KDF. Returns GPG_ERR_NO_ERROR + * if algorithm is allowed or GPG_ERR_NOT_SUPPORTED otherwise */ +-- +2.39.2 + +From 2d193a955d05b4b9caed2895cf25600add3484da Mon Sep 17 00:00:00 2001 +From: Tobias Heider +Date: Thu, 16 Feb 2023 03:21:26 +0100 +Subject: [PATCH] fips: Unblock MD5 in fips mode but mark non-approved in + indicator. + +* cipher/mac-hmac.c (_gcry_mac_type_spec_hmac_md5): allow in fips mode +* cipher/md5.c (_gcry_digest_spec_md5): allow in fips mode +-- + +Signed-off-by: Tobias Heider +--- + cipher/mac-hmac.c | 2 +- + cipher/md5.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/cipher/mac-hmac.c b/cipher/mac-hmac.c +index f1ab568b..9fac77dc 100644 +--- a/cipher/mac-hmac.c ++++ b/cipher/mac-hmac.c +@@ -1413,7 +1413,7 @@ const gcry_mac_spec_t _gcry_mac_type_spec_hmac_tiger1 = { + #endif + #if USE_MD5 + const gcry_mac_spec_t _gcry_mac_type_spec_hmac_md5 = { +- GCRY_MAC_HMAC_MD5, {0, 0}, "HMAC_MD5", ++ GCRY_MAC_HMAC_MD5, {0, 1}, "HMAC_MD5", + &hmac_ops + }; + #endif +diff --git a/cipher/md5.c b/cipher/md5.c +index 5457fc38..744a2cc1 100644 +--- a/cipher/md5.c ++++ b/cipher/md5.c +@@ -314,7 +314,7 @@ static const gcry_md_oid_spec_t oid_spec_md5[] = + + const gcry_md_spec_t _gcry_digest_spec_md5 = + { +- GCRY_MD_MD5, {0, 0}, ++ GCRY_MD_MD5, {0, 1}, + "MD5", asn, DIM (asn), oid_spec_md5, 16, + md5_init, _gcry_md_block_write, md5_final, md5_read, NULL, + NULL, +-- +2.39.2 + +From f52f33389da3302f51b6b00451cf9fc7e7a7e277 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 6 Mar 2023 17:26:17 +0100 +Subject: [PATCH] tests: Improve test coverage for FIPS service indicators + +* tests/basic.c (check_digests): Check the FIPS indicators + (check_mac): Ditto. +-- + +Signed-off-by: Jakub Jelen +--- + tests/basic.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/tests/basic.c b/tests/basic.c +index 095bdc97..5d5ceac9 100644 +--- a/tests/basic.c ++++ b/tests/basic.c +@@ -14086,6 +14086,7 @@ check_mac (void) + "\x13\x46\x76\xfb\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58" }, + { 0 }, + }; ++ gcry_error_t err; + int i; + + if (verbose) +@@ -15370,6 +15370,12 @@ check_digests (void) + { + if (in_fips_mode) + { ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_MD, algos[i].md); ++ if (err == GPG_ERR_NO_ERROR) ++ { ++ fail ("algo %d, gcry_md_test_algo failed while it should" ++ " have worked in FIPS mode\n", algos[i].md); ++ } + if (verbose) + fprintf (stderr, " algorithm %d not available in fips mode\n", + algos[i].md); +@@ -16948,6 +16954,7 @@ check_mac (void) + #endif /* USE_GOST28147 */ + { 0 }, + }; ++ gcry_error_t err; + int i; + + if (verbose) +@@ -16961,6 +16968,12 @@ check_mac (void) + { + if (in_fips_mode) + { ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_MAC, algos[i].algo); ++ if (err == GPG_ERR_NO_ERROR) ++ { ++ fail ("algo %d, gcry_mac_test_algo failed while it should" ++ " have worked in FIPS mode\n", algos[i].algo); ++ } + if (verbose) + fprintf (stderr, " algorithm %d not available in fips mode\n", + algos[i].algo); +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-indicator-pk-flags.patch b/SOURCES/libgcrypt-1.10.0-fips-indicator-pk-flags.patch new file mode 100644 index 0000000..059c596 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-indicator-pk-flags.patch @@ -0,0 +1,277 @@ +From 0c0268177666f6ce53c0a61e86c1c5bd2c53c0b0 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 6 Mar 2023 15:57:40 +0100 +Subject: [PATCH] fips: Explicitly allow only some PK flags + +* src/fips.c (_gcry_fips_indicator_pk_flags): New function for explicit + FIPS indicator for public key algorithm flags +* src/g10lib.h (_gcry_fips_indicator_pk_flags): New. +* src/gcrypt.h.in (GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS): New. +* src/global.c (_gcry_vcontrol): Handle the new option. +* doc/gcrypt.texi: Document new options. +-- + +Signed-off-by: Jakub Jelen +--- + doc/gcrypt.texi | 6 ++++++ + src/fips.c | 15 +++++++++++++++ + src/g10lib.h | 1 + + src/gcrypt.h.in | 3 ++- + src/global.c | 7 +++++++ + 5 files changed, 31 insertions(+), 1 deletion(-) + +diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi +index 462c5931..750b6718 100644 +--- a/doc/gcrypt.texi ++++ b/doc/gcrypt.texi +@@ -1005,6 +1005,12 @@ Check if the given message digest algorithm is approved under the current + FIPS 140-3 certification. If the algorithm is approved, this function returns + @code{GPG_ERR_NO_ERROR}. Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. + ++@item GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS; Arguments: const char * ++ ++Check if the given public key operation flag is approved under the current ++FIPS 140-3 certification. If the flag is approved, this function returns ++@code{GPG_ERR_NO_ERROR}. Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. ++ + @end table + + @end deftypefun +diff --git a/src/fips.c b/src/fips.c +index 974ed833..cb547aa2 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -457,6 +457,21 @@ _gcry_fips_indicator_function (va_list arg_ptr) + } + + ++int ++_gcry_fips_indicator_pk_flags (va_list arg_ptr) ++{ ++ const char *flag = va_arg (arg_ptr, const char *); ++ ++ if (strcmp (flag, "param") == 0 || ++ strcmp (flag, "raw") == 0 || ++ strcmp (flag, "no-blinding") == 0 || ++ strcmp (flag, "pss") == 0) ++ return GPG_ERR_NO_ERROR; ++ ++ return GPG_ERR_NOT_SUPPORTED; ++} ++ ++ + /* This is a test on whether the library is in the error or + operational state. */ + int +diff --git a/src/g10lib.h b/src/g10lib.h +index 86337eed..acff2d6b 100644 +--- a/src/g10lib.h ++++ b/src/g10lib.h +@@ -471,6 +471,7 @@ int _gcry_fips_indicator_mac (va_list arg_ptr); + int _gcry_fips_indicator_md (va_list arg_ptr); + int _gcry_fips_indicator_kdf (va_list arg_ptr); + int _gcry_fips_indicator_function (va_list arg_ptr); ++int _gcry_fips_indicator_pk_flags (va_list arg_ptr); + + int _gcry_fips_is_operational (void); + +diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in +index 54080d46..121a2061 100644 +--- a/src/gcrypt.h.in ++++ b/src/gcrypt.h.in +@@ -332,7 +332,8 @@ enum gcry_ctl_cmds + GCRYCTL_NO_FIPS_MODE = 83, + GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION = 84, + GCRYCTL_FIPS_SERVICE_INDICATOR_MAC = 85, +- GCRYCTL_FIPS_SERVICE_INDICATOR_MD = 86 ++ GCRYCTL_FIPS_SERVICE_INDICATOR_MD = 86, ++ GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS = 87 + }; + + /* Perform various operations defined by CMD. */ +diff --git a/src/global.c b/src/global.c +index d16d3709..f39df422 100644 +--- a/src/global.c ++++ b/src/global.c +@@ -818,6 +818,13 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr) + rc = _gcry_fips_indicator_function (arg_ptr); + break; + ++ case GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS: ++ /* Get FIPS Service Indicator for a public key operation flags. ++ * Returns GPG_ERR_NO_ERROR if the flag is allowed to be used or ++ * GPG_ERR_NOT_SUPPORTED otherwise */ ++ rc = _gcry_fips_indicator_pk_flags (arg_ptr); ++ break; ++ + case PRIV_CTL_INIT_EXTRNG_TEST: /* Init external random test. */ + rc = GPG_ERR_NOT_SUPPORTED; + break; +-- +2.39.2 + +From 22a40df4c0210a671b331932a434f70b50354873 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 6 Mar 2023 16:05:07 +0100 +Subject: [PATCH] fips: Explicitly disable overriding random in FIPS mode + +* src/fips.c: (_gcry_fips_indicator_function): Mark using random + override non-approved in FIPS mode. +-- + +Signed-off-by: Jakub Jelen +--- + src/fips.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/fips.c b/src/fips.c +index cb547aa2..a7342030 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -450,7 +450,8 @@ _gcry_fips_indicator_function (va_list arg_ptr) + if (strcmp (function, "gcry_pk_sign") == 0 || + strcmp (function, "gcry_pk_verify") == 0 || + strcmp (function, "gcry_pk_encrypt") == 0 || +- strcmp (function, "gcry_pk_decrypt") == 0) ++ strcmp (function, "gcry_pk_decrypt") == 0 || ++ strcmp (function, "gcry_pk_random_override_new") == 0) + return GPG_ERR_NOT_SUPPORTED; + + return GPG_ERR_NO_ERROR; +-- +2.39.2 + +From 1c916b8c99ea0e30f1d81d606fd63b0c45657186 Mon Sep 17 00:00:00 2001 +From: NIIBE Yutaka +Date: Fri, 24 Mar 2023 13:12:56 +0900 +Subject: [PATCH] fips: More elaborate way of getting FIPS pk flags indicators. + +* src/fips.c (_gcry_fips_indicator_pk_flags): List more allowed string +in the S-expression. +* doc/gcrypt.texi: Add document for the FIPS service indicator +GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS with example. + +-- + +GnuPG-bug-id: 6417 +Signed-off-by: Jakub Jelen +Signed-off-by: NIIBE Yutaka +--- + doc/gcrypt.texi | 42 +++++++++++++++++++++++++++++++++++++++--- + src/fips.c | 41 +++++++++++++++++++++++++++++++++++++---- + 2 files changed, 76 insertions(+), 7 deletions(-) + +diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi +index 750b6718..752f64d6 100644 +--- a/doc/gcrypt.texi ++++ b/doc/gcrypt.texi +@@ -1007,9 +1007,45 @@ FIPS 140-3 certification. If the algorithm is approved, this function returns + + @item GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS; Arguments: const char * + +-Check if the given public key operation flag is approved under the current +-FIPS 140-3 certification. If the flag is approved, this function returns +-@code{GPG_ERR_NO_ERROR}. Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. ++Check if the given public key operation flag or s-expression object name is ++approved under the current FIPS 140-3 certification. If the flag is ++approved, this function returns @code{GPG_ERR_NO_ERROR}. ++ ++Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. ++ ++For compound s-expression objects, if the object name is allowed, the user ++is responsible to check also the internal members. For example: ++ ++@example ++ gcry_sexp_t s_sig = NULL; ++ gcry_md_hd_t hd = NULL; ++ gcry_sexp_t s_sk = NULL; ++ const char *data_tmpl = "(data(flags pss)(hash %s %b)(salt-length 1:0))"; ++ ++ if (err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION, "gcry_md_open") && ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_MD, GCRY_MD_SHA512) && ++ err = gcry_md_open (&hd, GCRY_MD_SHA512, 0)) ++ @{ ++ printf ("gcry_md_open failed: %s", gpg_strerror (err)); ++ return; ++ @} ++ gcry_md_write (hd, buffer, buflen); ++ ++ /* initialize the key in s_sk */ ++ ++ if (err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION, "gcry_pk_hash_sign") && ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS, "data") && ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS, "flags") && ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS, "pss") && ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS, "hash") && ++ err = gcry_control(GCRYCTL_FIPS_SERVICE_INDICATOR_PK_FLAGS, "salt-length") ++ err = gcry_pk_hash_sign (&s_sig, data_tmpl, s_sk, hd, NULL)) ++ @{ ++ printf ("gcry_pk_hash_sign failed: %s", gpg_strerror (err)); ++ return; ++ @} ++ /* ok */ ++@end example + + @end table + +diff --git a/src/fips.c b/src/fips.c +index a7342030..669cfd0e 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -457,16 +457,49 @@ _gcry_fips_indicator_function (va_list arg_ptr) + return GPG_ERR_NO_ERROR; + } + ++/* Note: the array should be sorted. */ ++static const char *valid_string_in_sexp[] = { ++ "curve", ++ "d", ++ "data", ++ "e", ++ "ecdsa", ++ "flags", ++ "genkey", ++ "hash", ++ "n", ++ "nbits", ++ "pkcs1", ++ "private-key", ++ "pss", ++ "public-key", ++ "q", ++ "r", ++ "raw", ++ "rsa", ++ "rsa-use-e", ++ "s", ++ "salt-length", ++ "sig-val", ++ "value" ++}; ++ ++static int ++compare_string (const void *v1, const void *v2) ++{ ++ const char * const *p_str1 = v1; ++ const char * const *p_str2 = v2; ++ ++ return strcmp (*p_str1, *p_str2); ++} + + int + _gcry_fips_indicator_pk_flags (va_list arg_ptr) + { + const char *flag = va_arg (arg_ptr, const char *); + +- if (strcmp (flag, "param") == 0 || +- strcmp (flag, "raw") == 0 || +- strcmp (flag, "no-blinding") == 0 || +- strcmp (flag, "pss") == 0) ++ if (bsearch (&flag, valid_string_in_sexp, DIM (valid_string_in_sexp), ++ sizeof (char *), compare_string)) + return GPG_ERR_NO_ERROR; + + return GPG_ERR_NOT_SUPPORTED; +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-indicator.patch b/SOURCES/libgcrypt-1.10.0-fips-indicator.patch new file mode 100644 index 0000000..2fdf808 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-indicator.patch @@ -0,0 +1,55 @@ +From c34c9e70055ee43e5ef257384fa15941f064e5a4 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 15 Nov 2022 10:47:18 +0100 +Subject: [PATCH] fips: Mark AES key wrapping as approved. + +* src/fips.c (_gcry_fips_indicator_cipher): Add key wrapping mode as +approved. + +-- + +GnuPG-bug-id: 5512 +Signed-off-by: Jakub Jelen +--- + src/fips.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/fips.c b/src/fips.c +index 6599121c..272aabae 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -367,6 +367,7 @@ _gcry_fips_indicator_cipher (va_list arg_ptr) + case GCRY_CIPHER_MODE_CCM: + case GCRY_CIPHER_MODE_GCM: + case GCRY_CIPHER_MODE_XTS: ++ case GCRY_CIPHER_MODE_AESWRAP: + return GPG_ERR_NO_ERROR; + default: + return GPG_ERR_NOT_SUPPORTED; +-- + +commit d6117b04e0e4d5d68df8fb731f618b0d5126ee14 +Author: Jakub Jelen +Date: Tue Jan 17 14:39:34 2023 +0100 + + fips: Remove GCM mode from the allowed FIPS indicators + + * src/fips.c (_gcry_fips_indicator_cipher): Do not mark GCM mode as FIPS + approved. + --- + + Signed-off-by: Jakub Jelen + +diff --git a/src/fips.c b/src/fips.c +index 272aabae..774e7b4c 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -365,7 +365,6 @@ _gcry_fips_indicator_cipher (va_list arg_ptr) + case GCRY_CIPHER_MODE_OFB: + case GCRY_CIPHER_MODE_CTR: + case GCRY_CIPHER_MODE_CCM: +- case GCRY_CIPHER_MODE_GCM: + case GCRY_CIPHER_MODE_XTS: + case GCRY_CIPHER_MODE_AESWRAP: + return GPG_ERR_NO_ERROR; +-- diff --git a/SOURCES/libgcrypt-1.10.0-fips-integrity.patch b/SOURCES/libgcrypt-1.10.0-fips-integrity.patch new file mode 100644 index 0000000..8254381 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-integrity.patch @@ -0,0 +1,1008 @@ +From beb5d6df5c5785db7c32a24a5d2a351cb964bfbc Mon Sep 17 00:00:00 2001 +From: Clemens Lang via Gcrypt-devel +Date: Mon, 14 Feb 2022 18:49:59 +0100 +Subject: [PATCH] fips: Use ELF header to find hmac file offset + +* src/fips.c [ENABLE_HMAC_BINARY_CHECK] (hmac256_check): Use ELF headers + to locate the file offset for the HMAC in addition to information from + the loader + +-- + +The previous method of locating the offset of the .rodata1 section in +the ELF file on disk used information obtained from the loader. This +computed the address of the value in memory at runtime, but the offset +in the file can be different. Specifically, the old code computed +a value relative to ElfW(Phdr).p_vaddr, but the offset in the file is +relative to ElfW(Phdr).p_offset. These values can differ, so the +computed address at runtime must be translated into a file offset +relative to p_offset. + +This is largely cosmetic, since the text section that should contain the +HMAC usually has both p_vaddr and p_offset set to 0. + +Signed-off-by: Clemens Lang +--- + README | 3 ++- + src/fips.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++----- + 2 files changed, 69 insertions(+), 7 deletions(-) + +diff --git a/README b/README +index 3b465c1b..4d7697dd 100644 +--- a/README ++++ b/README +@@ -157,7 +157,8 @@ + --enable-hmac-binary-check + Include support to check the binary at runtime + against a HMAC checksum. This works only in FIPS +- mode and on systems providing the dladdr function. ++ mode on systems providing the dladdr function and using ++ the ELF binary format. + + --with-fips-module-version=version + Specify a string used as a module version for FIPS +diff --git a/src/fips.c b/src/fips.c +index 391b94f1..c40274d9 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -25,6 +25,8 @@ + #include + #ifdef ENABLE_HMAC_BINARY_CHECK + # include ++# include ++# include + # include + #endif + #ifdef HAVE_SYSLOG +@@ -594,6 +596,57 @@ run_random_selftests (void) + static const unsigned char __attribute__ ((section (".rodata1"))) + hmac_for_the_implementation[HMAC_LEN]; + ++/** ++ * Determine the offset of the given virtual address in the ELF file opened as ++ * fp and return it in offset. Rewinds fp to the beginning on success. ++ */ ++static gpg_error_t ++get_file_offset (FILE *fp, unsigned long paddr, unsigned long *offset) ++{ ++ ElfW (Ehdr) ehdr; ++ ElfW (Phdr) phdr; ++ uint16_t e_phidx; ++ ++ // read the ELF header ++ if (0 != fseek (fp, 0, SEEK_SET)) ++ return gpg_error_from_syserror (); ++ if (1 != fread (&ehdr, sizeof (ehdr), 1, fp)) ++ return gpg_error_from_syserror (); ++ ++ // the section header entry size should match the size of the shdr struct ++ if (ehdr.e_phentsize != sizeof (phdr)) ++ return gpg_error (GPG_ERR_INV_OBJ); ++ if (ehdr.e_phoff == 0) ++ return gpg_error (GPG_ERR_INV_OBJ); ++ ++ // jump to the first program header ++ if (0 != fseek (fp, ehdr.e_phoff, SEEK_SET)) ++ return gpg_error_from_syserror (); ++ ++ // iterate over the program headers, compare their virtual addresses with the ++ // address we are looking for, and if the program header matches, calculate ++ // the offset of the given paddr in the file using the program header's ++ // p_offset field. ++ for (e_phidx = 0; e_phidx < ehdr.e_phnum; e_phidx++) ++ { ++ if (1 != fread (&phdr, sizeof (phdr), 1, fp)) ++ return gpg_error_from_syserror (); ++ if (phdr.p_type == PT_LOAD && phdr.p_vaddr <= paddr ++ && phdr.p_vaddr + phdr.p_memsz > paddr) ++ { ++ // found section, compute the offset of paddr in the file ++ *offset = phdr.p_offset + (paddr - phdr.p_vaddr); ++ ++ if (0 != fseek (fp, 0, SEEK_SET)) ++ return gpg_error_from_syserror (); ++ return 0; ++ } ++ } ++ ++ // section not found in the file ++ return gpg_error (GPG_ERR_INV_OBJ); ++} ++ + static gpg_error_t + hmac256_check (const char *filename, const char *key, struct link_map *lm) + { +@@ -603,6 +656,7 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + size_t buffer_size, nread; + char *buffer; + unsigned long paddr; ++ unsigned long offset = 0; + unsigned long off = 0; + + paddr = (unsigned long)hmac_for_the_implementation - lm->l_addr; +@@ -611,6 +665,13 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + if (!fp) + return gpg_error (GPG_ERR_INV_OBJ); + ++ err = get_file_offset (fp, paddr, &offset); ++ if (err) ++ { ++ fclose (fp); ++ return err; ++ } ++ + err = _gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + if (err) + { +@@ -651,14 +712,14 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + nread = fread (buffer+HMAC_LEN, 1, buffer_size, fp); + if (nread < buffer_size) + { +- if (off - HMAC_LEN <= paddr && paddr <= off + nread) +- memset (buffer + HMAC_LEN + paddr - off, 0, HMAC_LEN); ++ if (off - HMAC_LEN <= offset && offset <= off + nread) ++ memset (buffer + HMAC_LEN + offset - off, 0, HMAC_LEN); + _gcry_md_write (hd, buffer, nread+HMAC_LEN); + break; + } + +- if (off - HMAC_LEN <= paddr && paddr <= off + nread) +- memset (buffer + HMAC_LEN + paddr - off, 0, HMAC_LEN); ++ if (off - HMAC_LEN <= offset && offset <= off + nread) ++ memset (buffer + HMAC_LEN + offset - off, 0, HMAC_LEN); + _gcry_md_write (hd, buffer, nread); + memcpy (buffer, buffer+buffer_size, HMAC_LEN); + off += nread; +@@ -694,8 +755,8 @@ check_binary_integrity (void) + const char *key = KEY_FOR_BINARY_CHECK; + void *extra_info; + +- if (!dladdr1 (hmac_for_the_implementation, +- &info, &extra_info, RTLD_DL_LINKMAP)) ++ if (!dladdr1 (hmac_for_the_implementation, &info, &extra_info, ++ RTLD_DL_LINKMAP)) + err = gpg_error_from_syserror (); + else + err = hmac256_check (info.dli_fname, key, extra_info); +-- +2.39.0 + + +From 521500624b4b11538d206137205e2a511dad7072 Mon Sep 17 00:00:00 2001 +From: NIIBE Yutaka +Date: Tue, 15 Feb 2022 20:38:02 +0900 +Subject: [PATCH] fips: Fix previous commit. + +-- + +Coding style fix. + +Signed-off-by: NIIBE Yutaka +--- + src/fips.c | 64 +++++++++++++++++++++++++++--------------------------- + 1 file changed, 32 insertions(+), 32 deletions(-) + +diff --git a/src/fips.c b/src/fips.c +index c40274d9..f16bce8b 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -596,54 +596,55 @@ run_random_selftests (void) + static const unsigned char __attribute__ ((section (".rodata1"))) + hmac_for_the_implementation[HMAC_LEN]; + +-/** +- * Determine the offset of the given virtual address in the ELF file opened as +- * fp and return it in offset. Rewinds fp to the beginning on success. ++/* ++ * In the ELF file opened as FP, determine the offset of the given ++ * virtual address ADDR and return it in OFFSET. Rewinds FP to the ++ * beginning on success. + */ + static gpg_error_t +-get_file_offset (FILE *fp, unsigned long paddr, unsigned long *offset) ++get_file_offset (FILE *fp, unsigned long addr, unsigned long *offset) + { + ElfW (Ehdr) ehdr; + ElfW (Phdr) phdr; + uint16_t e_phidx; + +- // read the ELF header +- if (0 != fseek (fp, 0, SEEK_SET)) ++ /* Read the ELF header */ ++ if (fseek (fp, 0, SEEK_SET) != 0) + return gpg_error_from_syserror (); +- if (1 != fread (&ehdr, sizeof (ehdr), 1, fp)) ++ if (fread (&ehdr, sizeof (ehdr), 1, fp) != 1) + return gpg_error_from_syserror (); + +- // the section header entry size should match the size of the shdr struct ++ /* The program header entry size should match the size of the phdr struct */ + if (ehdr.e_phentsize != sizeof (phdr)) + return gpg_error (GPG_ERR_INV_OBJ); + if (ehdr.e_phoff == 0) + return gpg_error (GPG_ERR_INV_OBJ); + +- // jump to the first program header +- if (0 != fseek (fp, ehdr.e_phoff, SEEK_SET)) ++ /* Jump to the first program header */ ++ if (fseek (fp, ehdr.e_phoff, SEEK_SET) != 0) + return gpg_error_from_syserror (); + +- // iterate over the program headers, compare their virtual addresses with the +- // address we are looking for, and if the program header matches, calculate +- // the offset of the given paddr in the file using the program header's +- // p_offset field. ++ /* Iterate over the program headers, compare their virtual addresses ++ with the address we are looking for, and if the program header ++ matches, calculate the offset of the given ADDR in the file using ++ the program header's p_offset field. */ + for (e_phidx = 0; e_phidx < ehdr.e_phnum; e_phidx++) + { +- if (1 != fread (&phdr, sizeof (phdr), 1, fp)) ++ if (fread (&phdr, sizeof (phdr), 1, fp) != 1) + return gpg_error_from_syserror (); +- if (phdr.p_type == PT_LOAD && phdr.p_vaddr <= paddr +- && phdr.p_vaddr + phdr.p_memsz > paddr) ++ if (phdr.p_type == PT_LOAD ++ && phdr.p_vaddr <= addr && addr < phdr.p_vaddr + phdr.p_memsz) + { +- // found section, compute the offset of paddr in the file +- *offset = phdr.p_offset + (paddr - phdr.p_vaddr); ++ /* Found segment, compute the offset of ADDR in the file */ ++ *offset = phdr.p_offset + (addr - phdr.p_vaddr); + +- if (0 != fseek (fp, 0, SEEK_SET)) ++ if (fseek (fp, 0, SEEK_SET) != 0) + return gpg_error_from_syserror (); + return 0; + } + } + +- // section not found in the file ++ /* Segment not found in the file */ + return gpg_error (GPG_ERR_INV_OBJ); + } + +@@ -655,17 +656,16 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + gcry_md_hd_t hd; + size_t buffer_size, nread; + char *buffer; +- unsigned long paddr; ++ unsigned long addr; + unsigned long offset = 0; +- unsigned long off = 0; +- +- paddr = (unsigned long)hmac_for_the_implementation - lm->l_addr; ++ unsigned long pos = 0; + ++ addr = (unsigned long)hmac_for_the_implementation - lm->l_addr; + fp = fopen (filename, "rb"); + if (!fp) + return gpg_error (GPG_ERR_INV_OBJ); + +- err = get_file_offset (fp, paddr, &offset); ++ err = get_file_offset (fp, addr, &offset); + if (err) + { + fclose (fp); +@@ -698,7 +698,7 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + } + + nread = fread (buffer, 1, HMAC_LEN, fp); +- off += nread; ++ pos += nread; + if (nread < HMAC_LEN) + { + xfree (buffer); +@@ -712,17 +712,17 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + nread = fread (buffer+HMAC_LEN, 1, buffer_size, fp); + if (nread < buffer_size) + { +- if (off - HMAC_LEN <= offset && offset <= off + nread) +- memset (buffer + HMAC_LEN + offset - off, 0, HMAC_LEN); ++ if (pos - HMAC_LEN <= offset && offset <= pos + nread) ++ memset (buffer + HMAC_LEN + offset - pos, 0, HMAC_LEN); + _gcry_md_write (hd, buffer, nread+HMAC_LEN); + break; + } + +- if (off - HMAC_LEN <= offset && offset <= off + nread) +- memset (buffer + HMAC_LEN + offset - off, 0, HMAC_LEN); ++ if (pos - HMAC_LEN <= offset && offset <= pos + nread) ++ memset (buffer + HMAC_LEN + offset - pos, 0, HMAC_LEN); + _gcry_md_write (hd, buffer, nread); + memcpy (buffer, buffer+buffer_size, HMAC_LEN); +- off += nread; ++ pos += nread; + } + + if (ferror (fp)) +-- +2.39.1 + + +From 9dcf9305962b90febdf2d7cc73b49feadbf6a01f Mon Sep 17 00:00:00 2001 +From: NIIBE Yutaka +Date: Wed, 16 Feb 2022 14:06:02 +0900 +Subject: [PATCH] fips: Integrity check improvement, with only loadable + segments. + +* configure.ac (READELF): Check the tool. +* src/Makefile.am (libgcrypt.so.hmac): Use genhmac.sh with hmac256. +* src/fips.c (get_file_offsets): Rename from get_file_offset. +Determine the OFFSET2 at the end of loadable segments, too. +Add fixup of the ELF header to exclude section information. +(hmac256_check): Finish scanning at the end of loadble segments. +* src/genhmac.sh: New. + +-- + +This change fixes the build with ld.gold. + +GnuPG-bug-id: 5835 +Signed-off-by: NIIBE Yutaka +--- + configure.ac | 1 + + src/Makefile.am | 4 +-- + src/fips.c | 73 +++++++++++++++++++++++++++++-------------- + src/genhmac.sh | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 136 insertions(+), 25 deletions(-) + create mode 100755 src/genhmac.sh + +diff --git a/configure.ac b/configure.ac +index f0f1637f..ea01f5a6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -579,6 +579,7 @@ else + AC_DEFINE(ENABLE_HMAC_BINARY_CHECK,1, + [Define to support an HMAC based integrity check]) + AC_CHECK_TOOL(OBJCOPY, [objcopy]) ++ AC_CHECK_TOOL(READELF, [readelf]) + if test "$use_hmac_binary_check" != yes ; then + DEF_HMAC_BINARY_CHECK=-DKEY_FOR_BINARY_CHECK="'\"$use_hmac_binary_check\"'" + fi +diff --git a/src/Makefile.am b/src/Makefile.am +index 018d5761..72100671 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -24,7 +24,7 @@ pkgconfigdir = $(libdir)/pkgconfig + pkgconfig_DATA = libgcrypt.pc + + EXTRA_DIST = libgcrypt-config.in libgcrypt.m4 libgcrypt.vers \ +- gcrypt.h.in libgcrypt.def libgcrypt.pc.in ++ gcrypt.h.in libgcrypt.def libgcrypt.pc.in genhmac.sh + + bin_SCRIPTS = libgcrypt-config + m4datadir = $(datadir)/aclocal +@@ -149,7 +149,7 @@ libgcrypt.la.done: libgcrypt.so.hmac + @touch libgcrypt.la.done + + libgcrypt.so.hmac: hmac256 libgcrypt.la +- ./hmac256 --stdkey --binary < .libs/libgcrypt.so > $@ ++ READELF=$(READELF) AWK=$(AWK) $(srcdir)/genhmac.sh > $@ + else !USE_HMAC_BINARY_CHECK + libgcrypt.la.done: libgcrypt.la + @touch libgcrypt.la.done +diff --git a/src/fips.c b/src/fips.c +index f16bce8b..134d0eae 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -598,50 +598,68 @@ hmac_for_the_implementation[HMAC_LEN]; + + /* + * In the ELF file opened as FP, determine the offset of the given +- * virtual address ADDR and return it in OFFSET. Rewinds FP to the ++ * virtual address ADDR and return it in R_OFFSET1. Determine the ++ * offset of last loadable section in R_OFFSET2. Rewinds FP to the + * beginning on success. + */ + static gpg_error_t +-get_file_offset (FILE *fp, unsigned long addr, unsigned long *offset) ++get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p, ++ unsigned long *r_offset1, unsigned long *r_offset2) + { +- ElfW (Ehdr) ehdr; + ElfW (Phdr) phdr; + uint16_t e_phidx; ++ long pos = 0; + + /* Read the ELF header */ + if (fseek (fp, 0, SEEK_SET) != 0) + return gpg_error_from_syserror (); +- if (fread (&ehdr, sizeof (ehdr), 1, fp) != 1) ++ if (fread (ehdr_p, sizeof (*ehdr_p), 1, fp) != 1) + return gpg_error_from_syserror (); + ++ /* Fix up the ELF header, clean all section information. */ ++ ehdr_p->e_shoff = 0; ++ ehdr_p->e_shentsize = 0; ++ ehdr_p->e_shnum = 0; ++ ehdr_p->e_shstrndx = 0; ++ + /* The program header entry size should match the size of the phdr struct */ +- if (ehdr.e_phentsize != sizeof (phdr)) ++ if (ehdr_p->e_phentsize != sizeof (phdr)) + return gpg_error (GPG_ERR_INV_OBJ); +- if (ehdr.e_phoff == 0) ++ if (ehdr_p->e_phoff == 0) + return gpg_error (GPG_ERR_INV_OBJ); + + /* Jump to the first program header */ +- if (fseek (fp, ehdr.e_phoff, SEEK_SET) != 0) ++ if (fseek (fp, ehdr_p->e_phoff, SEEK_SET) != 0) + return gpg_error_from_syserror (); + + /* Iterate over the program headers, compare their virtual addresses + with the address we are looking for, and if the program header + matches, calculate the offset of the given ADDR in the file using + the program header's p_offset field. */ +- for (e_phidx = 0; e_phidx < ehdr.e_phnum; e_phidx++) ++ for (e_phidx = 0; e_phidx < ehdr_p->e_phnum; e_phidx++) + { + if (fread (&phdr, sizeof (phdr), 1, fp) != 1) + return gpg_error_from_syserror (); +- if (phdr.p_type == PT_LOAD +- && phdr.p_vaddr <= addr && addr < phdr.p_vaddr + phdr.p_memsz) +- { +- /* Found segment, compute the offset of ADDR in the file */ +- *offset = phdr.p_offset + (addr - phdr.p_vaddr); + +- if (fseek (fp, 0, SEEK_SET) != 0) +- return gpg_error_from_syserror (); +- return 0; +- } ++ if (phdr.p_type == PT_PHDR) ++ continue; ++ ++ if (phdr.p_type != PT_LOAD) ++ break; ++ ++ pos = phdr.p_offset + phdr.p_filesz; ++ if (phdr.p_vaddr <= addr && addr < phdr.p_vaddr + phdr.p_memsz) ++ /* Found segment, compute the offset of ADDR in the file */ ++ *r_offset1 = phdr.p_offset + (addr - phdr.p_vaddr); ++ } ++ ++ if (*r_offset1) ++ { ++ if (fseek (fp, 0, SEEK_SET) != 0) ++ return gpg_error_from_syserror (); ++ ++ *r_offset2 = (unsigned long)pos; ++ return 0; + } + + /* Segment not found in the file */ +@@ -657,15 +675,17 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + size_t buffer_size, nread; + char *buffer; + unsigned long addr; +- unsigned long offset = 0; ++ unsigned long offset1 = 0; ++ unsigned long offset2 = 0; + unsigned long pos = 0; ++ ElfW (Ehdr) ehdr; + + addr = (unsigned long)hmac_for_the_implementation - lm->l_addr; + fp = fopen (filename, "rb"); + if (!fp) + return gpg_error (GPG_ERR_INV_OBJ); + +- err = get_file_offset (fp, addr, &offset); ++ err = get_file_offsets (fp, addr, &ehdr, &offset1, &offset2); + if (err) + { + fclose (fp); +@@ -710,16 +730,23 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + while (1) + { + nread = fread (buffer+HMAC_LEN, 1, buffer_size, fp); ++ if (pos + nread >= offset2) ++ nread = offset2 - pos; ++ ++ /* Copy, fixed ELF header at the beginning. */ ++ if (pos - HMAC_LEN == 0) ++ memcpy (buffer, &ehdr, sizeof (ehdr)); ++ + if (nread < buffer_size) + { +- if (pos - HMAC_LEN <= offset && offset <= pos + nread) +- memset (buffer + HMAC_LEN + offset - pos, 0, HMAC_LEN); ++ if (pos - HMAC_LEN <= offset1 && offset1 <= pos + nread) ++ memset (buffer + HMAC_LEN + offset1 - pos, 0, HMAC_LEN); + _gcry_md_write (hd, buffer, nread+HMAC_LEN); + break; + } + +- if (pos - HMAC_LEN <= offset && offset <= pos + nread) +- memset (buffer + HMAC_LEN + offset - pos, 0, HMAC_LEN); ++ if (pos - HMAC_LEN <= offset1 && offset1 <= pos + nread) ++ memset (buffer + HMAC_LEN + offset1 - pos, 0, HMAC_LEN); + _gcry_md_write (hd, buffer, nread); + memcpy (buffer, buffer+buffer_size, HMAC_LEN); + pos += nread; +diff --git a/src/genhmac.sh b/src/genhmac.sh +new file mode 100755 +index 00000000..bb33b9c6 +--- /dev/null ++++ b/src/genhmac.sh +@@ -0,0 +1,83 @@ ++#! /bin/sh ++ ++# ++# genhmac.sh - Build tool to generate hmac hash ++# ++# Copyright (C) 2022 g10 Code GmbH ++# ++# This file is part of libgcrypt. ++# ++# libgcrypt is free software; you can redistribute it and/or ++# modify it under the terms of the GNU Lesser General Public License ++# as published by the Free Software Foundation; either version 2.1 of ++# the License, or (at your option) any later version. ++# ++# libgcrypt is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# Lesser General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public ++# License along with this program; if not, see . ++# ++ ++set -e ++ ++# ++# Following variables should be defined to invoke this script ++# ++# READELF ++# AWK ++# ++ ++AWK_VERSION_OUTPUT=$($AWK 'BEGIN { print PROCINFO["version"] }') ++if test -n "$AWK_VERSION_OUTPUT"; then ++ # It's GNU awk, which supports PROCINFO. ++ AWK_OPTION=--non-decimal-data ++fi ++ ++FILE=.libs/libgcrypt.so ++ ++# ++# Fixup the ELF header to clean up section information ++# ++printf '%b' '\002' > 2.bin ++dd ibs=1 skip=4 count=1 if=$FILE status=none > class-byte.bin ++if cmp class-byte.bin 2.bin; then ++ CLASS=64 ++ HEADER_SIZE=64 ++else ++ CLASS=32 ++ HEADER_SIZE=52 ++fi ++ ++if test $CLASS -eq 64; then ++ dd ibs=1 count=40 if=$FILE status=none ++ dd ibs=1 count=8 if=/dev/zero status=none ++ dd ibs=1 skip=48 count=10 if=$FILE status=none ++ dd ibs=1 count=6 if=/dev/zero status=none ++else ++ dd ibs=1 count=32 if=$FILE status=none ++ dd ibs=1 count=4 if=/dev/zero status=none ++ dd ibs=1 skip=36 count=10 if=$FILE status=none ++ dd ibs=1 count=6 if=/dev/zero status=none ++fi > header-fixed.bin ++ ++# Compute the end of loadable segment. ++# ++# This require computation in hexadecimal, and GNU awk needs ++# --non-decimal-data option ++# ++OFFSET=$($READELF --wide --program-headers $FILE | \ ++ $AWK $AWK_OPTION "/^ LOAD/ { offset=\$2+\$5-$HEADER_SIZE }\ ++END { print offset}") ++ ++# ++# Feed the header fixed and loadable segments to HMAC256 ++# to generate hmac hash of the FILE ++# ++(cat header-fixed.bin; \ ++ dd ibs=1 skip=$HEADER_SIZE count=$OFFSET if=$FILE status=none) \ ++ | ./hmac256 --stdkey --binary ++ ++rm -f 2.bin class-byte.bin header-fixed.bin +-- +2.39.1 + + +From a340e980388243ceae6df57d101036f3f2a955be Mon Sep 17 00:00:00 2001 +From: NIIBE Yutaka +Date: Wed, 16 Feb 2022 20:08:15 +0900 +Subject: [PATCH] fips: More portable integrity check. + +* src/Makefile.am (EXTRA_DIST): Change the name of the script. +(libgcrypt.la.done): Invoce OBJCOPY with --add-section. +(libgcrypt.so.hmac): Specify ECHO_N. +* src/fips.c (get_file_offset): Rename from get_file_offsets. +Find the note section and return the value in HMAC. +(hmac256_check): Simplify by HMAC from the note section, not loaded. +(check_binary_integrity): Use dladdr instead of dladdr1. +* src/gen-note-integrity.sh: Rename from genhmac.sh. +Generate ElfN_Nhdr, and then the hmac. + +-- + +The idea of use of .note is by Daiki Ueno. + https://gitlab.com/dueno/integrity-notes + +Further, instead of NOTE segment loaded onto memory, use noload +section in the file. + +Thanks to Clemens Lang for initiating this direction of improvement. + +The namespace "FDO" would need to be changed. + +GnuPG-bug-id: 5835 +Signed-off-by: NIIBE Yutaka +--- + src/Makefile.am | 8 +- + src/fips.c | 167 +++++++++++++--------- + src/{genhmac.sh => gen-note-integrity.sh} | 34 ++++- + 3 files changed, 134 insertions(+), 75 deletions(-) + rename src/{genhmac.sh => gen-note-integrity.sh} (78%) + +diff --git a/src/Makefile.am b/src/Makefile.am +index 72100671..b8bb187a 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -24,7 +24,7 @@ pkgconfigdir = $(libdir)/pkgconfig + pkgconfig_DATA = libgcrypt.pc + + EXTRA_DIST = libgcrypt-config.in libgcrypt.m4 libgcrypt.vers \ +- gcrypt.h.in libgcrypt.def libgcrypt.pc.in genhmac.sh ++ gcrypt.h.in libgcrypt.def libgcrypt.pc.in gen-note-integrity.sh + + bin_SCRIPTS = libgcrypt-config + m4datadir = $(datadir)/aclocal +@@ -143,13 +143,15 @@ if USE_HMAC_BINARY_CHECK + CLEANFILES += libgcrypt.so.hmac + + libgcrypt.la.done: libgcrypt.so.hmac +- $(OBJCOPY) --update-section .rodata1=libgcrypt.so.hmac \ ++ $(OBJCOPY) --add-section .note.fdo.integrity=libgcrypt.so.hmac \ ++ --set-section-flags .note.fdo.integrity=noload,readonly \ + .libs/libgcrypt.so .libs/libgcrypt.so.new + mv -f .libs/libgcrypt.so.new .libs/libgcrypt.so.*.* + @touch libgcrypt.la.done + + libgcrypt.so.hmac: hmac256 libgcrypt.la +- READELF=$(READELF) AWK=$(AWK) $(srcdir)/genhmac.sh > $@ ++ ECHO_N=$(ECHO_N) READELF=$(READELF) AWK=$(AWK) \ ++ $(srcdir)/gen-note-integrity.sh > $@ + else !USE_HMAC_BINARY_CHECK + libgcrypt.la.done: libgcrypt.la + @touch libgcrypt.la.done +diff --git a/src/fips.c b/src/fips.c +index 134d0eae..d798d577 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -593,22 +593,20 @@ run_random_selftests (void) + # endif + #define HMAC_LEN 32 + +-static const unsigned char __attribute__ ((section (".rodata1"))) +-hmac_for_the_implementation[HMAC_LEN]; +- + /* +- * In the ELF file opened as FP, determine the offset of the given +- * virtual address ADDR and return it in R_OFFSET1. Determine the +- * offset of last loadable section in R_OFFSET2. Rewinds FP to the +- * beginning on success. ++ * In the ELF file opened as FP, fill the ELF header to the pointer ++ * EHDR_P, determine the offset of last loadable segment in R_OFFSET. ++ * Also, find the section which contains the hmac value and return it ++ * in HMAC. Rewinds FP to the beginning on success. + */ + static gpg_error_t +-get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p, +- unsigned long *r_offset1, unsigned long *r_offset2) ++get_file_offset (FILE *fp, ElfW (Ehdr) *ehdr_p, ++ unsigned long *r_offset, unsigned char hmac[HMAC_LEN]) + { + ElfW (Phdr) phdr; +- uint16_t e_phidx; +- long pos = 0; ++ ElfW (Shdr) shdr; ++ int i; ++ unsigned long off_segment = 0; + + /* Read the ELF header */ + if (fseek (fp, 0, SEEK_SET) != 0) +@@ -616,12 +614,6 @@ get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p, + if (fread (ehdr_p, sizeof (*ehdr_p), 1, fp) != 1) + return gpg_error_from_syserror (); + +- /* Fix up the ELF header, clean all section information. */ +- ehdr_p->e_shoff = 0; +- ehdr_p->e_shentsize = 0; +- ehdr_p->e_shnum = 0; +- ehdr_p->e_shstrndx = 0; +- + /* The program header entry size should match the size of the phdr struct */ + if (ehdr_p->e_phentsize != sizeof (phdr)) + return gpg_error (GPG_ERR_INV_OBJ); +@@ -632,11 +624,9 @@ get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p, + if (fseek (fp, ehdr_p->e_phoff, SEEK_SET) != 0) + return gpg_error_from_syserror (); + +- /* Iterate over the program headers, compare their virtual addresses +- with the address we are looking for, and if the program header +- matches, calculate the offset of the given ADDR in the file using +- the program header's p_offset field. */ +- for (e_phidx = 0; e_phidx < ehdr_p->e_phnum; e_phidx++) ++ /* Iterate over the program headers, determine the last loadable ++ segment. */ ++ for (i = 0; i < ehdr_p->e_phnum; i++) + { + if (fread (&phdr, sizeof (phdr), 1, fp) != 1) + return gpg_error_from_syserror (); +@@ -647,45 +637,100 @@ get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p, + if (phdr.p_type != PT_LOAD) + break; + +- pos = phdr.p_offset + phdr.p_filesz; +- if (phdr.p_vaddr <= addr && addr < phdr.p_vaddr + phdr.p_memsz) +- /* Found segment, compute the offset of ADDR in the file */ +- *r_offset1 = phdr.p_offset + (addr - phdr.p_vaddr); ++ off_segment = phdr.p_offset + phdr.p_filesz; + } + +- if (*r_offset1) ++ if (!off_segment) ++ /* The segment not found in the file */ ++ return gpg_error (GPG_ERR_INV_OBJ); ++ ++ /* The section header entry size should match the size of the shdr struct */ ++ if (ehdr_p->e_shentsize != sizeof (shdr)) ++ return gpg_error (GPG_ERR_INV_OBJ); ++ if (ehdr_p->e_shoff == 0) ++ return gpg_error (GPG_ERR_INV_OBJ); ++ ++ /* Jump to the first section header */ ++ if (fseek (fp, ehdr_p->e_shoff, SEEK_SET) != 0) ++ return gpg_error_from_syserror (); ++ ++ /* Iterate over the section headers, determine the note section, ++ read the hmac value. */ ++ for (i = 0; i < ehdr_p->e_shnum; i++) + { +- if (fseek (fp, 0, SEEK_SET) != 0) ++ long off; ++ ++ if (fread (&shdr, sizeof (shdr), 1, fp) != 1) + return gpg_error_from_syserror (); + +- *r_offset2 = (unsigned long)pos; +- return 0; ++ off = ftell (fp); ++ if (shdr.sh_type == SHT_NOTE && shdr.sh_flags == 0 && shdr.sh_size == 48) ++ { ++ const char header_of_the_note[] = { ++ 0x04, 0x00, 0x00, 0x00, ++ 0x20, 0x00, 0x00, 0x00, ++ 0xca, 0xfe, 0x2a, 0x8e, ++ 'F', 'D', 'O', 0x00 ++ }; ++ unsigned char header[16]; ++ ++ /* Jump to the note section. */ ++ if (fseek (fp, shdr.sh_offset, SEEK_SET) != 0) ++ return gpg_error_from_syserror (); ++ ++ if (fread (header, sizeof (header), 1, fp) != 1) ++ return gpg_error_from_syserror (); ++ ++ if (!memcmp (header, header_of_the_note, 16)) ++ { ++ /* Found. Read the hmac value into HMAC. */ ++ if (fread (hmac, HMAC_LEN, 1, fp) != 1) ++ return gpg_error_from_syserror (); ++ break; ++ } ++ ++ /* Back to the next section header. */ ++ if (fseek (fp, off, SEEK_SET) != 0) ++ return gpg_error_from_syserror (); ++ } + } + +- /* Segment not found in the file */ +- return gpg_error (GPG_ERR_INV_OBJ); ++ if (i == ehdr_p->e_shnum) ++ /* The note section not found. */ ++ return gpg_error (GPG_ERR_INV_OBJ); ++ ++ /* Fix up the ELF header, clean all section information. */ ++ ehdr_p->e_shoff = 0; ++ ehdr_p->e_shentsize = 0; ++ ehdr_p->e_shnum = 0; ++ ehdr_p->e_shstrndx = 0; ++ ++ *r_offset = off_segment; ++ if (fseek (fp, 0, SEEK_SET) != 0) ++ return gpg_error_from_syserror (); ++ ++ return 0; + } + + static gpg_error_t +-hmac256_check (const char *filename, const char *key, struct link_map *lm) ++hmac256_check (const char *filename, const char *key) + { + gpg_error_t err; + FILE *fp; + gcry_md_hd_t hd; +- size_t buffer_size, nread; ++ const size_t buffer_size = 32768; ++ size_t nread; + char *buffer; +- unsigned long addr; +- unsigned long offset1 = 0; +- unsigned long offset2 = 0; ++ unsigned long offset = 0; + unsigned long pos = 0; + ElfW (Ehdr) ehdr; ++ unsigned char hmac[HMAC_LEN]; + +- addr = (unsigned long)hmac_for_the_implementation - lm->l_addr; + fp = fopen (filename, "rb"); + if (!fp) + return gpg_error (GPG_ERR_INV_OBJ); + +- err = get_file_offsets (fp, addr, &ehdr, &offset1, &offset2); ++ err = get_file_offset (fp, &ehdr, &offset, hmac); + if (err) + { + fclose (fp); +@@ -707,8 +752,7 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + return err; + } + +- buffer_size = 32768; +- buffer = xtrymalloc (buffer_size + HMAC_LEN); ++ buffer = xtrymalloc (buffer_size); + if (!buffer) + { + err = gpg_error_from_syserror (); +@@ -717,38 +761,21 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + return err; + } + +- nread = fread (buffer, 1, HMAC_LEN, fp); +- pos += nread; +- if (nread < HMAC_LEN) +- { +- xfree (buffer); +- fclose (fp); +- _gcry_md_close (hd); +- return gpg_error (GPG_ERR_TOO_SHORT); +- } +- + while (1) + { +- nread = fread (buffer+HMAC_LEN, 1, buffer_size, fp); +- if (pos + nread >= offset2) +- nread = offset2 - pos; ++ nread = fread (buffer, 1, buffer_size, fp); ++ if (pos + nread >= offset) ++ nread = offset - pos; + +- /* Copy, fixed ELF header at the beginning. */ +- if (pos - HMAC_LEN == 0) ++ /* Copy the fixed ELF header at the beginning. */ ++ if (pos == 0) + memcpy (buffer, &ehdr, sizeof (ehdr)); + ++ _gcry_md_write (hd, buffer, nread); ++ + if (nread < buffer_size) +- { +- if (pos - HMAC_LEN <= offset1 && offset1 <= pos + nread) +- memset (buffer + HMAC_LEN + offset1 - pos, 0, HMAC_LEN); +- _gcry_md_write (hd, buffer, nread+HMAC_LEN); +- break; +- } ++ break; + +- if (pos - HMAC_LEN <= offset1 && offset1 <= pos + nread) +- memset (buffer + HMAC_LEN + offset1 - pos, 0, HMAC_LEN); +- _gcry_md_write (hd, buffer, nread); +- memcpy (buffer, buffer+buffer_size, HMAC_LEN); + pos += nread; + } + +@@ -759,7 +786,7 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm) + unsigned char *digest; + + digest = _gcry_md_read (hd, 0); +- if (!memcmp (digest, hmac_for_the_implementation, HMAC_LEN)) ++ if (!memcmp (digest, hmac, HMAC_LEN)) + /* Success. */ + err = 0; + else +@@ -780,13 +807,11 @@ check_binary_integrity (void) + gpg_error_t err; + Dl_info info; + const char *key = KEY_FOR_BINARY_CHECK; +- void *extra_info; + +- if (!dladdr1 (hmac_for_the_implementation, &info, &extra_info, +- RTLD_DL_LINKMAP)) ++ if (!dladdr (hmac256_check, &info)) + err = gpg_error_from_syserror (); + else +- err = hmac256_check (info.dli_fname, key, extra_info); ++ err = hmac256_check (info.dli_fname, key); + + reporter ("binary", 0, NULL, err? gpg_strerror (err):NULL); + #ifdef HAVE_SYSLOG +diff --git a/src/genhmac.sh b/src/gen-note-integrity.sh +similarity index 78% +rename from src/genhmac.sh +rename to src/gen-note-integrity.sh +index bb33b9c6..969fdca6 100755 +--- a/src/genhmac.sh ++++ b/src/gen-note-integrity.sh +@@ -1,7 +1,7 @@ + #! /bin/sh + + # +-# genhmac.sh - Build tool to generate hmac hash ++# gen-note-integrity.sh - Build tool to generate hmac hash section + # + # Copyright (C) 2022 g10 Code GmbH + # +@@ -28,8 +28,40 @@ set -e + # + # READELF + # AWK ++# ECHO_N + # + ++######## Emit ElfN_Nhdr for note.fdo.integrity ######## ++ ++NOTE_NAME="FDO" ++ ++# n_namesz = 4 including NUL ++printf '%b' '\004' ++printf '%b' '\000' ++printf '%b' '\000' ++printf '%b' '\000' ++ ++# n_descsz = 32 ++printf '%b' '\040' ++printf '%b' '\000' ++printf '%b' '\000' ++printf '%b' '\000' ++ ++# n_type: NT_FDO_INTEGRITY=0xCAFE2A8E ++printf '%b' '\312' ++printf '%b' '\376' ++printf '%b' '\052' ++printf '%b' '\216' ++ ++# the name ++echo $ECHO_N $NOTE_NAME ++printf '%b' '\000' ++ ++# Here comes the alignment. As the size of name is 4, it's none. ++# NO PADDING HERE. ++ ++######## Rest is to generate hmac hash ######## ++ + AWK_VERSION_OUTPUT=$($AWK 'BEGIN { print PROCINFO["version"] }') + if test -n "$AWK_VERSION_OUTPUT"; then + # It's GNU awk, which supports PROCINFO. +-- +2.39.1 + + diff --git a/SOURCES/libgcrypt-1.10.0-fips-integrity2.patch b/SOURCES/libgcrypt-1.10.0-fips-integrity2.patch new file mode 100644 index 0000000..a3c0235 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-integrity2.patch @@ -0,0 +1,190 @@ +From 3c8b6c4a9cad59c5e1db5706f6774a3141b60210 Mon Sep 17 00:00:00 2001 +From: NIIBE Yutaka +Date: Thu, 17 Feb 2022 10:28:05 +0900 +Subject: [PATCH] fips: Fix gen-note-integrity.sh script not to use cmp + utility. + +* src/gen-note-integrity.sh: Simplify detecting 32-bit machine +or 64-bit machine. + +-- + +GnuPG-bug-id: 5835 +Signed-off-by: NIIBE Yutaka +--- + src/gen-note-integrity.sh | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/gen-note-integrity.sh b/src/gen-note-integrity.sh +index 969fdca6..878d7095 100755 +--- a/src/gen-note-integrity.sh ++++ b/src/gen-note-integrity.sh +@@ -73,9 +73,9 @@ FILE=.libs/libgcrypt.so + # + # Fixup the ELF header to clean up section information + # +-printf '%b' '\002' > 2.bin +-dd ibs=1 skip=4 count=1 if=$FILE status=none > class-byte.bin +-if cmp class-byte.bin 2.bin; then ++BYTE002=$(printf '%b' '\002') ++CLASS_BYTE=$(dd ibs=1 skip=4 count=1 if=$FILE status=none) ++if test "$CLASS_BYTE" = "$BYTE002"; then + CLASS=64 + HEADER_SIZE=64 + else +@@ -112,4 +112,4 @@ END { print offset}") + dd ibs=1 skip=$HEADER_SIZE count=$OFFSET if=$FILE status=none) \ + | ./hmac256 --stdkey --binary + +-rm -f 2.bin class-byte.bin header-fixed.bin ++rm -f header-fixed.bin +-- +2.39.1 + + +From 052c5ef4cea56772b7015e36f231fa0bcbf91410 Mon Sep 17 00:00:00 2001 +From: NIIBE Yutaka +Date: Thu, 17 Feb 2022 11:21:35 +0900 +Subject: [PATCH] fips: Clarify what to be hashed for the integrity check. + +* src/fips.c (get_file_offset): Compute the maximum offset +of segments. +* src/gen-note-integrity.sh: Likewise. + +-- + +The result is same (in current format of ELF program). +Semantics is more clear. It hashes: + + - From the start of shared library file, + - fixed up the ELF header to exclude link-time information, + - up to the last segment. + +Signed-off-by: NIIBE Yutaka +--- + src/fips.c | 20 +++++++++----------- + src/gen-note-integrity.sh | 20 ++++++++++++++------ + 2 files changed, 23 insertions(+), 17 deletions(-) + +diff --git a/src/fips.c b/src/fips.c +index d798d577..89f8204b 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -595,7 +595,7 @@ run_random_selftests (void) + + /* + * In the ELF file opened as FP, fill the ELF header to the pointer +- * EHDR_P, determine the offset of last loadable segment in R_OFFSET. ++ * EHDR_P, determine the maximum offset of segments in R_OFFSET. + * Also, find the section which contains the hmac value and return it + * in HMAC. Rewinds FP to the beginning on success. + */ +@@ -624,24 +624,22 @@ get_file_offset (FILE *fp, ElfW (Ehdr) *ehdr_p, + if (fseek (fp, ehdr_p->e_phoff, SEEK_SET) != 0) + return gpg_error_from_syserror (); + +- /* Iterate over the program headers, determine the last loadable +- segment. */ ++ /* Iterate over the program headers, determine the last offset of ++ segments. */ + for (i = 0; i < ehdr_p->e_phnum; i++) + { ++ unsigned long off; ++ + if (fread (&phdr, sizeof (phdr), 1, fp) != 1) + return gpg_error_from_syserror (); + +- if (phdr.p_type == PT_PHDR) +- continue; +- +- if (phdr.p_type != PT_LOAD) +- break; +- +- off_segment = phdr.p_offset + phdr.p_filesz; ++ off = phdr.p_offset + phdr.p_filesz; ++ if (off_segment < off) ++ off_segment = off; + } + + if (!off_segment) +- /* The segment not found in the file */ ++ /* No segment found in the file */ + return gpg_error (GPG_ERR_INV_OBJ); + + /* The section header entry size should match the size of the shdr struct */ +diff --git a/src/gen-note-integrity.sh b/src/gen-note-integrity.sh +index 878d7095..50071bf5 100755 +--- a/src/gen-note-integrity.sh ++++ b/src/gen-note-integrity.sh +@@ -95,21 +95,29 @@ else + dd ibs=1 count=6 if=/dev/zero status=none + fi > header-fixed.bin + +-# Compute the end of loadable segment. ++# ++# Compute the end of segments, and emit the COUNT to read ++# (For each segment in program headers, calculate the offset ++# and select the maximum) + # + # This require computation in hexadecimal, and GNU awk needs + # --non-decimal-data option + # +-OFFSET=$($READELF --wide --program-headers $FILE | \ +- $AWK $AWK_OPTION "/^ LOAD/ { offset=\$2+\$5-$HEADER_SIZE }\ +-END { print offset}") ++COUNT=$($READELF --wide --program-headers $FILE | \ ++ $AWK $AWK_OPTION \ ++"BEGIN { max_offset=0 } ++/^\$/ { if (program_headers_start) program_headers_end=1 } ++(program_headers_start && !program_headers_end) { offset = \$2 + \$5 } ++(max_offset < offset) { max_offset = offset } ++/^ Type/ { program_headers_start=1 } ++END { print max_offset- $HEADER_SIZE }") + + # +-# Feed the header fixed and loadable segments to HMAC256 ++# Feed the header fixed and all segments to HMAC256 + # to generate hmac hash of the FILE + # + (cat header-fixed.bin; \ +- dd ibs=1 skip=$HEADER_SIZE count=$OFFSET if=$FILE status=none) \ ++ dd ibs=1 skip=$HEADER_SIZE count=$COUNT if=$FILE status=none) \ + | ./hmac256 --stdkey --binary + + rm -f header-fixed.bin +-- +2.39.1 + + +From 3fd3bb31597f80c76a94ea62e42d58d796beabf1 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 20 Feb 2023 16:16:01 +0100 +Subject: [PATCH] fips: Check return value from ftell + +* src/fips.c (get_file_offset): Check return value of ftell to be able + to detect errors. +-- + +Originally reported by coverity. + +Signed-off-by: Jakub Jelen +--- + src/fips.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/fips.c b/src/fips.c +index 272aabae..0d89b6da 100644 +--- a/src/fips.c ++++ b/src/fips.c +@@ -681,6 +681,8 @@ get_file_offset (FILE *fp, ElfW (Ehdr) *ehdr_p, + return gpg_error_from_syserror (); + + off = ftell (fp); ++ if (off < 0) ++ return gpg_error_from_syserror (); + if (shdr.sh_type == SHT_NOTE && shdr.sh_flags == 0 && shdr.sh_size == 48) + { + const char header_of_the_note[] = { +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-kdf.patch b/SOURCES/libgcrypt-1.10.0-fips-kdf.patch index de2a161..e9d3565 100644 --- a/SOURCES/libgcrypt-1.10.0-fips-kdf.patch +++ b/SOURCES/libgcrypt-1.10.0-fips-kdf.patch @@ -1,36 +1,3 @@ -From 857e6f467d0fc9fd858a73d84122695425970075 Mon Sep 17 00:00:00 2001 -From: NIIBE Yutaka -Date: Tue, 27 Sep 2022 13:26:16 +0900 -Subject: [PATCH] kdf:pkdf2: Require longer input when FIPS mode. - -* cipher/kdf.c (_gcry_kdf_pkdf2): Add length check. - --- - -GnuPG-bug-id: 6039 -Fixes-commit: 58c92098d053aae7c78cc42bdd7c80c13efc89bb -Signed-off-by: NIIBE Yutaka ---- - cipher/kdf.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/cipher/kdf.c b/cipher/kdf.c -index 3e51e115..81523320 100644 ---- a/cipher/kdf.c -+++ b/cipher/kdf.c -@@ -160,6 +160,9 @@ _gcry_kdf_pkdf2 (const void *passphrase, size_t passphraselen, - return GPG_ERR_INV_VALUE; - #endif - -+ /* HMAC requires longer input for approved use case. */ -+ if (fips_mode () && passphraselen < 14) -+ return GPG_ERR_INV_VALUE; - - /* Step 2 */ - l = ((dklen - 1)/ hlen) + 1; --- -2.37.3 - From 3c04b692de1e7b45b764ff8d66bf84609b012e3a Mon Sep 17 00:00:00 2001 From: Tobias Heider Date: Tue, 27 Sep 2022 13:31:05 +0900 @@ -58,9 +25,9 @@ index 81523320..67c60df8 100644 + if (fips_mode () && dklen < 14) + return GPG_ERR_INV_VALUE; + - /* HMAC requires longer input for approved use case. */ - if (fips_mode () && passphraselen < 14) - return GPG_ERR_INV_VALUE; + + /* Step 2 */ + l = ((dklen - 1)/ hlen) + 1; -- 2.37.3 From e5a5e847b66eb6b80e60a2dffa347268f059aee3 Mon Sep 17 00:00:00 2001 @@ -118,3 +85,103 @@ index c0192d7b..716fb53e 100644 -- 2.37.3 +From f4a861f3e5ae82f278284061e4829c03edf9c3a7 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 18 Nov 2022 09:49:50 +0900 +Subject: [PATCH] pkdf2: Add checks for FIPS. + +* cipher/kdf.c (_gcry_kdf_pkdf2): Require 8 chars passphrase for FIPS. +Set bounds for salt length and iteration count in FIPS mode. + +-- + +GnuPG-bug-id: 6039 +Signed-off-by: Jakub Jelen +--- + cipher/kdf.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/cipher/kdf.c b/cipher/kdf.c +index d22584da..823c744e 100644 +--- a/cipher/kdf.c ++++ b/cipher/kdf.c +@@ -160,6 +160,18 @@ _gcry_kdf_pkdf2 (const void *passphrase, size_t passphraselen, + return GPG_ERR_INV_VALUE; + #endif + ++ /* FIPS requires minimum passphrase length, see FIPS 140-3 IG D.N */ ++ if (fips_mode () && passphraselen < 8) ++ return GPG_ERR_INV_VALUE; ++ ++ /* FIPS requires minimum salt length of 128 b (SP 800-132 sec. 5.1, p.6) */ ++ if (fips_mode () && saltlen < 16) ++ return GPG_ERR_INV_VALUE; ++ ++ /* FIPS requires minimum iterations bound (SP 800-132 sec 5.2, p.6) */ ++ if (fips_mode () && iterations < 1000) ++ return GPG_ERR_INV_VALUE; ++ + /* Check minimum key size */ + if (fips_mode () && dklen < 14) + return GPG_ERR_INV_VALUE; +-- +2.39.0 + +From f5fe94810f3099c9ccc2ca3a5891502922ab0576 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 28 Feb 2023 12:53:28 +0100 +Subject: [PATCH] kdf: Update tests in regards to the allowed parameters in + FIPS mode. + +* cipher/kdf.c (check_one): run selftests for more approved parameters +and check that wrong parameters correctly fail in FIPS mode. + +-- + +Fixes-commit: 535a4d345872aa2cd2ab3a5f9c4411d0a0313328 +GnuPG-bug-id: 5512 +Signed-off-by: Jakub Jelen +--- + cipher/kdf.c | 22 +++++++++++++++------- + 1 file changed, 15 insertions(+), 7 deletions(-) + +diff --git a/cipher/kdf.c b/cipher/kdf.c +index 823c744e..12beec56 100644 +--- a/cipher/kdf.c ++++ b/cipher/kdf.c +@@ -2059,17 +2059,25 @@ check_one (int algo, int hash_algo, + { + unsigned char key[512]; /* hardcoded to avoid allocation */ + size_t keysize = expectlen; +- +- /* Skip test with shoter passphrase in FIPS mode. */ +- if (fips_mode () && passphraselen < 14) +- return NULL; ++ int rv; + + if (keysize > sizeof(key)) + return "invalid tests data"; + +- if (_gcry_kdf_derive (passphrase, passphraselen, algo, +- hash_algo, salt, saltlen, iterations, +- keysize, key)) ++ rv = _gcry_kdf_derive (passphrase, passphraselen, algo, ++ hash_algo, salt, saltlen, iterations, ++ keysize, key); ++ /* In fips mode we have special requirements for the input and ++ * output parameters */ ++ if (fips_mode ()) ++ { ++ if (rv && (passphraselen < 8 || saltlen < 16 || ++ iterations < 1000 || expectlen < 14)) ++ return NULL; ++ else if (rv) ++ return "gcry_kdf_derive unexpectedly failed in FIPS Mode"; ++ } ++ else if (rv) + return "gcry_kdf_derive failed"; + + if (memcmp (key, expect, expectlen)) +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-pct.patch b/SOURCES/libgcrypt-1.10.0-fips-pct.patch new file mode 100644 index 0000000..252bd31 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-pct.patch @@ -0,0 +1,145 @@ +From 2ddeec574bc1ae90bb4242c4ce9ad9e7975a27bd Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Wed, 1 Mar 2023 15:42:29 +0100 +Subject: [PATCH] ecc: Do not allow skipping tests in FIPS Mode. + +* cipher/ecc.c (ecc_generate): Do not allow skipping tests PCT tests +in FIPS mode. + +-- + +The new FIPS specification requires to run the PCT without any +exceptions. + +GnuPG-bug-id: 6394 +Signed-off-by: Jakub Jelen +--- + cipher/ecc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cipher/ecc.c b/cipher/ecc.c +index 1e80200e..797f2368 100644 +--- a/cipher/ecc.c ++++ b/cipher/ecc.c +@@ -677,7 +677,7 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) + log_debug ("ecgen result using Ed25519+EdDSA\n"); + } + +- if (!(flags & PUBKEY_FLAG_NO_KEYTEST) && fips_mode ()) ++ if (fips_mode ()) + test_keys_fips (*r_skey); + + leave: +-- +2.39.2 + +From 23a2d1285e35b2eb91bb422609eb1c965c8a9bf6 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Thu, 2 Mar 2023 09:43:44 +0100 +Subject: [PATCH] ecc: Make the PCT recoverable in FIPS mode and consistent + with RSA. + +* cipher/ecc.c (test_keys_fips): Replace calls to log_fatal with +return code on error. +(ecc_generate): Signal error when PCT fails in FIPS mode. + +-- + +GnuPG-bug-id: 6397 +Signed-off-by: Jakub Jelen +--- + cipher/ecc.c | 36 ++++++++++++++++++++++++++++-------- + 1 file changed, 28 insertions(+), 8 deletions(-) + +diff --git a/cipher/ecc.c b/cipher/ecc.c +index 797f2368..19520db3 100644 +--- a/cipher/ecc.c ++++ b/cipher/ecc.c +@@ -101,7 +101,7 @@ static void *progress_cb_data; + + /* Local prototypes. */ + static void test_keys (mpi_ec_t ec, unsigned int nbits); +-static void test_keys_fips (gcry_sexp_t skey); ++static int test_keys_fips (gcry_sexp_t skey); + static void test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags); + static unsigned int ecc_get_nbits (gcry_sexp_t parms); + +@@ -308,9 +308,10 @@ test_keys (mpi_ec_t ec, unsigned int nbits) + /* We should get here only with the NIST curves as they are the only ones + * having the fips bit set in ecc_domain_parms_t struct so this is slightly + * simpler than the whole ecc_generate function */ +-static void ++static int + test_keys_fips (gcry_sexp_t skey) + { ++ int result = -1; /* Default to failure */ + gcry_md_hd_t hd = NULL; + const char *data_tmpl = "(data (flags rfc6979) (hash %s %b))"; + gcry_sexp_t sig = NULL; +@@ -323,18 +324,27 @@ test_keys_fips (gcry_sexp_t skey) + /* Open MD context and feed the random data in */ + rc = _gcry_md_open (&hd, GCRY_MD_SHA256, 0); + if (rc) +- log_fatal ("ECDSA operation: failed to initialize MD context: %s\n", gpg_strerror (rc)); ++ { ++ log_error ("ECDSA operation: failed to initialize MD context: %s\n", gpg_strerror (rc)); ++ goto leave; ++ } + _gcry_md_write (hd, plaintext, sizeof(plaintext)); + + /* Sign the data */ + rc = _gcry_pk_sign_md (&sig, data_tmpl, hd, skey, NULL); + if (rc) +- log_fatal ("ECDSA operation: signing failed: %s\n", gpg_strerror (rc)); ++ { ++ log_error ("ECDSA operation: signing failed: %s\n", gpg_strerror (rc)); ++ goto leave; ++ } + + /* Verify this signature. */ + rc = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL); + if (rc) +- log_fatal ("ECDSA operation: verification failed: %s\n", gpg_strerror (rc)); ++ { ++ log_error ("ECDSA operation: verification failed: %s\n", gpg_strerror (rc)); ++ goto leave; ++ } + + /* Modify the data and check that the signing fails. */ + _gcry_md_reset(hd); +@@ -342,10 +352,16 @@ test_keys_fips (gcry_sexp_t skey) + _gcry_md_write (hd, plaintext, sizeof(plaintext)); + rc = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL); + if (rc != GPG_ERR_BAD_SIGNATURE) +- log_fatal ("ECDSA operation: signature verification worked on modified data\n"); ++ { ++ log_error ("ECDSA operation: signature verification worked on modified data\n"); ++ goto leave; ++ } + ++ result = 0; ++leave: + _gcry_md_close (hd); + sexp_release (sig); ++ return result; + } + + +@@ -677,8 +693,12 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) + log_debug ("ecgen result using Ed25519+EdDSA\n"); + } + +- if (fips_mode ()) +- test_keys_fips (*r_skey); ++ if (fips_mode () && test_keys_fips (*r_skey)) ++ { ++ sexp_release (*r_skey); r_skey = NULL; ++ fips_signal_error ("self-test after key generation failed"); ++ rc = GPG_ERR_SELFTEST_FAILED; ++ } + + leave: + mpi_free (public); +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-rsa-pss.patch b/SOURCES/libgcrypt-1.10.0-fips-rsa-pss.patch new file mode 100644 index 0000000..af3f772 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-rsa-pss.patch @@ -0,0 +1,109 @@ +From bf1e62e59200b2046680d1d3d1599facc88cfe63 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 29 Nov 2022 14:04:59 +0100 +Subject: [PATCH] rsa: Prevent usage of long salt in FIPS mode + +* cipher/rsa-common.c (_gcry_rsa_pss_encode): Prevent usage of large + salt lengths + (_gcry_rsa_pss_verify): Ditto. +* tests/basic.c (check_pubkey_sign): Check longer salt length fails in + FIPS mode +* tests/t-rsa-pss.c (one_test_sexp): Fix function name in error message +--- + cipher/rsa-common.c | 14 ++++++++++++++ + tests/basic.c | 19 ++++++++++++++++++- + tests/t-rsa-pss.c | 2 +- + 3 files changed, 33 insertions(+), 2 deletions(-) + +diff --git a/cipher/rsa-common.c b/cipher/rsa-common.c +index 233ddb2d..61cd60a4 100644 +--- a/cipher/rsa-common.c ++++ b/cipher/rsa-common.c +@@ -809,6 +809,13 @@ _gcry_rsa_pss_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo, + hlen = _gcry_md_get_algo_dlen (algo); + gcry_assert (hlen); /* We expect a valid ALGO here. */ + ++ /* The FIPS 186-4 Section 5.5 allows only 0 <= sLen <= hLen */ ++ if (fips_mode () && saltlen > hlen) ++ { ++ rc = GPG_ERR_INV_ARG; ++ goto leave; ++ } ++ + /* Allocate a help buffer and setup some pointers. */ + buflen = 8 + hlen + saltlen + (emlen - hlen - 1); + buf = xtrymalloc (buflen); +@@ -950,6 +957,13 @@ _gcry_rsa_pss_verify (gcry_mpi_t value, int hashed_already, + hlen = _gcry_md_get_algo_dlen (algo); + gcry_assert (hlen); /* We expect a valid ALGO here. */ + ++ /* The FIPS 186-4 Section 5.5 allows only 0 <= sLen <= hLen */ ++ if (fips_mode () && saltlen > hlen) ++ { ++ rc = GPG_ERR_INV_ARG; ++ goto leave; ++ } ++ + /* Allocate a help buffer and setup some pointers. + This buffer is used for two purposes: + +------------------------------+-------+ +diff --git a/tests/basic.c b/tests/basic.c +index 77e2fd93..429bd237 100644 +--- a/tests/basic.c ++++ b/tests/basic.c +@@ -16602,6 +16602,7 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo, + const char *data; + int algo; + int expected_rc; ++ int flags; + } datas[] = + { + { "(data\n (flags pkcs1)\n" +@@ -16672,6 +16673,22 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo, + " (random-override #4253647587980912233445566778899019283747#))\n", + GCRY_PK_RSA, + 0 }, ++ { "(data\n (flags pss)\n" ++ " (hash-algo sha256)\n" ++ " (value #11223344556677889900AABBCCDDEEFF#)\n" ++ " (salt-length 2:32)\n" ++ " (random-override #42536475879809122334455667788990192837465564738291" ++ "00122334455667#))\n", ++ GCRY_PK_RSA, ++ 0 }, ++ { "(data\n (flags pss)\n" ++ " (hash-algo sha256)\n" ++ " (value #11223344556677889900AABBCCDDEEFF#)\n" ++ " (salt-length 2:33)\n" ++ " (random-override #42536475879809122334455667788990192837465564738291" ++ "0012233445566778#))\n", ++ GCRY_PK_RSA, ++ 0, FLAG_NOFIPS }, + { NULL } + }; + +@@ -16695,7 +16712,7 @@ check_pubkey_sign (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo, + die ("converting data failed: %s\n", gpg_strerror (rc)); + + rc = gcry_pk_sign (&sig, hash, skey); +- if (in_fips_mode && (flags & FLAG_NOFIPS)) ++ if (in_fips_mode && (flags & FLAG_NOFIPS || datas[dataidx].flags & FLAG_NOFIPS)) + { + if (!rc) + fail ("gcry_pk_sign did not fail as expected in FIPS mode\n"); +diff --git a/tests/t-rsa-pss.c b/tests/t-rsa-pss.c +index c5f90116..82dd54b3 100644 +--- a/tests/t-rsa-pss.c ++++ b/tests/t-rsa-pss.c +@@ -340,7 +340,7 @@ one_test_sexp (const char *n, const char *e, const char *d, + snprintf (p, 3, "%02x", out[i]); + if (strcmp (sig_string, s)) + { +- fail ("gcry_pkhash_sign failed: %s", ++ fail ("gcry_pk_hash_sign failed: %s", + "wrong value returned"); + info (" expected: '%s'", s); + info (" got: '%s'", sig_string); +-- +2.39.0 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-status-sign-verify.patch b/SOURCES/libgcrypt-1.10.0-fips-status-sign-verify.patch new file mode 100644 index 0000000..9cf20c3 --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-status-sign-verify.patch @@ -0,0 +1,46 @@ +From 654d0dfa04993ebe28c0536d42f4bc6d87c28369 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Wed, 1 Mar 2023 17:14:00 +0100 +Subject: [PATCH] visibility: Check FIPS operational status for MD+Sign + operation. + +* src/visibility.c (gcry_pk_hash_sign): Check fips status before +calling the operation itself. +(gcry_pk_hash_verify): Ditto. + +-- + +GnuPG-bug-id: 6396 +Signed-off-by: Jakub Jelen +--- + src/visibility.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/visibility.c b/src/visibility.c +index 73db3dea..1f17e147 100644 +--- a/src/visibility.c ++++ b/src/visibility.c +@@ -1050,6 +1050,11 @@ gcry_error_t + gcry_pk_hash_sign (gcry_sexp_t *result, const char *data_tmpl, gcry_sexp_t skey, + gcry_md_hd_t hd, gcry_ctx_t ctx) + { ++ if (!fips_is_operational ()) ++ { ++ *result = NULL; ++ return gpg_error (fips_not_operational ()); ++ } + return gpg_error (_gcry_pk_sign_md (result, data_tmpl, hd, skey, ctx)); + } + +@@ -1065,6 +1070,8 @@ gcry_error_t + gcry_pk_hash_verify (gcry_sexp_t sigval, const char *data_tmpl, gcry_sexp_t pkey, + gcry_md_hd_t hd, gcry_ctx_t ctx) + { ++ if (!fips_is_operational ()) ++ return gpg_error (fips_not_operational ()); + return gpg_error (_gcry_pk_verify_md (sigval, data_tmpl, hd, pkey, ctx)); + } + +-- +2.39.2 + diff --git a/SOURCES/libgcrypt-1.10.0-fips-x931.patch b/SOURCES/libgcrypt-1.10.0-fips-x931.patch new file mode 100644 index 0000000..b4a99ba --- /dev/null +++ b/SOURCES/libgcrypt-1.10.0-fips-x931.patch @@ -0,0 +1,139 @@ +From 06ea5b5332ffdb44a0a394d766be8989bcb6a95c Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 6 Dec 2022 10:03:47 +0900 +Subject: [PATCH] fips,rsa: Prevent usage of X9.31 keygen in FIPS mode. + +* cipher/rsa.c (rsa_generate): Do not accept use-x931 or derive-parms +in FIPS mode. +* tests/pubkey.c (get_keys_x931_new): Expect failure in FIPS mode. +(check_run): Skip checking X9.31 keys in FIPS mode. +* doc/gcrypt.texi: Document "test-parms" and clarify some cases around +the X9.31 keygen. + +-- + +Signed-off-by: Jakub Jelen +--- + cipher/rsa.c | 5 +++++ + doc/gcrypt.texi | 41 ++++++++++++++++++++++++++++++++++++----- + tests/pubkey.c | 15 +++++++++++++-- + 3 files changed, 54 insertions(+), 7 deletions(-) + +diff --git a/cipher/rsa.c b/cipher/rsa.c +index df4af94b..45523e6b 100644 +--- a/cipher/rsa.c ++++ b/cipher/rsa.c +@@ -1256,6 +1256,11 @@ rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) + if (deriveparms || (flags & PUBKEY_FLAG_USE_X931)) + { + int swapped; ++ if (fips_mode ()) ++ { ++ sexp_release (deriveparms); ++ return GPG_ERR_INV_SEXP; ++ } + ec = generate_x931 (&sk, nbits, evalue, deriveparms, &swapped); + sexp_release (deriveparms); + if (!ec && swapped) +diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi +index d0372f3e..e845a4dd 100644 +--- a/doc/gcrypt.texi ++++ b/doc/gcrypt.texi +@@ -2699,8 +2699,7 @@ achieve fastest ECC key generation. + Force the use of the ANSI X9.31 key generation algorithm instead of + the default algorithm. This flag is only meaningful for RSA key + generation and usually not required. Note that this algorithm is +-implicitly used if either @code{derive-parms} is given or Libgcrypt is +-in FIPS mode. ++implicitly used if either @code{derive-parms} is given. + + @item use-fips186 + @cindex FIPS 186 +@@ -3310,9 +3309,9 @@ This is currently only implemented for RSA and DSA keys. It is not + allowed to use this together with a @code{domain} specification. If + given, it is used to derive the keys using the given parameters. + +-If given for an RSA key the X9.31 key generation algorithm is used +-even if libgcrypt is not in FIPS mode. If given for a DSA key, the +-FIPS 186 algorithm is used even if libgcrypt is not in FIPS mode. ++If given for an RSA key, the X9.31 key generation algorithm is used. ++If given for a DSA key, the FIPS 186 algorithm is used even if ++libgcrypt is not in FIPS mode. + + @example + (genkey +@@ -3342,6 +3341,38 @@ FIPS 186 algorithm is used even if libgcrypt is not in FIPS mode. + (seed @var{seed-mpi})))) + @end example + ++@item test-parms @var{list} ++This is currently only implemented for RSA keys. If given, the ++libgcrypt will not generate parameter, but tests whether the p,q is ++probably prime. Returns key with zeroes. ++ ++The FIPS key generation algorithm is used even if libgcrypt is not ++in FIPS mode. ++ ++@example ++(genkey ++ (rsa ++ (nbits 4:1024) ++ (rsa-use-e 1:3) ++ (test-parms ++ (e "65537") ++ (p #00bbccabcee15d343944a47e492d4b1f4de79633e2 ++ 0cbb46f7d2d6813392a807ad048cf77528edd19f77 ++ e7453f25173b9dcb70423afa2037aae147b81a33d5 ++ 41fc58f875eff1e852ab55e2e09a3debfbc151b3b0 ++ d17fef6f74d81fca14fbae531418e211ef818592af ++ 70de5cec3b92795cc3578572bf456099cd8727150e ++ 523261#) ++ (q #00ca87ecf2883f4ed00a9ec65abdeba81d28edbfcc ++ 34ecc563d587f166b52d42bfbe22bbc095b0b8426a ++ 2f8bbc55baaa8859b42cbc376ed3067db3ef7b135b ++ 63481322911ebbd7014db83aa051e0ca2dbf302b75 ++ cd37f2ae8df90e134226e92f6353a284b28bb30af0 ++ bbf925b345b955328379866ebac11d55bc80fe84f1 ++ 05d415#) ++ ++@end example ++ + + @item flags @var{flaglist} + This is preferred way to define flags. @var{flaglist} may contain any +diff --git a/tests/pubkey.c b/tests/pubkey.c +index bc44f3a5..2669b41a 100644 +--- a/tests/pubkey.c ++++ b/tests/pubkey.c +@@ -430,7 +430,17 @@ get_keys_x931_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) +- die ("error generating RSA key: %s\n", gcry_strerror (rc)); ++ { ++ if (in_fips_mode) ++ { ++ if (verbose) ++ fprintf (stderr, "The X9.31 RSA keygen is not available in FIPS modee.\n"); ++ return; ++ } ++ die ("error generating RSA key: %s\n", gcry_strerror (rc)); ++ } ++ else if (in_fips_mode) ++ die ("generating X9.31 RSA key unexpected worked in FIPS mode\n"); + + if (verbose > 1) + show_sexp ("generated RSA (X9.31) key:\n", key); +@@ -777,7 +787,8 @@ check_run (void) + if (verbose) + fprintf (stderr, "Checking generated RSA key (X9.31).\n"); + get_keys_x931_new (&pkey, &skey); +- check_keys (pkey, skey, 800, 0); ++ if (!in_fips_mode) ++ check_keys (pkey, skey, 800, 0); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + pkey = skey = NULL; +-- +2.39.0 + diff --git a/SPECS/libgcrypt.spec b/SPECS/libgcrypt.spec index a9297bb..1b1550a 100644 --- a/SPECS/libgcrypt.spec +++ b/SPECS/libgcrypt.spec @@ -16,7 +16,7 @@ print(string.sub(hash, 0, 16)) Name: libgcrypt Version: 1.10.0 -Release: 7%{?dist} +Release: 10%{?dist} URL: https://www.gnupg.org/ Source0: https://www.gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-%{version}.tar.bz2 Source1: https://www.gnupg.org/ftp/gcrypt/libgcrypt/libgcrypt-%{version}.tar.bz2.sig @@ -34,7 +34,35 @@ Patch9: libgcrypt-1.10.0-sha3-large.patch # https://dev.gnupg.org/T5919 Patch10: libgcrypt-1.10.0-fips-keygen.patch # https://dev.gnupg.org/T6219 +# f4a861f3e5ae82f278284061e4829c03edf9c3a7 Patch11: libgcrypt-1.10.0-fips-kdf.patch +# c34c9e70055ee43e5ef257384fa15941f064e5a4 +# https://gitlab.com/redhat-crypto/libgcrypt/libgcrypt-mirror/-/merge_requests/13 +Patch12: libgcrypt-1.10.0-fips-indicator.patch +# beb5d6df5c5785db7c32a24a5d2a351cb964bfbc +# 521500624b4b11538d206137205e2a511dad7072 +# 9dcf9305962b90febdf2d7cc73b49feadbf6a01f +# a340e980388243ceae6df57d101036f3f2a955be +Patch13: libgcrypt-1.10.0-fips-integrity.patch +# 3c8b6c4a9cad59c5e1db5706f6774a3141b60210 +# 052c5ef4cea56772b7015e36f231fa0bcbf91410 +# 3fd3bb31597f80c76a94ea62e42d58d796beabf1 +Patch14: libgcrypt-1.10.0-fips-integrity2.patch +# 06ea5b5332ffdb44a0a394d766be8989bcb6a95c +Patch15: libgcrypt-1.10.0-fips-x931.patch +# bf1e62e59200b2046680d1d3d1599facc88cfe63 +Patch16: libgcrypt-1.10.0-fips-rsa-pss.patch +# https://dev.gnupg.org/T6376 +Patch17: libgcrypt-1.10.0-fips-indicator-md-hmac.patch +# https://dev.gnupg.org/T6394 +# https://dev.gnupg.org/T6397 +Patch18: libgcrypt-1.10.0-fips-pct.patch +# https://dev.gnupg.org/T6396 +Patch19: libgcrypt-1.10.0-fips-status-sign-verify.patch +# https://dev.gnupg.org/T6393 +Patch20: libgcrypt-1.10.0-fips-drbg.patch +# https://dev.gnupg.org/T6417 +Patch21: libgcrypt-1.10.0-fips-indicator-pk-flags.patch %global gcrylibdir %{_libdir} %global gcrysoname libgcrypt.so.20 @@ -79,6 +107,16 @@ applications using libgcrypt. %patch9 -p1 %patch10 -p1 %patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 %build # This package has a configure test which uses ASMs, but does not link the @@ -105,6 +143,7 @@ autoreconf -f --enable-noexecstack \ --enable-hmac-binary-check=%{hmackey} \ --disable-brainpool \ + --disable-jent-support \ --enable-digests="$DIGESTS" \ --enable-ciphers="$CIPHERS" \ --with-fips-module-version="$FIPS_MODULE_NAME %{version}-%{srpmhash}" @@ -122,12 +161,12 @@ LIBGCRYPT_FORCE_FIPS_MODE=1 make check %{?__debug_package:%{__debug_install_post}} \ %{__arch_install_post} \ %{__os_install_post} \ - dd if=/dev/zero of=%{libpath}.hmac bs=32 count=1 \ - objcopy --update-section .rodata1=%{libpath}.hmac %{libpath} %{libpath}.empty \ - src/hmac256 --binary %{hmackey} %{libpath}.empty > %{libpath}.hmac \ - objcopy --update-section .rodata1=%{libpath}.hmac %{libpath}.empty %{libpath}.new \ + cd src \ + sed -i -e 's|FILE=.*|FILE=\\\$1|' gen-note-integrity.sh \ + READELF=readelf AWK=awk ECHO_N="-n" bash gen-note-integrity.sh %{libpath} > %{libpath}.hmac \ + objcopy --update-section .note.fdo.integrity=%{libpath}.hmac %{libpath} %{libpath}.new \ mv -f %{libpath}.new %{libpath} \ - rm -f %{libpath}.hmac %{libpath}.empty + rm -f %{libpath}.hmac %{nil} %install @@ -197,6 +236,24 @@ mkdir -p -m 755 $RPM_BUILD_ROOT/etc/gcrypt %license COPYING %changelog +* Mon Mar 20 2023 Jakub Jelen - 1.10.0-10 +- Provide FIPS indicators for MD and HMACs +- Improve PCT tests for ECDSA and always run them after key is generated +- Add missing guards for FIPS status in md_sign/verify function +- Provider FIPS indicators for public key operation flags + +* Tue Jan 24 2023 Jakub Jelen - 1.10.0-9 +- Avoid usage of invalid arguments sizes for PBKDF2 in FIPS mode +- Do not allow large salt lengths with RSA-PSS padding +- Disable X9.31 key generation in FIPS mode +- Update the FIPS integrity checking code to upstream version +- Update cipher modes FIPS indicators for AES WRAP and GCM +- Disable jitter entropy generator + +* Thu Oct 20 2022 Jakub Jelen - 1.10.0-8 +- Fix unneeded PBKDF2 passphrase length limitation in FIPS mode +- Enforce HMAC key lengths in MD API in FIPS mode + * Thu Oct 06 2022 Jakub Jelen - 1.10.0-7 - Properly enforce KDF limits in FIPS mode (#2130275) - Fix memory leak in large digest test (#2129150)