TUCoPS :: Crypto :: breakms.c

BreakMS - Break Microsoft Private Key Encryption with a dictionary attack



/* 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.

   Written by Peter Gutmann <pgut001@cs.auckland.ac.nz> 18 November 1997 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 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
   <rcs@cs.arizona.edu> for discovering this.  f3 was further optimized by
   Colin Plumb <colin@nyx10.cs.du.edu> 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 <colin@nyx10.cs.du.edu> 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 <pgut001@cs.auckland.ac.nz>, "
		  "November 1997\n" );

	/* Check args and open the server key file */
	if( argc != 3 )
		{
		puts( "Usage: breaksk <server key file> <dictionary>" );
		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 );
	}

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH