crypto: kdf - add SP800-108 counter key derivation function

SP800-108 defines three KDFs - this patch provides the counter KDF
implementation.

The KDF is implemented as a service function where the caller has to
maintain the hash / HMAC state. Apart from this hash/HMAC state, no
additional state is required to be maintained by either the caller or
the KDF implementation.

The key for the KDF is set with the crypto_kdf108_setkey function which
is intended to be invoked before the caller requests a key derivation
operation via crypto_kdf108_ctr_generate.

SP800-108 allows the use of either a HMAC or a hash as crypto primitive
for the KDF. When a HMAC primtive is intended to be used,
crypto_kdf108_setkey must be used to set the HMAC key. Otherwise, for a
hash crypto primitve crypto_kdf108_ctr_generate can be used immediately
after allocating the hash handle.

Signed-off-by: Stephan Mueller <smueller@chronox.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Stephan Müller 2021-11-19 07:55:58 +01:00 committed by Herbert Xu
parent b808f32023
commit 026a733e66
4 changed files with 223 additions and 0 deletions

View File

@ -1845,6 +1845,10 @@ config CRYPTO_JITTERENTROPY
random numbers. This Jitterentropy RNG registers with random numbers. This Jitterentropy RNG registers with
the kernel crypto API and can be used by any caller. the kernel crypto API and can be used by any caller.
config CRYPTO_KDF800108_CTR
tristate
select CRYPTO_HASH
config CRYPTO_USER_API config CRYPTO_USER_API
tristate tristate

View File

@ -200,3 +200,8 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/
obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o
crypto_simd-y := simd.o crypto_simd-y := simd.o
obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
#
# Key derivation function
#
obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o

153
crypto/kdf_sp800108.c Normal file
View File

