#include <stdio.h>

#include "types.h"

#define SHA1_INPUT_BYTES 64	/* 512 bits */
#define SHA1_INPUT_WORDS ( SHA1_INPUT_BYTES / 4 )
#define SHA1_DIGEST_BYTES 20	/* 160 bits */
#define SHA1_DIGEST_WORDS ( SHA1_DIGEST_BYTES / 4 )
#define SHA1_INPUT_MASK	(SHA1_INPUT_BYTES-1)

int sha1_verbose = 1;
int sha1_block = 1;

#define H0 0x67452301
#define H1 0xEFCDAB89
#define H2 0x98BADCFE
#define H3 0x10325476
#define H4 0xC3D2E1F0

word32 SHA1_IV[ SHA1_DIGEST_WORDS ] = { H0, H1, H2, H3, H4 };
/* function used for rounds 0..19 */

static word32 F1( word32 B, word32 C, word32 D )
{
    return ( B & C ) | ( ~B & D );
}

/* function used for rounds 20..39 */

static word32 F2( word32 B, word32 C, word32 D )
{
    return ( B ^ C ^ D );
}

/* function used for rounds 40..59 */

static word32 F3( word32 B, word32 C, word32 D )
{
    return ( B & C ) | ( B & D ) | ( C & D );   
}

/* function used for rounds 60..79 */

static word32 F4( word32 B, word32 C, word32 D )
{
    return ( B ^ C ^ D );
}

/* array F of 80 functions */

typedef word32 ( *FuncPtr )( word32, word32, word32 );
FuncPtr F[ 80 ] = {
    F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,F1,
    F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,F2,
    F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,F3,
    F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4,F4
};


#define K1 0x5A827999  /* constant used for rounds 0..19 */
#define K2 0x6ED9EBA1  /* constant used for rounds 20..39 */
#define K3 0x8F1BBCDC  /* constant used for rounds 40..59 */
#define K4 0xCA62C1D6  /* constant used for rounds 60..79 */

/* array K of 80 constants */

word32 K[ 80 ] = {
    K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,K1,
    K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,K2,
    K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,K3,
    K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4,K4
};

/* rotate X n bits left   ( X <<< n ) */

#define S( n, X ) (((X) << (n)) | ((X) >> (32 - (n))))


word32 SHA1_transform( word32 H[ SHA1_DIGEST_WORDS ], 
		       const word32 M[ SHA1_INPUT_WORDS ], int get );

int main( int argc, char* argv[] ) {
    word32 H[ SHA1_DIGEST_WORDS ];
    word32 M[ SHA1_INPUT_WORDS ];
    word32 out, in;
    int start = 0;
    int stop = SHA1_INPUT_WORDS-1;
    int instop = 0;		/* no limit */
    int i, c;
    
    if ( argc > 1 ) { start = atoi( argv[1] ); }
    if ( argc > 2 ) { stop = atoi( argv[2] ); }
    if ( argc > 3 ) { instop = atoi( argv[3] ); }

    memset( M, 0, SHA1_INPUT_BYTES );

    if ( !isatty( fileno( stdin ) ) ) {
        c = 0;
        while ( !feof(stdin) && (!instop || (instop && c < instop)) ) {
            scanf("%d %08X", &i, &in );
            M[i] = in;
            c++;
        }
    }
    for ( i = 0; i < start; i++ ) {
	printf("%d %08X\n",i,M[i]);
    }
    for ( i = start; i <= stop; i++ ) {
        memcpy( H, SHA1_IV, SHA1_DIGEST_BYTES );
        out = SHA1_transform( H, M, i );
	M[i] = 2*M[i] - out;
	printf("%i %08X\n",i,M[i]);
    }
}

word32 SHA1_transform( word32 H[ SHA1_DIGEST_WORDS ], 
		       const word32 M[ SHA1_INPUT_WORDS ], int get )
{
    int t;
    word32 A = H[ 0 ];
    word32 B = H[ 1 ];
    word32 C = H[ 2 ];
    word32 D = H[ 3 ];
    word32 E = H[ 4 ];
    word32 TEMP;
    word32 W[ 80 ];
    word32 ret;

    memcpy( W, M, SHA1_INPUT_BYTES );
    
#if defined( VERBOSE )
    if ( sha1_verbose )
    {
        fprintf( stderr, "Start processing block %d.  ", sha1_block );
        fprintf( stderr, "The words of block %d are\n", sha1_block );
        for ( t = 0; t < 16; t++ )
	{
            fprintf( stderr, "\nW[%2d] = %08X", t, W[ t ] );
	}
        fprintf( stderr, ".\n\n" );
        fprintf( stderr, "The hex values of A,B,C,D,E after pass t of the" );
        fprintf( stderr, " \"for t = 0 to 79\" loop (step\n(d) of" );
        fprintf( stderr, " Section 7 or step (c) of Section 8) are\n\n" );
        fprintf( stderr,
                 "\t    A          B          C          D          E\n" );
    }
#endif

    for ( t = 16; t < 80; t++ )
    {
	W[ t ] = S( 1, W[ t-3 ] ^ W[ t-8 ] ^ W[ t-14 ] ^ W[ t-16 ]);
    }

    for ( t = 0; t < 80; t++ )
    {
	TEMP = S( 5, A ) + F[ t ]( B, C, D ) + E + W[ t ] + K[ t ];
	E = D;
	D = C;
	C = S( 30, B );
	B = A;
	A = TEMP;
	if ( t == get ) { ret = A; }
#if defined( VERBOSE )
	if ( sha1_verbose )
	{
	    fprintf( stderr, "\nt = %2d: %08X   %08X   %08X   %08X   %08X",
		     t, A, B, C, D, E );
	}
#endif
    }

#if defined( VERBOSE )
    if ( sha1_verbose )
    {
        fprintf( stderr, ".\n\nBlock %d has been processed.  ", sha1_block );
        fprintf( stderr, "The values of {Hi} are\n\n" );
        sha1_block++;
        fprintf( stderr, "H0 = %08X  +  %08X  =  %08X\n\n",
                 H[ 0 ], A, H[ 0 ] + A );
        fprintf( stderr, "H1 = %08X  +  %08X  =  %08X\n\n",
                 H[ 1 ], B, H[ 1 ] + B );
        fprintf( stderr, "H2 = %08X  +  %08X  =  %08X\n\n",
                 H[ 2 ], C, H[ 2 ] + C );
        fprintf( stderr, "H3 = %08X  +  %08X  =  %08X\n\n",
                 H[ 3 ], D, H[ 3 ] + D );
        fprintf( stderr, "H4 = %08X  +  %08X  =  %08X.\n\n",
                 H[ 4 ], E, H[ 4 ] + E );
    }

#endif

    H[ 0 ] += A;
    H[ 1 ] += B;
    H[ 2 ] += C;
    H[ 3 ] += D;
    H[ 4 ] += E;
    return ret;
}
