You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
5.9 KiB
224 lines
5.9 KiB
/**
|
|
* @file sha1.cpp
|
|
*
|
|
* @brief Implementation of the SHA-1 hash.
|
|
*
|
|
* @copyright The contents of this file have been placed into the public domain;
|
|
* see the file COPYING for more details.
|
|
*/
|
|
|
|
#include "bytes.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include <cassert>
|
|
|
|
namespace CppTotp
|
|
{
|
|
|
|
static inline uint32_t lrot32(uint32_t num, uint8_t rotcount)
|
|
{
|
|
return (num << rotcount) | (num >> (32 - rotcount));
|
|
}
|
|
|
|
Bytes::ByteString sha1(const Bytes::ByteString & msg)
|
|
{
|
|
const size_t size_bytes = msg.size();
|
|
const uint64_t size_bits = size_bytes * 8;
|
|
Bytes::ByteString bstr = msg;
|
|
Bytes::ByteStringDestructor asplode(&bstr);
|
|
|
|
// the size of msg in bits is always even. adding the '1' bit will make
|
|
// it odd and therefore incongruent to 448 modulo 512, so we can get
|
|
// away with tacking on 0x80 and then the 0x00s.
|
|
bstr.push_back(0x80);
|
|
while (bstr.size() % (512/8) != (448/8))
|
|
{
|
|
bstr.push_back(0x00);
|
|
}
|
|
|
|
// append the size in bits (uint64be)
|
|
bstr.append(Bytes::u64beToByteString(size_bits));
|
|
|
|
assert(bstr.size() % (512/8) == 0);
|
|
|
|
// initialize the hash counters
|
|
uint32_t h0 = 0x67452301;
|
|
uint32_t h1 = 0xEFCDAB89;
|
|
uint32_t h2 = 0x98BADCFE;
|
|
uint32_t h3 = 0x10325476;
|
|
uint32_t h4 = 0xC3D2E1F0;
|
|
|
|
// for each 64-byte chunk
|
|
for (size_t i = 0; i < bstr.size()/64; ++i)
|
|
{
|
|
Bytes::ByteString chunk(bstr.begin() + i*64, bstr.begin() + (i+1)*64);
|
|
Bytes::ByteStringDestructor xplode(&chunk);
|
|
|
|
uint32_t words[80];
|
|
size_t j;
|
|
|
|
// 0-15: the chunk as a sequence of 32-bit big-endian integers
|
|
for (j = 0; j < 16; ++j)
|
|
{
|
|
words[j] =
|
|
(chunk[4*j + 0] << 24) |
|
|
(chunk[4*j + 1] << 16) |
|
|
(chunk[4*j + 2] << 8) |
|
|
(chunk[4*j + 3] << 0)
|
|
;
|
|
}
|
|
|
|
// 16-79: derivatives of 0-15
|
|
for (j = 16; j < 32; ++j)
|
|
{
|
|
// unoptimized
|
|
words[j] = lrot32(words[j-3] ^ words[j-8] ^ words[j-14] ^ words[j-16], 1);
|
|
}
|
|
for (j = 32; j < 80; ++j)
|
|
{
|
|
// Max Locktyuchin's optimization (SIMD)
|
|
words[j] = lrot32(words[j-6] ^ words[j-16] ^ words[j-28] ^ words[j-32], 2);
|
|
}
|
|
|
|
// initialize hash values for the round
|
|
uint32_t a = h0;
|
|
uint32_t b = h1;
|
|
uint32_t c = h2;
|
|
uint32_t d = h3;
|
|
uint32_t e = h4;
|
|
|
|
// the loop
|
|
for (j = 0; j < 80; ++j)
|
|
{
|
|
uint32_t f = 0, k = 0;
|
|
|
|
if (j < 20)
|
|
{
|
|
f = (b & c) | ((~ b) & d);
|
|
k = 0x5A827999;
|
|
}
|
|
else if (j < 40)
|
|
{
|
|
f = b ^ c ^ d;
|
|
k = 0x6ED9EBA1;
|
|
}
|
|
else if (j < 60)
|
|
{
|
|
f = (b & c) | (b & d) | (c & d);
|
|
k = 0x8F1BBCDC;
|
|
}
|
|
else if (j < 80)
|
|
{
|
|
f = b ^ c ^ d;
|
|
k = 0xCA62C1D6;
|
|
}
|
|
else
|
|
{
|
|
assert(0 && "how did I get here?");
|
|
}
|
|
|
|
uint32_t tmp = lrot32(a, 5) + f + e + k + words[j];
|
|
e = d;
|
|
d = c;
|
|
c = lrot32(b, 30);
|
|
b = a;
|
|
a = tmp;
|
|
}
|
|
|
|
// add that to the result so far
|
|
h0 += a;
|
|
h1 += b;
|
|
h2 += c;
|
|
h3 += d;
|
|
h4 += e;
|
|
}
|
|
|
|
// assemble the digest
|
|
Bytes::ByteString first = Bytes::u32beToByteString(h0);
|
|
Bytes::ByteStringDestructor x1(&first);
|
|
Bytes::ByteString second = Bytes::u32beToByteString(h1);
|
|
Bytes::ByteStringDestructor x2(&second);
|
|
Bytes::ByteString third = Bytes::u32beToByteString(h2);
|
|
Bytes::ByteStringDestructor x3(&third);
|
|
Bytes::ByteString fourth = Bytes::u32beToByteString(h3);
|
|
Bytes::ByteStringDestructor x4(&fourth);
|
|
Bytes::ByteString fifth = Bytes::u32beToByteString(h4);
|
|
Bytes::ByteStringDestructor x5(&fifth);
|
|
|
|
return first + second + third + fourth + fifth;
|
|
}
|
|
|
|
Bytes::ByteString hmacSha1(const Bytes::ByteString & key, const Bytes::ByteString & msg, size_t blockSize = 64);
|
|
|
|
Bytes::ByteString hmacSha1(const Bytes::ByteString & key, const Bytes::ByteString & msg, size_t blockSize)
|
|
{
|
|
Bytes::ByteString realKey = key;
|
|
Bytes::ByteStringDestructor asplode(&realKey);
|
|
|
|
if (realKey.size() > blockSize)
|
|
{
|
|
// resize by calculating hash
|
|
Bytes::ByteString newRealKey = sha1(realKey);
|
|
Bytes::swizzleByteStrings(&realKey, &newRealKey);
|
|
}
|
|
if (realKey.size() < blockSize)
|
|
{
|
|
// pad with zeroes
|
|
realKey.resize(blockSize, 0x00);
|
|
}
|
|
|
|
// prepare the pad keys
|
|
Bytes::ByteString innerPadKey = realKey;
|
|
Bytes::ByteStringDestructor xplodeI(&innerPadKey);
|
|
Bytes::ByteString outerPadKey = realKey;
|
|
Bytes::ByteStringDestructor xplodeO(&outerPadKey);
|
|
|
|
// transform the pad keys
|
|
for (size_t i = 0; i < realKey.size(); ++i)
|
|
{
|
|
innerPadKey[i] = innerPadKey[i] ^ 0x36;
|
|
outerPadKey[i] = outerPadKey[i] ^ 0x5c;
|
|
}
|
|
|
|
// sha1(outerPadKey + sha1(innerPadKey + msg))
|
|
Bytes::ByteString innerMsg = innerPadKey + msg;
|
|
Bytes::ByteStringDestructor xplodeIM(&innerMsg);
|
|
Bytes::ByteString innerHash = sha1(innerMsg);
|
|
Bytes::ByteStringDestructor xplodeIH(&innerHash);
|
|
Bytes::ByteString outerMsg = outerPadKey + innerHash;
|
|
Bytes::ByteStringDestructor xplodeOM(&outerMsg);
|
|
|
|
return sha1(outerMsg);
|
|
}
|
|
|
|
}
|
|
|
|
#if TEST_SHA1
|
|
int main(void)
|
|
{
|
|
using namespace CppTotp;
|
|
const uint8_t * strEmpty = reinterpret_cast<const uint8_t *>("");
|
|
const uint8_t * strDog = reinterpret_cast<const uint8_t *>("The quick brown fox jumps over the lazy dog");
|
|
const uint8_t * strCog = reinterpret_cast<const uint8_t *>("The quick brown fox jumps over the lazy cog");
|
|
const uint8_t * strKey = reinterpret_cast<const uint8_t *>("key");
|
|
|
|
Bytes::ByteString shaEmpty = sha1(Bytes::ByteString(strEmpty));
|
|
Bytes::ByteString shaDog = sha1(Bytes::ByteString(strDog));
|
|
Bytes::ByteString shaCog = sha1(Bytes::ByteString(strCog));
|
|
|
|
Bytes::ByteString hmacShaEmpty = hmacSha1(Bytes::ByteString(), Bytes::ByteString());
|
|
Bytes::ByteString hmacShaKeyDog = hmacSha1(strKey, strDog);
|
|
|
|
std::cout
|
|
<< (Bytes::toHexString(shaEmpty) == "da39a3ee5e6b4b0d3255bfef95601890afd80709") << std::endl
|
|
<< (Bytes::toHexString(shaDog) == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12") << std::endl
|
|
<< (Bytes::toHexString(shaCog) == "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3") << std::endl
|
|
<< std::endl
|
|
<< (Bytes::toHexString(hmacShaEmpty) == "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") << std::endl
|
|
<< (Bytes::toHexString(hmacShaKeyDog) == "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9") << std::endl
|
|
<< std::endl;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|