From 04ee35a71bb3eaedfac08ccab42f6167cc777365 Mon Sep 17 00:00:00 2001 From: fallenoak Date: Mon, 6 Feb 2023 17:00:19 -0600 Subject: [PATCH] feat(sha1): add SHA1_InterleaveHash --- common/SHA1.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ common/SHA1.hpp | 2 ++ test/SHA1.cpp | 32 ++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/common/SHA1.cpp b/common/SHA1.cpp index 8384a2a..2caa899 100644 --- a/common/SHA1.cpp +++ b/common/SHA1.cpp @@ -1,4 +1,5 @@ #include "common/SHA1.hpp" +#include #include #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) @@ -61,6 +62,60 @@ void SHA1_Init(SHA1_CONTEXT* context) { context->count[1] = 0; } +uint8_t* SHA1_InterleaveHash(uint8_t* const digest, const uint8_t* data, uint32_t len) { + // Terminate data at first null + uint32_t l; + for (l = len; l; l--) { + if (*data) { + break; + } + + data++; + } + + if (l & 1) { + data++; + l--; + } + + SHA1_CONTEXT ctx; + uint8_t scratchDigest[SHA1_DIGEST_SIZE]; + + auto scratchLen = l / 2; + auto scratch = static_cast(alloca(scratchLen)); + if (!scratch) { + return nullptr; + } + + // Even half + for (uint32_t i = 0; i < scratchLen; i++) { + scratch[i] = data[i * 2]; + } + + SHA1_Init(&ctx); + SHA1_Update(&ctx, scratch, scratchLen); + SHA1_Final(scratchDigest, &ctx); + + for (uint32_t i = 0; i < sizeof(scratchDigest); i++) { + digest[i * 2] = scratchDigest[i]; + } + + // Odd half + for (uint32_t i = 0; i < scratchLen; i++) { + scratch[i] = data[i * 2 + 1]; + } + + SHA1_Init(&ctx); + SHA1_Update(&ctx, scratch, scratchLen); + SHA1_Final(scratchDigest, &ctx); + + for (uint32_t i = 0; i < sizeof(scratchDigest); i++) { + digest[i * 2 + 1] = scratchDigest[i]; + } + + return digest; +} + void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) { uint32_t a, b, c, d, e; typedef union { diff --git a/common/SHA1.hpp b/common/SHA1.hpp index 56606a9..3f1afae 100644 --- a/common/SHA1.hpp +++ b/common/SHA1.hpp @@ -15,6 +15,8 @@ void SHA1_Final(uint8_t* const digest, SHA1_CONTEXT* context); void SHA1_Init(SHA1_CONTEXT* context); +uint8_t* SHA1_InterleaveHash(uint8_t* digest, const uint8_t* data, uint32_t len); + void SHA1_Update(SHA1_CONTEXT* context, const uint8_t* data, uint32_t len); #endif diff --git a/test/SHA1.cpp b/test/SHA1.cpp index 5d3847e..05047bf 100644 --- a/test/SHA1.cpp +++ b/test/SHA1.cpp @@ -55,3 +55,35 @@ TEST_CASE("SHA1_Final", "[util]") { REQUIRE(memcmp(digest, expected, sizeof(digest)) == 0); } } + +TEST_CASE("SHA1_InterleaveHash", "[sha1]") { + SECTION("correctly computes interleaved digest of empty string") { + auto input = ""; + uint8_t digest[SHA1_DIGEST_SIZE * 2]; + uint8_t expected[SHA1_DIGEST_SIZE * 2] = { 0xda, 0xda, 0x39, 0x39, 0xa3, 0xa3, 0xee, 0xee, 0x5e, 0x5e, 0x6b, 0x6b, 0x4b, 0x4b, 0x0d, 0x0d, 0x32, 0x32, 0x55, 0x55, 0xbf, 0xbf, 0xef, 0xef, 0x95, 0x95, 0x60, 0x60, 0x18, 0x18, 0x90, 0x90, 0xaf, 0xaf, 0xd8, 0xd8, 0x07, 0x07, 0x09, 0x09 }; + + SHA1_InterleaveHash(digest, reinterpret_cast(input), strlen(input)); + + REQUIRE(memcmp(digest, expected, sizeof(digest)) == 0); + } + + SECTION("correctly computes interleaved digest of 'xyz'") { + auto input = "xyz"; + uint8_t digest[SHA1_DIGEST_SIZE * 2]; + uint8_t expected[SHA1_DIGEST_SIZE * 2] = { 0x95, 0x39, 0xcb, 0x5d, 0x0b, 0xf8, 0xfd, 0xf7, 0x29, 0xc5, 0x77, 0x1f, 0xc7, 0x00, 0x61, 0x70, 0x29, 0x19, 0x8d, 0xcb, 0x96, 0x30, 0x24, 0x20, 0xe4, 0x1c, 0xb4, 0x49, 0xd4, 0xe8, 0xc7, 0x84, 0x2a, 0xb4, 0x39, 0x6b, 0x97, 0x92, 0x4a, 0xfa }; + + SHA1_InterleaveHash(digest, reinterpret_cast(input), strlen(input)); + + REQUIRE(memcmp(digest, expected, sizeof(digest)) == 0); + } + + SECTION("correctly computes interleaved digest of 'foobar'") { + auto input = "foobar"; + uint8_t digest[SHA1_DIGEST_SIZE * 2]; + uint8_t expected[SHA1_DIGEST_SIZE * 2] = { 0xba, 0xdf, 0x06, 0x40, 0x23, 0x4f, 0xbd, 0x14, 0xc6, 0x5f, 0x7c, 0x1c, 0x26, 0xe8, 0xd2, 0x31, 0x05, 0x06, 0xbb, 0x50, 0x58, 0x88, 0xac, 0x33, 0xc0, 0xf5, 0xaa, 0xe3, 0x9d, 0x03, 0xe6, 0x71, 0x6b, 0xdf, 0xa6, 0x84, 0x0c, 0x40, 0x24, 0xba }; + + SHA1_InterleaveHash(digest, reinterpret_cast(input), strlen(input)); + + REQUIRE(memcmp(digest, expected, sizeof(digest)) == 0); + } +}