From 60e4998d163a35cf6c71bb65dc4d6e67e49ce1eb Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Sun, 14 Apr 2024 22:16:04 -0500 Subject: [PATCH] Implement AES-CMAC. --- LICENSE | 2 +- Makefile | 14 ++++- aes.h | 23 +++++++ aescmac.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++ cmactest.c | 92 ++++++++++++++++++++++++++++ 5 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 aescmac.c create mode 100644 cmactest.c diff --git a/LICENSE b/LICENSE index 7a6695c..60f3ee2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017,2023 Stephen Heumann +Copyright (c) 2017,2023,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 diff --git a/Makefile b/Makefile index 468670f..f23899b 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ CFLAGS = -O255 -w255 LIBRARIES = lib65816crypto lib65816hash -PROGRAMS = aescbctest aesctrtest aestest aescrypt rc4test sha1sum sha1test \ - sha256sum sha256test md5sum md5test md4sum md4test hmactest +PROGRAMS = aescbctest aesctrtest aestest aescrypt rc4test cmactest sha1sum \ + sha1test sha256sum sha256test md5sum md5test md4sum md4test hmactest .PHONY: default default: $(LIBRARIES) $(PROGRAMS) @@ -14,6 +14,8 @@ pagealign.root: pagealign.asm mv pagealign.ROOT pagealign.root # Encryption/decryption algorithms +aescmac.a: aescmac.c aes.h + $(CC) $(CFLAGS) -c $< aesmodes.a: aesmodes.c aes.h $(CC) $(CFLAGS) -c $< aes.a: aes.asm aes.macros @@ -42,7 +44,7 @@ md4.a: md4.cc md4.h md4.asm md4.macros hmacimpl.h md4.B: md4.a # Libraries -lib65816crypto: aesmodes.a aes.a rc4.a rc4.B +lib65816crypto: aescmac.a aesmodes.a aes.a rc4.a rc4.B rm -f $@ iix makelib -P $@ $(patsubst %,+%,$^) lib65816hash: sha1.a sha1.B sha256.a sha256.B md5.a md5.B md4.a md4.B @@ -62,6 +64,9 @@ aescrypt.a: aescrypt.c aes.h rc4test.a: rc4test.c rc4.h $(CC) $(CFLAGS) -c $< +cmactest.a: cmactest.c aes.h + $(CC) $(CFLAGS) -c $< + sha1sum.a: sha1sum.c sha1.h cksumcommon.h $(CC) $(CFLAGS) -c $< sha1test.a: sha1test.c sha1.h @@ -97,6 +102,9 @@ aescrypt: aescrypt.a pagealign.root lib65816crypto rc4test: rc4test.a lib65816crypto $(CC) $(CFLAGS) $< -L. -llib65816crypto -o $@ +cmactest: cmactest.a lib65816crypto + $(CC) $(CFLAGS) pagealign.root $< -L. -llib65816crypto -o $@ + sha1sum: sha1sum.a lib65816hash $(CC) $(CFLAGS) $< -L. -llib65816hash -o $@ sha1test: sha1test.a lib65816hash diff --git a/aes.h b/aes.h index 3843546..b51b2c6 100644 --- a/aes.h +++ b/aes.h @@ -21,6 +21,12 @@ struct aes_context { unsigned char reserved2[16*13]; }; +struct aes_cmac_context { + struct aes_context ctx; + unsigned char k1[16]; + unsigned char k2[16]; +}; + /* * The context structure must be in bank 0, preferably page-aligned. * Note that a 256-byte (one page) context structure is sufficient for @@ -90,3 +96,20 @@ void aes_ctr_process(struct aes_context *context, unsigned char *out, unsigned long nblocks, const unsigned char *counter); + +/* + * Initialize a context for AES-CMAC computation with a specified key. + * This must be called before any calling aes_cmac_compute. + */ +void aes_cmac_init(struct aes_cmac_context *context, + const unsigned char key[16]); + +/* + * Compute the AES-CMAC of a message as a single operation. + * The result will be in context->ctx.data. + * The context can be reused for multiple aes_cmac_compute operations. + */ +void aes_cmac_compute(struct aes_cmac_context *context, + const unsigned char *message, + unsigned long message_length); + diff --git a/aescmac.c b/aescmac.c new file mode 100644 index 0000000..1b8ebb1 --- /dev/null +++ b/aescmac.c @@ -0,0 +1,176 @@ +/* + * 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. + */ + +#pragma noroot +#pragma lint -1 +#pragma optimize -1 + +#include +#include +#include "aes.h" + +extern void AES_ENCRYPT(void); + +static asm void aes_cmac_subkey_compute_step(struct aes_cmac_context *ctx) { + tdc + tay + phb + plx + pla + pld + sta 1,s + phx + plb + + sep #0x20 + asl 15 + rol 14 + rol 13 + rol 12 + rol 11 + rol 10 + rol 9 + rol 8 + rol 7 + rol 6 + rol 5 + rol 4 + rol 3 + rol 2 + rol 1 + rol 0 + bcc done + lda 15 + eor #0x87 + sta 15 +done: rep #0x20 + + tya + tcd + rtl +} + + +/* + * Initialize a context for AES-CMAC computation with a specified key. + * This must be called before any calling aes_cmac_compute. + */ +void aes_cmac_init(struct aes_cmac_context *context, + const unsigned char key[16]) +{ + memcpy(context->ctx.key, key, 16); + aes128_expandkey(&context->ctx); + + memset(context->ctx.data, 0, 16); + aes_encrypt(&context->ctx); + + aes_cmac_subkey_compute_step(context); + memcpy(context->k1, context->ctx.data, 16); + + aes_cmac_subkey_compute_step(context); + memcpy(context->k2, context->ctx.data, 16); +} + + +/* + * Compute the AES-CMAC of a message as a single operation. + * The result will be in context->ctx.data. + * The context can be reused for multiple aes_cmac_compute operations. + */ +void aes_cmac_compute(struct aes_cmac_context *context, + const unsigned char *message, + unsigned long message_length) +{ + unsigned i; + + memset(&context->ctx.data, 0, 16); + + while (message_length > 16) { + asm { + lda [message] + eor [context] + sta [context] + ldy #2 + lda [message],y + eor [context],y + sta [context],y + iny + iny + lda [message],y + eor [context],y + sta [context],y + iny + iny + lda [message],y + eor [context],y + sta [context],y + iny + iny + lda [message],y + eor [context],y + sta [context],y + iny + iny + lda [message],y + eor [context],y + sta [context],y + iny + iny + lda [message],y + eor [context],y + sta [context],y + iny + iny + lda [message],y + eor [context],y + sta [context],y + + phd + lda context + tcd + jsl AES_ENCRYPT + pld + } + + message_length -= 16; + message += 16; + } + + if (message_length == 16) { + for (i = 0; i < 16; i++) { + context->ctx.data[i] ^= message[i] ^ context->k1[i]; + } + } else { + for (i = 0; i < 16; i++) { + if (i < message_length) { + context->ctx.data[i] ^= message[i] ^ context->k2[i]; + } else if (i == message_length) { + context->ctx.data[i] ^= 0x80 ^ context->k2[i]; + } else { + context->ctx.data[i] ^= context->k2[i]; + } + } + } + + asm { + phd + lda context + tcd + jsl AES_ENCRYPT + pld + } +} + diff --git a/cmactest.c b/cmactest.c new file mode 100644 index 0000000..d60c132 --- /dev/null +++ b/cmactest.c @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#include +#include + +#include "aes.h" + +/* AES-CMAC test vectors from RFC 4493 */ + +unsigned char K[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c +}; + +unsigned char K1[16] = { + 0xfb,0xee,0xd6,0x18,0x35,0x71,0x33,0x66, + 0x7c,0x85,0xe0,0x8f,0x72,0x36,0xa8,0xde +}; + +unsigned char K2[16] = { + 0xf7,0xdd,0xac,0x30,0x6a,0xe2,0x66,0xcc, + 0xf9,0x0b,0xc1,0x1e,0xe4,0x6d,0x51,0x3b +}; + +unsigned char ex2[16] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a +}; + +unsigned char ex3[40] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c, + 0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, + 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11 +}; + +unsigned char ex4[64] = { + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96, + 0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c, + 0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, + 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11, + 0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef, + 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17, + 0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10 +}; + + +void print_hexbytes(char *prefix, unsigned char *data, unsigned int n) { + int i; + + printf("%s", prefix); + for (i = 0; i < n; i++) { + printf("%02x ", data[i]); + } + printf("\n"); +} + +int main(void) { + struct aes_cmac_context *ctx; + + aes_cmac_init(ctx, K); + + if (memcmp(ctx->k1, K1, 16) != 0) + print_hexbytes("wrong K1: got ", ctx->k1, 16); + if (memcmp(ctx->k2, K2, 16) != 0) + print_hexbytes("wrong K2: got ", ctx->k2, 16); + + aes_cmac_compute(ctx, "", 0); + print_hexbytes("Example 1 AES-CMAC: ", ctx->ctx.data, 16); + aes_cmac_compute(ctx, ex2, sizeof(ex2)); + print_hexbytes("Example 2 AES-CMAC: ", ctx->ctx.data, 16); + aes_cmac_compute(ctx, ex3, sizeof(ex3)); + print_hexbytes("Example 3 AES-CMAC: ", ctx->ctx.data, 16); + aes_cmac_compute(ctx, ex4, sizeof(ex4)); + print_hexbytes("Example 4 AES-CMAC: ", ctx->ctx.data, 16); +}