浏览代码

LibCrypto: Add the SHA-384 hash algorithm

This is a truncated version of SHA-512, so it was fairly trivial.
DexesTTP 4 年之前
父节点
当前提交
2c1916dd8d

+ 33 - 0
Userland/Libraries/LibCrypto/Hash/HashManager.h

@@ -20,6 +20,7 @@ enum class HashKind {
     None,
     SHA1,
     SHA256,
+    SHA384,
     SHA512,
     MD5,
 };
@@ -40,6 +41,12 @@ struct MultiHashDigestVariant {
     {
     }
 
+    MultiHashDigestVariant(SHA384::DigestType digest)
+        : sha384(digest)
+        , kind(HashKind::SHA384)
+    {
+    }
+
     MultiHashDigestVariant(SHA512::DigestType digest)
         : sha512(digest)
         , kind(HashKind::SHA512)
@@ -61,6 +68,8 @@ struct MultiHashDigestVariant {
             return sha1.value().immutable_data();
         case HashKind::SHA256:
             return sha256.value().immutable_data();
+        case HashKind::SHA384:
+            return sha384.value().immutable_data();
         case HashKind::SHA512:
             return sha512.value().immutable_data();
         default:
@@ -79,6 +88,8 @@ struct MultiHashDigestVariant {
             return sha1.value().data_length();
         case HashKind::SHA256:
             return sha256.value().data_length();
+        case HashKind::SHA384:
+            return sha384.value().data_length();
         case HashKind::SHA512:
             return sha512.value().data_length();
         default:
@@ -90,6 +101,7 @@ struct MultiHashDigestVariant {
 
     Optional<SHA1::DigestType> sha1;
     Optional<SHA256::DigestType> sha256;
+    Optional<SHA384::DigestType> sha384;
     Optional<SHA512::DigestType> sha512;
     Optional<MD5::DigestType> md5;
     HashKind kind { HashKind::None };
@@ -120,6 +132,7 @@ public:
     {
         m_sha1 = nullptr;
         m_sha256 = nullptr;
+        m_sha384 = nullptr;
         m_sha512 = nullptr;
         m_md5 = nullptr;
     }
@@ -133,6 +146,8 @@ public:
             return m_sha1->digest_size();
         case HashKind::SHA256:
             return m_sha256->digest_size();
+        case HashKind::SHA384:
+            return m_sha384->digest_size();
         case HashKind::SHA512:
             return m_sha512->digest_size();
         default:
@@ -149,6 +164,8 @@ public:
             return m_sha1->block_size();
         case HashKind::SHA256:
             return m_sha256->block_size();
+        case HashKind::SHA384:
+            return m_sha384->block_size();
         case HashKind::SHA512:
             return m_sha512->block_size();
         default:
@@ -173,6 +190,9 @@ public:
         case HashKind::SHA256:
             m_sha256 = make<SHA256>();
             break;
+        case HashKind::SHA384:
+            m_sha384 = make<SHA384>();
+            break;
         case HashKind::SHA512:
             m_sha512 = make<SHA512>();
             break;
@@ -201,6 +221,11 @@ public:
                 m_sha256->update(m_pre_init_buffer);
             m_sha256->update(data, length);
             break;
+        case HashKind::SHA384:
+            if (size)
+                m_sha384->update(m_pre_init_buffer);
+            m_sha384->update(data, length);
+            break;
         case HashKind::SHA512:
             if (size)
                 m_sha512->update(m_pre_init_buffer);
@@ -224,6 +249,8 @@ public:
             return { m_sha1->peek() };
         case HashKind::SHA256:
             return { m_sha256->peek() };
+        case HashKind::SHA384:
+            return { m_sha384->peek() };
         case HashKind::SHA512:
             return { m_sha512->peek() };
         default:
@@ -253,6 +280,9 @@ public:
         case HashKind::SHA256:
             m_sha256->reset();
             break;
+        case HashKind::SHA384:
+            m_sha384->reset();
+            break;
         case HashKind::SHA512:
             m_sha512->reset();
             break;
@@ -271,6 +301,8 @@ public:
             return m_sha1->class_name();
         case HashKind::SHA256:
             return m_sha256->class_name();
+        case HashKind::SHA384:
+            return m_sha384->class_name();
         case HashKind::SHA512:
             return m_sha512->class_name();
         default:
@@ -287,6 +319,7 @@ public:
 private:
     OwnPtr<SHA1> m_sha1;
     OwnPtr<SHA256> m_sha256;
+    OwnPtr<SHA384> m_sha384;
     OwnPtr<SHA512> m_sha512;
     OwnPtr<MD5> m_md5;
     HashKind m_kind { HashKind::None };

+ 116 - 0
Userland/Libraries/LibCrypto/Hash/SHA2.cpp

@@ -142,6 +142,122 @@ SHA256::DigestType SHA256::peek()
     return digest;
 }
 
+inline void SHA384::transform(const u8* data)
+{
+    u64 m[80];
+
+    size_t i = 0;
+    for (size_t j = 0; i < 16; ++i, j += 8) {
+        m[i] = ((u64)data[j] << 56) | ((u64)data[j + 1] << 48) | ((u64)data[j + 2] << 40) | ((u64)data[j + 3] << 32) | ((u64)data[j + 4] << 24) | ((u64)data[j + 5] << 16) | ((u64)data[j + 6] << 8) | (u64)data[j + 7];
+    }
+
+    for (; i < Rounds; ++i) {
+        m[i] = SIGN1(m[i - 2]) + m[i - 7] + SIGN0(m[i - 15]) + m[i - 16];
+    }
+
+    auto a = m_state[0], b = m_state[1],
+         c = m_state[2], d = m_state[3],
+         e = m_state[4], f = m_state[5],
+         g = m_state[6], h = m_state[7];
+
+    for (size_t i = 0; i < Rounds; ++i) {
+        // Note : SHA384 uses the SHA512 constants.
+        auto temp0 = h + EP1(e) + CH(e, f, g) + SHA512Constants::RoundConstants[i] + m[i];
+        auto temp1 = EP0(a) + MAJ(a, b, c);
+        h = g;
+        g = f;
+        f = e;
+        e = d + temp0;
+        d = c;
+        c = b;
+        b = a;
+        a = temp0 + temp1;
+    }
+
+    m_state[0] += a;
+    m_state[1] += b;
+    m_state[2] += c;
+    m_state[3] += d;
+    m_state[4] += e;
+    m_state[5] += f;
+    m_state[6] += g;
+    m_state[7] += h;
+}
+
+void SHA384::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 += 1024;
+            m_data_length = 0;
+        }
+        m_data_buffer[m_data_length++] = message[i];
+    }
+}
+
+SHA384::DigestType SHA384::digest()
+{
+    auto digest = peek();
+    reset();
+    return digest;
+}
+
+SHA384::DigestType SHA384::peek()
+{
+    DigestType digest;
+    size_t i = m_data_length;
+
+    if (BlockSize == m_data_length) {
+        transform(m_data_buffer);
+        m_bit_length += BlockSize * 8;
+        m_data_length = 0;
+        i = 0;
+    }
+
+    if (m_data_length < FinalBlockDataSize) {
+        m_data_buffer[i++] = 0x80;
+        while (i < FinalBlockDataSize)
+            m_data_buffer[i++] = 0x00;
+
+    } else {
+        // First, complete a block with some padding.
+        m_data_buffer[i++] = 0x80;
+        while (i < BlockSize)
+            m_data_buffer[i++] = 0x00;
+        transform(m_data_buffer);
+
+        // Then start another block with BlockSize - 8 bytes of zeros
+        __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);
+
+    // SHA uses big-endian and we assume little-endian
+    // FIXME: looks like a thing for AK::NetworkOrdered,
+    //        but he doesn't support shifting operations
+    for (size_t i = 0; i < 8; ++i) {
+        digest.data[i + 0] = (m_state[0] >> (56 - i * 8)) & 0x000000ff;
+        digest.data[i + 8] = (m_state[1] >> (56 - i * 8)) & 0x000000ff;
+        digest.data[i + 16] = (m_state[2] >> (56 - i * 8)) & 0x000000ff;
+        digest.data[i + 24] = (m_state[3] >> (56 - i * 8)) & 0x000000ff;
+        digest.data[i + 32] = (m_state[4] >> (56 - i * 8)) & 0x000000ff;
+        digest.data[i + 40] = (m_state[5] >> (56 - i * 8)) & 0x000000ff;
+    }
+    return digest;
+}
+
 inline void SHA512::transform(const u8* data)
 {
     u64 m[80];

+ 57 - 0
Userland/Libraries/LibCrypto/Hash/SHA2.h

@@ -39,6 +39,13 @@ constexpr static u32 InitializationHashes[8] = {
 };
 }
 
