diff --git a/Libraries/LibCrypto/Hash/SHA1.cpp b/Libraries/LibCrypto/Hash/SHA1.cpp new file mode 100644 index 00000000000..c6afad79b08 --- /dev/null +++ b/Libraries/LibCrypto/Hash/SHA1.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace Crypto { +namespace Hash { + +inline static constexpr auto ROTATE_LEFT(u32 value, size_t bits) +{ + return (value << bits) | (value >> (32 - bits)); +} + +inline void SHA1::transform(const u8* data) +{ + u32 blocks[80]; + for (size_t i = 0; i < 16; ++i) + blocks[i] = convert_between_host_and_network(((const u32*)data)[i]); + + // w[i] = (w[i-3] xor w[i-8] xor w[i-14] xor w[i-16]) leftrotate 1 + for (size_t i = 16; i < Rounds; ++i) + blocks[i] = ROTATE_LEFT(blocks[i - 3] ^ blocks[i - 8] ^ blocks[i - 14] ^ blocks[i - 16], 1); + + auto a = m_state[0], b = m_state[1], c = m_state[2], d = m_state[3], e = m_state[4]; + u32 f, k; + + for (size_t i = 0; i < Rounds; ++i) { + if (i <= 19) { + f = (b & c) | ((~b) & d); + k = SHA1Constants::RoundConstants[0]; + } else if (i <= 39) { + f = b ^ c ^ d; + k = SHA1Constants::RoundConstants[1]; + } else if (i <= 59) { + f = (b & c) | (b & d) | (c & d); + k = SHA1Constants::RoundConstants[2]; + } else { + f = b ^ c ^ d; + k = SHA1Constants::RoundConstants[3]; + } + auto temp = ROTATE_LEFT(a, 5) + f + e + k + blocks[i]; + e = d; + d = c; + c = ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + m_state[0] += a; + m_state[1] += b; + m_state[2] += c; + m_state[3] += d; + m_state[4] += e; + + // "security" measures, as if SHA1 is secure + a = 0; + b = 0; + c = 0; + d = 0; + e = 0; + __builtin_memset(blocks, 0, 16 * sizeof(u32)); +} + +void SHA1::update(const u8* message, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + if (m_data_length == BlockSize) { + transform(m_data_buffer); + m_bit_length += 512; + m_data_length = 0; + } + m_data_buffer[m_data_length++] = message[i]; + } +} + +SHA1::DigestType SHA1::digest() +{ + auto digest = peek(); + reset(); + return digest; +} + +SHA1::DigestType SHA1::peek() +{ + DigestType digest; + size_t i = m_data_length; + + // make a local copy of the data as we modify it + u8 data[BlockSize]; + u32 state[5]; + __builtin_memcpy(data, m_data_buffer, m_data_length); + __builtin_memcpy(state, m_state, 20); + + if (m_data_length < FinalBlockDataSize) { + m_data_buffer[i++] = 0x80; + while (i < FinalBlockDataSize) + m_data_buffer[i++] = 0x00; + + } else { + m_data_buffer[i++] = 0x80; + while (i < BlockSize) + m_data_buffer[i++] = 0x00; + + transform(m_data_buffer); + __builtin_memset(m_data_buffer, 0, FinalBlockDataSize); + } + + // append total message length + m_bit_length += m_data_length * 8; + m_data_buffer[BlockSize - 1] = m_bit_length; + m_data_buffer[BlockSize - 2] = m_bit_length >> 8; + m_data_buffer[BlockSize - 3] = m_bit_length >> 16; + m_data_buffer[BlockSize - 4] = m_bit_length >> 24; + m_data_buffer[BlockSize - 5] = m_bit_length >> 32; + m_data_buffer[BlockSize - 6] = m_bit_length >> 40; + m_data_buffer[BlockSize - 7] = m_bit_length >> 48; + m_data_buffer[BlockSize - 8] = m_bit_length >> 56; + + transform(m_data_buffer); + + for (size_t i = 0; i < 4; ++i) { + digest.data[i + 0] = (m_state[0] >> (24 - i * 8)) & 0x000000ff; + digest.data[i + 4] = (m_state[1] >> (24 - i * 8)) & 0x000000ff; + digest.data[i + 8] = (m_state[2] >> (24 - i * 8)) & 0x000000ff; + digest.data[i + 12] = (m_state[3] >> (24 - i * 8)) & 0x000000ff; + digest.data[i + 16] = (m_state[4] >> (24 - i * 8)) & 0x000000ff; + } + // restore the data + __builtin_memcpy(m_data_buffer, data, m_data_length); + __builtin_memcpy(m_state, state, 20); + return digest; +} + +} +} diff --git a/Libraries/LibCrypto/Hash/SHA1.h b/Libraries/LibCrypto/Hash/SHA1.h new file mode 100644 index 00000000000..fd15c6b962c --- /dev/null +++ b/Libraries/LibCrypto/Hash/SHA1.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace Crypto { +namespace Hash { + +namespace SHA1Constants { + +constexpr static u32 InitializationHashes[5] { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; + +constexpr static u32 RoundConstants[4] { + 0X5a827999, + 0X6ed9eba1, + 0X8f1bbcdc, + 0Xca62c1d6, +}; + +} + +template +struct SHA1Digest { + u8 data[Bytes]; +}; + +class SHA1 final : public HashFunction<512, SHA1Digest<160 / 8>> { +public: + SHA1() + { + reset(); + } + + virtual void update(const u8*, size_t) override; + + virtual void update(const ByteBuffer& buffer) override { update(buffer.data(), buffer.size()); }; + virtual void update(const StringView& string) override { update((const u8*)string.characters_without_null_termination(), string.length()); }; + + virtual DigestType digest() override; + virtual DigestType peek() override; + + inline static DigestType hash(const u8* data, size_t length) + { + SHA1 sha; + sha.update(data, length); + return sha.digest(); + } + + inline static DigestType hash(const ByteBuffer& buffer) { return hash(buffer.data(), buffer.size()); } + inline static DigestType hash(const StringView& buffer) { return hash((const u8*)buffer.characters_without_null_termination(), buffer.length()); } + + virtual String class_name() const override + { + return "SHA1"; + }; + inline virtual void reset() override + { + for (auto i = 0; i < 5; ++i) + m_state[i] = SHA1Constants::InitializationHashes[i]; + } + +private: + inline void transform(const u8*); + + u8 m_data_buffer[BlockSize]; + size_t m_data_length { 0 }; + + u64 m_bit_length { 0 }; + u32 m_state[5]; + + constexpr static auto FinalBlockDataSize = BlockSize - 8; + constexpr static auto Rounds = 80; +}; + +} +} diff --git a/Libraries/LibCrypto/Makefile b/Libraries/LibCrypto/Makefile index bda1e36c980..4d35735b3d2 100644 --- a/Libraries/LibCrypto/Makefile +++ b/Libraries/LibCrypto/Makefile @@ -1,6 +1,7 @@ LIBCRYPTO_OBJS = \ Cipher/AES.o \ Hash/MD5.o \ + Hash/SHA1.o \ Hash/SHA2.o \ PK/RSA.o \ BigInt/UnsignedBigInteger.o diff --git a/Userland/test-crypto.cpp b/Userland/test-crypto.cpp index 5fc18b7e024..f148527d425 100644 --- a/Userland/test-crypto.cpp +++ b/Userland/test-crypto.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ int aes_cbc_tests(); // Hash int md5_tests(); +int sha1_tests(); int sha256_tests(); int sha512_tests(); @@ -141,6 +143,15 @@ void hmac_md5(const char* message, size_t len) print_buffer(ByteBuffer::wrap(mac.data, hmac.DigestSize), -1); } +void sha1(const char* message, size_t len) +{ + auto digest = Crypto::Hash::SHA1::hash((const u8*)message, len); + if (binary) + printf("%.*s", (int)Crypto::Hash::SHA1::digest_size(), digest.data); + else + print_buffer(ByteBuffer::wrap(digest.data, Crypto::Hash::SHA1::digest_size()), -1); +} + void sha256(const char* message, size_t len) { auto digest = Crypto::Hash::SHA256::hash((const u8*)message, len); @@ -219,6 +230,11 @@ auto main(int argc, char** argv) -> int return md5_tests(); return run(md5); } + if (suite_sv == "SHA1") { + if (run_tests) + return sha1_tests(); + return run(sha1); + } if (suite_sv == "SHA256") { if (run_tests) return sha256_tests(); @@ -316,6 +332,9 @@ void md5_test_name(); void md5_test_hash(); void md5_test_consecutive_updates(); +void sha1_test_name(); +void sha1_test_hash(); + void sha256_test_name(); void sha256_test_hash(); @@ -677,6 +696,40 @@ void hmac_md5_test_process() } } +int sha1_tests() +{ + sha1_test_name(); + sha1_test_hash(); + return 0; +} + +void sha1_test_name() +{ + I_TEST((SHA1 class name)); + Crypto::Hash::SHA1 sha; + if (sha.class_name() != "SHA1") { + FAIL(Invalid class name); + printf("%s\n", sha.class_name().characters()); + } else + PASS; +} + +void sha1_test_hash() +{ + { + I_TEST((SHA256 Hashing | "")); + u8 result[] { + 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 + }; + auto digest = Crypto::Hash::SHA1::hash(""); + if (memcmp(result, digest.data, Crypto::Hash::SHA1::digest_size()) != 0) { + FAIL(Invalid hash); + print_buffer(ByteBuffer::wrap(digest.data, Crypto::Hash::SHA1::digest_size()), -1); + } else + PASS; + } +} + int sha256_tests() { sha256_test_name();