From f8c983cb14f8ba0921ce8fa52ea3519feae07861 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 7 Jun 2022 15:47:45 +0900 Subject: kdf: Add One-Step KDF with hash. * src/gcrypt.h.in (GCRY_KDF_ONESTEP_KDF): New. * cipher/kdf.c (onestep_kdf_open, onestep_kdf_compute): New. (onestep_kdf_final): New. (_gcry_kdf_open, _gcry_kdf_compute, _gcry_kdf_final): Add GCRY_KDF_ONESTEP_KDF support. * tests/t-kdf.c (check_onestep_kdf): Add the test. (main): Call check_onestep_kdf. -- GnuPG-bug-id: 5964 Signed-off-by: NIIBE Yutaka --- cipher/kdf.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/gcrypt.h.in | 15 +++++- tests/t-kdf.c | 79 ++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 6 deletions(-) diff --git a/cipher/kdf.c b/cipher/kdf.c index 0e196432..01a28667 100644 --- a/cipher/kdf.c +++ b/cipher/kdf.c @@ -1382,8 +1382,123 @@ balloon_close (balloon_ctx_t b) wipememory (b, n); xfree (b); } + +typedef struct onestep_kdf_context *onestep_kdf_ctx_t; + +/* OneStepKDF context */ +struct onestep_kdf_context { + int algo; + gcry_md_hd_t md; + unsigned int blklen; + unsigned int outlen; + const void *input; + size_t inputlen; + const void *fixedinfo; + size_t fixedinfolen; +}; + +static gpg_err_code_t +onestep_kdf_open (gcry_kdf_hd_t *hd, int hashalgo, + const unsigned long *param, unsigned int paramlen, + const void *input, size_t inputlen, + const void *fixedinfo, size_t fixedinfolen) +{ + gpg_err_code_t ec; + unsigned int outlen; + onestep_kdf_ctx_t o; + size_t n; + + if (paramlen != 1) + return GPG_ERR_INV_VALUE; + else + outlen = (unsigned int)param[0]; + + n = sizeof (struct onestep_kdf_context); + o = xtrymalloc (n); + if (!o) + return gpg_err_code_from_errno (errno); + o->blklen = _gcry_md_get_algo_dlen (hashalgo); + if (!o->blklen) + { + xfree (o); + return GPG_ERR_DIGEST_ALGO; + } + ec = _gcry_md_open (&o->md, hashalgo, 0); + if (ec) + { + xfree (o); + return ec; + } + o->algo = GCRY_KDF_ONESTEP_KDF; + o->outlen = outlen; + o->input = input; + o->inputlen = inputlen; + o->fixedinfo = fixedinfo; + o->fixedinfolen = fixedinfolen; + + *hd = (void *)o; + return 0; +} + +static gpg_err_code_t +onestep_kdf_compute (onestep_kdf_ctx_t o, const struct gcry_kdf_thread_ops *ops) +{ + (void)o; + + if (ops != NULL) + return GPG_ERR_INV_VALUE; + + return 0; +} + +static gpg_err_code_t +onestep_kdf_final (onestep_kdf_ctx_t o, size_t resultlen, void *result) +{ + u32 counter = 0; + unsigned char cnt[4]; + int i; + + if (resultlen != o->outlen) + return GPG_ERR_INV_VALUE; + + for (i = 0; i < o->outlen / o->blklen; i++) + { + counter++; + buf_put_be32 (cnt, counter); + _gcry_md_write (o->md, cnt, sizeof (cnt)); + _gcry_md_write (o->md, o->input, o->inputlen); + _gcry_md_write (o->md, o->fixedinfo, o->fixedinfolen); + _gcry_md_final (o->md); + memcpy ((char *)result + o->blklen * i, + _gcry_md_read (o->md, 0), o->blklen); + resultlen -= o->blklen; + _gcry_md_reset (o->md); + } + + if (resultlen) + { + counter++; + buf_put_be32 (cnt, counter); + _gcry_md_write (o->md, cnt, sizeof (cnt)); + _gcry_md_write (o->md, o->input, o->inputlen); + _gcry_md_write (o->md, o->fixedinfo, o->fixedinfolen); + _gcry_md_final (o->md); + memcpy ((char *)result + o->blklen * i, + _gcry_md_read (o->md, 0), resultlen); + } + + return 0; +} + +static void +onestep_kdf_close (onestep_kdf_ctx_t o) +{ + _gcry_md_close (o->md); + xfree (o); +} + struct gcry_kdf_handle { int algo; /* And algo specific parts come. */ @@ -1392,7 +1507,7 @@ struct gcry_kdf_handle { gpg_err_code_t _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, const unsigned long *param, unsigned int paramlen, - const void *passphrase, size_t passphraselen, + const void *input, size_t inputlen, const void *salt, size_t saltlen, const void *key, size_t keylen, const void *ad, size_t adlen) @@ -1402,23 +1517,35 @@ _gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo, switch (algo) { case GCRY_KDF_ARGON2: - if (!passphraselen || !saltlen) + if (!inputlen || !saltlen) ec = GPG_ERR_INV_VALUE; else ec = argon2_open (hd, subalgo, param, paramlen, - passphrase, passphraselen, salt, saltlen, + input, inputlen, salt, saltlen, key, keylen, ad, adlen); break; case GCRY_KDF_BALLOON: - if (!passphraselen || !saltlen || keylen || adlen) + if (!inputlen || !saltlen || keylen || adlen) ec = GPG_ERR_INV_VALUE; else { (void)key; (void)ad; ec = balloon_open (hd, subalgo, param, paramlen, - passphrase, passphraselen, salt, saltlen); + input, inputlen, salt, saltlen); + } + break; + + case GCRY_KDF_ONESTEP_KDF: + if (!inputlen || !paramlen || !adlen) + ec = GPG_ERR_INV_VALUE; + else + { + (void)salt; + (void)key; + ec = onestep_kdf_open (hd, subalgo, param, paramlen, + input, inputlen, ad, adlen); } break; @@ -1445,6 +1572,10 @@ _gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops) ec = balloon_compute_all ((balloon_ctx_t)(void *)h, ops); break; + case GCRY_KDF_ONESTEP_KDF: + ec = onestep_kdf_compute ((onestep_kdf_ctx_t)(void *)h, ops); + break; + default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; @@ -1469,6 +1600,10 @@ _gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result) ec = balloon_final ((balloon_ctx_t)(void *)h, resultlen, result); break; + case GCRY_KDF_ONESTEP_KDF: + ec = onestep_kdf_final ((onestep_kdf_ctx_t)(void *)h, resultlen, result); + break; + default: ec = GPG_ERR_UNKNOWN_ALGORITHM; break; @@ -1490,6 +1625,10 @@ _gcry_kdf_close (gcry_kdf_hd_t h) balloon_close ((balloon_ctx_t)(void *)h); break; + case GCRY_KDF_ONESTEP_KDF: + onestep_kdf_close ((onestep_kdf_ctx_t)(void *)h); + break; + default: break; } diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index b17e242f..5879acaf 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -1592,8 +1592,21 @@ enum gcry_kdf_algos GCRY_KDF_PBKDF1 = 33, GCRY_KDF_PBKDF2 = 34, GCRY_KDF_SCRYPT = 48, + /**/ GCRY_KDF_ARGON2 = 64, - GCRY_KDF_BALLOON = 65 + GCRY_KDF_BALLOON = 65, + /**/ + /* In the original SP 800-56A, it's called + * "Concatenation Key Derivation Function". + * Now (as of 2022), it's defined in SP 800-56C rev.2, as + * "One-Step Key Derivation". + */ + GCRY_KDF_ONESTEP_KDF = 96, /* One-Step Key Derivation with hash */ + /* One-Step Key Derivation with HMAC */ + /* One-Step Key Derivation with KMAC */ + /* Two-Step Key Derivation with HMAC */ + /* Two-Step Key Derivation with CMAC */ + /* KDF PRF in SP 800-108r1 */ }; enum gcry_kdf_subalgo_argon2 diff --git a/tests/t-kdf.c b/tests/t-kdf.c index 234bbac6..49116d20 100644 --- a/tests/t-kdf.c +++ b/tests/t-kdf.c @@ -1603,6 +1603,84 @@ check_balloon (void) } +static void +check_onestep_kdf (void) +{ + gcry_error_t err; + const unsigned long param[2] = { 38, 68 }; + unsigned char out[68]; + unsigned char input[2][16] = { + { + 0x3f, 0x89, 0x2b, 0xd8, 0xb8, 0x4d, 0xae, 0x64, + 0xa7, 0x82, 0xa3, 0x5f, 0x6e, 0xaa, 0x8f, 0x00 + }, + { + 0xe6, 0x5b, 0x19, 0x05, 0x87, 0x8b, 0x95, 0xf6, + 0x8b, 0x55, 0x35, 0xbd, 0x3b, 0x2b, 0x10, 0x13 + } + }; + unsigned char other[2][12] = { + { + 0xec, 0x3f, 0x1c, 0xd8, 0x73, 0xd2, 0x88, 0x58, + 0xa5, 0x8c, 0xc3, 0x9e + }, + { + 0x83, 0x02, 0x21, 0xb1, 0x73, 0x0d, 0x91, 0x76, + 0xf8, 0x07, 0xd4, 0x07 + } + }; + unsigned char expected[2][68] = { + { + 0xa7, 0xc0, 0x66, 0x52, 0x98, 0x25, 0x25, 0x31, + 0xe0, 0xdb, 0x37, 0x73, 0x7a, 0x37, 0x46, 0x51, + 0xb3, 0x68, 0x27, 0x5f, 0x20, 0x48, 0x28, 0x4d, + 0x16, 0xa1, 0x66, 0xc6, 0xd8, 0xa9, 0x0a, 0x91, + 0xa4, 0x91, 0xc1, 0x6f, 0x49, 0x64 + }, + { + 0xb8, 0xc4, 0x4b, 0xdf, 0x0b, 0x85, 0xa6, 0x4b, + 0x6a, 0x51, 0xc1, 0x2a, 0x06, 0x71, 0x0e, 0x37, + 0x3d, 0x82, 0x9b, 0xb1, 0xfd, 0xa5, 0xb4, 0xe1, + 0xa2, 0x07, 0x95, 0xc6, 0x19, 0x95, 0x94, 0xf6, + 0xfa, 0x65, 0x19, 0x8a, 0x72, 0x12, 0x57, 0xf7, + 0xd5, 0x8c, 0xb2, 0xf6, 0xf6, 0xdb, 0x9b, 0xb5, + 0x69, 0x9f, 0x73, 0x86, 0x30, 0x45, 0x90, 0x90, + 0x54, 0xb2, 0x38, 0x9e, 0x06, 0xec, 0x00, 0xfe, + 0x31, 0x8c, 0xab, 0xd9 + }, + }; + int i; + int hashalgo[2] = { GCRY_MD_SHA256, GCRY_MD_SHA512 }; + int count = 0; + + again: + + if (verbose) + fprintf (stderr, "checking OneStepKDF test vector %d\n", count); + + err = my_kdf_derive (0, + GCRY_KDF_ONESTEP_KDF, hashalgo[count], ¶m[count], 1, + input[count], 16, NULL, 0, NULL, 0, + other[count], 12, + param[count], out); + if (err) + 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); + fputs ("got:", stderr); + for (i=0; i < param[count]; i++) + fprintf (stderr, " %02x", out[i]); + putc ('\n', stderr); + } + + /* Next test vector */ + count++; + if (count < 2) + goto again; +} + + int main (int argc, char **argv) { @@ -1681,6 +1759,7 @@ main (int argc, char **argv) check_scrypt (); check_argon2 (); check_balloon (); + check_onestep_kdf (); } return error_count ? 1 : 0; -- cgit v1.2.1