tls

tls.git
git clone git://git.lenczewski.org/tls.git
Log | Files | Refs

commit 913cef345e46ea44aee5e08bb35d9d242ba98356
parent 12a3b1a4b12f4149dffb2660fa01515135981c03
Author: MikoĊ‚aj Lenczewski <mikolaj@lenczewski.org>
Date:   Sun, 20 Sep 2026 23:19:55 +0100

Add hkdf implementation

Diffstat:
Ahkdf.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahkdf.h | 23+++++++++++++++++++++++
Mhmac.c | 32++++++++++++++++++++++++--------
Mhmac.h | 17+++++++++++------
Atest/test-hkdf.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 192 insertions(+), 14 deletions(-)

diff --git a/hkdf.c b/hkdf.c @@ -0,0 +1,62 @@ +#include "hkdf.h" + +#include <stdio.h> + +size_t +hkdf_digest_size(struct hmac_hash const *hash, size_t rounds) +{ + return hash->digest_size * rounds; +} + +size_t +hkdf_max_digest_size(struct hmac_hash const *hash) +{ + return hkdf_digest_size(hash, HKDF_MAX_ROUNDS); +} + +void +hkdf(struct hmac_hash const *hash, void *restrict hash_ctx, + void const *restrict key, size_t key_len, + void const *salt, size_t salt_len, + void const *info, size_t info_len, + void *restrict digest, size_t rounds) +{ + // we must allow at least 1 round of hmac + assert(rounds); + + // bound output key material length + if (HKDF_MAX_ROUNDS < rounds) + rounds = HKDF_MAX_ROUNDS; + + struct hmac_ctx hmac; + + // generate pseudorandom key + hmac_gen_iv_block(&hmac, hash, hash_ctx, salt, salt_len); + hmac_init(&hmac); + hmac_push(&hmac, key, key_len); + hmac_dgst(&hmac, digest); + + // expand prk into outpt key material + hmac_gen_iv_block(&hmac, hash, hash_ctx, digest, hash->digest_size); + + unsigned char *base = digest; + size_t block_len = hash->digest_size; + + for (size_t i = 0; i < rounds; i++) { + unsigned char *dst = base + (i * block_len); + unsigned char *src = dst - block_len; + + // hkdf expects ctr to start from 01 + unsigned char ctr = (i + 1) & 0xff; + + // merge zero previous material on round 1 + size_t merged_len = i ? block_len : 0; + + hmac_init(&hmac); + hmac_push(&hmac, src, merged_len); + hmac_push(&hmac, info, info_len); + hmac_push(&hmac, &ctr, 1); + + hmac_dgst(&hmac, dst); + } +} diff --git a/hkdf.h b/hkdf.h @@ -0,0 +1,23 @@ +// hkdf: https://www.rfc-editor.org/info/rfc5869/ + +#ifndef HKDF_H +#define HKDF_H + +#include "hmac.h" + +#define HKDF_MAX_ROUNDS 255 + +size_t +hkdf_digest_size(struct hmac_hash const *hash, size_t rounds); + +size_t +hkdf_max_digest_size(struct hmac_hash const *hash); + +void +hkdf(struct hmac_hash const *hash, void *restrict hash_ctx, + void const *restrict key, size_t key_len, + void const *salt, size_t salt_len, + void const *info, size_t info_len, + void *restrict digest, size_t rounds); + +#endif /* HKDF_H */ diff --git a/hmac.c b/hmac.c @@ -1,9 +1,9 @@ #include "hmac.h" void -hmac_init(struct hmac_ctx *ctx, - struct hmac_hash const *hash, void *restrict hash_ctx, - void const *restrict key, size_t key_len) +hmac_gen_iv_block(struct hmac_ctx *ctx, + struct hmac_hash const *hash, void *restrict hash_ctx, + void const *restrict key, size_t key_len) { memset(ctx, 0, sizeof *ctx); @@ -27,15 +27,27 @@ hmac_init(struct hmac_ctx *ctx, } void -hmac_hash(struct hmac_ctx *ctx, - void const *restrict buf, size_t buf_len, - void *restrict digest) +hmac_init(struct hmac_ctx *ctx) { + // prepare ipad material ctx->hash->init(ctx->hash_ctx); ctx->hash->push(ctx->hash_ctx, ctx->ipad, sizeof ctx->ipad); +} + +void +hmac_push(struct hmac_ctx *ctx, void const *buf, size_t buf_len) +{ + // accumulate ipad material ctx->hash->push(ctx->hash_ctx, buf, buf_len); +} + +void +hmac_dgst(struct hmac_ctx *ctx, void *digest) +{ + // close ipad material ctx->hash->dgst(ctx->hash_ctx, digest); + // final digest against opad ctx->hash->init(ctx->hash_ctx); ctx->hash->push(ctx->hash_ctx, ctx->opad, sizeof ctx->opad); ctx->hash->push(ctx->hash_ctx, digest, ctx->hash->digest_size); @@ -49,6 +61,10 @@ hmac(struct hmac_hash const *hash, void *restrict hash_ctx, void *restrict digest) { struct hmac_ctx ctx; - hmac_init(&ctx, hash, hash_ctx, key, key_len); - hmac_hash(&ctx, buf, buf_len, digest); + + hmac_gen_iv_block(&ctx, hash, hash_ctx, key, key_len); + + hmac_init(&ctx); + hmac_push(&ctx, buf, buf_len); + hmac_dgst(&ctx, digest); } diff --git a/hmac.h b/hmac.h @@ -28,14 +28,19 @@ struct hmac_ctx { }; void -hmac_init(struct hmac_ctx *ctx, - struct hmac_hash const *hash, void *restrict hash_ctx, - void const *restrict key, size_t key_len); +hmac_gen_iv_block(struct hmac_ctx *ctx, + struct hmac_hash const *hash, void *restrict hash_ctx, + void const *restrict key, size_t key_len); void -hmac_hash(struct hmac_ctx *ctx, - void const *restrict buf, size_t buf_len, - void *restrict digest); +hmac_init(struct hmac_ctx *ctx); + +void +hmac_push(struct hmac_ctx *ctx, void const *buf, size_t buf_len); + +void +hmac_dgst(struct hmac_ctx *ctx, void *digest); + void hmac(struct hmac_hash const *hash, void *restrict hash_ctx, void const *restrict key, size_t key_len, diff --git a/test/test-hkdf.c b/test/test-hkdf.c @@ -0,0 +1,72 @@ +#include "hkdf.h" +#include "hmac-sha2.h" + +#include <stdio.h> + +#define HKDF_ROUNDS 2 +#define HKDF_DIGEST_SIZE (SHA256_DIGEST_SIZE * HKDF_ROUNDS) + +int +main(void) +{ + char key[] = "test"; + char salt[] = "salt"; + char info[] = "info"; + + struct sha256_ctx sha256_ctx; + uint8_t dgst[HKDF_DIGEST_SIZE]; + hkdf(&hmac_sha256, &sha256_ctx, + key, strlen(key), + salt, strlen(salt), + info, strlen(info), + dgst, HKDF_ROUNDS); + + + // generated with: + // -- + // openssl kdf + // -keylen 64 + // -kdfopt digest:sha2-256 + // -kdfopt key:test + // -kdfopt salt:salt + // -kdfopt info:info + // hkdf + // + uint8_t expected[HKDF_DIGEST_SIZE] = { + 0x8a, 0x43, 0x59, 0x8a, 0xc9, 0x49, 0x78, 0x8d, + 0xab, 0x1a, 0xc0, 0xae, 0x0e, 0xd4, 0x2f, 0x10, + 0x0e, 0xcf, 0x6a, 0x98, 0x29, 0x2d, 0xd8, 0x89, + 0x51, 0xf8, 0x32, 0xd5, 0xdd, 0xf7, 0x63, 0x49, + + 0xb0, 0x27, 0x5a, 0xfc, 0x2d, 0x0d, 0x8f, 0xe7, + 0x0c, 0x34, 0x46, 0x66, 0xcb, 0x97, 0xb1, 0x3e, + 0x85, 0x76, 0xa1, 0xc0, 0x13, 0x6c, 0x49, 0x84, + 0xe6, 0xd3, 0xf6, 0xee, 0x62, 0x55, 0x4d, 0x37, + }; + + printf("key: \"%.*s\" (%lu bytes)\n", + sizeof key, key, strlen(key)); + printf("salt: \"%.*s\" (%lu bytes)\n", + sizeof salt, salt, strlen(salt)); + printf("info: \"%.*s\" (%lu bytes)\n", + sizeof info, info, strlen(info)); + + printf("dgst: "); + for (size_t i = 0; i < sizeof dgst; i++) { + printf("%.2x", dgst[i]); + } + printf("\n"); + + printf("exp.: "); + for (size_t i = 0; i < sizeof expected; i++) { + printf("%.2x", expected[i]); + } + printf("\n"); + + return memcmp(expected, dgst, HKDF_DIGEST_SIZE); +} + +#include "hkdf.c" +#include "hmac.c" +#include "hmac-sha2.c" +#include "sha.c"