diff --git a/SOURCES/libgcrypt-1.8.3-cmac-selftest.patch b/SOURCES/libgcrypt-1.8.3-cmac-selftest.patch new file mode 100644 index 0000000..d480092 --- /dev/null +++ b/SOURCES/libgcrypt-1.8.3-cmac-selftest.patch @@ -0,0 +1,322 @@ +diff -up libgcrypt-1.8.3/cipher/cipher-cmac.c.cmac-selftest libgcrypt-1.8.3/cipher/cipher-cmac.c +--- libgcrypt-1.8.3/cipher/cipher-cmac.c.cmac-selftest 2017-11-23 19:16:58.000000000 +0100 ++++ libgcrypt-1.8.3/cipher/cipher-cmac.c 2019-05-31 17:33:35.594407152 +0200 +@@ -251,3 +251,246 @@ _gcry_cipher_cmac_set_subkeys (gcry_ciph + + return GPG_ERR_NO_ERROR; + } ++ ++/* CMAC selftests. ++ * Copyright (C) 2008 Free Software Foundation, Inc. ++ * Copyright (C) 2019 Red Hat, Inc. ++ */ ++ ++ ++ ++/* Check one MAC with MAC ALGO using the regular MAC ++ * API. (DATA,DATALEN) is the data to be MACed, (KEY,KEYLEN) the key ++ * and (EXPECT,EXPECTLEN) the expected result. If TRUNC is set, the ++ * EXPECTLEN may be less than the digest length. Returns NULL on ++ * success or a string describing the failure. */ ++static const char * ++check_one (int algo, ++ const void *data, size_t datalen, ++ const void *key, size_t keylen, ++ const void *expect, size_t expectlen) ++{ ++ gcry_mac_hd_t hd; ++ unsigned char mac[512]; /* hardcoded to avoid allocation */ ++ size_t macoutlen = expectlen; ++ ++/* printf ("MAC algo %d\n", algo); */ ++ if (_gcry_mac_get_algo_maclen (algo) != expectlen || ++ expectlen > sizeof (mac)) ++ return "invalid tests data"; ++ if (_gcry_mac_open (&hd, algo, 0, NULL)) ++ return "gcry_mac_open failed"; ++ if (_gcry_mac_setkey (hd, key, keylen)) ++ { ++ _gcry_mac_close (hd); ++ return "gcry_md_setkey failed"; ++ } ++ if (_gcry_mac_write (hd, data, datalen)) ++ { ++ _gcry_mac_close (hd); ++ return "gcry_mac_write failed"; ++ } ++ if (_gcry_mac_read (hd, mac, &macoutlen)) ++ { ++ _gcry_mac_close (hd); ++ return "gcry_mac_read failed"; ++ } ++ _gcry_mac_close (hd); ++ if (macoutlen != expectlen || memcmp (mac, expect, expectlen)) ++ { ++/* int i; */ ++ ++/* fputs (" {", stdout); */ ++/* for (i=0; i < expectlen-1; i++) */ ++/* { */ ++/* if (i && !(i % 8)) */ ++/* fputs ("\n ", stdout); */ ++/* printf (" 0x%02x,", mac[i]); */ ++/* } */ ++/* printf (" 0x%02x } },\n", mac[i]); */ ++ ++ return "does not match"; ++ } ++ return NULL; ++} ++ ++ ++static gpg_err_code_t ++selftests_cmac_tdes (int extended, selftest_report_func_t report) ++{ ++ const char *what; ++ const char *errtxt; ++ ++ what = "Basic TDES"; ++ errtxt = check_one (GCRY_MAC_CMAC_3DES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57", 20, ++ "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" ++ "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, ++ "\x74\x3d\xdb\xe0\xce\x2d\xc2\xed", 8); ++ if (errtxt) ++ goto failed; ++ ++ if (extended) ++ { ++ what = "Extended TDES #1"; ++ errtxt = check_one (GCRY_MAC_CMAC_3DES, ++ "", 0, ++ "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" ++ "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, ++ "\xb7\xa6\x88\xe1\x22\xff\xaf\x95", 8); ++ if (errtxt) ++ goto failed; ++ ++ what = "Extended TDES #2"; ++ errtxt = check_one (GCRY_MAC_CMAC_3DES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96", 8, ++ "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" ++ "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, ++ "\x8e\x8f\x29\x31\x36\x28\x37\x97", 8); ++ if (errtxt) ++ goto failed; ++ ++ what = "Extended TDES #3"; ++ errtxt = check_one (GCRY_MAC_CMAC_3DES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51", 32, ++ "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" ++ "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", 24, ++ "\x33\xe6\xb1\x09\x24\x00\xea\xe5", 8); ++ if (errtxt) ++ goto failed; ++ } ++ ++ return 0; /* Succeeded. */ ++ ++ failed: ++ if (report) ++ report ("cmac", GCRY_MAC_CMAC_3DES, what, errtxt); ++ return GPG_ERR_SELFTEST_FAILED; ++} ++ ++ ++ ++static gpg_err_code_t ++selftests_cmac_aes (int extended, selftest_report_func_t report) ++{ ++ const char *what; ++ const char *errtxt; ++ ++ what = "Basic AES128"; ++ errtxt = check_one (GCRY_MAC_CMAC_AES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, ++ "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, ++ "\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27", 16); ++ if (errtxt) ++ goto failed; ++ ++ what = "Basic AES192"; ++ errtxt = check_one (GCRY_MAC_CMAC_AES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, ++ "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" ++ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, ++ "\x8a\x1d\xe5\xbe\x2e\xb3\x1a\xad\x08\x9a\x82\xe6\xee\x90\x8b\x0e", 16); ++ if (errtxt) ++ goto failed; ++ ++ what = "Basic AES256"; ++ errtxt = check_one (GCRY_MAC_CMAC_AES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, ++ "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" ++ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, ++ "\xaa\xf3\xd8\xf1\xde\x56\x40\xc2\x32\xf5\xb1\x69\xb9\xc9\x11\xe6", 16); ++ if (errtxt) ++ goto failed; ++ if (extended) ++ { ++ what = "Extended AES #1"; ++ errtxt = check_one (GCRY_MAC_CMAC_AES, ++ "", 0, ++ "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, ++ "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46", 16); ++ if (errtxt) ++ goto failed; ++ ++ what = "Extended AES #2"; ++ errtxt = check_one (GCRY_MAC_CMAC_AES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", 16, ++ "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" ++ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, ++ "\x9e\x99\xa7\xbf\x31\xe7\x10\x90\x06\x62\xf6\x5e\x61\x7c\x51\x84", 16); ++ if (errtxt) ++ goto failed; ++ ++ what = "Extended AES #3"; ++ errtxt = check_one (GCRY_MAC_CMAC_AES, ++ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" ++ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" ++ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" ++ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, ++ "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" ++ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, ++ "\xe1\x99\x21\x90\x54\x9f\x6e\xd5\x69\x6a\x2c\x05\x6c\x31\x54\x10", 16 ); ++ if (errtxt) ++ goto failed; ++ } ++ ++ return 0; /* Succeeded. */ ++ ++ failed: ++ if (report) ++ report ("cmac", GCRY_MAC_CMAC_AES, what, errtxt); ++ return GPG_ERR_SELFTEST_FAILED; ++} ++ ++ ++/* Run a full self-test for ALGO and return 0 on success. */ ++static gpg_err_code_t ++run_cmac_selftests (int algo, int extended, selftest_report_func_t report) ++{ ++ gpg_err_code_t ec; ++ ++ switch (algo) ++ { ++ case GCRY_MAC_CMAC_3DES: ++ ec = selftests_cmac_tdes (extended, report); ++ break; ++ case GCRY_MAC_CMAC_AES: ++ ec = selftests_cmac_aes (extended, report); ++ break; ++ ++ default: ++ ec = GPG_ERR_MAC_ALGO; ++ break; ++ } ++ return ec; ++} ++ ++ ++ ++ ++/* Run the selftests for CMAC with CMAC algorithm ALGO with optional ++ reporting function REPORT. */ ++gpg_error_t ++_gcry_cmac_selftest (int algo, int extended, selftest_report_func_t report) ++{ ++ gcry_err_code_t ec = 0; ++ ++ if (!_gcry_mac_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, NULL )) ++ { ++ ec = run_cmac_selftests (algo, extended, report); ++ } ++ else ++ { ++ ec = GPG_ERR_MAC_ALGO; ++ if (report) ++ report ("mac", algo, "module", "algorithm not available"); ++ } ++ return gpg_error (ec); ++} +diff -up libgcrypt-1.8.3/src/cipher-proto.h.cmac-selftest libgcrypt-1.8.3/src/cipher-proto.h +--- libgcrypt-1.8.3/src/cipher-proto.h.cmac-selftest 2017-11-23 19:16:58.000000000 +0100 ++++ libgcrypt-1.8.3/src/cipher-proto.h 2019-05-31 17:29:34.574588234 +0200 +@@ -256,6 +256,8 @@ gcry_error_t _gcry_pk_selftest (int algo + selftest_report_func_t report); + gcry_error_t _gcry_hmac_selftest (int algo, int extended, + selftest_report_func_t report); ++gcry_error_t _gcry_cmac_selftest (int algo, int extended, ++ selftest_report_func_t report); + + gcry_error_t _gcry_random_selftest (selftest_report_func_t report); + +diff -up libgcrypt-1.8.3/src/fips.c.cmac-selftest libgcrypt-1.8.3/src/fips.c +--- libgcrypt-1.8.3/src/fips.c.cmac-selftest 2018-11-01 15:40:36.051865535 +0100 ++++ libgcrypt-1.8.3/src/fips.c 2019-05-31 17:31:20.157756640 +0200 +@@ -521,29 +521,32 @@ run_digest_selftests (int extended) + + /* Run self-tests for all HMAC algorithms. Return 0 on success. */ + static int +-run_hmac_selftests (int extended) ++run_mac_selftests (int extended) + { +- static int algos[] = ++ static int algos[][2] = + { +- GCRY_MD_SHA1, +- GCRY_MD_SHA224, +- GCRY_MD_SHA256, +- GCRY_MD_SHA384, +- GCRY_MD_SHA512, +- GCRY_MD_SHA3_224, +- GCRY_MD_SHA3_256, +- GCRY_MD_SHA3_384, +- GCRY_MD_SHA3_512, +- 0 ++ { GCRY_MD_SHA1, 0 }, ++ { GCRY_MD_SHA224, 0 }, ++ { GCRY_MD_SHA256, 0 }, ++ { GCRY_MD_SHA384, 0 }, ++ { GCRY_MD_SHA512, 0 }, ++ { GCRY_MD_SHA3_224, 0 }, ++ { GCRY_MD_SHA3_256, 0 }, ++ { GCRY_MD_SHA3_384, 0 }, ++ { GCRY_MD_SHA3_512, 0 }, ++ { GCRY_MAC_CMAC_3DES, 1 }, ++ { GCRY_MAC_CMAC_AES, 1 }, ++ { 0, 0 } + }; + int idx; + gpg_error_t err; + int anyerr = 0; + +- for (idx=0; algos[idx]; idx++) ++ for (idx=0; algos[idx][0]; idx++) + { +- err = _gcry_hmac_selftest (algos[idx], extended, reporter); +- reporter ("hmac", algos[idx], NULL, ++ err = algos[idx][1] ? _gcry_cmac_selftest (algos[idx][0], extended, reporter) : ++ _gcry_hmac_selftest (algos[idx][0], extended, reporter); ++ reporter (algos[idx][1] ? "cmac" : "hmac", algos[idx][0], NULL, + err? gpg_strerror (err):NULL); + if (err) + anyerr = 1; +@@ -747,7 +750,7 @@ _gcry_fips_run_selftests (int extended) + if (run_digest_selftests (extended)) + goto leave; + +- if (run_hmac_selftests (extended)) ++ if (run_mac_selftests (extended)) + goto leave; + + /* Run random tests before the pubkey tests because the latter diff --git a/SOURCES/libgcrypt-1.8.3-fips-enttest.patch b/SOURCES/libgcrypt-1.8.3-fips-enttest.patch new file mode 100644 index 0000000..b6b09ba --- /dev/null +++ b/SOURCES/libgcrypt-1.8.3-fips-enttest.patch @@ -0,0 +1,113 @@ +diff -up libgcrypt-1.8.3/random/random-drbg.c.fips-enttest libgcrypt-1.8.3/random/random-drbg.c +--- libgcrypt-1.8.3/random/random-drbg.c.fips-enttest 2017-11-23 19:16:58.000000000 +0100 ++++ libgcrypt-1.8.3/random/random-drbg.c 2019-06-24 10:04:23.219547141 +0200 +@@ -317,6 +317,7 @@ struct drbg_state_s + unsigned char *ctr_null; /* CTR mode zero buffer */ + int seeded:1; /* DRBG fully seeded? */ + int pr:1; /* Prediction resistance enabled? */ ++ int ent_primed:1; /* Previous entropy data primed? */ + /* Taken from libgcrypt ANSI X9.31 DRNG: We need to keep track of the + * process which did the initialization so that we can detect a fork. + * The volatile modifier is required so that the compiler does not +@@ -324,6 +325,7 @@ struct drbg_state_s + pid_t seed_init_pid; + const struct drbg_state_ops_s *d_ops; + const struct drbg_core_s *core; ++ unsigned char ent_hash[64]; /* Hash of previous entropy data */ + struct drbg_test_data_s *test_data; + }; + +@@ -610,11 +612,13 @@ drbg_get_entropy (drbg_state_t drbg, uns + size_t len) + { + int rc = 0; ++ unsigned char newhash[64]; + + /* Perform testing as defined in 11.3.2 */ + if (drbg->test_data && drbg->test_data->fail_seed_source) + return -1; + ++redo: + read_cb_buffer = buffer; + read_cb_size = len; + read_cb_len = 0; +@@ -634,6 +638,27 @@ drbg_get_entropy (drbg_state_t drbg, uns + #else + rc = -1; + #endif ++ ++ /* to avoid storing the actual entropy obtained for indefinite ++ time, we just store the SHA-512 hash of the entropy gathered ++ */ ++ _gcry_md_hash_buffer (GCRY_MD_SHA512, newhash, buffer, len); ++ ++ if (!drbg->ent_primed) ++ { ++ memcpy (drbg->ent_hash, newhash, sizeof (drbg->ent_hash)); ++ drbg->ent_primed = 1; ++ goto redo; ++ } ++ ++ if (memcmp (newhash, drbg->ent_hash, sizeof (drbg->ent_hash)) == 0) ++ { ++ fips_signal_error ("Entropy source failed the continuous test"); ++ return -1; /* continuous entropy test failed */ ++ } ++ ++ memcpy (drbg->ent_hash, newhash, sizeof (drbg->ent_hash)); ++ + return rc; + } + +@@ -1341,26 +1366,38 @@ drbg_seed (drbg_state_t drbg, drbg_strin + } + else + { ++ int nonce = 0; + /* Gather entropy equal to the security strength of the DRBG. + * With a derivation function, a nonce is required in addition + * to the entropy. A nonce must be at least 1/2 of the security + * strength of the DRBG in size. Thus, entropy * nonce is 3/2 + * of the strength. The consideration of a nonce is only +- * applicable during initial seeding. */ ++ * applicable during initial seeding. ++ * To avoid pulling different length of data from entropy ++ * source, we use 2 * strength for initial seeding. */ + entropylen = drbg_sec_strength (drbg->core->flags); + if (!entropylen) + return GPG_ERR_GENERAL; + if (0 == reseed) +- /* make sure we round up strength/2 in +- * case it is not divisible by 2 */ +- entropylen = ((entropylen + 1) / 2) * 3; ++ { ++ nonce = 1; ++ } + dbg (("DRBG: (re)seeding with %lu bytes of entropy\n", entropylen)); +- entropy = xcalloc_secure (1, entropylen); ++ entropy = xcalloc_secure (nonce + 1, entropylen); + if (!entropy) + return GPG_ERR_ENOMEM; + ret = drbg_get_entropy (drbg, entropy, entropylen); + if (ret) + goto out; ++ if (nonce) ++ { ++ ret = drbg_get_entropy (drbg, entropy + entropylen, entropylen); ++ if (ret) ++ goto out; ++ /* make sure we round up strength/2 in ++ * case it is not divisible by 2 */ ++ entropylen = 2 * entropylen; ++ } + drbg_string_fill (&data1, entropy, entropylen); + } + +@@ -1597,6 +1634,7 @@ drbg_instantiate (drbg_state_t drbg, + drbg->core = &drbg_cores[coreref]; + drbg->pr = pr; + drbg->seeded = 0; ++ drbg->ent_primed = 0; + if (drbg->core->flags & DRBG_HMAC) + drbg->d_ops = &drbg_hmac_ops; + else if (drbg->core->flags & DRBG_HASH_MASK) diff --git a/SOURCES/libgcrypt-1.8.3-md-fips-enforce.patch b/SOURCES/libgcrypt-1.8.3-md-fips-enforce.patch new file mode 100644 index 0000000..d040bfb --- /dev/null +++ b/SOURCES/libgcrypt-1.8.3-md-fips-enforce.patch @@ -0,0 +1,12 @@ +diff -up libgcrypt-1.8.3/cipher/md.c.fips-enforce libgcrypt-1.8.3/cipher/md.c +--- libgcrypt-1.8.3/cipher/md.c.fips-enforce 2018-11-01 15:40:36.051865535 +0100 ++++ libgcrypt-1.8.3/cipher/md.c 2019-06-03 11:50:21.435401753 +0200 +@@ -409,7 +409,7 @@ md_enable (gcry_md_hd_t hd, int algorith + } + + +- if (!err && algorithm == GCRY_MD_MD5 && fips_mode ()) ++ if (!err && !spec->flags.fips && fips_mode ()) + { + if (_gcry_enforced_fips_mode () ) + { diff --git a/SPECS/libgcrypt.spec b/SPECS/libgcrypt.spec index f6bf893..7eaf176 100644 --- a/SPECS/libgcrypt.spec +++ b/SPECS/libgcrypt.spec @@ -1,6 +1,6 @@ Name: libgcrypt Version: 1.8.3 -Release: 2%{?dist} +Release: 4%{?dist} URL: http://www.gnupg.org/ Source0: libgcrypt-%{version}-hobbled.tar.xz # The original libgcrypt sources now contain potentially patented ECC @@ -35,6 +35,12 @@ Patch18: libgcrypt-1.8.3-fips-ctor.patch Patch22: libgcrypt-1.7.3-fips-reqs.patch # Do not try to open /dev/urandom if getrandom() works Patch24: libgcrypt-1.8.3-getrandom.patch +# CMAC selftest for FIPS POST +Patch25: libgcrypt-1.8.3-cmac-selftest.patch +# Continuous FIPS entropy test +Patch26: libgcrypt-1.8.3-fips-enttest.patch +# Disable non-approved FIPS hashes in the enforced FIPS mode +Patch27: libgcrypt-1.8.3-md-fips-enforce.patch %define gcrylibdir %{_libdir} @@ -80,6 +86,9 @@ applications using libgcrypt. %patch18 -p1 -b .fips-ctor %patch22 -p1 -b .fips-reqs %patch24 -p1 -b .getrandom +%patch25 -p1 -b .cmac-selftest +%patch26 -p1 -b .fips-enttest +%patch27 -p1 -b .fips-enforce cp %{SOURCE4} cipher/ cp %{SOURCE5} %{SOURCE6} tests/ @@ -191,6 +200,14 @@ exit 0 %license COPYING %changelog +* Mon Jun 24 2019 Tomáš Mráz 1.8.3-4 +- improve the continuous FIPS entropy test + +* Mon Jun 3 2019 Tomáš Mráz 1.8.3-3 +- add CMAC selftest for FIPS POST +- add continuous FIPS entropy test +- disable non-approved FIPS hashes in the enforced FIPS mode + * Thu Jul 12 2018 Tomáš Mráz 1.8.3-2 - make only_urandom a default in non-presence of configuration file - run the full FIPS selftests only when the library is called from