From c1e1caa766fc3d9aa5d19db616fe5dde3fcd0a90 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Wed, 29 Nov 2023 19:43:03 -0600 Subject: [PATCH] Implement HMAC-MD5, HMAC-SHA1, and HMAC-SHA256. --- LICENSE | 2 +- Makefile | 13 +++++--- hmacimpl.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hmactest.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ md5.cc | 5 +++- md5.h | 28 +++++++++++++++++- sha1.cc | 5 +++- sha1.h | 28 +++++++++++++++++- sha256.cc | 5 +++- sha256.h | 28 +++++++++++++++++- 10 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 hmacimpl.h create mode 100644 hmactest.c diff --git a/LICENSE b/LICENSE index 279be95..7a6695c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017 Stephen Heumann +Copyright (c) 2017,2023 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 diff --git a/Makefile b/Makefile index ba53ab6..d293d18 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CFLAGS = -O255 -w255 LIBRARIES = lib65816crypto lib65816hash PROGRAMS = aescbctest aesctrtest aestest aescrypt sha1sum sha1test \ - sha256sum sha256test md5sum md5test + sha256sum sha256test md5sum md5test hmactest .PHONY: default default: $(LIBRARIES) $(PROGRAMS) @@ -21,15 +21,15 @@ aes.a: aes.asm aes.macros mv aes.A aes.a # Hash algorithms -sha1.a: sha1.cc sha1.h sha1.asm sha1.macros +sha1.a: sha1.cc sha1.h sha1.asm sha1.macros hmacimpl.h $(CC) $(CFLAGS) -c $< sha1.B: sha1.a -sha256.a: sha256.cc sha1.h sha256.asm sha1.macros +sha256.a: sha256.cc sha256.h sha256.asm sha256.macros hmacimpl.h $(CC) $(CFLAGS) -c $< sha256.B: sha256.a -md5.a: md5.cc md5.h md5.asm md5.macros +md5.a: md5.cc md5.h md5.asm md5.macros hmacimpl.h $(CC) $(CFLAGS) -c $< md5.B: md5.a @@ -66,6 +66,9 @@ md5sum.a: md5sum.c md5.h cksumcommon.h md5test.a: md5test.c md5.h $(CC) $(CFLAGS) -c $< +hmactest.a: hmactest.c sha256.h sha1.h md5.h + $(CC) $(CFLAGS) -c $< + aescbctest: aescbctest.a pagealign.root lib65816crypto $(CC) $(CFLAGS) pagealign.root $< -L. -llib65816crypto -o $@ aesctrtest: aesctrtest.a pagealign.root lib65816crypto @@ -90,6 +93,8 @@ md5sum: md5sum.a pagealign.root lib65816hash md5test: md5test.a pagealign.root lib65816hash $(CC) $(CFLAGS) pagealign.root $< -L. -llib65816hash -o $@ +hmactest: hmactest.a lib65816hash + $(CC) $(CFLAGS) pagealign.root $< -L. -llib65816hash -o $@ .PHONY: clean clean: diff --git a/hmacimpl.h b/hmacimpl.h new file mode 100644 index 0000000..3d9fdee --- /dev/null +++ b/hmacimpl.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 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 HMACs using the hash functions + * from lib65816hash. It is #included from the files implementing the + * hash functions, with HASH_ALG defined to the name of the hash function. + */ + +#define join(a,b) a##b +#define concat(a,b) join(a,b) + +#define join3(a,b,c) a##b##c +#define concat3(a,b,c) join3(a,b,c) + +#define hash_init concat(HASH_ALG,_init) +#define hash_update concat(HASH_ALG,_update) +#define hash_finalize concat(HASH_ALG,_finalize) + +#define hmac_init concat3(hmac_,HASH_ALG,_init) +#define hmac_compute concat3(hmac_,HASH_ALG,_compute) +#define hmac_context concat3(hmac_,HASH_ALG,_context) + +void hmac_init(struct hmac_context *context, + const unsigned char *key, + unsigned long key_length) +{ +#define BLOCK_SIZE sizeof(context->u[0].ctx.block) +#define HASH_SIZE sizeof(context->u[0].ctx.hash) + + unsigned i; + + // Compute adjusted key (hashed from original key if necessary) + memset(context->u[0].k, 0, BLOCK_SIZE); + if (key_length <= BLOCK_SIZE) { + memcpy(context->u[0].k, key, key_length); + } else { + hash_init(&context->u[1].ctx); + hash_update(&context->u[1].ctx, key, key_length); + hash_finalize(&context->u[1].ctx); + memcpy(context->u[0].k, context->u[1].ctx.hash, HASH_SIZE); + } + + // Set context->u[0].k to K XOR opad, context->u[1].k to K XOR ipad + for (i = 0; i < BLOCK_SIZE; i++) { + context->u[1].k[i] = context->u[0].k[i] ^ 0x36; + context->u[0].k[i] ^= 0x5C; + } + + // Save inner hash context following initial block as context->u[2].ctx + hash_init(&context->u[2].ctx); + hash_update(&context->u[2].ctx, context->u[1].k, BLOCK_SIZE); + + // Save outer hash context following initial block as context->u[1].ctx + hash_init(&context->u[1].ctx); + hash_update(&context->u[1].ctx, context->u[0].k, BLOCK_SIZE); +} + + +void hmac_compute(struct hmac_context *context, + const unsigned char *message, + unsigned long message_length) +{ + // Compute inner hash + context->u[0].ctx = context->u[2].ctx; + hash_update(&context->u[0].ctx, message, message_length); + hash_finalize(&context->u[0].ctx); + memcpy(context->inner_hash, context->u[0].ctx.hash, HASH_SIZE); + + // Compute outer hash + context->u[0].ctx = context->u[1].ctx; + hash_update(&context->u[0].ctx, context->inner_hash, HASH_SIZE); + hash_finalize(&context->u[0].ctx); +} diff --git a/hmactest.c b/hmactest.c new file mode 100644 index 0000000..ea8ec9d --- /dev/null +++ b/hmactest.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 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. + */ + +#include +#include +#include + +#include "sha256.h" +#include "sha1.h" +#include "md5.h" + +int main(void) { + Handle context_hndl; + struct hmac_sha256_context *hmac_sha256_context; + struct hmac_sha1_context *hmac_sha1_context; + struct hmac_md5_context *hmac_md5_context; + + char key[] = "key"; + char msg[] = "The quick brown fox jumps over the lazy dog"; + + context_hndl = NewHandle(sizeof(struct hmac_sha256_context), + userid(), attrFixed|attrPage|attrBank|attrNoCross, 0x000000); + if (toolerror()) + return 0; + + + hmac_sha256_context = (struct hmac_sha256_context *)*context_hndl; + hmac_sha256_init(hmac_sha256_context, key, sizeof(key)-1); + hmac_sha256_compute(hmac_sha256_context, msg, sizeof(msg)-1); + + printf("HMAC-SHA256: "); + for (int i = 0; i < sizeof(hmac_sha256_context->u[0].ctx.hash); i++) { + printf("%02x", hmac_sha256_context->u[0].ctx.hash[i]); + } + printf("\n"); + + + hmac_sha1_context = (struct hmac_sha1_context *)*context_hndl; + hmac_sha1_init(hmac_sha1_context, key, sizeof(key)-1); + hmac_sha1_compute(hmac_sha1_context, msg, sizeof(msg)-1); + + printf("HMAC-SHA1: "); + for (int i = 0; i < sizeof(hmac_sha1_context->u[0].ctx.hash); i++) { + printf("%02x", hmac_sha1_context->u[0].ctx.hash[i]); + } + printf("\n"); + + + hmac_md5_context = (struct hmac_md5_context *)*context_hndl; + hmac_md5_init(hmac_md5_context, key, sizeof(key)-1); + hmac_md5_compute(hmac_md5_context, msg, sizeof(msg)-1); + + printf("HMAC-MD5: "); + for (int i = 0; i < sizeof(hmac_md5_context->u[0].ctx.hash); i++) { + printf("%02x", hmac_md5_context->u[0].ctx.hash[i]); + } + printf("\n"); +} diff --git a/md5.cc b/md5.cc index d46648c..383a725 100644 --- a/md5.cc +++ b/md5.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Stephen Heumann + * Copyright (c) 2017,2023 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 @@ -135,4 +135,7 @@ void md5_finalize(struct md5_context *context) } } +#define HASH_ALG md5 +#include "hmacimpl.h" + #append "md5.asm" diff --git a/md5.h b/md5.h index b3a4555..3d40b16 100644 --- a/md5.h +++ b/md5.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Stephen Heumann + * Copyright (c) 2017,2023 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 @@ -24,6 +24,14 @@ struct md5_context { unsigned char block[64]; }; +struct hmac_md5_context { + union { + struct md5_context ctx; + unsigned char k[64]; + } u[3]; + unsigned char inner_hash[16]; +}; + /* * The context structure must be in bank 0, preferably page-aligned. */ @@ -49,3 +57,21 @@ void md5_finalize(struct md5_context *context); * This is a low-level function; users should normally not call this directly. */ void md5_processblock(struct md5_context *context); + +/* + * Initialize a context for HMAC-MD5 computation with a specified key. + * This must be called before any calls to hmac_md5_compute. After + * initialization, the context can be used to compute the HMAC for any + * number of messages. + */ +void hmac_md5_init(struct hmac_md5_context *context, + const unsigned char *key, + unsigned long key_length); + +/* + * Compute the HMAC-MD5 of a message, using an already-initialized context. + * The result will be in context->u[0].ctx.hash. + */ +void hmac_md5_compute(struct hmac_md5_context *context, + const unsigned char *message, + unsigned long message_length); diff --git a/sha1.cc b/sha1.cc index fa3cc50..44663f2 100644 --- a/sha1.cc +++ b/sha1.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Stephen Heumann + * Copyright (c) 2017,2023 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 @@ -180,4 +180,7 @@ void sha1_finalize(struct sha1_context *context) } } +#define HASH_ALG sha1 +#include "hmacimpl.h" + #append "sha1.asm" diff --git a/sha1.h b/sha1.h index 83152f1..042ada4 100644 --- a/sha1.h +++ b/sha1.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Stephen Heumann + * Copyright (c) 2017,2023 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 @@ -24,6 +24,14 @@ struct sha1_context { unsigned char reserved2[16]; }; +struct hmac_sha1_context { + union { + struct sha1_context ctx; + unsigned char k[64]; + } u[3]; + unsigned char inner_hash[20]; +}; + /* * The context structure must be in bank 0, preferably page-aligned. */ @@ -49,3 +57,21 @@ void sha1_finalize(struct sha1_context *context); * This is a low-level function; users should normally not call this directly. */ void sha1_processblock(struct sha1_context *context); + +/* + * Initialize a context for HMAC-SHA1 computation with a specified key. + * This must be called before any calls to hmac_sha1_compute. After + * initialization, the context can be used to compute the HMAC for any + * number of messages. + */ +void hmac_sha1_init(struct hmac_sha1_context *context, + const unsigned char *key, + unsigned long key_length); + +/* + * Compute the HMAC-SHA1 of a message, using an already-initialized context. + * The result will be in context->u[0].ctx.hash. + */ +void hmac_sha1_compute(struct hmac_sha1_context *context, + const unsigned char *message, + unsigned long message_length); diff --git a/sha256.cc b/sha256.cc index eefac18..f662d0e 100644 --- a/sha256.cc +++ b/sha256.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Stephen Heumann + * Copyright (c) 2017,2023 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 @@ -204,4 +204,7 @@ void sha256_finalize(struct sha256_context *context) } } +#define HASH_ALG sha256 +#include "hmacimpl.h" + #append "sha256.asm" diff --git a/sha256.h b/sha256.h index da56560..63319ca 100644 --- a/sha256.h +++ b/sha256.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Stephen Heumann + * Copyright (c) 2017,2023 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 @@ -24,6 +24,14 @@ struct sha256_context { unsigned char reserved2[60]; }; +struct hmac_sha256_context { + union { + struct sha256_context ctx; + unsigned char k[64]; + } u[3]; + unsigned char inner_hash[32]; +}; + /* * The context structure must be in bank 0, preferably page-aligned. */ @@ -57,3 +65,21 @@ void sha256_finalize(struct sha256_context *context); * This is a low-level function; users should normally not call this directly. */ void sha256_processblock(struct sha256_context *context); + +/* + * Initialize a context for HMAC-SHA256 computation with a specified key. + * This must be called before any calls to hmac_sha256_compute. After + * initialization, the context can be used to compute the HMAC for any + * number of messages. + */ +void hmac_sha256_init(struct hmac_sha256_context *context, + const unsigned char *key, + unsigned long key_length); + +/* + * Compute the HMAC-SHA256 of a message, using an already-initialized context. + * The result will be in context->u[0].ctx.hash. + */ +void hmac_sha256_compute(struct hmac_sha256_context *context, + const unsigned char *message, + unsigned long message_length);