/*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include "sha1hashgen.h" #include "functions.h" namespace bt { static inline Uint32 LeftRotate(Uint32 x,Uint32 n) { return ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))); } SHA1HashGen::SHA1HashGen() : tmp_len(0),total_len(0) { } SHA1HashGen::~SHA1HashGen() {} SHA1Hash SHA1HashGen::generate(const Uint8* data,Uint32 len) { h0 = 0x67452301; h1 = 0xEFCDAB89; h2 = 0x98BADCFE; h3 = 0x10325476; h4 = 0xC3D2E1F0; Uint32 num_64_byte_chunks = len / 64; Uint32 left_over = len % 64; // proces regular data for (Uint32 i = 0;i < num_64_byte_chunks;i++) { processChunk(data + (64*i)); } // calculate the low and high byte of the data length Uint32 total[2] = {0,0}; total[0] += len; total[0] &= 0xFFFFFFFF; if (total[0] < len) total[1]++; Uint32 high = ( total[0] >> 29 ) | ( total[1] << 3 ); Uint32 low = ( total[0] << 3 ); if (left_over == 0) { tmp[0] = 0x80; for (Uint32 i = 1;i < 56;i++) tmp[i] = 0; // put in the length as 64-bit integer (BIG-ENDIAN) WriteUint32(tmp,56,high); WriteUint32(tmp,60,low); // process the padding processChunk(tmp); } else if (left_over < 56) { Uint32 off = num_64_byte_chunks * 64; // copy left over bytes in tmp memcpy(tmp,data + off, left_over); tmp[left_over] = 0x80; for (Uint32 i = left_over + 1;i < 56;i++) tmp[i] = 0; // put in the length as 64-bit integer (BIG-ENDIAN) WriteUint32(tmp,56,high); WriteUint32(tmp,60,low); // process the padding processChunk(tmp); } else { // now we need to process 2 chunks Uint32 off = num_64_byte_chunks * 64; // copy left over bytes in tmp memcpy(tmp,data + off, left_over); tmp[left_over] = 0x80; for (Uint32 i = left_over + 1;i < 64;i++) tmp[i] = 0; // process first chunk processChunk(tmp); for (Uint32 i = 0;i < 56;i++) tmp[i] = 0; // put in the length as 64-bit integer (BIG-ENDIAN) WriteUint32(tmp,56,high); WriteUint32(tmp,60,low); // process the second chunk processChunk(tmp); } // construct final message Uint8 hash[20]; WriteUint32(hash,0,h0); WriteUint32(hash,4,h1); WriteUint32(hash,8,h2); WriteUint32(hash,12,h3); WriteUint32(hash,16,h4); return SHA1Hash(hash); } void SHA1HashGen::processChunk(const Uint8* chunk) { Uint32 w[80]; for (int i = 0;i < 80;i++) { if (i < 16) { w[i] = ntohl(*(const Uint32*)(chunk + (4*i))); /* w[i] = (chunk[4*i] << 24) | (chunk[4*i + 1] << 16) | (chunk[4*i + 2] << 8) | chunk[4*i + 3]; */ } else { w[i] = LeftRotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16],1); } } Uint32 a = h0; Uint32 b = h1; Uint32 c = h2; Uint32 d = h3; Uint32 e = h4; for (int i = 0;i < 80;i++) { Uint32 f,k; if (i < 20) { f = (b & c) | ((~b) & d); k = 0x5A827999; } else if (i < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1; } else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; } else { f = b ^ c ^ d; k = 0xCA62C1D6; } Uint32 temp = LeftRotate(a,5) + f + e + k + w[i]; e = d; d = c; c = LeftRotate(b,30); b = a; a = temp; } h0 = (h0 + a) & 0xffffffff; h1 = (h1 + b) & 0xffffffff; h2 = (h2 + c) & 0xffffffff; h3 = (h3 + d) & 0xffffffff; h4 = (h4 + e) & 0xffffffff; } void SHA1HashGen::start() { h0 = 0x67452301; h1 = 0xEFCDAB89; h2 = 0x98BADCFE; h3 = 0x10325476; h4 = 0xC3D2E1F0; tmp_len = total_len = 0; memset(tmp,0,64); } void SHA1HashGen::update(const Uint8* data,Uint32 len) { if (tmp_len == 0) { Uint32 num_64_byte_chunks = len / 64; Uint32 left_over = len % 64; // proces data in chunks of 64 byte for (Uint32 i = 0;i < num_64_byte_chunks;i++) { processChunk(data + (64*i)); } if (left_over > 0) { // if there is anything left over, copy it in tmp memcpy(tmp,data + (64 * num_64_byte_chunks),left_over); tmp_len = left_over; } total_len += len; } else { if (tmp_len + len < 64) { // special case, not enough of data to fill tmp completely memcpy(tmp + tmp_len,data,len); tmp_len += len; total_len += len; } else { // copy start of data in tmp and process it Uint32 off = 64 - tmp_len; memcpy(tmp + tmp_len,data, 64 - tmp_len); processChunk(tmp); tmp_len = 0; Uint32 num_64_byte_chunks = (len - off) / 64; Uint32 left_over = (len - off) % 64; for (Uint32 i = 0;i < num_64_byte_chunks;i++) { processChunk(data + (off + (64*i))); } if (left_over > 0) { // if there is anything left over, copy it in tmp memcpy(tmp,data + (off + 64 * num_64_byte_chunks),left_over); tmp_len = left_over; } total_len += len; } } } void SHA1HashGen::end() { // calculate the low and high byte of the data length Uint32 total[2] = {0,0}; total[0] += total_len; total[0] &= 0xFFFFFFFF; if (total[0] < total_len) total[1]++; Uint32 high = ( total[0] >> 29 ) | ( total[1] << 3 ); Uint32 low = ( total[0] << 3 ); if (tmp_len == 0) { tmp[0] = 0x80; for (Uint32 i = 1;i < 56;i++) tmp[i] = 0; // put in the length as 64-bit integer (BIG-ENDIAN) WriteUint32(tmp,56,high); WriteUint32(tmp,60,low); // process the padding processChunk(tmp); } else if (tmp_len < 56) { tmp[tmp_len] = 0x80; for (Uint32 i = tmp_len + 1;i < 56;i++) tmp[i] = 0; // put in the length as 64-bit integer (BIG-ENDIAN) WriteUint32(tmp,56,high); WriteUint32(tmp,60,low); // process the padding processChunk(tmp); } else { // now we need to process 2 chunks tmp[tmp_len] = 0x80; for (Uint32 i = tmp_len + 1;i < 56;i++) tmp[i] = 0; // process first chunk processChunk(tmp); for (Uint32 i = 0;i < 56;i++) tmp[i] = 0; // put in the length as 64-bit integer (BIG-ENDIAN) WriteUint32(tmp,56,high); WriteUint32(tmp,60,low); // process the second chunk processChunk(tmp); } } SHA1Hash SHA1HashGen::get() const { // construct final message Uint8 hash[20]; WriteUint32(hash,0,h0); WriteUint32(hash,4,h1); WriteUint32(hash,8,h2); WriteUint32(hash,12,h3); WriteUint32(hash,16,h4); return SHA1Hash(hash); } }