#include <algorithm>
#include <array>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class BitInputStream;
class StringBitInputStream;
extern shared_ptr<array<unsigned long, 5> > computeHashBySHA1(const shared_ptr<BitInputStream>& msg);
class BitInputStream {
public:
virtual bool getBit() = 0;
virtual bool isEOF() = 0;
};
class StringBitInputStream : public BitInputStream {
public:
StringBitInputStream(const string& str);
virtual bool getBit();
virtual bool isEOF();
protected:
string str;
size_t str_pos;
unsigned int ch_pos;
};
#define ASSERT(pred) assert(#pred, pred);
extern void assert(const char* pred_str, const bool& pred);
typedef unsigned long (*FUNCTION_IN_SHA1)(const unsigned long& x, const unsigned long& y, const unsigned long& z);
static unsigned long rotateLeft(const unsigned long& x, const unsigned int& n);
static unsigned long chInSHA1(const unsigned long& x, const unsigned long& y, const unsigned long& z);
static unsigned long parityInSHA1(const unsigned long& x, const unsigned long& y, const unsigned long& z);
static unsigned long majInSHA1(const unsigned long& x, const unsigned long& y, const unsigned long& z);
static const FUNCTION_IN_SHA1 FUNCTIONS_IN_SHA1[] = {
chInSHA1,
parityInSHA1,
majInSHA1,
parityInSHA1,
};
static const unsigned long K_IN_SHA1[] = {
0x5a827999,
0x6ed9eba1,
0x8f1bbcdc,
0xca62c1d6,
};
static const unsigned long INITIAL_HASH_VALUES_IN_SHA1[] = {
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
0xc3d2e1f0,
};
shared_ptr<array<unsigned long, 5> > computeHashBySHA1(const shared_ptr<BitInputStream>& msg) {
shared_ptr<array<unsigned long, 5> > hash(new array<unsigned long, 5>);
copy(INITIAL_HASH_VALUES_IN_SHA1, INITIAL_HASH_VALUES_IN_SHA1 + 5, &hash->front());
shared_ptr<array<unsigned long, 16> > block(new array<unsigned long, 16>);
shared_ptr<array<unsigned long, 80> > sche(new array<unsigned long, 80>);
unsigned long long len = 0;
bool put_end = false;
bool put_len = false;
while (!put_len) {
block->fill(0);
unsigned int block_pos = 0;
for (; block_pos < 512 && !msg->isEOF(); block_pos++) {
block->at(block_pos >> 5) |= (msg->getBit() == true ? 1 : 0) << (31 - (block_pos & 0x1F));
len++;
}
if (block_pos < 512) {
if (!put_end) {
block->at(block_pos >> 5) |= 1 << (31 - (block_pos & 0x1F));
block_pos++;
put_end = true;
}
if (block_pos + 64 <= 512) {
block->at(14) = len >> 32;
block->at(15) = len & 0xFFFFFFFF;
put_len = true;
}
}
for (unsigned int i = 0; i < 80; i++) {
if (i < 16) sche->at(i) = block->at(i);
else sche->at(i) = rotateLeft(sche->at(i - 3) ^ sche->at(i - 8) ^ sche->at(i - 14) ^ sche->at(i - 16), 1);
}
unsigned long a = hash->at(0);
unsigned long b = hash->at(1);
unsigned long c = hash->at(2);
unsigned long d = hash->at(3);
unsigned long e = hash->at(4);
for (unsigned int i = 0; i < 80; i++) {
unsigned long t = 0;
t += rotateLeft(a, 5);
t += FUNCTIONS_IN_SHA1[i / 20](b, c, d);
t += e;
t += K_IN_SHA1[i / 20];
t += sche->at(i);
e = d;
d = c;
c = rotateLeft(b, 30);
b = a;
a = t;
}
hash->at(0) += a;
hash->at(1) += b;
hash->at(2) += c;
hash->at(3) += d;
hash->at(4) += e;
}
return hash;
}
unsigned long rotateLeft(const unsigned long& x, const unsigned int& n) {
return (x << n) | (x >> (32 - n));
}
unsigned long chInSHA1(const unsigned long& x, const unsigned long& y, const unsigned long& z) {
return (x & y) ^ (~x & z);
}
unsigned long parityInSHA1(const unsigned long& x, const unsigned long& y, const unsigned long& z) {
return x ^ y ^ z;
}
unsigned long majInSHA1(const unsigned long& x, const unsigned long& y, const unsigned long& z) {
return (x & y) ^ (x & z) ^ (y & z);
}
StringBitInputStream::StringBitInputStream(const string& str) : str(str), str_pos(0), ch_pos(7) {}
bool StringBitInputStream::getBit() {
bool res = ((this->str.at(this->str_pos) >> this->ch_pos) & 1) == 1;
if (this->ch_pos == 0) {
this->str_pos++;
this->ch_pos = 7;
}
else this->ch_pos--;
return res;
}
bool StringBitInputStream::isEOF() {
return this->str_pos >= this->str.length();
}
void assert(const char* pred_str, const bool& pred) {
if (pred) {
cout << "アサート成功: " << pred_str << endl;
}
else {
cerr << "アサート失敗: " << pred_str << endl;
exit(1);
}
}
int main() {
{
shared_ptr<array<unsigned long, 5> > hash = computeHashBySHA1(shared_ptr<BitInputStream>(new StringBitInputStream(
"abc")));
ASSERT(hash->at(0) == 0xa9993e36)
ASSERT(hash->at(1) == 0x4706816a)
ASSERT(hash->at(2) == 0xba3e2571)
ASSERT(hash->at(3) == 0x7850c26c)
ASSERT(hash->at(4) == 0x9cd0d89d)
}
{
shared_ptr<array<unsigned long, 5> > hash = computeHashBySHA1(shared_ptr<BitInputStream>(new StringBitInputStream(
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")));
ASSERT(hash->at(0) == 0x84983e44)
ASSERT(hash->at(1) == 0x1c3bd26e)
ASSERT(hash->at(2) == 0xbaae4aa1)
ASSERT(hash->at(3) == 0xf95129e5)
ASSERT(hash->at(4) == 0xe54670f1)
}
return 0;
}