diff --git a/Makefile b/Makefile index 3b602cf..f72e72a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ CFLAGS = -O255 -w255 -PROGRAMS = aescbctest aesctrtest aestest aescrypt sha1sum sha1test sha256sum sha256test +PROGRAMS = aescbctest aesctrtest aestest aescrypt sha1sum sha1test \ + sha256sum sha256test md5sum md5test .PHONY: default default: $(PROGRAMS) @@ -29,6 +30,12 @@ sha256sum: sha256sum.c sha256.cc sha256.asm sha256.macros sha256.h sha256test: sha256test.c sha256.cc sha256.asm sha256.macros sha256.h occ $(CFLAGS) sha256test.c sha256.cc -o sha256test +md5sum: md5sum.c md5.cc md5.asm md5.macros md5.h + occ $(CFLAGS) md5sum.c md5.cc -o md5sum + +md5test: md5test.c md5.cc md5.asm md5.macros md5.h + occ $(CFLAGS) md5test.c md5.cc -o md5test + .PHONY: clean clean: rm -f *.a *.A *.b *.B *.root *.ROOT *.o $(PROGRAMS) diff --git a/md5.asm b/md5.asm new file mode 100644 index 0000000..02e362b --- /dev/null +++ b/md5.asm @@ -0,0 +1,169 @@ +* Copyright (c) 2017 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. + + +* Implementation of the MD5 hash function for the 65816 + + case on + mcopy md5.macros + +* Direct page locations +length gequ 0 +extra gequ 8 +idx gequ 10 +a_ gequ 12 ; elements of state +b gequ 16 +c gequ 20 +d gequ 24 +;unused gequ 28 +f_plus_a gequ 32 +temp gequ 36 +h0 gequ 40 +h1 gequ 44 +h2 gequ 48 +h3 gequ 52 +;unused gequ 56 +m gequ 60 + + +* Precomputed values of g*4 for each loop iteration, for indexing the message +g_times_4 private + dc i4' 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60' + dc i4' 4, 24, 44, 0, 20, 40, 60, 16, 36, 56, 12, 32, 52, 8, 28, 48' + dc i4'20, 32, 44, 56, 4, 16, 28, 40, 52, 0, 12, 24, 36, 48, 60, 8' + dc i4' 0, 28, 56, 20, 48, 12, 40, 4, 32, 60, 24, 52, 16, 44, 8, 36' + end + +k private + dc i4'$d76aa478, $e8c7b756, $242070db, $c1bdceee' + dc i4'$f57c0faf, $4787c62a, $a8304613, $fd469501' + dc i4'$698098d8, $8b44f7af, $ffff5bb1, $895cd7be' + dc i4'$6b901122, $fd987193, $a679438e, $49b40821' + dc i4'$f61e2562, $c040b340, $265e5a51, $e9b6c7aa' + dc i4'$d62f105d, $02441453, $d8a1e681, $e7d3fbc8' + dc i4'$21e1cde6, $c33707d6, $f4d50d87, $455a14ed' + dc i4'$a9e3e905, $fcefa3f8, $676f02d9, $8d2a4c8a' + dc i4'$fffa3942, $8771f681, $6d9d6122, $fde5380c' + dc i4'$a4beea44, $4bdecfa9, $f6bb4b60, $bebfbc70' + dc i4'$289b7ec6, $eaa127fa, $d4ef3085, $04881d05' + dc i4'$d9d4d039, $e6db99e5, $1fa27cf8, $c4ac5665' + dc i4'$f4292244, $432aff97, $ab9423a7, $fc93a039' + dc i4'$655b59c3, $8f0ccc92, $ffeff47d, $85845dd1' + dc i4'$6fa87e4f, $fe2ce6e0, $a3014314, $4e0811a1' + dc i4'$f7537e82, $bd3af235, $2ad7d2bb, $eb86d391' + end + +* Initialize a MD5 context. +* This must be called before any of the other MD5 functions. +md5_init start + CFunction MD5_INIT + end + +MD5_INIT start + lda #$2301 + sta h0 + lda #$6745 + sta h0+2 + + lda #$AB89 + sta h1 + lda #$EFCD + sta h1+2 + + lda #$DCFE + sta h2 + lda #$98BA + sta h2+2 + + lda #$5476 + sta h3 + lda #$1032 + sta h3+2 + + stz length + stz length+2 + stz length+4 + stz length+6 + stz extra + rtl + end + + +* Process one 64-byte block through the MD5 hashing function. +* This is a low-level function; users should normally not call this directly. +md5_processblock start + CFunction MD5_PROCESSBLOCK + end + +MD5_PROCESSBLOCK start + lda h0 + sta a_ + lda h0+2 + sta a_+2 + + lda h1 + sta b + lda h1+2 + sta b+2 + + lda h2 + sta c + lda h2+2 + sta c+2 + + lda h3 + sta d + lda h3+2 + sta d+2 + + stz idx + BlockLoopPart 1 + BlockLoopPart 2 + BlockLoopPart 3 + BlockLoopPart 4 + +endloop clc + lda h0 + adc a_ + sta h0 + lda h0+2 + adc a_+2 + sta h0+2 + + clc + lda h1 + adc b + sta h1 + lda h1+2 + adc b+2 + sta h1+2 + + clc + lda h2 + adc c + sta h2 + lda h2+2 + adc c+2 + sta h2+2 + + clc + lda h3 + adc d + sta h3 + lda h3+2 + adc d+2 + sta h3+2 + + rtl + end diff --git a/md5.cc b/md5.cc new file mode 100644 index 0000000..d46648c --- /dev/null +++ b/md5.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 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 "md5.h" +#include + +#define length_offset 0 +#define extra_offset 8 +#define hash_offset 40 +#define data_offset 60 + +extern void MD5_PROCESSBLOCK(void); + +/* + * Update a MD5 context based on the specified data. + */ +void md5_update(struct md5_context *context, + const unsigned char *data, + unsigned long length) +{ + unsigned int extra = context->extra; + unsigned long oldlength = context->length; + + if ((context->length += length) < oldlength) + context->length2++; + + if (extra > 0) { + if (length >= 64 - extra) { + memcpy(&context->block[extra], data, 64 - extra); + asm { + phd + lda context + tcd + jsl MD5_PROCESSBLOCK + pld + } + length -= 64 - extra; + data += 64 - extra; + } else { + memcpy(&context->block[extra], data, length); + context->extra += length; + return; + } + } + + while (length >= 64) { + memcpy(&context->block, data, 64); + asm { + phd + lda context + tcd + jsl MD5_PROCESSBLOCK + pld + } + length -= 64; + data += 64; + } + + memcpy(&context->block, data, length); + context->extra = length; +} + + +/* + * Finish MD5 processing and generate the final hash code. + */ +void md5_finalize(struct md5_context *context) +{ + unsigned int extra = context->extra; + + context->block[extra++] = 0x80; + + memset(&context->block[extra], 0, 64 - extra); + + if (extra > 64 - 8) { + asm { + phd + lda context + tcd + jsl MD5_PROCESSBLOCK + pld + } + memset(&context->block, 0, 64 - 8); + } + + asm { + phd + lda context + tcd + + /* Append total length in bits */ + asl length_offset + rol length_offset+2 + rol length_offset+4 + rol length_offset+6 + asl length_offset + rol length_offset+2 + rol length_offset+4 + rol length_offset+6 + asl length_offset + rol length_offset+2 + rol length_offset+4 + rol length_offset+6 + + lda length_offset + sta data_offset+56 + lda length_offset+2 + sta data_offset+58 + lda length_offset+4 + sta data_offset+60 + lda length_offset+6 + sta data_offset+62 + + /* Process final block */ + jsl MD5_PROCESSBLOCK + + pld + } +} + +#append "md5.asm" diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..b3a4555 --- /dev/null +++ b/md5.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 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. + */ + +struct md5_context { + unsigned long length; + unsigned long length2; + unsigned short extra; + unsigned char reserved1[30]; + unsigned char hash[16]; + unsigned char reserved2[4]; + unsigned char block[64]; +}; + +/* + * The context structure must be in bank 0, preferably page-aligned. + */ + +/* + * Initialize a MD5 context. + * This must be called before any of the other MD5 functions. + */ +void md5_init(struct md5_context *context); + +/* + * Update a MD5 context based on the specified data. + */ +void md5_update(struct md5_context *context, const unsigned char *data, unsigned long length); + +/* + * Finish MD5 processing and generate the final hash code. + */ +void md5_finalize(struct md5_context *context); + +/* + * Process one 64-byte block through the MD5 hashing function. + * This is a low-level function; users should normally not call this directly. + */ +void md5_processblock(struct md5_context *context); diff --git a/md5.macros b/md5.macros new file mode 100644 index 0000000..8908c50 --- /dev/null +++ b/md5.macros @@ -0,0 +1,273 @@ +* Copyright (c) 2017 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. + + +* Right-rotate 32-bit value in &loc (DP or 16-bit address) by &n positions + macro + ROTR4 &loc,&n + aif &n>16,.dorotl + lda &loc+2 + lcla &i +&i seta &n +.rotrloop + lsr a ;to set carry + ror &loc + ror &loc+2 +&i seta &i-1 + aif &i>0,.rotrloop + ago .end +.dorotl + ROTL4 &loc,32-&n +.end + mend + +* Left-rotate 32-bit value in &loc (DP or 16-bit address) by &n positions + macro + ROTL4 &loc,&n + aif &n>16,.dorotr2 + lda &loc + lcla &i +&i seta &n +.rotlloop2 + asl a ;to set carry + rol &loc+2 + rol &loc +&i seta &i-1 + aif &i>0,.rotlloop2 + ago .end2 +.dorotr2 + ROTR4 &loc,32-&n +.end2 + mend + +* &to := &from ROTR4 &n + macro + ROTR4MOVE &to,&from,&n + aif &n>16,.dorotl3 + lda &from + sta &to + lda &from+2 + sta &to+2 + lcla &i +&i seta &n +.rotrloop3 + lsr a ;to set carry + ror &to + ror &to+2 +&i seta &i-1 + aif &i>0,.rotrloop3 + ago .end3 +.dorotl3 + ROTL4MOVE &to,&from,32-&n +.end3 + mend + +* &to := &from ROTL4 &n + macro + ROTL4MOVE &to,&from,&n + aif &n>16,.dorotr4 + lda &from+2 + sta &to+2 + lda &from + sta &to + lcla &i +&i seta &n +.rotlloop4 + asl a ;to set carry + rol &to+2 + rol &to +&i seta &i-1 + aif &i>0,.rotlloop4 + ago .end4 +.dorotr4 + ROTR4MOVE &to,&from,32-&n +.end4 + mend + + +* This makes a function wrapper that is callable from C, +* taking a pointer to the context structure as its argument. + macro + CFunction &fn + phb + plx + ply + tdc + pld + plb + plb + phy + phx + plb + pha + jsl &fn + pld + rtl + mend + + +* One iteration of the loop for processing blocks. +* The a,b,c,d variables are given as parameters so we can avoid cycling them. +* shift is a per-round shift amount. + macro + BlockLoopIter &a,&b,&c,&d,&shift + +* f_0 to f_15 + aif &part<>1,.skip1 + lda &c + eor &d + and &b + eor &d + clc + adc &a + sta f_plus_a + + lda &c+2 + eor &d+2 + and &b+2 + eor &d+2 + adc &a+2 + sta f_plus_a+2 +.skip1 + +* f_16 to f_31 + aif &part<>2,.skip2 + lda &b + eor &c + and &d + eor &c + clc + adc &a + sta f_plus_a + + lda &b+2 + eor &c+2 + and &d+2 + eor &c+2 + adc &a+2 + sta f_plus_a+2 +.skip2 + +* f_32 to f_47 + aif &part<>3,.skip3 + lda &b + eor &c + eor &d + clc + adc &a + sta f_plus_a + + lda &b+2 + eor &c+2 + eor &d+2 + adc &a+2 + sta f_plus_a+2 +.skip3 + +* f_48 to f_63 + aif &part<>4,.skip4 + lda &d + eor #$FFFF + ora &b + eor &c + clc + adc &a + sta f_plus_a + + lda &d+2 + eor #$FFFF + ora &b+2 + eor &c+2 + adc &a+2 + sta f_plus_a+2 +.skip4 + + ldy idx + ldx g_times_4,y + lda m,x + clc + adc f_plus_a + sta temp + inx + inx + lda m,x + adc f_plus_a+2 + sta temp+2 + + lda k,y + clc + adc temp + sta temp + iny + iny + lda k,y + adc temp+2 + sta temp+2 + + ROTL4 temp,&shift + + clc + lda &b + adc temp + sta &a + lda &b+2 + adc temp+2 + sta &a+2 + + inc idx + inc idx + inc idx + inc idx + + mend + + +* One part of the loop for processing blocks (20 iterations) + macro + BlockLoopPart &part + +loop&part anop + + aif &part<>1,.skip1a + BlockLoopIter a_,b,c,d,7 + BlockLoopIter d,a_,b,c,12 + BlockLoopIter c,d,a_,b,17 + BlockLoopIter b,c,d,a_,22 +.skip1a + aif &part<>2,.skip2a + BlockLoopIter a_,b,c,d,5 + BlockLoopIter d,a_,b,c,9 + BlockLoopIter c,d,a_,b,14 + BlockLoopIter b,c,d,a_,20 +.skip2a + aif &part<>3,.skip3a + BlockLoopIter a_,b,c,d,4 + BlockLoopIter d,a_,b,c,11 + BlockLoopIter c,d,a_,b,16 + BlockLoopIter b,c,d,a_,23 +.skip3a + aif &part<>4,.skip4a + BlockLoopIter a_,b,c,d,6 + BlockLoopIter d,a_,b,c,10 + BlockLoopIter c,d,a_,b,15 + BlockLoopIter b,c,d,a_,21 +.skip4a + + lda idx + cmp #16*4*&part + bge endloop&part + jmp loop&part +endloop&part anop + mend + diff --git a/md5sum.c b/md5sum.c new file mode 100644 index 0000000..64d200e --- /dev/null +++ b/md5sum.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 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 "md5.h" + +unsigned char buf[0x8000]; + +int main(int argc, char **argv) { + struct md5_context ctx; + FILE *file; + size_t count; + int i; + + srand(time(NULL)); + + if (argc != 2) + return EXIT_FAILURE; + + file = fopen(argv[1], "rb"); + if (file == NULL) + return EXIT_FAILURE; + + md5_init(&ctx); + do { + count = (rand() & 0x7FFF) + 1; + count = fread(buf, 1, count, file); + md5_update(&ctx, buf, count); + } while (count != 0); + + fclose(file); + md5_finalize(&ctx); + + for (i = 0; i < 16; i++) { + printf("%02x", ctx.hash[i]); + } + printf("\n"); + + return 0; +} diff --git a/md5test.c b/md5test.c new file mode 100644 index 0000000..1505aba --- /dev/null +++ b/md5test.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017 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 "md5.h" +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + unsigned int i; + unsigned long tick_count; + long double bytes_per_sec; + + struct md5_context *context, **context_hndl; + struct md5_context context_init = {0,0,0, {0}, {0}, {0}, + {0x61,0x62,0x63,0x80, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }}; + + context_hndl = (struct md5_context **)NewHandle(sizeof(struct md5_context), + userid(), attrFixed|attrPage|attrBank|attrNoCross, 0x000000); + if (toolerror()) + return 0; + context = *context_hndl; + *context = context_init; + + md5_init(context); + md5_processblock(context); + + printf("h[..] = %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + context->hash[0], context->hash[1], context->hash[2], context->hash[3], + context->hash[4], context->hash[5], context->hash[6], context->hash[7], + context->hash[8], context->hash[9], context->hash[10], context->hash[11], + context->hash[12], context->hash[13], context->hash[14], context->hash[15]); + + tick_count = GetTick(); + for (i = 0; i < 1000; i++) { + md5_processblock(context); + } + tick_count = GetTick() - tick_count; + + bytes_per_sec = (long double)1000 * 64 * 60 / tick_count; + printf("Time for 1000 iters = %lu ticks (%lf bytes/sec)\n", tick_count, bytes_per_sec); + + tick_count = GetTick(); + md5_init(context); + md5_update(context, (void*)0x030000, 64000); + md5_finalize(context); + tick_count = GetTick() - tick_count; + bytes_per_sec = (long double)1000 * 64 * 60 / tick_count; + printf("Append time = %lu ticks (%lf bytes/sec)\n", tick_count, bytes_per_sec); + + if (argc > 1) { + md5_init(context); + md5_update(context, argv[1], strlen(argv[1])); + md5_finalize(context); + + printf("h[..] = %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + context->hash[0], context->hash[1], context->hash[2], context->hash[3], + context->hash[4], context->hash[5], context->hash[6], context->hash[7], + context->hash[8], context->hash[9], context->hash[10], context->hash[11], + context->hash[12], context->hash[13], context->hash[14], context->hash[15]); + } + +}