diff options
author | DexesTTP <dexes.ttp@gmail.com> | 2021-05-17 22:24:32 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-19 09:18:45 +0200 |
commit | 2c1916dd8dfda1bddb47aff34ecd40d44987e94f (patch) | |
tree | 1f0ac9cf8c573c3912ff8a64fba3c770db7426ab | |
parent | b985eb16130c8422a27a83151a00d4096cfcf559 (diff) | |
download | serenity-2c1916dd8dfda1bddb47aff34ecd40d44987e94f.zip |
LibCrypto: Add the SHA-384 hash algorithm
This is a truncated version of SHA-512, so it was fairly trivial.
-rw-r--r-- | Userland/Libraries/LibCrypto/Hash/HashManager.h | 33 | ||||
-rw-r--r-- | Userland/Libraries/LibCrypto/Hash/SHA2.cpp | 116 | ||||
-rw-r--r-- | Userland/Libraries/LibCrypto/Hash/SHA2.h | 57 | ||||
-rw-r--r-- | Userland/Utilities/test-crypto.cpp | 65 |
4 files changed, 271 insertions, 0 deletions
diff --git a/Userland/Libraries/LibCrypto/Hash/HashManager.h b/Userland/Libraries/LibCrypto/Hash/HashManager.h index 77a1659276..d018b661b5 100644 --- a/Userland/Libraries/LibCrypto/Hash/HashManager.h +++ b/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 }; diff --git a/Userland/Libraries/LibCrypto/Hash/SHA2.cpp b/Userland/Libraries/LibCrypto/Hash/SHA2.cpp index beda25dbad..4a3698dc92 100644 --- a/Userland/Libraries/LibCrypto/Hash/SHA2.cpp +++ b/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]; diff --git a/Userland/Libraries/LibCrypto/Hash/SHA2.h b/Userland/Libraries/LibCrypto/Hash/SHA2.h index 1134c4f03e..c49b5dae71 100644 --- a/Userland/Libraries/LibCrypto/Hash/SHA2.h +++ b/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; diff --git a/Userland/Utilities/test-crypto.cpp b/Userland/Utilities/test-crypto.cpp index 5c3749ed57..89ebc0a16e 100644 --- a/Userland/Utilities/test-crypto.cpp +++ b/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(); |