@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0
/*
* SP800-108 Key-derivation function
*
* Copyright (C) 2021, Stephan Mueller <smueller@chronox.de>
*/
#include <linux/fips.h>
#include <linux/module.h>
#include <crypto/kdf_sp800108.h>
#include <crypto/internal/kdf_selftest.h>
/*
* SP800-108 CTR KDF implementation
*/
int crypto_kdf108_ctr_generate(struct crypto_shash *kmd,
const struct kvec *info, unsigned int info_nvec,
u8 *dst, unsigned int dlen)
{
SHASH_DESC_ON_STACK(desc, kmd);
__be32 counter = cpu_to_be32(1);
const unsigned int h = crypto_shash_digestsize(kmd), dlen_orig = dlen;
unsigned int i;
int err = 0;
u8 *dst_orig = dst;
desc->tfm = kmd;
while (dlen) {
err = crypto_shash_init(desc);
if (err)
goto out;
err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
if (err)
goto out;
for (i = 0; i < info_nvec; i++) {
err = crypto_shash_update(desc, info[i].iov_base,
info[i].iov_len);
if (err)
goto out;
}
if (dlen < h) {
u8 tmpbuffer[HASH_MAX_DIGESTSIZE];
err = crypto_shash_final(desc, tmpbuffer);
if (err)
goto out;
memcpy(dst, tmpbuffer, dlen);
memzero_explicit(tmpbuffer, h);
goto out;
}
err = crypto_shash_final(desc, dst);
if (err)
goto out;
dlen -= h;
dst += h;
counter = cpu_to_be32(be32_to_cpu(counter) + 1);
}
out:
if (err)
memzero_explicit(dst_orig, dlen_orig);
shash_desc_zero(desc);
return err;
}
EXPORT_SYMBOL(crypto_kdf108_ctr_generate);
/*
* The seeding of the KDF
*/
int crypto_kdf108_setkey(struct crypto_shash *kmd,
const u8 *key, size_t keylen,
const u8 *ikm, size_t ikmlen)
{
unsigned int ds = crypto_shash_digestsize(kmd);
/* SP800-108 does not support IKM */
if (ikm || ikmlen)
return -EINVAL;
/* Check according to SP800-108 section 7.2 */
if (ds > keylen)
return -EINVAL;
/* Set the key for the MAC used for the KDF. */
return crypto_shash_setkey(kmd, key, keylen);
}
EXPORT_SYMBOL(crypto_kdf108_setkey);
/*
* Test vector obtained from
* http://csrc.nist.gov/groups/STM/cavp/documents/KBKDF800-108/CounterMode.zip
*/
static const struct kdf_testvec kdf_ctr_hmac_sha256_tv_template[] = {
{
.key = "\xdd\x1d\x91\xb7\xd9\x0b\x2b\xd3"
"\x13\x85\x33\xce\x92\xb2\x72\xfb"
"\xf8\xa3\x69\x31\x6a\xef\xe2\x42"
"\xe6\x59\xcc\x0a\xe2\x38\xaf\xe0",
.keylen = 32,
.ikm = NULL,
.ikmlen = 0,
.info = {
.iov_base = "\x01\x32\x2b\x96\xb3\x0a\xcd\x19"
"\x79\x79\x44\x4e\x46\x8e\x1c\x5c"
"\x68\x59\xbf\x1b\x1c\xf9\x51\xb7"
"\xe7\x25\x30\x3e\x23\x7e\x46\xb8"
"\x64\xa1\x45\xfa\xb2\x5e\x51\x7b"
"\x08\xf8\x68\x3d\x03\x15\xbb\x29"
"\x11\xd8\x0a\x0e\x8a\xba\x17\xf3"
"\xb4\x13\xfa\xac",
.iov_len = 60
},
.expected = "\x10\x62\x13\x42\xbf\xb0\xfd\x40"
"\x04\x6c\x0e\x29\xf2\xcf\xdb\xf0",
.expectedlen = 16
}
};
static int __init crypto_kdf108_init(void)
{
int ret = kdf_test(&kdf_ctr_hmac_sha256_tv_template[0], "hmac(sha256)",
crypto_kdf108_setkey, crypto_kdf108_ctr_generate);
if (ret) {
if (fips_enabled)
panic("alg: self-tests for CTR-KDF (hmac(sha256)) failed (rc=%d)\n",
ret);
WARN(1,
"alg: self-tests for CTR-KDF (hmac(sha256)) failed (rc=%d)\n",
ret);
} else {
pr_info("alg: self-tests for CTR-KDF (hmac(sha256)) passed\n");
}
return ret;
}
static void __exit crypto_kdf108_exit(void) { }
module_init(crypto_kdf108_init);
module_exit(crypto_kdf108_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
MODULE_DESCRIPTION("Key Derivation Function conformant to SP800-108");

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021, Stephan Mueller <smueller@chronox.de>
*/
#ifndef _CRYPTO_KDF108_H
#define _CRYPTO_KDF108_H
#include <crypto/hash.h>
#include <linux/uio.h>
/**
* Counter KDF generate operation according to SP800-108 section 5.1
* as well as SP800-56A section 5.8.1 (Single-step KDF).
*
* @kmd Keyed message digest whose key was set with crypto_kdf108_setkey or
* unkeyed message digest
* @info optional context and application specific information - this may be
* NULL
* @info_vec number of optional context/application specific information entries
* @dst destination buffer that the caller already allocated
* @dlen length of the destination buffer - the KDF derives that amount of
* bytes.
*
* To comply with SP800-108, the caller must provide Label || 0x00 || Context
* in the info parameter.
*
* @return 0 on success, < 0 on error
*/
int crypto_kdf108_ctr_generate(struct crypto_shash *kmd,
const struct kvec *info, unsigned int info_nvec,
u8 *dst, unsigned int dlen);
/**
* Counter KDF setkey operation
*
* @kmd Keyed message digest allocated by the caller. The key should not have
* been set.
* @key Seed key to be used to initialize the keyed message digest context.
* @keylen This length of the key buffer.
* @ikm The SP800-108 KDF does not support IKM - this parameter must be NULL
* @ikmlen This parameter must be 0.
*
* According to SP800-108 section 7.2, the seed key must be at least as large as
* the message digest size of the used keyed message digest. This limitation
* is enforced by the implementation.
*
* SP800-108 allows the use of either a HMAC or a hash primitive. When
* the caller intends to use a hash primitive, the call to
* crypto_kdf108_setkey is not required and the key derivation operation can
* immediately performed using crypto_kdf108_ctr_generate after allocating
* a handle.
*
* @return 0 on success, < 0 on error
*/
int crypto_kdf108_setkey(struct crypto_shash *kmd,
const u8 *key, size_t keylen,
const u8 *ikm, size_t ikmlen);
#endif /* _CRYPTO_KDF108_H */