/* BreakMS - Break Microsoft private key encryption via dictionary attack, based on the almost identical BreakNS program which did the same thing for Netscape keys about a year earlier. Despite the publicity, after more than a year Microsoft still hasn't changed their key storage format (... flammis acribus addictis). As an additional bonus, this version also breaks Microsoft's newer (and even more braindamaged) PKCS #12-like format, leaving users of MS products with no way to secure their keys. Before I released this I backported it to compile as a 16-bit DOS exe (the lowest common denominator which would run on all Windows boxes), but in the process I seem to have broken the ability of the RC2 code to function when compiled on a 32-bit machine. Until I get around to fixing this, you can grab a pre-compiled DOS version from http://www.cs.auckland.ac.nz/~pgut001/breakms.exe. Failing that, grab the RC2 code from cryptlib, that'll run on anything. You can use this code in whatever way you want, as long as you don't try to claim you wrote it. Written by Peter Gutmann 18 November 1997 */ #include #include #include /* Most of the code here was ripped out of cryptlib and is somewhat messy as a consquence. cryptlib has fairly extensive self-configuration and an internal API which hides machine-specific details, this program doesn't (the code here really wasn't meant for standalone use). As a result you need to manually define LITTLE_ENDIAN or BIG_ENDIAN, and it won't work at all on 64-bit systems. If you want the portability, use cryptlib instead: http://www.cs.auckland.ac.nz/~pgut001/cryptlib/ */ #define LITTLE_ENDIAN /* #define BIG_ENDIAN */ /* Workarounds for cryptlib defines, constants, and macros */ #if UINT_MAX > 0xFFFFUL #define MASK16( x ) ( ( x ) & 0xFFFFUL ) #else #define MASK16( x ) x #endif /* > 16-bit ints */ #define MASK32(x) x #define FALSE 0 #define TRUE !FALSE typedef unsigned char BYTE; typedef unsigned short int WORD; typedef unsigned long LONG; typedef int BOOLEAN; #define roundUp( size, roundSize ) \ ( ( size + ( roundSize - 1 ) ) & ~( roundSize - 1 ) ) /* Functions to convert the endianness from the canonical form to the internal form. bigToLittle() convert from big-endian in-memory to little-endian in-CPU, littleToBig() convert from little-endian in-memory to big-endian in-CPU */ void longReverse( LONG *buffer, int count ); #ifdef LITTLE_ENDIAN #define bigToLittleLong( x, y ) longReverse(x,y) #define littleToBigLong( x, y ) #else #define bigToLittleLong( x, y ) #define littleToBigLong( x, y ) longReverse(x,y) #endif /* LITTLE_ENDIAN */ /* Byte-reverse an array of 16- and 32-bit words to/from network byte order to account for processor endianness. These routines assume the given count is a multiple of 16 or 32 bits. They are safe even for CPU's with a word size > 32 bits since on a little-endian CPU the important 32 bits are stored first, so that by zeroizing the first 32 bits and oring the reversed value back in we don't need to rely on the processor only writing 32 bits into memory */ void longReverse( LONG *buffer, int count ) { LONG value; count /= sizeof( LONG ); while( count-- ) { value = *buffer; value = ( ( value & 0xFF00FF00UL ) >> 8 ) | \ ( ( value & 0x00FF00FFUL ) << 8 ); *buffer++ = ( value << 16 ) | ( value >> 16 ); } } #define mputLLong( memPtr, data ) \ memPtr[ 0 ] = ( BYTE ) ( ( data ) & 0xFF ); \ memPtr[ 1 ] = ( BYTE ) ( ( ( data ) >> 8 ) & 0xFF ); \ memPtr[ 2 ] = ( BYTE ) ( ( ( data ) >> 16 ) & 0xFF ); \ memPtr[ 3 ] = ( BYTE ) ( ( ( data ) >> 24 ) & 0xFF ); \ memPtr += 4 #define mputBLong( memPtr, data ) \ memPtr[ 0 ] = ( BYTE ) ( ( ( data ) >> 24 ) & 0xFF ); \ memPtr[ 1 ] = ( BYTE ) ( ( ( data ) >> 16 ) & 0xFF ); \ memPtr[ 2 ] = ( BYTE ) ( ( ( data ) >> 8 ) & 0xFF ); \ memPtr[ 3 ] = ( BYTE ) ( ( data ) & 0xFF ); \ memPtr += 4 #define mgetLWord(memPtr) \ ( ( WORD ) memPtr[ 0 ] ) | ( ( WORD ) memPtr[ 1 ] << 8 ); \ memPtr += 2 #define mputLWord(memPtr,data) \ memPtr[ 0 ] = ( BYTE ) ( ( data ) & 0xFF ); \ memPtr[ 1 ] = ( BYTE ) ( ( ( data ) >> 8 ) & 0xFF ); \ memPtr += 2 /**************************************************************************** * * * MD5 * * * ****************************************************************************/ /* The MD5 block size and message digest sizes, in bytes */ #define MD5_DATASIZE 64 #define MD5_DIGESTSIZE 16 /* The structure for storing MD5 info */ typedef struct { LONG digest[ 4 ]; /* Message digest */ LONG countLo, countHi; /* 64-bit bit count */ LONG data[ 16 ]; /* MD5 data buffer */ BOOLEAN done; /* Whether final digest present */ } MD5_INFO; /* Round 1 shift amounts */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 /* Round 2 shift amounts */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 /* Round 3 shift amounts */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 /* Round 4 shift amounts */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 /* F, G, H and I are basic MD5 functions */ #define F(X,Y,Z) ( ( X & Y ) | ( ~X & Z ) ) #define G(X,Y,Z) ( ( X & Z ) | ( Y & ~Z ) ) #define H(X,Y,Z) ( X ^ Y ^ Z ) #define I(X,Y,Z) ( Y ^ ( X | ~Z ) ) /* ROTATE_LEFT rotates x left n bits */ #define ROTATE_LEFT(x,n) ( ( x << n ) | ( x >> ( 32 - n ) ) ) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ #define FF(A,B,C,D,X,shiftAmt,magicConst) \ A += F( B, C, D ) + X + magicConst; \ A = MASK32( ROTATE_LEFT( MASK32( A ), shiftAmt ) + B ) #define GG(A,B,C,D,X,shiftAmt,magicConst) \ A += G( B, C, D ) + X + magicConst; \ A = MASK32( ROTATE_LEFT( MASK32( A ), shiftAmt ) + B ) #define HH(A,B,C,D,X,shiftAmt,magicConst) \ A += H( B, C, D ) + X + magicConst; \ A = MASK32( ROTATE_LEFT( MASK32( A ), shiftAmt ) + B ) #define II(A,B,C,D,X,shiftAmt,magicConst) \ A += I( B, C, D ) + X + magicConst; \ A = MASK32( ROTATE_LEFT( MASK32( A ), shiftAmt ) + B ) /* Basic MD5 step. Transforms digest based on data. Note that if the Mysterious Constants are arranged backwards in little-endian order and decrypted with DES they produce OCCULT MESSAGES! */ void MD5Transform( LONG *digest, LONG *data ) { LONG A, B, C, D; /* Set up local data */ A = digest[ 0 ]; B = digest[ 1 ]; C = digest[ 2 ]; D = digest[ 3 ]; /* Round 1 */ FF( A, B, C, D, data[ 0 ], S11, 3614090360UL ); /* 1 */ FF( D, A, B, C, data[ 1 ], S12, 3905402710UL ); /* 2 */ FF( C, D, A, B, data[ 2 ], S13, 606105819UL ); /* 3 */ FF( B, C, D, A, data[ 3 ], S14, 3250441966UL ); /* 4 */ FF( A, B, C, D, data[ 4 ], S11, 4118548399UL ); /* 5 */ FF( D, A, B, C, data[ 5 ], S12, 1200080426UL ); /* 6 */ FF( C, D, A, B, data[ 6 ], S13, 2821735955UL ); /* 7 */ FF( B, C, D, A, data[ 7 ], S14, 4249261313UL ); /* 8 */ FF( A, B, C, D, data[ 8 ], S11, 1770035416UL ); /* 9 */ FF( D, A, B, C, data[ 9 ], S12, 2336552879UL ); /* 10 */ FF( C, D, A, B, data[ 10 ], S13, 4294925233UL ); /* 11 */ FF( B, C, D, A, data[ 11 ], S14, 2304563134UL ); /* 12 */ FF( A, B, C, D, data[ 12 ], S11, 1804603682UL ); /* 13 */ FF( D, A, B, C, data[ 13 ], S12, 4254626195UL ); /* 14 */ FF( C, D, A, B, data[ 14 ], S13, 2792965006UL ); /* 15 */ FF( B, C, D, A, data[ 15 ], S14, 1236535329UL ); /* 16 */ /* Round 2 */ GG( A, B, C, D, data[ 1 ], S21, 4129170786UL ); /* 17 */ GG( D, A, B, C, data[ 6 ], S22, 3225465664UL ); /* 18 */ GG( C, D, A, B, data[ 11 ], S23, 643717713UL ); /* 19 */ GG( B, C, D, A, data[ 0 ], S24, 3921069994UL ); /* 20 */ GG( A, B, C, D, data[ 5 ], S21, 3593408605UL ); /* 21 */ GG( D, A, B, C, data[ 10 ], S22, 38016083UL ); /* 22 */ GG( C, D, A, B, data[ 15 ], S23, 3634488961UL ); /* 23 */ GG( B, C, D, A, data[ 4 ], S24, 3889429448UL ); /* 24 */ GG( A, B, C, D, data[ 9 ], S21, 568446438UL ); /* 25 */ GG( D, A, B, C, data[ 14 ], S22, 3275163606UL ); /* 26 */ GG( C, D, A, B, data[ 3 ], S23, 4107603335UL ); /* 27 */ GG( B, C, D, A, data[ 8 ], S24, 1163531501UL ); /* 28 */ GG( A, B, C, D, data[ 13 ], S21, 2850285829UL ); /* 29 */ GG( D, A, B, C, data[ 2 ], S22, 4243563512UL ); /* 30 */ GG( C, D, A, B, data[ 7 ], S23, 1735328473UL ); /* 31 */ GG( B, C, D, A, data[ 12 ], S24, 2368359562UL ); /* 32 */ /* Round 3 */ HH( A, B, C, D, data[ 5 ], S31, 4294588738UL ); /* 33 */ HH( D, A, B, C, data[ 8 ], S32, 2272392833UL ); /* 34 */ HH( C, D, A, B, data[ 11 ], S33, 1839030562UL ); /* 35 */ HH( B, C, D, A, data[ 14 ], S34, 4259657740UL ); /* 36 */ HH( A, B, C, D, data[ 1 ], S31, 2763975236UL ); /* 37 */ HH( D, A, B, C, data[ 4 ], S32, 1272893353UL ); /* 38 */ HH( C, D, A, B, data[ 7 ], S33, 4139469664UL ); /* 39 */ HH( B, C, D, A, data[ 10 ], S34, 3200236656UL ); /* 40 */ HH( A, B, C, D, data[ 13 ], S31, 681279174UL ); /* 41 */ HH( D, A, B, C, data[ 0 ], S32, 3936430074UL ); /* 42 */ HH( C, D, A, B, data[ 3 ], S33, 3572445317UL ); /* 43 */ HH( B, C, D, A, data[ 6 ], S34, 76029189UL ); /* 44 */ HH( A, B, C, D, data[ 9 ], S31, 3654602809UL ); /* 45 */ HH( D, A, B, C, data[ 12 ], S32, 3873151461UL ); /* 46 */ HH( C, D, A, B, data[ 15 ], S33, 530742520UL ); /* 47 */ HH( B, C, D, A, data[ 2 ], S34, 3299628645UL ); /* 48 */ /* Round 4 */ II( A, B, C, D, data[ 0 ], S41, 4096336452UL ); /* 49 */ II( D, A, B, C, data[ 7 ], S42, 1126891415UL ); /* 50 */ II( C, D, A, B, data[ 14 ], S43, 2878612391UL ); /* 51 */ II( B, C, D, A, data[ 5 ], S44, 4237533241UL ); /* 52 */ II( A, B, C, D, data[ 12 ], S41, 1700485571UL ); /* 53 */ II( D, A, B, C, data[ 3 ], S42, 2399980690UL ); /* 54 */ II( C, D, A, B, data[ 10 ], S43, 4293915773UL ); /* 55 */ II( B, C, D, A, data[ 1 ], S44, 2240044497UL ); /* 56 */ II( A, B, C, D, data[ 8 ], S41, 1873313359UL ); /* 57 */ II( D, A, B, C, data[ 15 ], S42, 4264355552UL ); /* 58 */ II( C, D, A, B, data[ 6 ], S43, 2734768916UL ); /* 59 */ II( B, C, D, A, data[ 13 ], S44, 1309151649UL ); /* 60 */ II( A, B, C, D, data[ 4 ], S41, 4149444226UL ); /* 61 */ II( D, A, B, C, data[ 11 ], S42, 3174756917UL ); /* 62 */ II( C, D, A, B, data[ 2 ], S43, 718787259UL ); /* 63 */ II( B, C, D, A, data[ 9 ], S44, 3951481745UL ); /* 64 */ /* Build message digest */ digest[ 0 ] = MASK32( digest[ 0 ] + A ); digest[ 1 ] = MASK32( digest[ 1 ] + B ); digest[ 2 ] = MASK32( digest[ 2 ] + C ); digest[ 3 ] = MASK32( digest[ 3 ] + D ); } /**************************************************************************** * * * MD5 Support Routines * * * ****************************************************************************/ /* The routine md5Initial initializes the message-digest context md5Info */ void md5Initial( MD5_INFO *md5Info ) { /* Clear all fields */ memset( md5Info, 0, sizeof( MD5_INFO ) ); /* Load magic initialization constants */ md5Info->digest[ 0 ] = 0x67452301L; md5Info->digest[ 1 ] = 0xEFCDAB89L; md5Info->digest[ 2 ] = 0x98BADCFEL; md5Info->digest[ 3 ] = 0x10325476L; /* Initialise bit count */ md5Info->countLo = md5Info->countHi = 0L; } /* The routine MD5Update updates the message-digest context to account for the presence of each of the characters buffer[ 0 .. count-1 ] in the message whose digest is being computed */ void md5Update( MD5_INFO *md5Info, BYTE *buffer, int count ) { LONG tmp; int dataCount; /* Update bitcount */ tmp = md5Info->countLo; if ( ( md5Info->countLo = tmp + ( ( LONG ) count << 3 ) ) < tmp ) md5Info->countHi++; /* Carry from low to high */ md5Info->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 * ) md5Info->data + dataCount; dataCount = MD5_DATASIZE - dataCount; if( count < dataCount ) { memcpy( p, buffer, count ); return; } memcpy( p, buffer, dataCount ); littleToBigLong( md5Info->data, MD5_DATASIZE ); MD5Transform( md5Info->digest, md5Info->data ); buffer += dataCount; count -= dataCount; } /* Process data in MD5_DATASIZE chunks */ while( count >= MD5_DATASIZE ) { memcpy( md5Info->data, buffer, MD5_DATASIZE ); littleToBigLong( md5Info->data, MD5_DATASIZE ); MD5Transform( md5Info->digest, md5Info->data ); buffer += MD5_DATASIZE; count -= MD5_DATASIZE; } /* Handle any remaining bytes of data. */ memcpy( md5Info->data, buffer, count ); } /* Final wrapup - pad to MD5_DATASIZE-byte boundary with the bit pattern 1 0* (64-bit count of bits processed, MSB-first) */ void md5Final( MD5_INFO *md5Info ) { int count; BYTE *dataPtr; /* Compute number of bytes mod 64 */ count = ( int ) md5Info->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 * ) md5Info->data + count; *dataPtr++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = MD5_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 ); littleToBigLong( md5Info->data, MD5_DATASIZE ); MD5Transform( md5Info->digest, md5Info->data ); /* Now fill the next block with 56 bytes */ memset( md5Info->data, 0, MD5_DATASIZE - 8 ); } else /* Pad block to 56 bytes */ memset( dataPtr, 0, count - 8 ); /* Append length in bits and transform */ md5Info->data[ 14 ] = md5Info->countLo; md5Info->data[ 15 ] = md5Info->countHi; littleToBigLong( md5Info->data, MD5_DATASIZE - 8 ); MD5Transform( md5Info->digest, md5Info->data ); md5Info->done = TRUE; } /**************************************************************************** * * * SHA-1 * * * ****************************************************************************/ /* The SHS block size and message digest sizes, in bytes */ #define SHA_DATASIZE 64 #define SHA_DIGESTSIZE 20 /* The structure for storing SHA info */ typedef struct { LONG digest[ 5 ]; /* Message digest */ LONG countLo, countHi; /* 64-bit bit count */ LONG data[ 16 ]; /* SHS data buffer */ BOOLEAN done; /* Whether final digest present */ BOOLEAN isSHA; /* Whether to use SHA */ } SHA_INFO; /* SHA initial values */ #define h0init 0x67452301UL #define h1init 0xEFCDAB89UL #define h2init 0x98BADCFEUL #define h3init 0x10325476UL #define h4init 0xC3D2E1F0UL /* The SHA f()-functions. The f1 and f3 functions can be optimized to save one boolean operation each - thanks to Rich Schroeppel for discovering this. f3 was further optimized by Colin Plumb to produce code which uses one instead of two temporary registers and can be scheduled in any order by the compiler once it's part of the basic SHA sub-round */ /*#define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) // Rounds 0-19 */ #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 ) | ( x & z ) | ( y & z ) ) // Rounds 40-59 */ /*#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) // Rounds 40-59 */ #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 SHA Mysterious Constants */ #define K1 0x5A827999UL /* Rounds 0-19 */ #define K2 0x6ED9EBA1UL /* Rounds 20-39 */ #define K3 0x8F1BBCDCUL /* Rounds 40-59 */ #define K4 0xCA62C1D6UL /* Rounds 60-79 */ /* SHA initial values */ #define h0init 0x67452301UL #define h1init 0xEFCDAB89UL #define h2init 0x98BADCFEUL #define h3init 0x10325476UL #define h4init 0xC3D2E1F0UL /* 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 unless we can tell the compiler how to do it (some compilers like gcc get it right anyway) */ #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 for this optimization. The updated SHA 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 */ #define expand(W,i) ( W[ i & 15 ] = MASK32( ROTL( 1, ( W[ i & 15 ] ^ W[ i - 14 & 15 ] ^ \ W[ i - 8 & 15 ] ^ W[ i - 3 & 15 ] ) ) ) ) /* The prototype SHA 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 = MASK32( e + ROTL( 5, a ) + f( b, c, d ) + k + data ); \ b = MASK32( ROTL( 30, b ) ) /* Perform the SHA 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 */ void SHA1Transform( LONG *digest, LONG *data ) { LONG A, B, C, D, E; /* Local vars */ LONG eData[ 16 ]; /* Expanded data */ int i; /* Set up first buffer and local data buffer */ A = digest[ 0 ]; B = digest[ 1 ]; C = digest[ 2 ]; D = digest[ 3 ]; E = digest[ 4 ]; for( i = 0; i < 16; i++ ) eData[ i ] = data[ i ]; /* 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 ] = MASK32( digest[ 0 ] + A ); digest[ 1 ] = MASK32( digest[ 1 ] + B ); digest[ 2 ] = MASK32( digest[ 2 ] + C ); digest[ 3 ] = MASK32( digest[ 3 ] + D ); digest[ 4 ] = MASK32( digest[ 4 ] + E ); } /**************************************************************************** * * * SHA Support Routines * * * ****************************************************************************/ /* Initialize the SHA values */ void sha1Initial( SHA_INFO *shaInfo ) { /* Clear all fields */ memset( shaInfo, 0, sizeof( SHA_INFO ) ); /* Set the h-vars to their initial values */ shaInfo->digest[ 0 ] = h0init; shaInfo->digest[ 1 ] = h1init; shaInfo->digest[ 2 ] = h2init; shaInfo->digest[ 3 ] = h3init; shaInfo->digest[ 4 ] = h4init; /* Initialise bit count */ shaInfo->countLo = shaInfo->countHi = 0; } /* Update SHA for a block of data. This is a cut-down form of the usual SHA transformation which assumes we'll always be passed a chunk of 64 bytes, and that the total will never exceed 2Gbits */ void sha1Update( SHA_INFO *shaInfo, BYTE *buffer ) { /* Update bitcount and transform data */ shaInfo->countLo += 512; #if defined( LITTLE_ENDIAN ) memcpy( shaInfo->data, buffer, SHA_DATASIZE ); bigToLittleLong( shaInfo->data, SHA_DATASIZE ); SHA1Transform( shaInfo->digest, shaInfo->data ); #else SHA1Transform( shaInfo->digest, ( LONG * ) buffer ); #endif /* Endianness dependant data move */ } /* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern 1 0* (64-bit count of bits processed, MSB-first) */ void sha1Final( SHA_INFO *shaInfo ) { int count; BYTE *dataPtr; /* Compute number of bytes mod 64 */ count = ( int ) shaInfo->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 * ) shaInfo->data + count; *dataPtr++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = SHA_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 ); bigToLittleLong( shaInfo->data, SHA_DATASIZE ); SHA1Transform( shaInfo->digest, shaInfo->data ); /* Now fill the next block with 56 bytes */ memset( shaInfo->data, 0, SHA_DATASIZE - 8 ); } else /* Pad block to 56 bytes */ memset( dataPtr, 0, count - 8 ); /* Append length in bits and transform */ shaInfo->data[ 14 ] = shaInfo->countHi; shaInfo->data[ 15 ] = shaInfo->countLo; /* The length count has to be in big-endian format when we feed it to the asm SHATransform() */ bigToLittleLong( shaInfo->data, SHA_DATASIZE - 8 ); SHA1Transform( shaInfo->digest, shaInfo->data ); shaInfo->done = TRUE; } /**************************************************************************** * * * RC4 * * * ****************************************************************************/ /* If the system can handle byte ops, we use those so we don't have to do a lot of masking. Otherwise, we use machine-word-size ops which will be faster on RISC machines */ #if UINT_MAX > 0xFFFFL /* System has 32-bit ints */ #define USE_LONG_RC4 typedef unsigned int rc4word; #else typedef unsigned char rc4word; #endif /* UINT_MAX > 0xFFFFL */ /* The scheduled RC4 key */ typedef struct { rc4word state[ 256 ]; rc4word x, y; } RC4KEY ; /* Expand an RC4 key */ void rc4ExpandKey( RC4KEY *rc4, unsigned char const *key, int keylen ) { int x, keypos = 0; rc4word sx, y = 0; rc4word *state = &rc4->state[ 0 ]; rc4->x = rc4->y = 0; for( x = 0; x < 256; x++ ) state[ x ] = x; for( x = 0; x < 256; x++ ) { sx = state[ x ]; y += sx + key[ keypos ]; #ifdef USE_LONG_RC4 y &= 0xFF; #endif /* USE_LONG_RC4 */ state[ x ] = state[ y ]; state[ y ] = sx; if( ++keypos == keylen ) keypos = 0; } } void rc4Crypt( RC4KEY *rc4, unsigned char *data, int len ) { rc4word x = rc4->x, y = rc4->y; rc4word sx, sy; rc4word *state = &rc4->state[ 0 ]; while( len-- ) { x++; #ifdef USE_LONG_RC4 x &= 0xFF; #endif /* USE_LONG_RC4 */ sx = state[ x ]; y += sx; #ifdef USE_LONG_RC4 y &= 0xFF; #endif /* USE_LONG_RC4 */ sy = state[ y ]; state[ y ] = sx; state[ x ] = sy; #ifdef USE_LONG_RC4 *data++ ^= state[ ( unsigned char ) ( sx+sy ) ]; #else *data++ ^= state[ ( sx+sy ) & 0xFF ]; #endif /* USE_LONG_RC4 */ } rc4->x = x; rc4->y = y; } /**************************************************************************** * * * RC2 * * * ****************************************************************************/ /* The size of the key in bytes and 16-bit words */ #define RC2_KEY_SIZE 128 #define RC2_KEY_SIZE_WORDS ( RC2_KEY_SIZE / 2 ) /* The RC2 block size */ #define RC2_BLOCKSIZE 8 /* The RC2 key */ typedef struct { unsigned int key[ RC2_KEY_SIZE_WORDS ]; } RC2_KEY; /* The following code uses unsigned int rather than WORD since many 32-bit compilers generate awful code when working with 16-bit data. We know when a result will overflow 16 bits so we use ints and manually mask off the extra bits rather than have the compiler do it after every operation on a WORD */ /* ROTATE_LEFT/RIGHT rotates x by n bits */ #define ROTATE_LEFT16(x,n) ( ( ( x ) << n ) | ( ( x ) >> ( 16 - n ) ) ) #define ROTATE_RIGHT16(x,n) ( ( ( x ) >> n ) | ( ( x ) << ( 16 - n ) ) ) /* The basic en/decryption operations */ #define enc(A,B,C,D,S,round,rotAmount) \ MASK16( ROTATE_LEFT16( MASK16( A + ( B & ~C ) + ( D & C ) + S[ round ] ), rotAmount ) ) #define dec(A,B,C,D,S,round,rotAmount) \ MASK16( MASK16( ROTATE_RIGHT16( A, rotAmount ) ) - ( B & ~D ) - ( C & D ) - S[ round ] ) /* One round of en/decryption */ #define encRound(A,B,C,D,S,round) \ A = enc( A, B, D, C, S, ( round * 4 ) + 0, 1 ); \ B = enc( B, C, A, D, S, ( round * 4 ) + 1, 2 ); \ C = enc( C, D, B, A, S, ( round * 4 ) + 2, 3 ); \ D = enc( D, A, C, B, S, ( round * 4 ) + 3, 5 ) #define decRound(A,B,C,D,S,round) \ D = dec( D, A, B, C, S, ( round * 4 ) + 3, 5 ); \ C = dec( C, D, A, B, S, ( round * 4 ) + 2, 3 ); \ B = dec( B, C, D, A, S, ( round * 4 ) + 1, 2 ); \ A = dec( A, B, C, D, S, ( round * 4 ) + 0, 1 ) /* The addition/subtraction of the S-boxes which occurs on the 4th and 10th rounds. The addition will overflow the 16-bit limit, but this isn't a problem since the next round of en/decryption gets things back down to 16 bits (unfortunately this doesn't work for subtraction) */ #define addSboxes(A,B,C,D,S) \ A += S[ D & RC2_KEY_SIZE_WORDS - 1 ]; \ B += S[ A & RC2_KEY_SIZE_WORDS - 1 ]; \ C += S[ B & RC2_KEY_SIZE_WORDS - 1 ]; \ D += S[ C & RC2_KEY_SIZE_WORDS - 1 ] #define subSboxes(A,B,C,D,S) \ D -= S[ C & RC2_KEY_SIZE_WORDS - 1 ]; \ D = MASK16( D ); \ C -= S[ B & RC2_KEY_SIZE_WORDS - 1 ]; \ C = MASK16( C ); \ B -= S[ A & RC2_KEY_SIZE_WORDS - 1 ]; \ B = MASK16( B ); \ A -= S[ D & RC2_KEY_SIZE_WORDS - 1 ]; \ A = MASK16( A ) /* The permutation table used for the RC2 key setup */ static BYTE sBox[] = { 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD }; /* Perform an RC2 key schedule */ void rc2keyInit( RC2_KEY *rc2key, BYTE *userKey, int length, int reducedLength ) { BYTE keyTemp[ RC2_KEY_SIZE ], *keyTempPtr = keyTemp; int maxValue, i; /* Expand the secret key to 128 bytes by taking the sum of the first and last bytes of the current key and appending the S-box entry this corresponds to to the current key */ memcpy( keyTemp, userKey, length ); for( i = length; i < RC2_KEY_SIZE; i++ ) keyTemp[ i ] = sBox[ ( keyTemp[ i - length ] + \ keyTemp[ i - 1 ] ) & 0xFF ]; /* Cripple the key size. Normally we would just replace the first byte of the key with the entry it selects from the S-box: keyTemp[ 0 ] = sBox[ keyTemp[ 0 ] ]; to create an arbitrary-length key, but MS only uses 40 bits (even within the US!) so we reduce the effective size to 40 bits */ maxValue = 128 - reducedLength; keyTemp[ maxValue ] = sBox[ keyTemp[ maxValue ] ]; for( i = maxValue - 1; i >= 0; i-- ) keyTemp[ i ] = sBox[ keyTemp[ i + 1 ] ^ keyTemp[ i + reducedLength ] ]; /* Copy the scheduled key to the RC2 key structure and erase it */ for( i = 0; i < RC2_KEY_SIZE_WORDS; i++ ) { rc2key->key[ i ] = mgetLWord( keyTempPtr ); } memset( keyTemp, 0, RC2_KEY_SIZE ); } /* Encrypt a block of data with RC2 */ void rc2encrypt( RC2_KEY *rc2key, BYTE *buffer ) { unsigned int word0, word1, word2, word3; unsigned int *key = rc2key->key; BYTE *bufPtr = buffer; /* Extract the data from the buffer */ word0 = mgetLWord( bufPtr ); word1 = mgetLWord( bufPtr ); word2 = mgetLWord( bufPtr ); word3 = mgetLWord( bufPtr ); /* Perform 16 rounds of encryption */ encRound( word0, word1, word2, word3, key, 0 ); encRound( word0, word1, word2, word3, key, 1 ); encRound( word0, word1, word2, word3, key, 2 ); encRound( word0, word1, word2, word3, key, 3 ); encRound( word0, word1, word2, word3, key, 4 ); addSboxes( word0, word1, word2, word3, key ); encRound( word0, word1, word2, word3, key, 5 ); encRound( word0, word1, word2, word3, key, 6 ); encRound( word0, word1, word2, word3, key, 7 ); encRound( word0, word1, word2, word3, key, 8 ); encRound( word0, word1, word2, word3, key, 9 ); encRound( word0, word1, word2, word3, key, 10 ); addSboxes( word0, word1, word2, word3, key ); encRound( word0, word1, word2, word3, key, 11 ); encRound( word0, word1, word2, word3, key, 12 ); encRound( word0, word1, word2, word3, key, 13 ); encRound( word0, word1, word2, word3, key, 14 ); encRound( word0, word1, word2, word3, key, 15 ); /* Deposit the data back in the buffer */ mputLWord( buffer, word0 ); mputLWord( buffer, word1 ); mputLWord( buffer, word2 ); mputLWord( buffer, word3 ); } /* Decrypt a block of data with RC2 */ void rc2decrypt( RC2_KEY *rc2key, BYTE *buffer ) { unsigned int word0, word1, word2, word3; unsigned int *key = rc2key->key; BYTE *bufPtr = buffer; /* Extract the data from the buffer */ word0 = mgetLWord( bufPtr ); word1 = mgetLWord( bufPtr ); word2 = mgetLWord( bufPtr ); word3 = mgetLWord( bufPtr ); /* Perform 16 rounds of decryption */ decRound( word0, word1, word2, word3, key, 15 ); decRound( word0, word1, word2, word3, key, 14 ); decRound( word0, word1, word2, word3, key, 13 ); decRound( word0, word1, word2, word3, key, 12 ); decRound( word0, word1, word2, word3, key, 11 ); subSboxes( word0, word1, word2, word3, key ); decRound( word0, word1, word2, word3, key, 10 ); decRound( word0, word1, word2, word3, key, 9 ); decRound( word0, word1, word2, word3, key, 8 ); decRound( word0, word1, word2, word3, key, 7 ); decRound( word0, word1, word2, word3, key, 6 ); decRound( word0, word1, word2, word3, key, 5 ); subSboxes( word0, word1, word2, word3, key ); decRound( word0, word1, word2, word3, key, 4 ); decRound( word0, word1, word2, word3, key, 3 ); decRound( word0, word1, word2, word3, key, 2 ); decRound( word0, word1, word2, word3, key, 1 ); decRound( word0, word1, word2, word3, key, 0 ); /* Deposit the data back in the buffer */ mputLWord( buffer, word0 ); mputLWord( buffer, word1 ); mputLWord( buffer, word2 ); mputLWord( buffer, word3 ); } void rc2DecryptCBC( RC2_KEY *rc2Key, BYTE *iv, BYTE *buffer, int noBytes ) { BYTE temp[ RC2_BLOCKSIZE ]; int blockCount = noBytes / RC2_BLOCKSIZE; while( blockCount-- ) { int i; /* Save the ciphertext */ memcpy( temp, buffer, RC2_BLOCKSIZE ); /* Decrypt a block of data */ rc2decrypt( rc2Key, buffer ); /* XOR the buffer contents with the IV */ for( i = 0; i < RC2_BLOCKSIZE; i++ ) buffer[ i ] ^= iv[ i ]; /* Shift the ciphertext into the IV */ memcpy( iv, temp, RC2_BLOCKSIZE ); /* Move on to next block of data */ buffer += RC2_BLOCKSIZE; } } /**************************************************************************** * * * Driver Code * * * ****************************************************************************/ /* Various magic values in the key file (these are from the Netscape keyfile breaker, but MS use almost the same values, the only difference is that they get all the AlgorithmIdentifier's wrong). The remainder are PKCS #12 values, but again MS use some weird ObjectIdentifiers of their own devising rather than what's in PKCS #12 */ static BYTE netscapeKeyfileID[] = { 0x04, 0x0B, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2D, 0x6B, 0x65, 0x79 }; /* static BYTE rc4EncryptionID[] = { 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x04, 0x05, 0x00 }; */ static BYTE rc4EncryptionID[] = { 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x04 }; static BYTE version[] = { 0x02, 0x01, 0x00 }; static BYTE pfxVersion[] = { 0x02, 0x01, 0x03 }; static BYTE iterations[] = { 0x02, 0x01, 0x01 }; /*static BYTE rsaPrivateKeyID[] = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; */ static BYTE rsaPrivateKeyID[] = { 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; static BYTE pkcs7dataID[] = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; static BYTE pkcs7encrDataID[] = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; static BYTE pkcs12modeID[] = { 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06 }; static BYTE pkcs12keyBagID[] = { 0x06, 0x0B, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x01 }; static BYTE pkcs12certBagID[] = { 0x06, 0x0B, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x03 }; /* General error handler */ static int dataLevel = 0; void errorExit( void ) { switch( dataLevel ) { case 0: puts( "This doesn't look like a Microsoft private key file." ); break; case 1: puts( "Unexpected data object found in key file. The file " "parsing code is fairly\nminimal and can be fooled by " "minor changes in the data format, you need to\nchange " "the code to accept this data type." ); break; case 2: puts( "Unexpected data object found in decrypted key data. The " "key data parsing\ncode is fairly minimal and can be " "fooled by minor changes in the data format,\nyou need to " "change the code to accept this data type." ); break; } exit( EXIT_FAILURE ); } /* Read a 32-bit little-endian length */ static long fgetLong( FILE *file ) { long value; value = ( long ) getc( file ); value |= ( ( long ) getc( file ) ) << 8; value |= ( ( long ) getc( file ) ) << 16; value |= ( ( long ) getc( file ) ) << 24; return( value ); } /* Skip an object header */ static void skipHeader( FILE *file, const int tag ) { int count; if( getc( file ) != tag ) errorExit(); count = getc( file ); if( count > 0x80 ) { count &= 0x7F; while( count-- ) getc( file ); } } static void skipDataHeader( BYTE *data, const int tag, int *index ) { int count; if( data[ *index ] != tag ) errorExit(); count = data[ *index + 1 ]; ( *index ) += 2; if( count > 0x80 ) { count &= 0x7F; while( count-- ) ( *index )++; } } /* General-purpose buffer. We make them static buffers to keep them off the stack on DOS/Win16 boxes */ static BYTE buffer[ 2048 ], temp[ 2048 ]; /* Print a key component */ int printKeyComponent( BYTE *buffer, char *title ) { int count, length = 0, totalLength = 2; printf( "%s = ", title ); if( *buffer++ != 0x02 ) errorExit(); /* Get the length of the component */ if( *buffer & 0x80 ) { count = *buffer++ & 0x7F; totalLength += count; while( count-- ) length = ( length << 8 ) | *buffer++; } else length = *buffer++; totalLength += length; /* Print the data */ for( count = 0; count < length; count++ ) printf( "%02X", buffer[ count ] ); putchar( '\n' ); return( totalLength ); } /* Print a private key */ void printPrivateKey( BYTE *buffer, int index ) { int count; /* Skip the OCTET STRING encapsulation */ if( buffer[ index++ ] != 0x04 ) errorExit(); count = buffer[ index++ ] & 0x7F; while( count-- ) index++; /* Skip the inner SEQUENCE encapsulation */ if( buffer[ index++ ] != 0x30 ) errorExit(); count = buffer[ index++ ] & 0x7F; while( count-- ) index++; /* Skip the version number */ if( buffer[ index++ ] != 0x02 || buffer[ index++ ] != 0x01 || buffer[ index++ ] != 0x00 ) errorExit(); /* OK, now we've reached the key components. Print each one out */ index += printKeyComponent( buffer + index, "Modulus" ); index += printKeyComponent( buffer + index, "Public exponent" ); index += printKeyComponent( buffer + index, "Private exponent" ); index += printKeyComponent( buffer + index, "Prime 1" ); index += printKeyComponent( buffer + index, "Prime 2" ); index += printKeyComponent( buffer + index, "Exponent 1" ); index += printKeyComponent( buffer + index, "Exponent 2" ); index += printKeyComponent( buffer + index, "Coefficient" ); } /* Set up a key as per PKCS #12. This is somewhat cut down and assumes that Microsoft implemented the corresponding key init routine. In fact because of the PKCS #12 design, we never need to compute the IV at all (although the following routine does include code to do it in case it's ever needed) */ static void pkcs12keyInit( BYTE *key, BYTE *salt, char *password, int passwordLen, const BOOLEAN isKey ) { static SHA_INFO staticShaInfoKey, staticShaInfoIV; static BOOLEAN firstTime = TRUE; SHA_INFO shaInfo; BYTE data[ 64 ]; int i, j = 0; if( firstTime ) { SHA_INFO *shaInfoPtr = ( isKey ) ? &staticShaInfoKey : &staticShaInfoIV; /* Normally we'd have to set up a diversifier string as the first 64 bytes of I, however we can precompute this and load a hash context with the precomputed value, eliminating 1/4 of the number of passes through the hash algorithms compression function (in practice we just combine it with the one-off calculation below, since it's only ever evaluated once) */ memset( data, ( isKey ) ? 1 : 2, 64 ); sha1Initial( shaInfoPtr ); sha1Update( shaInfoPtr, data ); /* But wait, there's more! Since the salt is applied at the start and never changes, we can precompute this the first time and reuse it every following time. We've now cut out 1/2 of the number of passes through the compression function */ for( i = 0; i < 64; i++ ) data[ i ] = salt[ i & 7 ]; sha1Update( shaInfoPtr, data ); /* If we've set the key and IV, don't set them again */ if( !isKey ) firstTime = FALSE; } /* Copy the precomputed data across, saving 2 of the 4 passes through the SHA compression function */ shaInfo = ( isKey ) ? staticShaInfoKey : staticShaInfoIV; /* Initialise the I value. This should be the salt stretched out to the nearest multiple of 64 bytes, followed by the password stretched out to the nearest multiple of 64 bytes, but since we've precomputed the salt we only set the password. In addition we convert the password from ASCII to Microsoft's weird interpretation of a BMPString as we copy it across */ for( i = 0; i < 64; ) { data[ i++ ] = '\0'; data[ i++ ] = password[ j++ ]; if( j > passwordLen ) /* This includes the trailing '\0' */ j = 0; } /* Hash the I value to produce the key */ sha1Update( &shaInfo, data ); sha1Final( &shaInfo ); for( i = 0; i < SHA_DIGESTSIZE / 4; i++ ) { mputBLong( key, shaInfo.digest[ i ] ); } } /* Hash self-test code. We use a nonstandard test for SHA1 since the implementation here has been cut down to be efficient for the special preprocessing used in PKCS #12 */ static void selfTest( void ) { SHA_INFO shaInfo; MD5_INFO md5Info; RC2_KEY key; /* BYTE shaTest[] = { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }; */ BYTE shaTest[] = { 0xCF, 0x08, 0x00, 0xF7, 0x64, 0x4A, 0xCE, 0x3C, 0xB4, 0xC3, 0xFA, 0x33, 0x38, 0x8D, 0x3B, 0xA0, 0xEA, 0x3C, 0x8B, 0x6E }; BYTE md5Test[] = { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 }; BYTE rc2Cipher40[] = { 0x65, 0x8A, 0x83, 0x3A, 0x5D, 0xE3, 0x45, 0x55 }; BYTE data[ 20 ], *hashPtr = data; int i; sha1Initial( &shaInfo ); /* sha1Update( &shaInfo, ( BYTE * ) "abc", 3 ); */ sha1Update( &shaInfo, ( BYTE * ) "012345678901234567890123456789" "012345678901234567890123456789" "0123" ); sha1Final( &shaInfo ); for( i = 0; i < SHA_DIGESTSIZE / 4; i++ ) { mputBLong( hashPtr, shaInfo.digest[ i ] ); } if( memcmp( data, shaTest, SHA_DIGESTSIZE ) ) { puts( "Error: SHA1 self-test failed." ); exit( EXIT_FAILURE ); } hashPtr = data; md5Initial( &md5Info ); md5Update( &md5Info, ( BYTE * ) "abc", 3 ); md5Final( &md5Info ); for( i = 0; i < MD5_DIGESTSIZE / 4; i++ ) { mputLLong( hashPtr, md5Info.digest[ i ] ); } if( memcmp( data, md5Test, MD5_DIGESTSIZE ) ) { puts( "Error: MD5 self-test failed." ); exit( EXIT_FAILURE ); } memset( data, 0, sizeof( data ) ); rc2keyInit( &key, data, 16, 5 ); rc2encrypt( &key, data ); if( memcmp( data, rc2Cipher40, RC2_BLOCKSIZE ) ) { puts( "Error: RC2 self-test failed." ); exit( EXIT_FAILURE ); } rc2decrypt( &key, data ); if( memcmp( data, "\x00\x00\x00\x00\x00\x00\x00\x00", 8 ) ) { puts( "Error: RC2 self-test failed." ); exit( EXIT_FAILURE ); } } /* The main program */ int main( int argc, char *argv[] ) { FILE *keyFile, *dictFile; BOOLEAN isOldFormat = TRUE; BYTE salt[ 8 ]; int count, length = 0; long dataLength; /* Spew forth some useless garbage */ puts( "BreakMS - Microsoft private key file/PFX/PKCS #12 encryption " "breaker\nWritten by Peter Gutmann , " "November 1997\n" ); /* Check args and open the server key file */ if( argc != 3 ) { puts( "Usage: breaksk " ); return( EXIT_FAILURE ); } if( ( keyFile = fopen( argv[ 1 ], "rb" ) ) == NULL ) { perror( argv[ 1 ] ); return( EXIT_FAILURE ); } /* Make sure everything is in order */ selfTest(); /* Read the key file outer wrapper */ if( fread( buffer, 1, 4, keyFile ) != 4 ) errorExit(); if( !memcmp( buffer, "KBRK", 4 ) ) { /* It's a .KEY file */ dataLength = fgetLong( keyFile ); /* Doesn't include trailing '\0' */ if( dataLength < 1 || dataLength > 127 ) { puts( "Key file format error." ); exit( EXIT_FAILURE ); } fread( buffer, 1, ( size_t ) dataLength + 1, keyFile ); dataLength = fgetLong( keyFile ); printf( "File is an old-format .KEY file containing the private key " "for\n'%s', data length %ld bytes.\n", buffer, dataLength ); dataLevel++; /* Read the outer wrapper */ skipHeader( keyFile, 0x30 ); if( ( fread( buffer, 1, 13, keyFile ) != 13 ) || \ memcmp( buffer, netscapeKeyfileID, 13 ) ) errorExit(); /* Read the PKCS #8 EncryptedPrivateKey wrapper */ skipHeader( keyFile, 0x30 ); if( ( fread( buffer, 1, 12, keyFile ) != 12 ) || \ memcmp( buffer, rc4EncryptionID, 12 ) ) { puts( "This doesn't look like an RC4-encrypted server key." ); exit( EXIT_FAILURE ); } /* Read the start of the EncryptedData field */ if( getc( keyFile ) != 0x04 ) errorExit(); count = getc( keyFile ) & 0x7F; while( count-- ) length = ( length << 8 ) | getc( keyFile ); printf( "Encrypted data is %d bytes long.\n", length ); /* Read the encrypted RSAPrivateKey */ if( fread( buffer, 1, length, keyFile ) != length ) { puts( "Key file length fields are inconsistent." ); exit( EXIT_FAILURE ); } } else { /* It should be a PFX file */ fseek( keyFile, 0, SEEK_SET ); skipHeader( keyFile, 0x30 ); fread( buffer, 1, 3, keyFile ); if( memcmp( buffer, pfxVersion, 3 ) ) errorExit(); dataLevel++; puts( "File is a PFX/PKCS #12 key file." ); skipHeader( keyFile, 0x30 ); fread( buffer, 1, 11, keyFile ); if( memcmp( buffer, pkcs7dataID, 11 ) ) errorExit(); skipHeader( keyFile, 0xA0 ); skipHeader( keyFile, 0x04 ); skipHeader( keyFile, 0x30 ); skipHeader( keyFile, 0x30 ); fread( buffer, 1, 11, keyFile ); if( memcmp( buffer, pkcs7encrDataID, 11 ) ) errorExit(); skipHeader( keyFile, 0xA0 ); skipHeader( keyFile, 0x30 ); fread( buffer, 1, 3, keyFile ); if( memcmp( buffer, version, 3 ) ) errorExit(); skipHeader( keyFile, 0x30 ); fread( buffer, 1, 11, keyFile ); if( memcmp( buffer, pkcs7dataID, 11 ) ) errorExit(); skipHeader( keyFile, 0x30 ); /* All this, and data too! */ fread( buffer, 1, 12, keyFile ); if( memcmp( buffer, pkcs12modeID, 12 ) ) errorExit(); skipHeader( keyFile, 0x30 ); if( getc( keyFile ) != 0x04 ) errorExit(); if( ( count = getc( keyFile ) ) != 8 ) errorExit(); fread( salt, 1, 8, keyFile ); fread( buffer, 1, 3, keyFile ); if( memcmp( buffer, iterations, 3 ) ) errorExit(); /* Finally, the data itself. Read the start of the encrypted data */ if( getc( keyFile ) != 0x80 ) errorExit(); count = getc( keyFile ) & 0x7F; while( count-- ) length = ( length << 8 ) | getc( keyFile ); printf( "Encrypted data is %d bytes long.\n", length ); /* Read the encrypted RSAPrivateKey */ if( fread( buffer, 1, length, keyFile ) != length ) { puts( "Key file length fields are inconsistent." ); exit( EXIT_FAILURE ); } isOldFormat = FALSE; } fclose( keyFile ); /* We've got the data we want, now rumble through the dictionary trying each key on it. First, make sure we can open the thing */ if( ( dictFile = fopen( argv[ 2 ], "r" ) ) == NULL ) { perror( argv[ 2 ] ); return( EXIT_FAILURE ); } while( TRUE ) { BYTE hashedPassword[ MD5_DIGESTSIZE ], *hashedPassPtr = hashedPassword; MD5_INFO md5Info; RC4KEY rc4key; RC2_KEY rc2key; char dictWord[ 100 ]; int dictWordLength, index; /* Get the next word from the dictionary */ if( fgets( dictWord, 100, dictFile ) == NULL ) { puts( "No more words in dictionary." ); break; } dictWordLength = strlen( dictWord ) - 1; dictWord[ dictWordLength ] = '\0'; /* Set up the key and decrypt the first block of data as appropriate */ if( isOldFormat ) { /* Hash the word using MD5 */ md5Initial( &md5Info ); md5Update( &md5Info, ( BYTE * ) dictWord, dictWordLength ); md5Final( &md5Info ); for( index = 0; index < MD5_DIGESTSIZE / 4; index++ ) { mputLLong( hashedPassPtr, md5Info.digest[ index ] ); } /* Set up the RC4 key based on the hashed password */ rc4ExpandKey( &rc4key, hashedPassword, MD5_DIGESTSIZE ); /* Copy the data to a temporary buffer and try to decrypt it */ memcpy( temp, buffer, length ); rc4Crypt( &rc4key, temp, 20 ); /* Check for known plaintext */ if( temp[ 0 ] != 0x30 || ( temp[ 1 ] != 0x81 && temp[ 1 ] != 0x82 ) ) continue; index = 1; count = temp[ index++ ] & 0x7F; while( count-- ) index++; if( memcmp( temp + index, version, 3 ) ) continue; index += 3; if( memcmp( temp + index, rsaPrivateKeyID, 13 ) ) continue; index += 13; /* We've found the password, display it and decrypt the rest of the data */ printf( "The password which was used to encrypt this Microsoft " "private key file is\n '%s'.\n\n", dictWord ); rc4Crypt( &rc4key, temp + 20, length - 20 ); dataLevel++; } else { BYTE key[ 20 ], iv[ 20 ]; /* Process the password using Microsoft's broken PKCS #12 subset to get the decryption key. In theory we'd need to repeat this to generate the IV, however the start of the plaintext is: SEQUENCE { SEQUENCE { OBJECT IDENTIFIER, ... and the SEQUENCE is encoded as 30 82 xx xx (where xx xx are the length bytes). This means the first 8 bytes will be 30 82 xx xx 30 82 xx xx, and will be followed by the OID. We can therefore skip the first 8 bytes and, using them as the IV, decrypt the second 8 bytes and check for the OID. This eliminates one of the two PKCS12 key initialisation calls */ pkcs12keyInit( key, salt, dictWord, dictWordLength, TRUE ); /* Set up the RC2 key based on the processed password */ rc2keyInit( &rc2key, key, 5, 5 ); /* Copy the data to a temporary buffer and try to decrypt it */ memcpy( temp, buffer, length ); rc2decrypt( &rc2key, temp + RC2_BLOCKSIZE ); for( count = 0; count < RC2_BLOCKSIZE; count++ ) temp[ count + RC2_BLOCKSIZE ] ^= temp[ count ]; /* Check for the OID. It doesn't really matter what we check for since the first 8 bytes are only enough to encode down to the RSADSI arc */ if( memcmp( temp + RC2_BLOCKSIZE, pkcs12certBagID, RC2_BLOCKSIZE ) ) continue; /* We've found the password, display it and decrypt the rest of the data (it's always a multiple of 8 bytes in length in CBC mode) */ printf( "The password which was used to encrypt this Microsoft " "PFX/PKCS #12 file is\n '%s'.\n\n", dictWord ); rc2DecryptCBC( &rc2key, buffer + RC2_BLOCKSIZE, temp + ( 2 * RC2_BLOCKSIZE ), length - ( 2 * RC2_BLOCKSIZE ) ); dataLevel++; /* Just for forms sake, generate the IV and go back and decrypt the first 8 bytes which we skipped earlier (this also gives us the outer SEQUENCE headers which means we can skip the cruft contained in the PDU^H^H^H"bag" (sigh) */ pkcs12keyInit( iv, salt, dictWord, dictWordLength, FALSE ); rc2DecryptCBC( &rc2key, iv, temp, RC2_BLOCKSIZE ); #if 0 /* Uncomment the following quick hack to write the inner, decrypted data to the given file. You can dump this data in human-readable form using dumpasn1, http://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c */ { FILE *file = fopen( "inner.der", "wb" ); fwrite( temp, 1, length, file ); fclose( file ); } #endif /* 0 */ /* Find the length of the SEQUENCE OF SafeBag */ if( temp[ 0 ] != 0x30 ) errorExit(); count = temp[ 1 ] & 0x7F; index = 2; while( count-- ) length = ( length << 8 ) | temp[ index++ ]; /* Skip SafeBag objects until we find the private key */ while( length > 0 ) { int innerLength = 0; /* Find the length of the object and check whether it contains the private key */ if( temp[ index++ ] != 0x30 ) errorExit(); count = temp[ index++ ] & 0x7F; length -= 2 + count; while( count-- ) innerLength = ( innerLength << 8 ) | temp[ index++ ]; if( !memcmp( temp + index, pkcs12keyBagID, 13 ) ) { index += 13; break; } /* Skip this object and continue */ index += innerLength; length -= innerLength; } /* If we didn't find a key, complain */ if( length <= 0 ) { puts( "No private key data found (although there's all " "sorts of other stuff in\nthere)." ); exit( EXIT_FAILURE ); } /* Burrow down through more ASN.1 */ skipDataHeader( temp, 0xA0, &index ); skipDataHeader( temp, 0x30, &index ); skipDataHeader( temp, 0x02, &index ); if( temp[ index++ ] != 0x00 ) errorExit(); if( temp[ index++ ] != 0x30 ) errorExit(); index += temp[ index ] + 1; /* Skip AlgorithmIdentifier */ } /* Display the key */ printPrivateKey( temp, index ); break; } fclose( dictFile ); return( EXIT_SUCCESS ); }