From 09c40a420317081d9815b0d691666caccbd43522 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Mon, 12 May 2025 17:42:46 +0800 Subject: [PATCH] Add SHA1 --- hash_lib.cpp | 127 ++++++++++++++++++++++++++++++++++++++++- hash_lib.h | 22 +++++++ test/hash_lib_test.cpp | 6 ++ 3 files changed, 153 insertions(+), 2 deletions(-) diff --git a/hash_lib.cpp b/hash_lib.cpp index b13b642..089f762 100644 --- a/hash_lib.cpp +++ b/hash_lib.cpp @@ -9,6 +9,8 @@ #define SHA256_DIGEST_LENGTH 32 #define SHA256_BLOCK_SIZE 64 #define SHA224_DIGEST_LENGTH 28 +#define SHA1_DIGEST_LENGTH 20 +#define SHA1_BLOCK_SIZE 64 using namespace hash_lib; @@ -84,7 +86,7 @@ void SHA512::clean() { cleanBuffer(_buffer); cleanBuffer(_tempHi); cleanBuffer(_tempLo); - _initState(); + reset(); } void SHA512::_initState() { @@ -477,7 +479,7 @@ Hash* SHA256::reset() { void SHA256::clean() { cleanBuffer(_buffer); cleanBuffer(_temp); - _initState(); + reset(); } Hash* SHA256::update(const uint8_t* data, size_t len) { @@ -598,3 +600,124 @@ void SHA224::_initState() { state[6] = 0x64f98fa7; state[7] = 0xbefa4fa4; } + +SHA1::SHA1() { + this->reset(); +} + +int SHA1::digestLength() { + return SHA1_DIGEST_LENGTH; // SHA-1 produces a 160-bit hash value (20 bytes) +} + +int SHA1::blockSize() { + return SHA1_BLOCK_SIZE; // SHA-1 processes data in 512-bit blocks (64 bytes) +} + +void SHA1::_initState() { + state[0] = 0x67452301; + state[1] = 0xEFCDAB89; + state[2] = 0x98BADCFE; + state[3] = 0x10325476; + state[4] = 0xC3D2E1F0; +} + +Hash* SHA1::reset() { + _initState(); + _bufferLength = 0; + _bytesHashed = 0; + _finished = false; + return this; +} + +void SHA1::clean() { + cleanBuffer(_buffer); + cleanBuffer(_temp); + reset(); +} + +Hash* SHA1::update(const uint8_t* data, size_t len) { + if (_finished) return this; + size_t dataPos = 0; + _bytesHashed += len; + if (_bufferLength > 0) { + while (_bufferLength < SHA1_BLOCK_SIZE && len > 0) { + _buffer[_bufferLength++] = data[dataPos++]; + len--; + } + if (_bufferLength == SHA1_BLOCK_SIZE) { + hashBlocks(_buffer, 0, SHA1_BLOCK_SIZE); + _bufferLength = 0; + } + } + if (len >= SHA1_BLOCK_SIZE) { + dataPos = hashBlocks(data, dataPos, len); + len %= SHA1_BLOCK_SIZE; + } + while (len > 0) { + _buffer[_bufferLength++] = data[dataPos++]; + len--; + } + return this; +} + +Hash* SHA1::finish(uint8_t* data, size_t len) { + if (!_finished) { + size_t bytesHashed = _bytesHashed; + size_t left = _bufferLength; + uint64_t bitLen = bytesHashed << 3; + size_t padLength = (bytesHashed % SHA1_BLOCK_SIZE) < 56 ? 64 : 128; + _buffer[left] = 0x80; + memset(_buffer + left + 1, 0, padLength - left - 9); + cstr_write_uint64(_buffer + padLength - 8, bitLen, 1); + hashBlocks(_buffer, 0, padLength); + _finished = true; + } + for (int i = 0; i < this->digestLength() / 4 && i < len / 4; i++) { + cstr_write_uint32(data + i * 4, state[i], 1); + } + return this; +} + +size_t SHA1::hashBlocks(const uint8_t* m, size_t pos, size_t len) { + while (len >= 64) { + for (int i = 0; i < 16; i++) { + size_t j = i * 4 + pos; + _temp[i] = cstr_read_uint32(m + j, 1); + } + for (int i = 16; i < 80; i++) { + uint32_t u = _temp[i - 3] ^ _temp[i - 8] ^ _temp[i - 14] ^ _temp[i - 16]; + _temp[i] = (u << 1) | (u >> (32 - 1)); + } + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; + for (int i = 0; i < 80; i++) { + uint32_t f, k; + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = ((a << 5) | (a >> (32 - 5))) + f + e + k + _temp[i]; + e = d; + d = c; + c = ((b << 30) | (b >> (32 - 30))); + b = a; + a = temp; + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + pos += 64; + len -= 64; + } + return pos; +} diff --git a/hash_lib.h b/hash_lib.h index c5a93ed..1f15e7f 100644 --- a/hash_lib.h +++ b/hash_lib.h @@ -104,6 +104,28 @@ namespace hash_lib { protected: void _initState() override; }; + class SHA1: public Hash { + public: + SHA1(); + virtual int digestLength() override; + int blockSize() override; + Hash* update(const uint8_t* data, size_t len) override; + using Hash::update; + Hash* reset() override; + Hash* finish(uint8_t* data, size_t len) override; + using Hash::finish; + void clean() override; + protected: + uint32_t state[5]; + virtual void _initState(); + private: + uint32_t _temp[80]; + uint8_t _buffer[128]; + size_t _bufferLength = 0; + size_t _bytesHashed = 0; + bool _finished = false; + size_t hashBlocks(const uint8_t* m, size_t pos, size_t len); + }; template std::vector hash(const uint8_t* data, size_t len) { H h; diff --git a/test/hash_lib_test.cpp b/test/hash_lib_test.cpp index d7550e8..d9e92a1 100644 --- a/test/hash_lib_test.cpp +++ b/test/hash_lib_test.cpp @@ -42,3 +42,9 @@ TEST(HashLibTest, SHA224Test) { GTEST_ASSERT_EQ(hashHex("Hello, World!"), "72a23dfa411ba6fde01dbfabf3b00a709c93ebf273dc29e2d8b261ff"); GTEST_ASSERT_EQ(hashHex("随便来一些中文。测试超过一百二十八字节时的状况。用于测试是否存在问题。还是不够长呢。啊啊啊。"), "6dc50e486071b01bf45d5d228207e9c0a8254e1cc48c88b989f8527d"); } + +TEST(HashLibTest, SHA1Test) { + GTEST_ASSERT_EQ(hashHex(""), "da39a3ee5e6b4b0d3255bfef95601890afd80709"); + GTEST_ASSERT_EQ(hashHex("Hello, World!"), "0a0a9f2a6772942557ab5355d76af442f8f65e01"); + GTEST_ASSERT_EQ(hashHex("随便来一些中文。测试超过一百二十八字节时的状况。用于测试是否存在问题。还是不够长呢。啊啊啊。"), "21c05e3532d593ec382b8e361d43a17e8fb8774a"); +}