libcruft-crypto/hash/sha1.cpp

181 lines
5.4 KiB
C++
Raw Normal View History

2018-01-14 17:17:34 +11:00
/*
2018-08-04 15:18:16 +10:00
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
2018-01-14 17:17:34 +11:00
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#include "sha1.hpp"
#include <cruft/util/iterator.hpp>
#include <cruft/util/bitwise.hpp>
#include <cruft/util/debug.hpp>
#include <cruft/util/endian.hpp>
#include <cruft/util/cast.hpp>
#include <algorithm>
#include <cstdint>
#include <limits>
using cruft::crypto::hash::SHA1;
///////////////////////////////////////////////////////////////////////////////
// Constant words for sequence of rounds
2019-02-04 20:46:57 +11:00
static constexpr u32 K_00 = 0x5A827999;
static constexpr u32 K_20 = 0x6ED9EBA1;
static constexpr u32 K_40 = 0x8F1BBCDC;
static constexpr u32 K_60 = 0xCA62C1D6;
2018-01-14 17:17:34 +11:00
//-----------------------------------------------------------------------------
static constexpr
2019-02-04 20:46:57 +11:00
std::array<u32,5> INITIAL_H = {
2018-01-14 17:17:34 +11:00
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
};
//-----------------------------------------------------------------------------
static constexpr size_t BLOCK_WORDS = 16;
2019-02-04 20:46:57 +11:00
static constexpr size_t BLOCK_BYTES = BLOCK_WORDS * sizeof (u32);
2018-01-14 17:17:34 +11:00
///////////////////////////////////////////////////////////////////////////////
// Logical function for sequence of rounds
2019-02-04 20:46:57 +11:00
static constexpr u32 f_00 (u32 B, u32 C, u32 D) { return (B & C) | (~B & D); }
static constexpr u32 f_20 (u32 B, u32 C, u32 D) { return B ^ C ^ D; }
static constexpr u32 f_40 (u32 B, u32 C, u32 D) { return (B & C) | (B & D) | (C & D); }
static constexpr u32 f_60 (u32 B, u32 C, u32 D) { return B ^ C ^ D; }
2018-01-14 17:17:34 +11:00
///////////////////////////////////////////////////////////////////////////////
static void
2019-02-04 20:46:57 +11:00
process (std::array<u32,5> &H, std::array<u32,16+64> &W)
2018-01-14 17:17:34 +11:00
{
// Byteswap the raw input we have buffered ready for arithmetic
std::transform (std::begin (W),
std::end (W),
std::begin (W),
2019-02-04 20:46:57 +11:00
[] (u32 x) {
2018-08-05 14:51:17 +10:00
return cruft::ntoh (x);
2018-01-14 17:17:34 +11:00
});
// Initialise the work buffer and the state variables
2019-02-04 20:46:57 +11:00
for (int t = 16; t < 80; ++t)
2018-08-05 14:51:17 +10:00
W[t] = cruft::rotatel (W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
2018-01-14 17:17:34 +11:00
2019-02-04 20:46:57 +11:00
u32 A = H[0],
2018-01-14 17:17:34 +11:00
B = H[1],
C = H[2],
D = H[3],
E = H[4];
// Perform each of the four rounds
2019-02-04 20:46:57 +11:00
#define ROTATE_STATE(i) do { \
u32 temp = cruft::rotatel (A, 5) + f_##i (B, C, D) + E + W[t] + K_##i; \
E = D; \
D = C; \
C = cruft::rotatel (B, 30); \
B = A; \
A = temp; \
2018-01-14 17:17:34 +11:00
} while (0)
2019-02-04 20:46:57 +11:00
for (int t = 0; t < 20; ++t) ROTATE_STATE(00);
for (int t = 20; t < 40; ++t) ROTATE_STATE(20);
for (int t = 40; t < 60; ++t) ROTATE_STATE(40);
for (int t = 60; t < 80; ++t) ROTATE_STATE(60);
2018-01-14 17:17:34 +11:00
// Update the resulting hash state
H[0] += A;
H[1] += B;
H[2] += C;
H[3] += D;
H[4] += E;
}
///////////////////////////////////////////////////////////////////////////////
SHA1::digest_t
2019-02-04 20:46:57 +11:00
SHA1::operator() (cruft::view<const u08*> data) noexcept
2018-01-14 17:17:34 +11:00
{
return (*this) (data, nullptr);
}
//-----------------------------------------------------------------------------
SHA1::digest_t
SHA1::operator() (
2019-02-04 20:46:57 +11:00
cruft::view<const u08*> data_a,
cruft::view<const u08*> data_b
2018-01-14 17:17:34 +11:00
) noexcept {
/* RESET */
2019-02-04 20:46:57 +11:00
u64 total = 0;
2018-01-14 17:17:34 +11:00
union {
2019-02-04 20:46:57 +11:00
std::array<u32,5> w;
std::array<u08,20> b;
2018-01-14 17:17:34 +11:00
} H;
union {
2019-02-04 20:46:57 +11:00
std::array<u08, 16*4+64*4> c;
std::array<u32, 16 +64 > W;
2018-01-14 17:17:34 +11:00
} state;
H.w = INITIAL_H;
/* UPDATE */
transform_by_block (
2018-08-05 14:51:17 +10:00
cruft::view {state.c.data (), BLOCK_BYTES },
2018-01-14 17:17:34 +11:00
[&] (auto) { process (H.w, state.W); },
data_a, data_b
);
total += data_a.size () + data_b.size ();
/* FINISH */
{
size_t offset = total % BLOCK_BYTES;
size_t used = total * 8;
// Append a single one bit
state.c[offset++] = 0x80;
total += 1;
// Zero fill if we can't append length
size_t chunk = BLOCK_BYTES - offset;
if (chunk < sizeof (total)) {
std::fill_n (std::begin (state.c) + offset, chunk, 0);
total += chunk;
process (H.w, state.W);
chunk = BLOCK_BYTES;
offset = 0;
}
// Zero fill and append total length
std::fill_n (std::begin (state.c) + offset, chunk - sizeof (total), 0);
state.c[BLOCK_BYTES - 1] = (used >> 0 * 8) & 0xFF;
state.c[BLOCK_BYTES - 2] = (used >> 1 * 8) & 0xFF;
state.c[BLOCK_BYTES - 3] = (used >> 2 * 8) & 0xFF;
state.c[BLOCK_BYTES - 4] = (used >> 3 * 8) & 0xFF;
state.c[BLOCK_BYTES - 5] = (used >> 4 * 8) & 0xFF;
state.c[BLOCK_BYTES - 6] = (used >> 5 * 8) & 0xFF;
state.c[BLOCK_BYTES - 7] = (used >> 6 * 8) & 0xFF;
state.c[BLOCK_BYTES - 8] = (used >> 7 * 8) & 0xFF;
2018-01-14 17:17:34 +11:00
total += chunk;
process (H.w, state.W);
}
/* DIGEST */
2019-02-04 20:46:57 +11:00
std::transform (std::begin (H.w), std::end (H.w), std::begin (H.w), cruft::hton<u32>);
2018-01-14 17:17:34 +11:00
return H.b;
}