commit b48f82e2e64faa4e74d62728984572b3af076030
parent 9b592418ccb31c6299973689031c5f40e5cc1a34
Author: MikoĊaj Lenczewski <mikolaj@lenczewski.org>
Date: Wed, 16 Sep 2026 19:57:56 +0100
Add hmac implementation
Diffstat:
| A | hmac-sha2.c | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
| A | hmac-sha2.h | | | 14 | ++++++++++++++ |
| A | hmac.c | | | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | hmac.h | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
| A | test/test-hmac.c | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 201 insertions(+), 0 deletions(-)
diff --git a/hmac-sha2.c b/hmac-sha2.c
@@ -0,0 +1,43 @@
+#include "hmac-sha2.h"
+
+struct hmac_hash const hmac_sha256 = {
+ .init = (void *) sha256_init,
+ .push = (void *) sha256_push,
+ .dgst = (void *) sha256_dgst,
+ .digest_size = SHA256_DIGEST_SIZE,
+};
+
+struct hmac_hash const hmac_sha224 = {
+ .init = (void *) sha224_init,
+ .push = (void *) sha224_push,
+ .dgst = (void *) sha224_dgst,
+ .digest_size = SHA224_DIGEST_SIZE,
+};
+
+struct hmac_hash const hmac_sha512 = {
+ .init = (void *) sha512_init,
+ .push = (void *) sha512_push,
+ .dgst = (void *) sha512_dgst,
+ .digest_size = SHA512_DIGEST_SIZE,
+};
+
+struct hmac_hash const hmac_sha384 = {
+ .init = (void *) sha384_init,
+ .push = (void *) sha384_push,
+ .dgst = (void *) sha384_dgst,
+ .digest_size = SHA384_DIGEST_SIZE,
+};
+
+struct hmac_hash const hmac_sha512_224 = {
+ .init = (void *) sha512_224_init,
+ .push = (void *) sha512_224_push,
+ .dgst = (void *) sha512_224_dgst,
+ .digest_size = SHA512_224_DIGEST_SIZE,
+};
+
+struct hmac_hash const hmac_sha512_256 = {
+ .init = (void *) sha512_256_init,
+ .push = (void *) sha512_256_push,
+ .dgst = (void *) sha512_256_dgst,
+ .digest_size = SHA512_256_DIGEST_SIZE,
+};
diff --git a/hmac-sha2.h b/hmac-sha2.h
@@ -0,0 +1,14 @@
+#ifndef HMAC_SHA2_H
+#define HMAC_SHA2_H
+
+#include "hmac.h"
+#include "sha.h"
+
+extern struct hmac_hash const hmac_sha224;
+extern struct hmac_hash const hmac_sha256;
+extern struct hmac_hash const hmac_sha512;
+extern struct hmac_hash const hmac_sha384;
+extern struct hmac_hash const hmac_sha512_224;
+extern struct hmac_hash const hmac_sha512_256;
+
+#endif /* HMAC_SHA2_H */
diff --git a/hmac.c b/hmac.c
@@ -0,0 +1,54 @@
+#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)
+{
+ memset(ctx, 0, sizeof *ctx);
+
+ ctx->hash = hash;
+ ctx->hash_ctx = hash_ctx;
+
+ if (key_len <= HMAC_BLOCK_SIZE) { // can use the key directly
+ assert(key_len <= HMAC_BLOCK_SIZE);
+ memcpy(ctx->iv, key, key_len);
+ } else { // must hash the key first to generate the iv block
+ assert(hash->digest_size <= HMAC_BLOCK_SIZE);
+ hash->init(hash_ctx);
+ hash->push(hash_ctx, key, key_len);
+ hash->dgst(hash_ctx, ctx->iv);
+ }
+
+ for (size_t i = 0; i < HMAC_BLOCK_SIZE; i++) {
+ ctx->ipad[i] = ctx->iv[i] ^ HMAC_IPAD_BYTE;
+ ctx->opad[i] = ctx->iv[i] ^ HMAC_OPAD_BYTE;
+ }
+}
+
+void
+hmac_hash(struct hmac_ctx *ctx,
+ void const *restrict buf, size_t buf_len,
+ void *restrict digest)
+{
+ ctx->hash->init(ctx->hash_ctx);
+ ctx->hash->push(ctx->hash_ctx, ctx->ipad, sizeof ctx->ipad);
+ ctx->hash->push(ctx->hash_ctx, buf, buf_len);
+ ctx->hash->dgst(ctx->hash_ctx, digest);
+
+ 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);
+ ctx->hash->dgst(ctx->hash_ctx, digest);
+}
+
+void
+hmac(struct hmac_hash const *hash, void *restrict hash_ctx,
+ void const *restrict key, size_t key_len,
+ void const *restrict buf, size_t buf_len,
+ void *restrict digest)
+{
+ struct hmac_ctx ctx;
+ hmac_init(&ctx, hash, hash_ctx, key, key_len);
+ hmac_hash(&ctx, buf, buf_len, digest);
+}
diff --git a/hmac.h b/hmac.h
@@ -0,0 +1,45 @@
+// hmac: https://datatracker.ietf.org/doc/html/rfc2104
+
+#ifndef HMAC_H
+#define HMAC_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#define HMAC_BLOCK_SIZE 64
+#define HMAC_IPAD_BYTE 0x36
+#define HMAC_OPAD_BYTE 0x5c
+
+struct hmac_hash {
+ void (*init)(void *ctx);
+ void (*push)(void *ctx, void const *buf, size_t len);
+ void (*dgst)(void *ctx, void *digest);
+ size_t digest_size;
+};
+
+struct hmac_ctx {
+ unsigned char iv[HMAC_BLOCK_SIZE];
+ unsigned char opad[HMAC_BLOCK_SIZE];
+ unsigned char ipad[HMAC_BLOCK_SIZE];
+
+ struct hmac_hash const *hash;
+ void *hash_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);
+
+void
+hmac_hash(struct hmac_ctx *ctx,
+ void const *restrict buf, size_t buf_len,
+ void *restrict digest);
+void
+hmac(struct hmac_hash const *hash, void *restrict hash_ctx,
+ void const *restrict key, size_t key_len,
+ void const *restrict buf, size_t buf_len,
+ void *restrict digest);
+
+#endif /* HMAC_H */
diff --git a/test/test-hmac.c b/test/test-hmac.c
@@ -0,0 +1,45 @@
+#include "hmac-sha2.h"
+
+#include <stdio.h>
+
+int
+main(void)
+{
+ char msg[] = "Hi There";
+
+ unsigned char key[] = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ };
+
+ struct sha256_ctx sha256_ctx;
+ uint8_t dgst[SHA256_DIGEST_SIZE];
+ hmac(&hmac_sha256, &sha256_ctx, key, sizeof key, msg, strlen(msg), dgst);
+
+ uint8_t expected[SHA256_DIGEST_SIZE] = {
+ 0x49, 0x2c, 0xe0, 0x20, 0xfe, 0x25, 0x34, 0xa5,
+ 0x78, 0x9d, 0xc3, 0x84, 0x88, 0x06, 0xc7, 0x8f,
+ 0x4f, 0x67, 0x11, 0x39, 0x7f, 0x08, 0xe7, 0xe7,
+ 0xa1, 0x2c, 0xa5, 0xa4, 0x48, 0x3c, 0x8a, 0xa6,
+ };
+
+ printf("msg: \"%.*s\" (%lu bytes)\n", sizeof msg, msg, strlen(msg));
+
+ 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, SHA224_DIGEST_SIZE);
+}
+
+#include "hmac.c"
+#include "hmac-sha2.c"
+#include "sha.c"