+namespace SHA384Constants {
+constexpr static u64 InitializationHashes[8] = {
+    0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939,
+    0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4
+};
+}
+
 namespace SHA512Constants {
 constexpr static u64 RoundConstants[80] {
     0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
@@ -124,6 +131,56 @@ private:
     constexpr static auto Rounds = 64;
 };
 
+class SHA384 final : public HashFunction<1024, SHA2Digest<384 / 8>> {
+public:
+    using HashFunction::update;
+
+    SHA384()
+    {
+        reset();
+    }
+
+    virtual void update(const u8*, size_t) override;
+
+    virtual DigestType digest() override;
+    virtual DigestType peek() override;
+
+    inline static DigestType hash(const u8* data, size_t length)
+    {
+        SHA384 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 String::formatted("SHA{}", DigestSize * 8);
+    }
+
+    inline virtual void reset() override
+    {
+        m_data_length = 0;
+        m_bit_length = 0;
+        for (size_t i = 0; i < 8; ++i)
+            m_state[i] = SHA384Constants::InitializationHashes[i];
+    }
+
+private:
+    inline void transform(const u8*);
+
+    u8 m_data_buffer[BlockSize];
+    size_t m_data_length { 0 };
+
+    u64 m_bit_length { 0 };
+    u64 m_state[8];
+
+    constexpr static auto FinalBlockDataSize = BlockSize - 8;
+    constexpr static auto Rounds = 80;
+};
+
 class SHA512 final : public HashFunction<1024, SHA2Digest<512 / 8>> {
 public:
     using HashFunction::update;

+ 65 - 0
Userland/Utilities/test-crypto.cpp

@@ -64,6 +64,7 @@ static int aes_gcm_tests();
 static int md5_tests();
 static int sha1_tests();
 static int sha256_tests();
+static int sha384_tests();
 static int sha512_tests();
 
 // Authentication
@@ -269,6 +270,15 @@ static void hmac_sha256(const char* message, size_t len)
         print_buffer({ mac.data, hmac.digest_size() }, -1);
 }
 
+static void sha384(const char* message, size_t len)
+{
+    auto digest = Crypto::Hash::SHA384::hash((const u8*)message, len);
+    if (binary)
+        printf("%.*s", (int)Crypto::Hash::SHA384::digest_size(), digest.data);
+    else
+        print_buffer({ digest.data, Crypto::Hash::SHA384::digest_size() }, -1);
+}
+
 static void sha512(const char* message, size_t len)
 {
     auto digest = Crypto::Hash::SHA512::hash((const u8*)message, len);
@@ -344,6 +354,11 @@ auto main(int argc, char** argv) -> int
                 return sha256_tests();
             return run(sha256);
         }
+        if (suite_sv == "SHA384") {
+            if (run_tests)
+                return sha384_tests();
+            return run(sha384);
+        }
         if (suite_sv == "SHA512") {
             if (run_tests)
                 return sha512_tests();
@@ -443,6 +458,7 @@ auto main(int argc, char** argv) -> int
         md5_tests();
         sha1_tests();
         sha256_tests();
+        sha384_tests();
         sha512_tests();
 
         hmac_md5_tests();
@@ -568,6 +584,9 @@ static void sha1_test_hash();
 static void sha256_test_name();
 static void sha256_test_hash();
 
+static void sha384_test_name();
+static void sha384_test_hash();
+
 static void sha512_test_name();
 static void sha512_test_hash();
 
@@ -1698,6 +1717,52 @@ static void hmac_sha256_test_process()
     }
 }
 
