/* * Copyright (C) 2007-2010 Xagasoft, All rights reserved. * * This file is part of the libbu++ library and is released under the * terms of the license contained in the file LICENSE. */ #include #include #include #include "bu/sha1.h" Sha1::Sha1() : H0( 0x67452301 ), H1( 0xefcdab89 ), H2( 0x98badcfe ), H3( 0x10325476 ), H4( 0xc3d2e1f0 ), unprocessedBytes( 0 ), size( 0 ) { } Sha1::~Sha1() { } void Sha1::process() { int t; uint32_t 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_t temp; for( t = 0; t < 80; t++ ) { if( t < 20 ) { K = 0x5a827999; f = (b & c) | ((~b) & d); } 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; } void Sha1::update( const char* data, int num ) { // 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; // 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(); } } unsigned char* Sha1::getDigest() { // save the message size uint32_t totalBitsL = size << 3; uint32_t totalBitsH = size >> 29; // add 0x80 to the message update( "\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 ) update( (char*)footer, 64 - unprocessedBytes); // how many zeros do we need int neededZeros = 56 - unprocessedBytes; // store file size (in bits) in big-endian format toBigEndian( totalBitsH, footer + neededZeros ); toBigEndian( totalBitsL, footer + neededZeros + 4 ); // finish the final block update( (char*)footer, neededZeros + 8 ); // allocate memory for the digest bytes unsigned char* digest = new unsigned char[20]; // copy the digest bytes toBigEndian( H0, digest ); toBigEndian( H1, digest + 4 ); toBigEndian( H2, digest + 8 ); toBigEndian( H3, digest + 12 ); toBigEndian( H4, digest + 16 ); // return the digest return digest; } uint32_t Sha1::lrot( uint32_t x, int bits ) { return (x<>(32 - bits)); }; void Sha1::toBigEndian( uint32_t num, unsigned char* byte ) { byte[0] = (unsigned char)(num>>24); byte[1] = (unsigned char)(num>>16); byte[2] = (unsigned char)(num>>8); byte[3] = (unsigned char)num; }