192 lines
4.6 KiB
C++
192 lines
4.6 KiB
C++
|
/* sha1.cpp
|
||
|
|
||
|
Copyright (c) 2005 Michael D. Leonhard
|
||
|
|
||
|
http://tamale.net/
|
||
|
|
||
|
This file is licensed under the terms described in the
|
||
|
accompanying LICENSE file.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "sha1.h"
|
||
|
|
||
|
// print out memory in hexadecimal
|
||
|
void SHA1::hexPrinter( unsigned char* c, int l )
|
||
|
{
|
||
|
assert( c );
|
||
|
assert( l > 0 );
|
||
|
while( l > 0 )
|
||
|
{
|
||
|
printf( " %02x", *c );
|
||
|
l--;
|
||
|
c++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// circular left bit rotation. MSB wraps around to LSB
|
||
|
Uint32 SHA1::lrot( Uint32 x, int bits )
|
||
|
{
|
||
|
return (x<<bits) | (x>>(32 - bits));
|
||
|
};
|
||
|
|
||
|
// Save a 32-bit unsigned integer to memory, in big-endian order
|
||
|
void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num )
|
||
|
{
|
||
|
assert( byte );
|
||
|
byte[0] = (unsigned char)(num>>24);
|
||
|
byte[1] = (unsigned char)(num>>16);
|
||
|
byte[2] = (unsigned char)(num>>8);
|
||
|
byte[3] = (unsigned char)num;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Constructor *******************************************************
|
||
|
SHA1::SHA1()
|
||
|
{
|
||
|
// make sure that the data type is the right size
|
||
|
assert( sizeof( Uint32 ) * 5 == 20 );
|
||
|
|
||
|
// initialize
|
||
|
H0 = 0x67452301;
|
||
|
H1 = 0xefcdab89;
|
||
|
H2 = 0x98badcfe;
|
||
|
H3 = 0x10325476;
|
||
|
H4 = 0xc3d2e1f0;
|
||
|
unprocessedBytes = 0;
|
||
|
size = 0;
|
||
|
}
|
||
|
|
||
|
// Destructor ********************************************************
|
||
|
SHA1::~SHA1()
|
||
|
{
|
||
|
// erase data
|
||
|
H0 = H1 = H2 = H3 = H4 = 0;
|
||
|
for( int c = 0; c < 64; c++ ) bytes[c] = 0;
|
||
|
unprocessedBytes = size = 0;
|
||
|
}
|
||
|
|
||
|
// process ***********************************************************
|
||
|
void SHA1::process()
|
||
|
{
|
||
|
assert( unprocessedBytes == 64 );
|
||
|
//printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" );
|
||
|
int t;
|
||
|
Uint32 a, b, c, d, e, K, f, W[80];
|
||
|
// starting values
|
||
|
a = H0;
|
||
|
b = H1;
|
||
|
c = H2;
|
||
|
d = H3;
|
||
|
e = H4;
|
||
|
// copy and expand the message block
|
||
|
for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24)
|
||
|
+(bytes[t*4 + 1] << 16)
|
||
|
+(bytes[t*4 + 2] << 8)
|
||
|
+ bytes[t*4 + 3];
|
||
|
for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 );
|
||
|
|
||
|
/* main loop */
|
||
|
Uint32 temp;
|
||
|
for( t = 0; t < 80; t++ )
|
||
|
{
|
||
|
if( t < 20 ) {
|
||
|
K = 0x5a827999;
|
||
|
f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~
|
||
|
} else if( t < 40 ) {
|
||
|
K = 0x6ed9eba1;
|
||
|
f = b ^ c ^ d;
|
||
|
} else if( t < 60 ) {
|
||
|
K = 0x8f1bbcdc;
|
||
|
f = (b & c) | (b & d) | (c & d);
|
||
|
} else {
|
||
|
K = 0xca62c1d6;
|
||
|
f = b ^ c ^ d;
|
||
|
}
|
||
|
temp = lrot(a,5) + f + e + W[t] + K;
|
||
|
e = d;
|
||
|
d = c;
|
||
|
c = lrot(b,30);
|
||
|
b = a;
|
||
|
a = temp;
|
||
|
//printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e );
|
||
|
}
|
||
|
/* add variables */
|
||
|
H0 += a;
|
||
|
H1 += b;
|
||
|
H2 += c;
|
||
|
H3 += d;
|
||
|
H4 += e;
|
||
|
//printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 );
|
||
|
/* all bytes have been processed */
|
||
|
unprocessedBytes = 0;
|
||
|
}
|
||
|
|
||
|
// addBytes **********************************************************
|
||
|
void SHA1::addBytes( const char* data, int num )
|
||
|
{
|
||
|
assert( data );
|
||
|
assert( num > 0 );
|
||
|
// add these bytes to the running total
|
||
|
size += num;
|
||
|
// repeat until all data is processed
|
||
|
while( num > 0 )
|
||
|
{
|
||
|
// number of bytes required to complete block
|
||
|
int needed = 64 - unprocessedBytes;
|
||
|
assert( needed > 0 );
|
||
|
// number of bytes to copy (use smaller of two)
|
||
|
int toCopy = (num < needed) ? num : needed;
|
||
|
// Copy the bytes
|
||
|
memcpy( bytes + unprocessedBytes, data, toCopy );
|
||
|
// Bytes have been copied
|
||
|
num -= toCopy;
|
||
|
data += toCopy;
|
||
|
unprocessedBytes += toCopy;
|
||
|
|
||
|
// there is a full block
|
||
|
if( unprocessedBytes == 64 ) process();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// digest ************************************************************
|
||
|
unsigned char* SHA1::getDigest()
|
||
|
{
|
||
|
// save the message size
|
||
|
Uint32 totalBitsL = size << 3;
|
||
|
Uint32 totalBitsH = size >> 29;
|
||
|
// add 0x80 to the message
|
||
|
addBytes( "\x80", 1 );
|
||
|
|
||
|
unsigned char footer[64] = {
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
// block has no room for 8-byte filesize, so finish it
|
||
|
if( unprocessedBytes > 56 )
|
||
|
addBytes( (char*)footer, 64 - unprocessedBytes);
|
||
|
assert( unprocessedBytes <= 56 );
|
||
|
// how many zeros do we need
|
||
|
int neededZeros = 56 - unprocessedBytes;
|
||
|
// store file size (in bits) in big-endian format
|
||
|
storeBigEndianUint32( footer + neededZeros , totalBitsH );
|
||
|
storeBigEndianUint32( footer + neededZeros + 4, totalBitsL );
|
||
|
// finish the final block
|
||
|
addBytes( (char*)footer, neededZeros + 8 );
|
||
|
// allocate memory for the digest bytes
|
||
|
unsigned char* digest = (unsigned char*)malloc( 20 );
|
||
|
// copy the digest bytes
|
||
|
storeBigEndianUint32( digest, H0 );
|
||
|
storeBigEndianUint32( digest + 4, H1 );
|
||
|
storeBigEndianUint32( digest + 8, H2 );
|
||
|
storeBigEndianUint32( digest + 12, H3 );
|
||
|
storeBigEndianUint32( digest + 16, H4 );
|
||
|
// return the digest
|
||
|
return digest;
|
||
|
}
|