/* * 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/. * * Copyright 2013 Danny Robson */ #include "sha1.hpp" #include #include #include #include #include #include #include #include using cruft::crypto::hash::SHA1; /////////////////////////////////////////////////////////////////////////////// // Constant words for sequence of rounds static constexpr u32 K_00 = 0x5A827999; static constexpr u32 K_20 = 0x6ED9EBA1; static constexpr u32 K_40 = 0x8F1BBCDC; static constexpr u32 K_60 = 0xCA62C1D6; //----------------------------------------------------------------------------- static constexpr std::array INITIAL_H = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; //----------------------------------------------------------------------------- static constexpr size_t BLOCK_WORDS = 16; static constexpr size_t BLOCK_BYTES = BLOCK_WORDS * sizeof (u32); /////////////////////////////////////////////////////////////////////////////// // Logical function for sequence of rounds 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; } /////////////////////////////////////////////////////////////////////////////// static void process (std::array &H, std::array &W) { // Byteswap the raw input we have buffered ready for arithmetic std::transform (std::begin (W), std::end (W), std::begin (W), [] (u32 x) { return cruft::ntoh (x); }); // Initialise the work buffer and the state variables for (int t = 16; t < 80; ++t) W[t] = cruft::rotatel (W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); u32 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 { \ 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; \ } while (0) 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); // Update the resulting hash state H[0] += A; H[1] += B; H[2] += C; H[3] += D; H[4] += E; } /////////////////////////////////////////////////////////////////////////////// SHA1::digest_t SHA1::operator() (cruft::view data) noexcept { return (*this) (data, nullptr); } //----------------------------------------------------------------------------- SHA1::digest_t SHA1::operator() ( cruft::view data_a, cruft::view data_b ) noexcept { /* RESET */ u64 total = 0; union { std::array w; std::array b; } H; union { std::array c; std::array W; } state; H.w = INITIAL_H; /* UPDATE */ cruft::iterator::transform_by_block ( cruft::view {state.c.data (), BLOCK_BYTES }, [&] (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; total += chunk; process (H.w, state.W); } /* DIGEST */ std::transform (std::begin (H.w), std::end (H.w), std::begin (H.w), cruft::hton); return H.b; }