/* * 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 "md4.hpp" #include #include #include #include using cruft::crypto::hash::MD4; /////////////////////////////////////////////////////////////////////////////// // Auxiliary functions for each set of rounds static constexpr u32 F (u32 X, u32 Y, u32 Z) { return (X & Y) | (~X & Z); } static constexpr u32 G (u32 X, u32 Y, u32 Z) { return (X & Y) | (X & Z) | (Y & Z); } static constexpr u32 H (u32 X, u32 Y, u32 Z) { return X ^ Y ^ Z; } //----------------------------------------------------------------------------- static constexpr u32 INITIAL_A = 0x67452301; static constexpr u32 INITIAL_B = 0xefcdab89; static constexpr u32 INITIAL_C = 0x98badcfe; static constexpr u32 INITIAL_D = 0x10325476; /////////////////////////////////////////////////////////////////////////////// static void transform (std::array &ABCD, const std::array &X) noexcept { u32 A = ABCD[0], B = ABCD[1], C = ABCD[2], D = ABCD[3]; #define ROUND1(a,b,c,d,k,s) do { \ (a) += F((b), (c), (d)) + X[k]; \ (a) = cruft::rotatel ((a), (s)); \ } while (0) ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); #define ROUND2(a,b,c,d,k,s) do { \ (a) += G((b),(c),(d)) + X[k] + 0x5A827999u; \ (a) = cruft::rotatel ((a), (s)); \ } while (0) ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); #define ROUND3(a,b,c,d,k,s) do { \ (a) += H((b),(c),(d)) + X[k] + 0x6ED9EBA1u; \ (a) = cruft::rotatel ((a), (s)); \ } while (0) ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); ABCD[0] += A; ABCD[1] += B; ABCD[2] += C; ABCD[3] += D; } /////////////////////////////////////////////////////////////////////////////// MD4::digest_t MD4::operator() (cruft::view data) noexcept { /* RESET */ u64 total = 0; std::array ABCD { INITIAL_A, INITIAL_B, INITIAL_C, INITIAL_D, }; union { std::array X; std::array Xb; }; static_assert (sizeof (X) == sizeof (Xb)); static_assert (sizeof (ABCD) == sizeof (digest_t)); std::fill (std::begin (X), std::end (X), 0); /* UPDATE */ { auto remain = data; while (remain.size () >= sizeof (Xb)) { std::copy_n (std::begin (remain), sizeof (Xb), std::begin (Xb)); transform (ABCD, X); remain = { remain.begin () + sizeof (Xb), remain.end () }; total += sizeof (Xb); } std::copy (std::begin (remain), std::end (remain), std::begin (Xb)); total += remain.size (); } u64 bits = total * 8; /* FINISH */ { // Pad with the mandatory 1 bit size_t offset = total % sizeof (Xb); Xb[offset] = 0x80; } { // Pad the remainder with 0's, until 56 bytes size_t offset = (total + 1) % sizeof (Xb); // HACK: This is never used, so I've no idea what's happening here. // Marking as unused so that we don't run into -Werror issues for // more critical code. Sorry... size_t remain [[maybe_unused]] = (56 - offset % sizeof (Xb)) % sizeof (Xb); if (offset > 56) { std::fill_n (std::begin (Xb) + offset, sizeof (Xb) - offset, 0); transform (ABCD, X); remain -= sizeof (Xb) - offset; offset = 0; } std::fill (std::begin (Xb) + offset, std::end (Xb), 0); // Put in the length (in bits) least significant first for (size_t i = 0; i < sizeof (bits); ++i) { Xb[56 + i] = bits & 0xFF; bits >>= 8; } transform (ABCD, X); } /* DIGEEST */ digest_t d; memcpy (d.data (), ABCD.data(), sizeof (ABCD)); return d; }