+static int sha384_tests()
+{
+    sha384_test_name();
+    sha384_test_hash();
+    return g_some_test_failed ? 1 : 0;
+}
+
+static void sha384_test_name()
+{
+    I_TEST((SHA384 class name));
+    Crypto::Hash::SHA384 sha;
+    if (sha.class_name() != "SHA384") {
+        FAIL(Invalid class name);
+        printf("%s\n", sha.class_name().characters());
+    } else
+        PASS;
+}
+
+static void sha384_test_hash()
+{
+    {
+        I_TEST((SHA384 Hashing | "Well hello friends"));
+        u8 result[] {
+            0x2f, 0x01, 0x8e, 0x9a, 0x4f, 0xd1, 0x36, 0xb9, 0x0f, 0xcc, 0x21, 0xde, 0x1a, 0xd4, 0x49, 0x51, 0x57, 0x82, 0x86, 0x84, 0x54, 0x09, 0x82, 0x7b, 0x54, 0x56, 0x93, 0xac, 0x2c, 0x46, 0x0c, 0x1f, 0x5e, 0xec, 0xe0, 0xf7, 0x8b, 0x0b, 0x84, 0x27, 0xc8, 0xb8, 0xbe, 0x49, 0xce, 0x8f, 0x1c, 0xff
+        };
+        auto digest = Crypto::Hash::SHA384::hash("Well hello friends");
+        if (memcmp(result, digest.data, Crypto::Hash::SHA384::digest_size()) != 0) {
+            FAIL(Invalid hash);
+            print_buffer({ digest.data, Crypto::Hash::SHA384::digest_size() }, -1);
+        } else
+            PASS;
+    }
+    {
+        I_TEST((SHA384 Hashing | ""));
+        u8 result[] {
+            0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b
+        };
+        auto digest = Crypto::Hash::SHA384::hash("");
+        if (memcmp(result, digest.data, Crypto::Hash::SHA384::digest_size()) != 0) {
+            FAIL(Invalid hash);
+            print_buffer({ digest.data, Crypto::Hash::SHA384::digest_size() }, -1);
+        } else
+            PASS;
+    }
+}
+
 static int sha512_tests()
 {
     sha512_test_name();