Преглед на файлове

LibCrypto: Implement SHA1 Hash Function

AnotherTest преди 5 години
родител
ревизия
e997661e26
променени са 4 файла, в които са добавени 315 реда и са изтрити 0 реда
  1. 159 0
      Libraries/LibCrypto/Hash/SHA1.cpp
  2. 102 0
      Libraries/LibCrypto/Hash/SHA1.h
  3. 1 0
      Libraries/LibCrypto/Makefile
  4. 53 0
      Userland/test-crypto.cpp

+ 159 - 0
Libraries/LibCrypto/Hash/SHA1.cpp

@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2020, Ali Mohammad Pur <ali.mpfard@gmail.com>
+ * 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 <AK/Types.h>
+#include <LibCrypto/Hash/SHA1.h>
+
+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;
+}
+
+}
+}

+ 102 - 0
Libraries/LibCrypto/Hash/SHA1.h

@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020, Ali Mohammad Pur <ali.mpfard@gmail.com>
+ * 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 <AK/String.h>
+#include <LibCrypto/Hash/HashFunction.h>
+
+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<size_t Bytes>
+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;
+};
+
+}
+}

+ 1 - 0
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

+ 53 - 0
Userland/test-crypto.cpp

@@ -6,6 +6,7 @@
 #include <LibCrypto/BigInt/UnsignedBigInteger.h>
 #include <LibCrypto/Cipher/AES.h>
 #include <LibCrypto/Hash/MD5.h>
+#include <LibCrypto/Hash/SHA1.h>
 #include <LibCrypto/Hash/SHA2.h>
 #include <LibCrypto/PK/RSA.h>
 #include <LibLine/Editor.h>
@@ -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();