From: Richard Basch Date: Fri, 10 May 1996 07:16:28 +0000 (+0000) Subject: NIST-SHA support X-Git-Tag: krb5-1.0-beta6~114 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2e2b512c6fb5b54d43d0cf59f42ae172df574aa2;p=krb5.git NIST-SHA support git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@7966 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/crypto/sha/.Sanitize b/src/lib/crypto/sha/.Sanitize new file mode 100644 index 000000000..c9f08bb77 --- /dev/null +++ b/src/lib/crypto/sha/.Sanitize @@ -0,0 +1,41 @@ +# Sanitize.in for Kerberos V5 + +# Each directory to survive it's way into a release will need a file +# like this one called "./.Sanitize". All keyword lines must exist, +# and must exist in the order specified by this file. Each directory +# in the tree will be processed, top down, in the following order. + +# Hash started lines like this one are comments and will be deleted +# before anything else is done. Blank lines will also be squashed +# out. + +# The lines between the "Do-first:" line and the "Things-to-keep:" +# line are executed as a /bin/sh shell script before anything else is +# done in this + +Do-first: + +# All files listed between the "Things-to-keep:" line and the +# "Files-to-sed:" line will be kept. All other files will be removed. +# Directories listed in this section will have their own Sanitize +# called. Directories not listed will be removed in their entirety +# with rm -rf. + +Things-to-keep: + +.cvsignore +ChangeLog +Makefile.in +configure +configure.in +shs.c +shs.h +sha_crypto.c +sha_glue.c +t_shs.c + +Things-to-lose: + +Do-last: + +# End of file. diff --git a/src/lib/crypto/sha/.cvsignore b/src/lib/crypto/sha/.cvsignore new file mode 100644 index 000000000..e8c05a6b1 --- /dev/null +++ b/src/lib/crypto/sha/.cvsignore @@ -0,0 +1 @@ +configure diff --git a/src/lib/crypto/sha/ChangeLog b/src/lib/crypto/sha/ChangeLog new file mode 100644 index 000000000..2cb3cf5f9 --- /dev/null +++ b/src/lib/crypto/sha/ChangeLog @@ -0,0 +1,6 @@ +Fri May 10 01:19:18 1996 Richard Basch + + * Makefile.in configure.in t_shs.c sha_glue.c sha_crypto.c shs.c shs.h: + Initial check-in of the functions to support the NIST FIPS 180 + SHA algorithm. Provide interfaces for cksum-sha, cksum-sha-des3. + (enctype-des3-sha is also being defined) diff --git a/src/lib/crypto/sha/Makefile.in b/src/lib/crypto/sha/Makefile.in new file mode 100644 index 000000000..288e72ca7 --- /dev/null +++ b/src/lib/crypto/sha/Makefile.in @@ -0,0 +1,40 @@ +CFLAGS = $(CCOPTS) $(DEFS) -I$(srcdir)/../des + +##DOSBUILDTOP = ..\..\.. +##DOSLIBNAME=..\crypto.lib + +.c.o: + $(CC) $(CFLAGS) -c $(srcdir)/$*.c +@SHARED_RULE@ + +OBJS= shs.$(OBJEXT) sha_glue.$(OBJEXT) sha_crypto.$(OBJEXT) + +SRCS= $(srcdir)/shs.c $(srcdir)/sha_glue.c $(srcdir)/sha_crypto.c + +all-unix:: shared $(OBJS) +all-mac:: shared $(OBJS) +all-windows:: $(OBJS) + +shared: + mkdir shared + +t_shs: t_shs.o shs.o + $(CC) $(CFLAGS) $(LDFLAGS) -o t_shs t_shs.o shs.o + +t_shs.exe: + $(CC) $(CFLAGS2) -o t_shs.exe t_shs.c shs.c + +check-unix:: t_shs + $(C)t_shs -x + +check-windows:: t_shs$(EXEEXT) + $(C)t_shs$(EXEEXT) -x + +clean:: + $(RM) t_shs$(EXEEXT) t_shs.$(OBJEXT) + +clean-unix:: + $(RM) shared/* +clean-mac:: + $(RM) shared/* +clean-windows:: diff --git a/src/lib/crypto/sha/configure.in b/src/lib/crypto/sha/configure.in new file mode 100644 index 000000000..c23c1ce7e --- /dev/null +++ b/src/lib/crypto/sha/configure.in @@ -0,0 +1,6 @@ +AC_INIT(configure.in) +CONFIG_RULES +dnl AC_DEFINE(NEW_SHS) +V5_SHARED_LIB_OBJS +SubdirLibraryRule([${OBJS}]) +V5_AC_OUTPUT_MAKEFILE diff --git a/src/lib/crypto/sha/sha_crypto.c b/src/lib/crypto/sha/sha_crypto.c new file mode 100644 index 000000000..f554a49e2 --- /dev/null +++ b/src/lib/crypto/sha/sha_crypto.c @@ -0,0 +1,212 @@ +#include "k5-int.h" +#include "shs.h" +#include "des_int.h" /* we cheat a bit and call it directly... */ + +/* Windows needs to these prototypes for the assignment below */ + +krb5_error_code +krb5_sha_crypto_sum_func + PROTOTYPE((krb5_pointer in, + size_t in_length, + krb5_pointer seed, + size_t seed_length, + krb5_checksum FAR *outcksum)); + +krb5_error_code +krb5_sha_crypto_verify_func + PROTOTYPE((krb5_checksum FAR *cksum, + krb5_pointer in, + size_t in_length, + krb5_pointer seed, + size_t seed_length)); + +static void +krb5_sha_calculate_cksum(ctx, in, in_length, confound, confound_length) + SHS_INFO *ctx; + krb5_pointer in; + size_t in_length; + krb5_pointer confound; + size_t confound_length; +{ + shsInit(ctx); + if (confound && confound_length) + shsUpdate(ctx, confound, confound_length); + shsUpdate(ctx, in, in_length); + shsFinal(ctx); +} + +static krb5_error_code +shs_crypto_sum_func(in, in_length, seed, seed_length, outcksum) + krb5_pointer in; + size_t in_length; + krb5_pointer seed; + size_t seed_length; + krb5_checksum FAR *outcksum; +{ + krb5_octet outtmp[NIST_SHA_DES3_CKSUM_LENGTH+ + NIST_SHA_DES3_CONFOUND_LENGTH]; + krb5_octet *input = (krb5_octet *)in; + krb5_encrypt_block eblock; + krb5_keyblock keyblock; + krb5_error_code retval; + mit_des3_cblock tmpkey; + size_t i; + + SHS_INFO working; + + /* Generate the confounder in place */ + if (retval = krb5_random_confounder(NIST_SHA_DES3_CONFOUND_LENGTH, + outtmp)) + return(retval); + + /* Calculate the checksum */ + krb5_sha_calculate_cksum(&working, + in, + in_length, + (krb5_pointer) outtmp, + (size_t) NIST_SHA_DES3_CONFOUND_LENGTH); + + outcksum->checksum_type = CKSUMTYPE_NIST_SHA_DES3; + outcksum->length = + NIST_SHA_DES3_CKSUM_LENGTH + NIST_SHA_DES3_CONFOUND_LENGTH; + + /* Now blast in the digest */ + memset((char *)&outtmp[NIST_SHA_DES3_CONFOUND_LENGTH], 0, + NIST_SHA_DES3_CKSUM_LENGTH); + memcpy((char *)&outtmp[NIST_SHA_DES3_CONFOUND_LENGTH], + (char *)&working.digest[0], NIST_SHA_CKSUM_LENGTH); + + /* Clean up the droppings */ + memset((char *)&working, 0, sizeof(working)); + + /* Set up the temporary copy of the key (see RFC 1510 section 6.4.5) */ + memset((char *) tmpkey, 0, sizeof(tmpkey)); + for (i=0; (icontents, + NIST_SHA_DES3_CKSUM_LENGTH + + NIST_SHA_DES3_CONFOUND_LENGTH, + (struct mit_des_ks_struct *)eblock.priv, + ((struct mit_des_ks_struct *)eblock.priv)+1, + ((struct mit_des_ks_struct *)eblock.priv)+2, + keyblock.contents, + MIT_DES_ENCRYPT); + + if (retval) + (void) mit_des_finish_key(&eblock); + else + retval = mit_des_finish_key(&eblock); + + return retval; +} + +static krb5_error_code +shs_crypto_verify_func(cksum, in, in_length, seed, seed_length) + krb5_checksum FAR *cksum; + krb5_pointer in; + size_t in_length; + krb5_pointer seed; + size_t seed_length; +{ + krb5_octet outtmp[NIST_SHA_DES3_CKSUM_LENGTH + + NIST_SHA_DES3_CONFOUND_LENGTH]; + krb5_octet *input = (krb5_octet *)in; + krb5_encrypt_block eblock; + krb5_keyblock keyblock; + krb5_error_code retval; + mit_des3_cblock tmpkey; + size_t i; + + SHS_INFO working; + + retval = 0; + if (cksum->checksum_type == CKSUMTYPE_NIST_SHA_DES3) { + if (cksum->length == (NIST_SHA_DES3_CKSUM_LENGTH + + NIST_SHA_DES3_CONFOUND_LENGTH)) { + /* + * If we're verifying the correct implementation, then we have + * to do a little more work because we must decrypt the checksum + * because it contains the confounder in it. So, figure out + * what our key variant is and then do it! + */ + + /* Set up the variant of the key (see RFC 1510 section 6.4.5) */ + memset((char *) tmpkey, 0, sizeof(tmpkey)); + for (i=0; (icontents, + (mit_des_cblock *)&outtmp[0], + NIST_SHA_DES3_CKSUM_LENGTH + + NIST_SHA_DES3_CONFOUND_LENGTH, + (struct mit_des_ks_struct *) + eblock.priv, + ((struct mit_des_ks_struct *) + eblock.priv) + 1, + ((struct mit_des_ks_struct *) + eblock.priv) + 2, + keyblock.contents, + MIT_DES_DECRYPT); + if (retval) + (void) mit_des_finish_key(&eblock); + else + retval = mit_des_finish_key(&eblock); + if (retval) return retval; + + /* Now that we have the decrypted checksum, try to regenerate it */ + krb5_sha_calculate_cksum(&working, + in, + in_length, + (krb5_pointer) outtmp, + (size_t) NIST_SHA_DES3_CONFOUND_LENGTH); + + /* Compare the checksums */ + if (memcmp((char *) &outtmp[NIST_SHA_DES3_CONFOUND_LENGTH], + (char *) &working.digest[0], + NIST_SHA_CKSUM_LENGTH)) + retval = KRB5KRB_AP_ERR_BAD_INTEGRITY; + } + else + retval = KRB5KRB_AP_ERR_BAD_INTEGRITY; + } + else + retval = KRB5KRB_AP_ERR_INAPP_CKSUM; + + /* Clean up droppings */ + memset((char *)&working, 0, sizeof(working)); + return(retval); +} + +krb5_checksum_entry nist_sha_des3_cksumtable_entry = +{ + 0, + shs_crypto_sum_func, + shs_crypto_verify_func, + NIST_SHA_DES3_CKSUM_LENGTH + NIST_SHA_DES3_CONFOUND_LENGTH, + 1, /* is collision proof */ + 1, /* uses key */ +}; diff --git a/src/lib/crypto/sha/sha_glue.c b/src/lib/crypto/sha/sha_glue.c new file mode 100644 index 000000000..3028c1147 --- /dev/null +++ b/src/lib/crypto/sha/sha_glue.c @@ -0,0 +1,82 @@ +#include "k5-int.h" +#include "shs.h" + +krb5_error_code +krb5_sha_sum_func + PROTOTYPE((krb5_pointer in, + size_t in_length, + krb5_pointer seed, + size_t seed_length, + krb5_checksum *outcksum)); + +krb5_error_code +krb5_sha_verify_func + PROTOTYPE((krb5_checksum FAR *cksum, + krb5_pointer in, + size_t in_length, + krb5_pointer seed, + size_t seed_length)); + +krb5_error_code +krb5_sha_sum_func(in, in_length, seed, seed_length, outcksum) + krb5_pointer in; + size_t in_length; + krb5_pointer seed; + size_t seed_length; + krb5_checksum *outcksum; +{ + krb5_octet *input = (krb5_octet *)in; + SHS_INFO working; + + shsInit(&working); + shsUpdate(&working, input, in_length); + shsFinal(&working); + + outcksum->checksum_type = CKSUMTYPE_NIST_SHA; + outcksum->length = SHS_DIGESTSIZE; + + memcpy((char *)outcksum->contents, + (char *)&working.digest[0], + outcksum->length); + memset((char *)&working, 0, sizeof(working)); + return 0; +} + +krb5_error_code +krb5_sha_verify_func(cksum, in, in_length, seed, seed_length) + krb5_checksum FAR *cksum; + krb5_pointer in; + size_t in_length; + krb5_pointer seed; + size_t seed_length; +{ + krb5_octet *input = (krb5_octet *)in; + SHS_INFO working; + krb5_error_code retval; + + if (cksum->checksum_type != CKSUMTYPE_NIST_SHA) + return KRB5KRB_AP_ERR_INAPP_CKSUM; + if (cksum->length != SHS_DIGESTSIZE) + return KRB5KRB_AP_ERR_BAD_INTEGRITY; + + shsInit(&working); + shsUpdate(&working, input, in_length); + shsFinal(&working); + + retval = 0; + if (memcmp((char *) cksum->contents, + (char *) &working.digest[0], + cksum->length)) + retval = KRB5KRB_AP_ERR_BAD_INTEGRITY; + memset((char *) &working, 0, sizeof(working)); + return retval; +} + +krb5_checksum_entry nist_sha_cksumtable_entry = { + 0, + krb5_sha_sum_func, + krb5_sha_verify_func, + SHS_DIGESTSIZE, + 1, /* is collision proof */ + 0, /* doesn't use key */ +}; diff --git a/src/lib/crypto/sha/shs.c b/src/lib/crypto/sha/shs.c new file mode 100644 index 000000000..ed6ee8de6 --- /dev/null +++ b/src/lib/crypto/sha/shs.c @@ -0,0 +1,332 @@ +#include +#include +#include "shs.h" + +/* The SHS f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ + +/* The SHS Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +/* SHS initial values */ + +#define h0init 0x67452301L +#define h1init 0xEFCDAB89L +#define h2init 0x98BADCFEL +#define h3init 0x10325476L +#define h4init 0xC3D2E1F0L + +/* Note that it may be necessary to add parentheses to these macros if they + are to be called with expressions as arguments */ + +/* 32-bit rotate left - kludged with shifts */ + +#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) + +/* The initial expanding function. The hash function is defined over an + 80-word expanded input array W, where the first 16 are copies of the input + data, and the remaining 64 are defined by + + W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] + + This implementation generates these values on the fly in a circular + buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this + optimization. + + The updated SHS changes the expanding function by adding a rotate of 1 + bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor + for this information */ + +#ifdef NEW_SHS + #define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ i - 14 & 15 ] ^ \ + W[ i - 8 & 15 ] ^ W[ i - 3 & 15 ] ) ) ) +#else + #define expand(W,i) ( W[ i & 15 ] ^= W[ i - 14 & 15 ] ^ W[ i - 8 & 15 ] ^ W[ i - 3 & 15 ] ) +#endif /* NEW_SHS */ + +/* The prototype SHS sub-round. The fundamental sub-round is: + + a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data; + b' = a; + c' = ROTL( 30, b ); + d' = c; + e' = d; + + but this is implemented by unrolling the loop 5 times and renaming the + variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. + This code is then replicated 20 times for each of the 4 functions, using + the next 20 values from the W[] array each time */ + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + +/* Initialize the SHS values */ + +void shsInit(shsInfo) + SHS_INFO *shsInfo; +{ + /* Set the h-vars to their initial values */ + shsInfo->digest[ 0 ] = h0init; + shsInfo->digest[ 1 ] = h1init; + shsInfo->digest[ 2 ] = h2init; + shsInfo->digest[ 3 ] = h3init; + shsInfo->digest[ 4 ] = h4init; + + /* Initialise bit count */ + shsInfo->countLo = shsInfo->countHi = 0; +} + +/* Perform the SHS transformation. Note that this code, like MD5, seems to + break some optimizing compilers due to the complexity of the expressions + and the size of the basic block. It may be necessary to split it into + sections, e.g. based on the four subrounds + + Note that this corrupts the shsInfo->data area */ + +static void SHSTransform KRB5_PROTOTYPE((LONG *digest, LONG *data)); + +static +void SHSTransform(digest, data) + LONG *digest; + LONG *data; +{ + LONG A, B, C, D, E; /* Local vars */ + LONG eData[ 16 ]; /* Expanded data */ + + /* Set up first buffer and local data buffer */ + A = digest[ 0 ]; + B = digest[ 1 ]; + C = digest[ 2 ]; + D = digest[ 3 ]; + E = digest[ 4 ]; + memcpy( eData, data, SHS_DATASIZE ); + + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound( A, B, C, D, E, f1, K1, eData[ 0 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 1 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 2 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 3 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 4 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 5 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 6 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 7 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 8 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 9 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 10 ] ); + subRound( E, A, B, C, D, f1, K1, eData[ 11 ] ); + subRound( D, E, A, B, C, f1, K1, eData[ 12 ] ); + subRound( C, D, E, A, B, f1, K1, eData[ 13 ] ); + subRound( B, C, D, E, A, f1, K1, eData[ 14 ] ); + subRound( A, B, C, D, E, f1, K1, eData[ 15 ] ); + subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) ); + subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) ); + subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) ); + subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) ); + + subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) ); + subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) ); + + subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) ); + subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) ); + + subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) ); + subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) ); + + /* Build message digest */ + digest[ 0 ] += A; + digest[ 1 ] += B; + digest[ 2 ] += C; + digest[ 3 ] += D; + digest[ 4 ] += E; +} + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. It is possible to make the code endianness- + independant by fiddling around with data at the byte level, but this + makes for very slow code, so we rely on the user to sort out endianness + at compile time */ + +void longReverse( LONG *buffer, int byteCount ) +{ + LONG value; + static int init = 0; + + switch (init) { + case 0: + if (htonl(1) != 1) { + init=2; + break; + } + init=1; + /* fall through - MSB */ + case 1: + return; + } + + byteCount /= sizeof( LONG ); + while( byteCount-- ) { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } +} + +/* Update SHS for a block of data */ + +void shsUpdate(shsInfo, buffer, count) + SHS_INFO *shsInfo; + BYTE *buffer; + int count; +{ + LONG tmp; + int dataCount; + + /* Update bitcount */ + tmp = shsInfo->countLo; + if ( ( shsInfo->countLo = tmp + ( ( LONG ) count << 3 ) ) < tmp ) + shsInfo->countHi++; /* Carry from low to high */ + shsInfo->countHi += count >> 29; + + /* Get count of bytes already in data */ + dataCount = ( int ) ( tmp >> 3 ) & 0x3F; + + /* Handle any leading odd-sized chunks */ + if( dataCount ) + { + BYTE *p = ( BYTE * ) shsInfo->data + dataCount; + + dataCount = SHS_DATASIZE - dataCount; + if( count < dataCount ) + { + memcpy( p, buffer, count ); + return; + } + memcpy( p, buffer, dataCount ); + longReverse( shsInfo->data, SHS_DATASIZE ); + SHSTransform( shsInfo->digest, shsInfo->data ); + buffer += dataCount; + count -= dataCount; + } + + /* Process data in SHS_DATASIZE chunks */ + while( count >= SHS_DATASIZE ) + { + memcpy( shsInfo->data, buffer, SHS_DATASIZE ); + longReverse( shsInfo->data, SHS_DATASIZE ); + SHSTransform( shsInfo->digest, shsInfo->data ); + buffer += SHS_DATASIZE; + count -= SHS_DATASIZE; + } + + /* Handle any remaining bytes of data. */ + memcpy( shsInfo->data, buffer, count ); +} + +/* Final wrapup - pad to SHS_DATASIZE-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) */ + +void shsFinal(shsInfo) + SHS_INFO *shsInfo; +{ + int count; + BYTE *dataPtr; + + /* Compute number of bytes mod 64 */ + count = ( int ) shsInfo->countLo; + count = ( count >> 3 ) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + dataPtr = ( BYTE * ) shsInfo->data + count; + *dataPtr++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = SHS_DATASIZE - 1 - count; + + /* Pad out to 56 mod 64 */ + if( count < 8 ) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset( dataPtr, 0, count ); + longReverse( shsInfo->data, SHS_DATASIZE ); + SHSTransform( shsInfo->digest, shsInfo->data ); + + /* Now fill the next block with 56 bytes */ + memset( shsInfo->data, 0, SHS_DATASIZE - 8 ); + } + else + /* Pad block to 56 bytes */ + memset( dataPtr, 0, count - 8 ); + + /* Append length in bits and transform */ + shsInfo->data[ 14 ] = shsInfo->countHi; + shsInfo->data[ 15 ] = shsInfo->countLo; + + longReverse( shsInfo->data, SHS_DATASIZE - 8 ); + SHSTransform( shsInfo->digest, shsInfo->data ); +} diff --git a/src/lib/crypto/sha/shs.h b/src/lib/crypto/sha/shs.h new file mode 100644 index 000000000..4c846bf66 --- /dev/null +++ b/src/lib/crypto/sha/shs.h @@ -0,0 +1,52 @@ +#ifndef _SHS_DEFINED + +#include + +#define _SHS_DEFINED + +/* Some useful types */ + +typedef krb5_octet BYTE; +typedef krb5_ui_4 LONG; + +/* Exit status of functions. */ + +#define OK 0 +#define ERROR -1 + +/* Define the following to use the updated SHS implementation */ + +#define NEW_SHS /**/ + +/* The SHS block size and message digest sizes, in bytes */ + +#define SHS_DATASIZE 64 +#define SHS_DIGESTSIZE 20 + +/* The structure for storing SHS info */ + +typedef struct { + LONG digest[ 5 ]; /* Message digest */ + LONG countLo, countHi; /* 64-bit bit count */ + LONG data[ 16 ]; /* SHS data buffer */ + } SHS_INFO; + +/* Message digest functions */ + +void shsInit + KRB5_PROTOTYPE((SHS_INFO *shsInfo)); +void shsUpdate + KRB5_PROTOTYPE((SHS_INFO *shsInfo, BYTE *buffer, int count)); +void shsFinal + KRB5_PROTOTYPE((SHS_INFO *shsInfo)); + + +#define NIST_SHA_CKSUM_LENGTH SHS_DIGESTSIZE +#define NIST_SHA_DES3_CKSUM_LENGTH 24 +#define NIST_SHA_DES3_CONFOUND_LENGTH 8 + +extern krb5_checksum_entry + nist_sha_cksumtable_entry, + nist_sha_des3_cksumtable_entry; + +#endif /* _SHS_DEFINED */ diff --git a/src/lib/crypto/sha/t_shs.c b/src/lib/crypto/sha/t_shs.c new file mode 100644 index 000000000..8c1a1290a --- /dev/null +++ b/src/lib/crypto/sha/t_shs.c @@ -0,0 +1,118 @@ +/**************************************************************************** +* * +* SHS Test Code * +* * +****************************************************************************/ + +#include +#include +#include +#include "shs.h" + +/* Test the SHS implementation */ + +#ifdef NEW_SHS + +static LONG shsTestResults[][ 5 ] = { + { 0xA9993E36L, 0x4706816AL, 0xBA3E2571L, 0x7850C26CL, 0x9CD0D89DL, }, + { 0x84983E44L, 0x1C3BD26EL, 0xBAAE4AA1L, 0xF95129E5L, 0xE54670F1L, }, + { 0x34AA973CL, 0xD4C4DAA4L, 0xF61EEB2BL, 0xDBAD2731L, 0x6534016FL, } + }; + +#else + +static LONG shsTestResults[][ 5 ] = { + { 0x0164B8A9L, 0x14CD2A5EL, 0x74C4F7FFL, 0x082C4D97L, 0xF1EDF880L }, + { 0xD2516EE1L, 0xACFA5BAFL, 0x33DFC1C4L, 0x71E43844L, 0x9EF134C8L }, + { 0x3232AFFAL, 0x48628A26L, 0x653B5AAAL, 0x44541FD9L, 0x0D690603L } + }; +#endif /* NEW_SHS */ + +static int compareSHSresults(shsInfo, shsTestLevel) +SHS_INFO *shsInfo; +int shsTestLevel; +{ + int i; + + /* Compare the returned digest and required values */ + for( i = 0; i < 5; i++ ) + if( shsInfo->digest[ i ] != shsTestResults[ shsTestLevel ][ i ] ) + return( ERROR ); + return( OK ); +} + +main() +{ + SHS_INFO shsInfo; + unsigned int i; + time_t secondCount; + BYTE data[ 200 ]; + + /* Make sure we've got the endianness set right. If the machine is + big-endian (up to 64 bits) the following value will be signed, + otherwise it will be unsigned. Unfortunately we can't test for odd + things like middle-endianness without knowing the size of the data + types */ + + /* Test SHS against values given in SHS standards document */ + printf( "Running SHS test 1 ... " ); + shsInit( &shsInfo ); + shsUpdate( &shsInfo, ( BYTE * ) "abc", 3 ); + shsFinal( &shsInfo ); + if( compareSHSresults( &shsInfo, 0 ) == ERROR ) + { + putchar( '\n' ); + puts( "SHS test 1 failed" ); + exit( ERROR ); + } +#ifdef NEW_SHS + puts( "passed, result= A9993E364706816ABA3E25717850C26C9CD0D89D" ); +#else + puts( "passed, result= 0164B8A914CD2A5E74C4F7FF082C4D97F1EDF880" ); +#endif /* NEW_SHS */ + + printf( "Running SHS test 2 ... " ); + shsInit( &shsInfo ); + shsUpdate( &shsInfo, ( BYTE * ) "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56 ); + shsFinal( &shsInfo ); + if( compareSHSresults( &shsInfo, 1 ) == ERROR ) + { + putchar( '\n' ); + puts( "SHS test 2 failed" ); + exit( ERROR ); + } +#ifdef NEW_SHS + puts( "passed, result= 84983E441C3BD26EBAAE4AA1F95129E5E54670F1" ); +#else + puts( "passed, result= D2516EE1ACFA5BAF33DFC1C471E438449EF134C8" ); +#endif /* NEW_SHS */ + + printf( "Running SHS test 3 ... " ); + shsInit( &shsInfo ); + for( i = 0; i < 15625; i++ ) + shsUpdate( &shsInfo, ( BYTE * ) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 64 ); + shsFinal( &shsInfo ); + if( compareSHSresults( &shsInfo, 2 ) == ERROR ) + { + putchar( '\n' ); + puts( "SHS test 3 failed" ); + exit( ERROR ); + } +#ifdef NEW_SHS + puts( "passed, result= 34AA973CD4C4DAA4F61EEB2BDBAD27316534016F" ); +#else + puts( "passed, result= 3232AFFA48628A26653B5AAA44541FD90D690603" ); +#endif /* NEW_SHS */ + + printf( "\nTesting speed for 100MB data... " ); + shsInit( &shsInfo ); + secondCount = time( NULL ); + for( i = 0; i < 500000U; i++ ) + shsUpdate( &shsInfo, data, 200 ); + secondCount = time( NULL ) - secondCount; + printf( "done. Time = %ld seconds, %ld kbytes/second.\n", \ + secondCount, 100500L / secondCount ); + + puts( "\nAll SHS tests passed" ); + exit( OK ); +}