From 58a9c564ae22b8986fdae22e609697286e45ff6b Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Mon, 15 Apr 2024 19:46:50 -0500 Subject: [PATCH] Implement "KDF in Counter Mode" from NIST SP 800-108. This is currently instantiated with HMAC-SHA256 as the pseudo-random function, but is implemented using a template so that other PRFs could also be used. --- Makefile | 2 +- kdfimpl.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sha256.cc | 4 +++ sha256.h | 16 ++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 kdfimpl.h diff --git a/Makefile b/Makefile index f23899b..b34cfff 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ sha1.a: sha1.cc sha1.h sha1.asm sha1.macros hmacimpl.h $(CC) $(CFLAGS) -c $< sha1.B: sha1.a -sha256.a: sha256.cc sha256.h sha256.asm sha256.macros hmacimpl.h +sha256.a: sha256.cc sha256.h sha256.asm sha256.macros hmacimpl.h kdfimpl.h $(CC) $(CFLAGS) -c $< sha256.B: sha256.a diff --git a/kdfimpl.h b/kdfimpl.h new file mode 100644 index 0000000..cf4749d --- /dev/null +++ b/kdfimpl.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 Stephen Heumann + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file is a template for computing a key-derivation function + * ("KDF in Counter Mode" as specified in NIST SP 800-108), which may + * use various HMAC functions (among others) as a "pseudo-random function." + * + * This file is #included from the files implementing the hash functions, + * with KDF_PRF defined to the name of the pseudo-random function being used, + * and KDF_PRF_h defined to the width of its output in bits. + */ + +#include + +#define join(a,b) a##b +#define concat(a,b) join(a,b) + +#define KDF_PRF_init concat(KDF_PRF,_init) +#define KDF_PRF_update concat(KDF_PRF,_update) +#define KDF_PRF_finalize concat(KDF_PRF,_finalize) +#define KDF_PRF_result concat(KDF_PRF,_result) +#define KDF_PRF_context concat(KDF_PRF,_context) + +#define KDF_PRF_kdf_ctr concat(KDF_PRF,_kdf_ctr) + +/* + * This implements "KDF in Counter Mode" as specified in NIST SP 800-108. + * See that specification for details of its parameters. + * + * This implementation can be instantiated for various pseudo-random functions + * (e.g. HMACs). It assumes r = 32, and assumes big-endian byte order is used + * for integers. L must be a multiple of 8. The result buffer must be L bits + * (i.e. L/8 bytes) long. The PRF context structure must be provided for use + * within this function; its state at the beginning and end is not meaningful. + */ +void KDF_PRF_kdf_ctr(struct KDF_PRF_context *ctx, + const unsigned char *k_in, unsigned key_len, + unsigned long L, unsigned char *result, + const char *label, unsigned long label_len, + const char *context, unsigned long context_len) +{ + unsigned long n, i; + static const char zero = 0; + unsigned long lastSize; + + n = L / KDF_PRF_h; + lastSize = L % KDF_PRF_h; + if (lastSize != 0) { + n++; + } else { + lastSize = KDF_PRF_h; + } + + for (i = 1; i && i <= n; i++) { + KDF_PRF_init(ctx, k_in, key_len); + KDF_PRF_update(ctx, (unsigned char *)&i + 3, 1); + KDF_PRF_update(ctx, (unsigned char *)&i + 2, 1); + KDF_PRF_update(ctx, (unsigned char *)&i + 1, 1); + KDF_PRF_update(ctx, (unsigned char *)&i + 0, 1); + KDF_PRF_update(ctx, label, label_len); + KDF_PRF_update(ctx, &zero, 1); + KDF_PRF_update(ctx, context, context_len); + KDF_PRF_update(ctx, (unsigned char *)&L + 3, 1); + KDF_PRF_update(ctx, (unsigned char *)&L + 2, 1); + KDF_PRF_update(ctx, (unsigned char *)&L + 1, 1); + KDF_PRF_update(ctx, (unsigned char *)&L + 0, 1); + KDF_PRF_finalize(ctx); + if (i != n) { + memcpy(result, KDF_PRF_result(ctx), KDF_PRF_h/8); + result += KDF_PRF_h/8; + } else { + memcpy(result, KDF_PRF_result(ctx), lastSize/8); + } + } +} diff --git a/sha256.cc b/sha256.cc index f662d0e..14a5a56 100644 --- a/sha256.cc +++ b/sha256.cc @@ -207,4 +207,8 @@ void sha256_finalize(struct sha256_context *context) #define HASH_ALG sha256 #include "hmacimpl.h" +#define KDF_PRF hmac_sha256 +#define KDF_PRF_h 256 +#include "kdfimpl.h" + #append "sha256.asm" diff --git a/sha256.h b/sha256.h index 226d852..82be52c 100644 --- a/sha256.h +++ b/sha256.h @@ -101,3 +101,19 @@ void hmac_sha256_compute(struct hmac_sha256_context *context, * or hmac_sha256_compute. */ #define hmac_sha256_result(context) ((context)->u[0].ctx.hash) + +/* + * This implements "KDF in Counter Mode" as specified in NIST SP 800-108, with + * HMAC-SHA256 as the pseudo-random function. See that specification for + * details of its parameters. + * + * This implementation assumes r = 32 and assumes big-endian byte order is used + * for integers. L must be a multiple of 8. The result buffer must be L bits + * (i.e. L/8 bytes) long. The HMAC-SHA256 context must be provided for use + * within this function; its state at the beginning and end is not meaningful. + */ +void hmac_sha256_kdf_ctr(struct hmac_sha256_context *ctx, + const unsigned char *k_in, unsigned key_len, + unsigned long L, unsigned char *result, + const char *label, unsigned long label_len, + const char *context, unsigned long context_len);