summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2020-10-05 16:12:46 +0200
committerDaiki Ueno <ueno@gnu.org>2020-10-06 14:15:32 +0200
commit93c0e3ba4d2cfee86b32f28f33303a2193c4133c (patch)
tree31e62f55e2949e0bb8169f8fcbc71e9f275f6d68
parent6f034aa2e9f140626de2b9413715651dffe9e394 (diff)
downloadgnutls-93c0e3ba4d2cfee86b32f28f33303a2193c4133c.tar.gz
fips: add self-tests for HKDF
FIPS140-2 IG D.8 mandates self-test on approved KDF algorithms. As the guidance only requires running a single instance of each KDF mechanism, this only exercises HKDF-Extract and HKDF-Expand operations with HMAC-SHA-256 as the underlying MAC. Although HKDF is non-approved, it would be sensible to do that as it will be approved in FIPS140-3. Signed-off-by: Daiki Ueno <ueno@gnu.org>
-rw-r--r--devel/libgnutls-latest-x86_64.abi1
-rw-r--r--lib/crypto-selftests.c159
-rw-r--r--lib/fips.c7
-rw-r--r--lib/includes/gnutls/self-test.h1
-rw-r--r--lib/libgnutls.map1
5 files changed, 169 insertions, 0 deletions
diff --git a/devel/libgnutls-latest-x86_64.abi b/devel/libgnutls-latest-x86_64.abi
index 7f0ddd46c7..7ad5dc71f2 100644
--- a/devel/libgnutls-latest-x86_64.abi
+++ b/devel/libgnutls-latest-x86_64.abi
@@ -323,6 +323,7 @@
<elf-symbol name='gnutls_hex_encode' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_hkdf_expand' version='GNUTLS_3_6_13' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_hkdf_extract' version='GNUTLS_3_6_13' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='gnutls_hkdf_self_test' version='GNUTLS_FIPS140_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_hmac' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_hmac_copy' version='GNUTLS_3_6_9' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_hmac_deinit' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
diff --git a/lib/crypto-selftests.c b/lib/crypto-selftests.c
index 7a1c7729c8..bd148b6af1 100644
--- a/lib/crypto-selftests.c
+++ b/lib/crypto-selftests.c
@@ -2917,3 +2917,162 @@ int gnutls_digest_self_test(unsigned flags, gnutls_digest_algorithm_t digest)
return 0;
}
+
+struct hkdf_vectors_st {
+ const uint8_t *ikm;
+ unsigned int ikm_size;
+ const uint8_t *salt;
+ unsigned int salt_size;
+ const uint8_t *prk;
+ unsigned int prk_size;
+ const uint8_t *info;
+ unsigned int info_size;
+ const uint8_t *okm;
+ unsigned int okm_size;
+};
+
+const struct hkdf_vectors_st hkdf_sha256_vectors[] = {
+ /* RFC 5869: A.1. Test Case 1: Basic test case with SHA-256 */
+ {
+ STR(ikm, ikm_size,
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"),
+ STR(salt, salt_size,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"),
+ STR(prk, prk_size,
+ "\x07\x77\x09\x36\x2c\x2e\x32\xdf\x0d\xdc\x3f\x0d\xc4\x7b"
+ "\xba\x63\x90\xb6\xc7\x3b\xb5\x0f\x9c\x31\x22\xec\x84\x4a"
+ "\xd7\xc2\xb3\xe5"),
+ STR(info, info_size,
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9"),
+ STR(okm, okm_size,
+ "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a\x90\x43\x4f\x64\xd0\x36"
+ "\x2f\x2a\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c\x5d\xb0\x2d\x56"
+ "\xec\xc4\xc5\xbf\x34\x00\x72\x08\xd5\xb8\x87\x18\x58\x65"),
+ },
+ /* RFC 5869: A.2. Test Case 2: Test with SHA-256 and longer inputs/outputs */
+ {
+ STR(ikm, ikm_size,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
+ "\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b"
+ "\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29"
+ "\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"
+ "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45"
+ "\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"),
+ STR(salt, salt_size,
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d"
+ "\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b"
+ "\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89"
+ "\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97"
+ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5"
+ "\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"),
+ STR(prk, prk_size,
+ "\x06\xa6\xb8\x8c\x58\x53\x36\x1a\x06\x10\x4c\x9c\xeb\x35"
+ "\xb4\x5c\xef\x76\x00\x14\x90\x46\x71\x01\x4a\x19\x3f\x40"
+ "\xc1\x5f\xc2\x44"),
+ STR(info, info_size,
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd"
+ "\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb"
+ "\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9"
+ "\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+ "\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5"
+ "\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"),
+ STR(okm, okm_size,
+ "\xb1\x1e\x39\x8d\xc8\x03\x27\xa1\xc8\xe7\xf7\x8c\x59\x6a"
+ "\x49\x34\x4f\x01\x2e\xda\x2d\x4e\xfa\xd8\xa0\x50\xcc\x4c"
+ "\x19\xaf\xa9\x7c\x59\x04\x5a\x99\xca\xc7\x82\x72\x71\xcb"
+ "\x41\xc6\x5e\x59\x0e\x09\xda\x32\x75\x60\x0c\x2f\x09\xb8"
+ "\x36\x77\x93\xa9\xac\xa3\xdb\x71\xcc\x30\xc5\x81\x79\xec"
+ "\x3e\x87\xc1\x4c\x01\xd5\xc1\xf3\x43\x4f\x1d\x87"),
+ },
+};
+
+static int test_hkdf(gnutls_mac_algorithm_t mac,
+ const struct hkdf_vectors_st *vectors,
+ size_t vectors_size, unsigned flags)
+{
+ unsigned int i;
+
+ for (i = 0; i < vectors_size; i++) {
+ gnutls_datum_t ikm, prk, salt, info;
+ uint8_t output[4096];
+ int ret;
+
+ ikm.data = (void *) vectors[i].ikm;
+ ikm.size = vectors[i].ikm_size;
+ salt.data = (void *) vectors[i].salt;
+ salt.size = vectors[i].salt_size;
+
+ ret = gnutls_hkdf_extract(mac, &ikm, &salt, output);
+ if (ret < 0) {
+ _gnutls_debug_log("error extracting HKDF: MAC-%s\n",
+ gnutls_mac_get_name(mac));
+ return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
+ }
+
+ if (memcmp(output, vectors[i].prk, vectors[i].prk_size) != 0) {
+ _gnutls_debug_log
+ ("HKDF extract: MAC-%s test vector failed!\n",
+ gnutls_mac_get_name(mac));
+
+ return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
+ }
+
+ prk.data = (void *) vectors[i].prk;
+ prk.size = vectors[i].prk_size;
+ info.data = (void *) vectors[i].info;
+ info.size = vectors[i].info_size;
+
+ ret = gnutls_hkdf_expand(mac, &prk, &info,
+ output, vectors[i].okm_size);
+ if (ret < 0) {
+ _gnutls_debug_log("error extracting HKDF: MAC-%s\n",
+ gnutls_mac_get_name(mac));
+ return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
+ }
+
+ if (memcmp(output, vectors[i].okm, vectors[i].okm_size) != 0) {
+ _gnutls_debug_log
+ ("HKDF expand: MAC-%s test vector failed!\n",
+ gnutls_mac_get_name(mac));
+
+ return gnutls_assert_val(GNUTLS_E_SELF_TEST_ERROR);
+ }
+ }
+
+ _gnutls_debug_log
+ ("HKDF: MAC-%s self check succeeded\n",
+ gnutls_mac_get_name(mac));
+
+ return 0;
+}
+
+/*-
+ * gnutls_hkdf_self_test:
+ * @flags: GNUTLS_SELF_TEST_FLAG flags
+ * @mac: the message authentication algorithm to use
+ *
+ * This function will run self tests on HKDF with the provided mac.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.3.0-FIPS140
+ -*/
+int gnutls_hkdf_self_test(unsigned flags, gnutls_mac_algorithm_t mac)
+{
+ int ret;
+
+ if (flags & GNUTLS_SELF_TEST_FLAG_ALL)
+ mac = GNUTLS_MAC_UNKNOWN;
+
+ switch (mac) {
+ case GNUTLS_MAC_UNKNOWN:
+ CASE(GNUTLS_MAC_SHA256, test_hkdf, hkdf_sha256_vectors);
+
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_NO_SELF_TEST);
+ }
+
+ return 0;
+}
diff --git a/lib/fips.c b/lib/fips.c
index f8b10f7502..48891ed573 100644
--- a/lib/fips.c
+++ b/lib/fips.c
@@ -423,6 +423,13 @@ int _gnutls_fips_perform_self_checks2(void)
goto error;
}
+ /* HKDF */
+ ret = gnutls_hkdf_self_test(0, GNUTLS_MAC_SHA256);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
if (_gnutls_rnd_ops.self_test == NULL) {
gnutls_assert();
goto error;
diff --git a/lib/includes/gnutls/self-test.h b/lib/includes/gnutls/self-test.h
index aacbe94ca6..9b7be81590 100644
--- a/lib/includes/gnutls/self-test.h
+++ b/lib/includes/gnutls/self-test.h
@@ -34,5 +34,6 @@ int gnutls_cipher_self_test(unsigned flags, gnutls_cipher_algorithm_t cipher);
int gnutls_mac_self_test(unsigned flags, gnutls_mac_algorithm_t mac);
int gnutls_digest_self_test(unsigned flags, gnutls_digest_algorithm_t digest);
int gnutls_pk_self_test(unsigned flags, gnutls_pk_algorithm_t pk);
+int gnutls_hkdf_self_test(unsigned flags, gnutls_mac_algorithm_t mac);
#endif /* GNUTLS_SELF_TEST_H */
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 61276e5340..386b66f83e 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1347,6 +1347,7 @@ GNUTLS_FIPS140_3_4 {
gnutls_pk_self_test;
gnutls_mac_self_test;
gnutls_digest_self_test;
+ gnutls_hkdf_self_test;
#for FIPS140-2 validation
drbg_aes_reseed;
drbg_aes_init;