libcruft-util/hash/sha1.cpp

282 lines
7.5 KiB
C++
Raw Normal View History

2013-03-11 20:47:48 +11:00
/*
2015-04-13 18:05:28 +10:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2013-03-11 20:47:48 +11:00
*
2015-04-13 18:05:28 +10:00
* http://www.apache.org/licenses/LICENSE-2.0
2014-05-20 13:44:27 +10:00
*
2015-04-13 18:05:28 +10:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
2013-03-11 20:47:48 +11:00
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#include "sha1.hpp"
2013-03-11 20:47:48 +11:00
2016-04-05 11:06:01 +10:00
#include "../bitwise.hpp"
#include "../debug.hpp"
#include "../endian.hpp"
#include "../cast.hpp"
2013-03-11 20:47:48 +11:00
#include <algorithm>
#include <cstdint>
#include <limits>
2016-07-27 13:21:37 +10:00
#include <ostream>
2013-03-11 20:47:48 +11:00
using util::hash::SHA1;
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2014-07-02 15:40:20 +10:00
std::ostream&
2016-06-17 16:20:09 +10:00
operator<< (std::ostream &os, SHA1::state_t t)
{
2014-07-02 15:40:20 +10:00
switch (t) {
2016-07-27 13:21:37 +10:00
case SHA1::READY: return os << "READY";
case SHA1::FINISHED: return os << "FINISHED";
2014-07-02 15:40:20 +10:00
}
unreachable ();
2014-07-02 15:40:20 +10:00
}
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2013-03-11 20:47:48 +11:00
// Logical function for sequence of rounds
2016-06-17 16:20:09 +10:00
static constexpr
uint32_t
2013-03-11 20:47:48 +11:00
f_00 (uint32_t B, uint32_t C, uint32_t D)
2016-06-17 16:20:09 +10:00
{
return (B & C) | (~B & D);
}
2013-03-11 20:47:48 +11:00
2016-06-17 16:20:09 +10:00
//-----------------------------------------------------------------------------
static constexpr
uint32_t
2013-03-11 20:47:48 +11:00
f_20 (uint32_t B, uint32_t C, uint32_t D)
2016-06-17 16:20:09 +10:00
{
return B ^ C ^ D;
}
2013-03-11 20:47:48 +11:00
2016-06-17 16:20:09 +10:00
//-----------------------------------------------------------------------------
static constexpr
uint32_t
2013-03-11 20:47:48 +11:00
f_40 (uint32_t B, uint32_t C, uint32_t D)
2016-06-17 16:20:09 +10:00
{
return (B & C) | (B & D) | (C & D);
}
2013-03-11 20:47:48 +11:00
2016-06-17 16:20:09 +10:00
//-----------------------------------------------------------------------------
static constexpr
uint32_t
2013-03-11 20:47:48 +11:00
f_60 (uint32_t B, uint32_t C, uint32_t D)
2016-06-17 16:20:09 +10:00
{
return B ^ C ^ D;
}
2013-03-11 20:47:48 +11:00
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2013-03-11 20:47:48 +11:00
// Constant words for sequence of rounds
2016-06-17 16:20:09 +10:00
static constexpr uint32_t K_00 = 0x5A827999;
static constexpr uint32_t K_20 = 0x6ED9EBA1;
static constexpr uint32_t K_40 = 0x8F1BBCDC;
static constexpr uint32_t K_60 = 0xCA62C1D6;
2013-03-11 20:47:48 +11:00
2016-06-17 16:20:09 +10:00
static
constexpr uint32_t DEFAULT_H[] = {
2014-04-16 18:35:09 +10:00
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
};
2013-03-11 20:47:48 +11:00
2016-06-17 16:20:09 +10:00
static constexpr size_t BLOCK_WORDS = 16;
static constexpr size_t BLOCK_BYTES = BLOCK_WORDS * sizeof (uint32_t);
2014-07-02 15:38:47 +10:00
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2013-03-11 20:47:48 +11:00
SHA1::SHA1()
{
reset ();
}
2016-06-17 16:20:09 +10:00
//-----------------------------------------------------------------------------
2013-03-11 20:47:48 +11:00
void
2016-06-17 16:20:09 +10:00
SHA1::reset (void)
{
2013-03-11 20:47:48 +11:00
total = 0;
state = READY;
2014-04-16 18:35:09 +10:00
std::copy (std::begin (DEFAULT_H), std::end (DEFAULT_H), H);
2013-03-11 20:47:48 +11:00
}
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
void
2016-06-17 16:20:09 +10:00
SHA1::update (
const uint8_t *restrict first,
const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
update (first, last - first);
}
2014-07-02 15:38:47 +10:00
//-----------------------------------------------------------------------------
2013-03-11 20:47:48 +11:00
void
SHA1::update (const void *restrict _data, size_t size) noexcept
2016-06-17 16:20:09 +10:00
{
CHECK (_data);
CHECK_EQ (+state, +READY);
2016-06-17 16:20:09 +10:00
CHECK_GE (std::numeric_limits<decltype(total)>::max () - total, size);
2013-03-11 20:47:48 +11:00
auto data = static_cast<const uint8_t *restrict> (_data);
2013-03-11 20:47:48 +11:00
while (size > 0) {
2014-04-16 18:35:09 +10:00
// Copy the data into the remaining available buffer slots
const size_t offset = total % BLOCK_BYTES;
const size_t chunk = std::min (BLOCK_BYTES - offset, size);
2014-05-20 13:44:27 +10:00
2014-04-16 18:35:09 +10:00
std::copy (data, data + chunk, c + offset);
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
total += chunk;
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
// Attempt to process if full
if (total % BLOCK_BYTES == 0)
2013-03-11 20:47:48 +11:00
process ();
2014-04-16 18:35:09 +10:00
size -= chunk;
data += chunk;
2013-03-11 20:47:48 +11:00
}
}
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2013-03-11 20:47:48 +11:00
void
2016-06-17 16:20:09 +10:00
SHA1::process (void)
{
CHECK_EQ (total % BLOCK_BYTES, 0u);
2014-04-16 18:35:09 +10:00
// Byteswap the raw input we have buffered ready for arithmetic
std::transform (std::begin (W),
std::end (W),
std::begin (W),
[] (uint32_t x) {
return ntoh (x);
});
// Initialise the work buffer and the state variables
2013-03-11 20:47:48 +11:00
for (size_t t = 16; t < 80; ++t)
W[t] = rotatel (W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
uint32_t A = H[0],
B = H[1],
C = H[2],
D = H[3],
E = H[4];
// Perform each of the four rounds
#define ROTATE_STATE(i) do { \
uint32_t temp = rotatel (A, 5) + f_##i (B, C, D) + E + W[t] + K_##i; \
E = D; \
D = C; \
C = rotatel (B, 30); \
B = A; \
A = temp; \
} while (0)
for (size_t t = 0; t < 20; ++t) ROTATE_STATE(00);
for (size_t t = 20; t < 40; ++t) ROTATE_STATE(20);
for (size_t t = 40; t < 60; ++t) ROTATE_STATE(40);
for (size_t t = 60; t < 80; ++t) ROTATE_STATE(60);
2014-04-16 18:35:09 +10:00
// Update the resulting hash state
2013-03-11 20:47:48 +11:00
H[0] += A;
H[1] += B;
H[2] += C;
H[3] += D;
H[4] += E;
}
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2013-03-11 20:47:48 +11:00
void
2016-06-17 16:20:09 +10:00
SHA1::finish (void)
{
2014-04-16 18:35:09 +10:00
size_t offset = total % BLOCK_BYTES;
size_t used = total * 8;
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
// Append a single one bit
c[offset++] = 0x80;
total += 1;
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
// Zero fill if we can't append length
size_t chunk = BLOCK_BYTES - offset;
if (chunk < sizeof (total)) {
std::fill_n (c + offset, chunk, 0);
total += chunk;
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
process ();
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
chunk = BLOCK_BYTES;
offset = 0;
}
2013-03-11 20:47:48 +11:00
2014-04-16 18:35:09 +10:00
// Zero fill and append total length
std::fill_n (c + offset, chunk - sizeof (total), 0);
c[BLOCK_BYTES - 1] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 2] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 3] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 4] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 5] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 6] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 7] = used & 0xFF; used >>= 8;
2014-05-20 13:44:36 +10:00
c[BLOCK_BYTES - 8] = used & 0xFF;
2014-04-16 18:35:09 +10:00
total += chunk;
2013-03-11 20:47:48 +11:00
process ();
state = FINISHED;
}
2016-06-17 16:20:09 +10:00
///////////////////////////////////////////////////////////////////////////////
2013-03-11 20:47:48 +11:00
SHA1::digest_t
2016-06-17 16:20:09 +10:00
SHA1::digest (void) const
{
CHECK_EQ (+state, +FINISHED);
2013-03-11 20:47:48 +11:00
return { {
trunc_cast<uint8_t> ((H[0] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[0] ) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[1] ) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[2] ) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[3] ) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[4] ) & 0xFF)
2013-03-11 20:47:48 +11:00
} };
}