From fbddfb964f0b1c1ec131194b2273c3f834041c84 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 16 Jun 2022 14:54:30 +0900 Subject: kdf: Add HKDF of RFC5869. * src/gcrypt.h.in (GCRY_KDF_HKDF): New. * cipher/kdf.c (hkdf_open, hkdf_compute, hkdf_final, hkdf_close): New. (_gcry_kdf_open, _gcry_kdf_compute, _gcry_kdf_final, _gcry_kdf_close): Handle GCRY_KDF_HKDF. * tests/t-kdf.c (check_hkdf): New. Test vectors from RFC5869. (main): Call check_hkdf. -- GnuPG-bug-id: 5964 Signed-off-by: NIIBE Yutaka --- cipher/kdf.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/gcrypt.h.in | 1 + tests/t-kdf.c | 163 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 381 insertions(+), 2 deletions(-) diff --git a/cipher/kdf.c b/cipher/kdf.c index dc6aaeb7..c3e45f84 100644 --- a/cipher/kdf.c +++ b/cipher/kdf.c @@ -1642,6 +1642,200 @@ onestep_kdf_mac_close (onestep_kdf_mac_ctx_t o) xfree (o); } +typedef struct hkdf_context *hkdf_ctx_t; + +/* Hkdf context */ +struct hkdf_context { + int algo; + gcry_mac_hd_t md; + int mode; + unsigned int blklen; + unsigned int outlen; + const void *input; + size_t inputlen; + const void *salt; + size_t saltlen; + const void *fixedinfo; + size_t fixedinfolen; + unsigned char *prk; +}; + +static gpg_err_code_t +hkdf_open (gcry_kdf_hd_t *hd, int macalgo, + const unsigned long *param, unsigned int paramlen, + const void *input, size_t inputlen, + const void *salt, size_t saltlen, + const void *fixedinfo, size_t fixedinfolen) +{ + gpg_err_code_t ec; + unsigned int outlen; + int mode; + hkdf_ctx_t h; + size_t n; + unsigned char *prk; + + if (paramlen != 1 && paramlen != 2) + return GPG_ERR_INV_VALUE; + else + { + outlen = (unsigned int)param[0]; + /* MODE: support extract only, expand only: FIXME*/ + if (paramlen == 2) + mode = (unsigned int)param[1]; + else + mode = 0; + } + + n = sizeof (struct hkdf_context); + h = xtrymalloc (n); + if (!h) + return gpg_err_code_from_errno (errno); + + h->blklen = _gcry_mac_get_algo_maclen (macalgo); + if (!h->blklen) + { + xfree (h); + return GPG_ERR_MAC_ALGO; + } + ec = _gcry_mac_open (&h->md, macalgo, 0, NULL); + if (ec) + { + xfree (h); + return ec; + } + prk = xtrymalloc (h->blklen); + if (!prk) + { + _gcry_mac_close (h->md); + xfree (h); + return gpg_err_code_from_errno (errno); + } + h->prk = prk; + h->algo = GCRY_KDF_HKDF; + h->outlen = outlen; + h->mode = mode; + h->input = input; + h->inputlen = inputlen; + h->salt = salt; + h->saltlen = saltlen; + h->fixedinfo = fixedinfo; + h->fixedinfolen = fixedinfolen; + + *hd = (void *)h; + return 0; +} + + +static gpg_err_code_t +hkdf_compute (hkdf_ctx_t h, const struct gcry_kdf_thread_ops *ops) +{ + gcry_err_code_t ec; + size_t len = h->blklen; + + if (ops != NULL) + return GPG_ERR_INV_VALUE; + + /* Extract */ + ec = _gcry_mac_setkey (h->md, h->salt, h->saltlen); + if (ec) + return ec; + + ec = _gcry_mac_write (h->md, h->input, h->inputlen); + if (ec) + return ec; + + ec = _gcry_mac_read (h->md, h->prk, &len); + if (ec) + return ec; + + ec = _gcry_mac_ctl (h->md, GCRYCTL_RESET, NULL, 0); + if (ec) + return ec; + + return 0; +} + +static gpg_err_code_t +hkdf_final (hkdf_ctx_t h, size_t resultlen, void *result) +{ + unsigned char counter = 0; + int i; + gcry_err_code_t ec; + size_t len = h->blklen; + + if (resultlen != h->outlen) + return GPG_ERR_INV_VALUE; + + /* Expand */ + ec = _gcry_mac_setkey (h->md, h->prk, h->blklen); + if (ec) + return ec; + + /* We re-use the memory of ->prk. */ + + for (i = 0; i < h->outlen / h->blklen; i++) + { + counter++; + if (i) + { + ec = _gcry_mac_write (h->md, h->prk, h->blklen); + if (ec) + return ec; + } + if (h->fixedinfo) + { + ec = _gcry_mac_write (h->md, h->fixedinfo, h->fixedinfolen); + if (ec) + return ec; + } + ec = _gcry_mac_write (h->md, &counter, 1); + if (ec) + return ec; + ec = _gcry_mac_read (h->md, h->prk, &len); + if (ec) + return ec; + memcpy ((char *)result + h->blklen * i, h->prk, len); + resultlen -= h->blklen; + ec = _gcry_mac_ctl (h->md, GCRYCTL_RESET, NULL, 0); + if (ec) + return ec; + } + + if (resultlen) + { + counter++; + len = resultlen; + if (i) + { + ec = _gcry_mac_write (h->md, h->prk, h->blklen); + if (ec) + return ec; + } + if (h->fixedinfo) + { + ec = _gcry_mac_write (h->md, h->fixedinfo, h->fixedinfolen); + if (ec) + return ec; + } + ec = _gcry_mac_write (h->md, &counter, 1); + if (ec) + return ec; + ec = _gcry_mac_read (h->md, (char *)result + h->blklen * i, &len); + if (ec) + return ec; + } + + return 0; +} + +static void +hkdf_close (hkdf_ctx_t h) +{ + _gcry_mac_close (h->md); + xfree (h->prk); + xfree (h); +} + struct gcry_kdf_handle { int algo; /* And algo specific parts come. */ @@ -1703,6 +1897,17 @@ _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, } break; + case GCRY_KDF_HKDF: + if (!inputlen || !paramlen) + ec = GPG_ERR_INV_VALUE; + else + { + (void)salt; + ec = hkdf_open (hd, subalgo, param, paramlen, + input, inputlen, key, keylen, ad, adlen); + } + break; + default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; @@ -1734,6 +1939,10 @@ _gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops) ec = onestep_kdf_mac_compute ((onestep_kdf_mac_ctx_t)(void *)h, ops); break; + case GCRY_KDF_HKDF: + ec = hkdf_compute ((hkdf_ctx_t)(void *)h, ops); + break; + default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; @@ -1767,6 +1976,10 @@ _gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result) resultlen, result); break; + case GCRY_KDF_HKDF: + ec = hkdf_final ((hkdf_ctx_t)(void *)h, resultlen, result); + break; + default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; @@ -1796,11 +2009,15 @@ _gcry_kdf_close (gcry_kdf_hd_t h) onestep_kdf_mac_close ((onestep_kdf_mac_ctx_t)(void *)h); break; + case GCRY_KDF_HKDF: + hkdf_close ((hkdf_ctx_t)(void *)h); + break; + default: break; } } - + /* Check one KDF call with ALGO and HASH_ALGO using the regular KDF * API. (passphrase,passphraselen) is the password to be derived, * (salt,saltlen) the salt for the key derivation, diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index 809848b7..299261db 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -1603,6 +1603,7 @@ enum gcry_kdf_algos */ GCRY_KDF_ONESTEP_KDF = 96, /* One-Step Key Derivation with hash */ GCRY_KDF_ONESTEP_KDF_MAC = 97, /* One-Step Key Derivation with MAC */ + GCRY_KDF_HKDF = 98, /* Two-Step Key Derivation with HMAC */ /* Two-Step Key Derivation with CMAC */ /* KDF PRF in SP 800-108r1 */ diff --git a/tests/t-kdf.c b/tests/t-kdf.c index d10a0e34..508e4bbe 100644 --- a/tests/t-kdf.c +++ b/tests/t-kdf.c @@ -1718,7 +1718,7 @@ check_onestep_kdf (void) fail ("OneStepKDF test %d failed: %s\n", count, gpg_strerror (err)); else if (memcmp (out, expected[count], param[count])) { - fail ("OneStepKDF test %d failed: mismatch\n", count*2+0); + fail ("OneStepKDF test %d failed: mismatch\n", count); fputs ("got:", stderr); for (i=0; i < param[count]; i++) fprintf (stderr, " %02x", out[i]); @@ -1732,6 +1732,166 @@ check_onestep_kdf (void) } +static void +check_hkdf (void) +{ + gcry_error_t err; + unsigned long param[1]; + unsigned char out[82]; + const unsigned char input0[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + const unsigned char input1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; + const unsigned char salt0[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c + }; + const unsigned char salt1[] = { + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf + }; + const unsigned char info0[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 + }; + const unsigned char info1[] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + const unsigned char expected0[] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 + }; + const unsigned char expected1[] = { + 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, + 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, + 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, + 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, + 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, + 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, + 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, + 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87 + }; + const unsigned char expected2[] = { + 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, + 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, + 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, + 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, + 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, + 0x96, 0xc8 + }; + + int i; + int count = 0; + const unsigned char *input; + const unsigned char *salt; + const unsigned char *info; + const unsigned char *expected; + size_t inputlen; + size_t saltlen; + size_t infolen; + size_t expectedlen; + + again: + + if (verbose) + fprintf (stderr, "checking HKDF test vector %d\n", count); + + switch (count) + { + case 0: + input = input0; + inputlen = sizeof (input0); + salt = salt0; + saltlen = sizeof (salt0); + info = info0; + infolen = sizeof (info0); + expected = expected0; + expectedlen = sizeof (expected0); + break; + case 1: + input = input1; + inputlen = sizeof (input1); + salt = salt1; + saltlen = sizeof (salt1); + info = info1; + infolen = sizeof (info1); + expected = expected1; + expectedlen = sizeof (expected1); + break; + case 2: + input = input0; + inputlen = sizeof (input0); + salt = NULL; + saltlen = 0; + info = NULL; + infolen = 0; + expected = expected2; + expectedlen = sizeof (expected2); + break; + } + + param[0] = expectedlen; + err = my_kdf_derive (0, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256, + param, 1, + input, inputlen, NULL, 0, + salt, saltlen, + info, infolen, + expectedlen, out); + if (err) + fail ("HKDF test %d failed: %s\n", count, gpg_strerror (err)); + else if (memcmp (out, expected, expectedlen)) + { + fail ("HKDF test %d failed: mismatch\n", count); + fputs ("got:", stderr); + for (i=0; i < expectedlen; i++) + fprintf (stderr, " %02x", out[i]); + putc ('\n', stderr); + } + + /* Next test vector */ + count++; + if (count < 3) + goto again; +} + + int main (int argc, char **argv) { @@ -1811,6 +1971,7 @@ main (int argc, char **argv) check_argon2 (); check_balloon (); check_onestep_kdf (); + check_hkdf (); } return error_count ? 1 : 0; -- cgit v1.2.1