LibCrypto: Replace all hashes implementation with OpenSSL

This required multiple changes:
- Make hashes non-copiable because they contain a heap allocated pointer
- Reference classes via `NonnullOwnPtr` only (they are non-copiable)
- Drop all existing hashes implementations
- Use the `OpenSSLHashFunction` base class to implement the same hashes

I was not able to come up with a way to divide this commit into multiple
without increasing the amount of changes.

Nothing breaks with this commit!
This commit is contained in:
devgianlu 2024-12-20 09:11:10 +01:00 committed by Ali Mohammad Pur
commit 89061dd3c4
Notes: github-actions[bot] 2024-12-22 17:54:42 +00:00
16 changed files with 164 additions and 1350 deletions

View file

@ -24,12 +24,12 @@ public:
using HashType = HashT; using HashType = HashT;
using TagType = typename HashType::DigestType; using TagType = typename HashType::DigestType;
constexpr size_t digest_size() const { return m_inner_hasher.digest_size(); } size_t digest_size() const { return m_inner_hasher->digest_size(); }
template<typename KeyBufferType, typename... Args> template<typename KeyBufferType, typename... Args>
HMAC(KeyBufferType key, Args... args) HMAC(KeyBufferType key, Args... args)
: m_inner_hasher(args...) : m_inner_hasher(move(HashT::create(args...)))
, m_outer_hasher(args...) , m_outer_hasher(move(HashT::create(args...)))
{ {
derive_key(key); derive_key(key);
reset(); reset();
@ -44,7 +44,7 @@ public:
void update(u8 const* message, size_t length) void update(u8 const* message, size_t length)
{ {
m_inner_hasher.update(message, length); m_inner_hasher->update(message, length);
} }
TagType process(ReadonlyBytes span) { return process(span.data(), span.size()); } TagType process(ReadonlyBytes span) { return process(span.data(), span.size()); }
@ -55,32 +55,32 @@ public:
TagType digest() TagType digest()
{ {
m_outer_hasher.update(m_inner_hasher.digest().immutable_data(), m_inner_hasher.digest_size()); m_outer_hasher->update(m_inner_hasher->digest().immutable_data(), m_inner_hasher->digest_size());
auto result = m_outer_hasher.digest(); auto result = m_outer_hasher->digest();
reset(); reset();
return result; return result;
} }
void reset() void reset()
{ {
m_inner_hasher.reset(); m_inner_hasher->reset();
m_outer_hasher.reset(); m_outer_hasher->reset();
m_inner_hasher.update(m_key_data, m_inner_hasher.block_size()); m_inner_hasher->update(m_key_data, m_inner_hasher->block_size());
m_outer_hasher.update(m_key_data + m_inner_hasher.block_size(), m_outer_hasher.block_size()); m_outer_hasher->update(m_key_data + m_inner_hasher->block_size(), m_outer_hasher->block_size());
} }
ByteString class_name() const ByteString class_name() const
{ {
StringBuilder builder; StringBuilder builder;
builder.append("HMAC-"sv); builder.append("HMAC-"sv);
builder.append(m_inner_hasher.class_name()); builder.append(m_inner_hasher->class_name());
return builder.to_byte_string(); return builder.to_byte_string();
} }
private: private:
void derive_key(u8 const* key, size_t length) void derive_key(u8 const* key, size_t length)
{ {
auto block_size = m_inner_hasher.block_size(); auto block_size = m_inner_hasher->block_size();
// Note: The block size of all the current hash functions is 512 bits. // Note: The block size of all the current hash functions is 512 bits.
Vector<u8, 64> v_key; Vector<u8, 64> v_key;
v_key.resize(block_size); v_key.resize(block_size);
@ -89,10 +89,10 @@ private:
// the first few bytes leaves the rest zero, which // the first few bytes leaves the rest zero, which
// is exactly what we want (zero padding) // is exactly what we want (zero padding)
if (length > block_size) { if (length > block_size) {
m_inner_hasher.update(key, length); m_inner_hasher->update(key, length);
auto digest = m_inner_hasher.digest(); auto digest = m_inner_hasher->digest();
// FIXME: should we check if the hash function creates more data than its block size? // FIXME: should we check if the hash function creates more data than its block size?
key_buffer.overwrite(0, digest.immutable_data(), m_inner_hasher.digest_size()); key_buffer.overwrite(0, digest.immutable_data(), m_inner_hasher->digest_size());
} else if (length > 0) { } else if (length > 0) {
key_buffer.overwrite(0, key, length); key_buffer.overwrite(0, key, length);
} }
@ -110,7 +110,7 @@ private:
void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); } void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); }
void derive_key(StringView key) { derive_key(key.bytes()); } void derive_key(StringView key) { derive_key(key.bytes()); }
HashType m_inner_hasher, m_outer_hasher; NonnullOwnPtr<HashType> m_inner_hasher, m_outer_hasher;
u8 m_key_data[2048]; u8 m_key_data[2048];
}; };

View file

@ -28,10 +28,6 @@ set(SOURCES
Curves/Ed25519.cpp Curves/Ed25519.cpp
Curves/X25519.cpp Curves/X25519.cpp
Curves/X448.cpp Curves/X448.cpp
Hash/BLAKE2b.cpp
Hash/MD5.cpp
Hash/SHA1.cpp
Hash/SHA2.cpp
NumberTheory/ModularFunctions.cpp NumberTheory/ModularFunctions.cpp
PK/RSA.cpp PK/RSA.cpp
PK/EC.cpp PK/EC.cpp

View file

@ -79,15 +79,15 @@ ErrorOr<ByteBuffer> Ed25519::sign(ReadonlyBytes public_key, ReadonlyBytes privat
memcpy(p, h.data + 32, 32); memcpy(p, h.data + 32, 32);
// 2. Compute SHA-512(dom2(F, C) || p || PH(M)), where M is the message to be signed. // 2. Compute SHA-512(dom2(F, C) || p || PH(M)), where M is the message to be signed.
Crypto::Hash::SHA512 hash; auto hash = Hash::SHA512::create();
// NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519 // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519
hash.update(p, 32); hash->update(p, 32);
// NOTE: PH(M) = M // NOTE: PH(M) = M
hash.update(message.data(), message.size()); hash->update(message.data(), message.size());
// Interpret the 64-octet digest as a little-endian integer r. // Interpret the 64-octet digest as a little-endian integer r.
// For efficiency, do this by first reducing r modulo L, the group order of B. // For efficiency, do this by first reducing r modulo L, the group order of B.
auto digest = hash.digest(); auto digest = hash->digest();
barrett_reduce(r, digest.data); barrett_reduce(r, digest.data);
// 3. Compute the point [r]B. // 3. Compute the point [r]B.
@ -100,13 +100,13 @@ ErrorOr<ByteBuffer> Ed25519::sign(ReadonlyBytes public_key, ReadonlyBytes privat
// 4. Compute SHA512(dom2(F, C) || R || A || PH(M)), // 4. Compute SHA512(dom2(F, C) || R || A || PH(M)),
// NOTE: We can reuse hash here, since digest() calls reset() // NOTE: We can reuse hash here, since digest() calls reset()
// NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519 // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519
hash.update(R.data(), R.size()); hash->update(R.data(), R.size());
// NOTE: A == public_key // NOTE: A == public_key
hash.update(public_key.data(), public_key.size()); hash->update(public_key.data(), public_key.size());
// NOTE: PH(M) = M // NOTE: PH(M) = M
hash.update(message.data(), message.size()); hash->update(message.data(), message.size());
digest = hash.digest(); digest = hash->digest();
// and interpret the 64-octet digest as a little-endian integer k. // and interpret the 64-octet digest as a little-endian integer k.
memcpy(k, digest.data, 64); memcpy(k, digest.data, 64);
@ -160,15 +160,15 @@ bool Ed25519::verify(ReadonlyBytes public_key, ReadonlyBytes signature, Readonly
not_valid |= decode_point(&ka, public_key.data()); not_valid |= decode_point(&ka, public_key.data());
// 2. Compute SHA512(dom2(F, C) || R || A || PH(M)), and interpret the 64-octet digest as a little-endian integer k. // 2. Compute SHA512(dom2(F, C) || R || A || PH(M)), and interpret the 64-octet digest as a little-endian integer k.
Crypto::Hash::SHA512 hash; auto hash = Hash::SHA512::create();
// NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519 // NOTE: dom2(F, C) is a blank octet string when signing or verifying Ed25519
hash.update(r, half_signature_size); hash->update(r, half_signature_size);
// NOTE: A == public_key // NOTE: A == public_key
hash.update(public_key.data(), key_size()); hash->update(public_key.data(), key_size());
// NOTE: PH(M) = M // NOTE: PH(M) = M
hash.update(message.data(), message.size()); hash->update(message.data(), message.size());
auto digest = hash.digest(); auto digest = hash->digest();
auto k = digest.data; auto k = digest.data;
// 3. Check the group equation [8][S]B = [8]R + [8][k]A'. // 3. Check the group equation [8][S]B = [8]R + [8][k]A'.

View file

@ -1,126 +0,0 @@
/*
* Copyright (c) 2023, the SerenityOS developers
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteReader.h>
#include <LibCrypto/Hash/BLAKE2b.h>
namespace Crypto::Hash {
constexpr static auto ROTRIGHT(u64 a, size_t b) { return (a >> b) | (a << (64 - b)); }
void BLAKE2b::update(u8 const* in, size_t inlen)
{
if (inlen > 0) {
size_t left = m_internal_state.buffer_length;
size_t fill = BLAKE2bConstants::blockbytes - left;
if (inlen > fill) {
m_internal_state.buffer_length = 0;
// Fill the buffer.
__builtin_memcpy(m_internal_state.buffer + left, in, fill);
increment_counter_by(BLAKE2bConstants::blockbytes);
transform(m_internal_state.buffer);
in += fill;
inlen -= fill;
while (inlen > BLAKE2bConstants::blockbytes) {
increment_counter_by(BLAKE2bConstants::blockbytes);
transform(in);
in += BLAKE2bConstants::blockbytes;
inlen -= BLAKE2bConstants::blockbytes;
}
}
__builtin_memcpy(m_internal_state.buffer + m_internal_state.buffer_length, in, inlen);
m_internal_state.buffer_length += inlen;
}
}
BLAKE2b::DigestType BLAKE2b::peek()
{
DigestType digest;
increment_counter_by(m_internal_state.buffer_length);
// Set this as the last block
m_internal_state.is_at_last_block = UINT64_MAX;
// Pad the buffer with zeros
__builtin_memset(m_internal_state.buffer + m_internal_state.buffer_length, 0, BLAKE2bConstants::blockbytes - m_internal_state.buffer_length);
transform(m_internal_state.buffer);
for (size_t i = 0; i < 8; ++i)
__builtin_memcpy(&digest.data[0] + sizeof(m_internal_state.hash_state[i]) * i, &m_internal_state.hash_state[i], sizeof(m_internal_state.hash_state[i]));
return digest;
}
BLAKE2b::DigestType BLAKE2b::digest()
{
auto digest = peek();
reset();
return digest;
}
void BLAKE2b::increment_counter_by(u64 const amount)
{
m_internal_state.message_byte_offset[0] += amount;
m_internal_state.message_byte_offset[1] += (m_internal_state.message_byte_offset[0] < amount);
}
void BLAKE2b::mix(u64* work_array, u64 a, u64 b, u64 c, u64 d, u64 x, u64 y)
{
constexpr auto rotation_constant_1 = 32;
constexpr auto rotation_constant_2 = 24;
constexpr auto rotation_constant_3 = 16;
constexpr auto rotation_constant_4 = 63;
work_array[a] = work_array[a] + work_array[b] + x;
work_array[d] = ROTRIGHT(work_array[d] ^ work_array[a], rotation_constant_1);
work_array[c] = work_array[c] + work_array[d];
work_array[b] = ROTRIGHT(work_array[b] ^ work_array[c], rotation_constant_2);
work_array[a] = work_array[a] + work_array[b] + y;
work_array[d] = ROTRIGHT(work_array[d] ^ work_array[a], rotation_constant_3);
work_array[c] = work_array[c] + work_array[d];
work_array[b] = ROTRIGHT(work_array[b] ^ work_array[c], rotation_constant_4);
}
void BLAKE2b::transform(u8 const* block)
{
u64 m[16];
u64 v[16];
for (size_t i = 0; i < 16; ++i)
m[i] = ByteReader::load64(block + i * sizeof(m[i]));
for (size_t i = 0; i < 8; ++i)
v[i] = m_internal_state.hash_state[i];
v[8] = SHA512Constants::InitializationHashes[0];
v[9] = SHA512Constants::InitializationHashes[1];
v[10] = SHA512Constants::InitializationHashes[2];
v[11] = SHA512Constants::InitializationHashes[3];
v[12] = SHA512Constants::InitializationHashes[4] ^ m_internal_state.message_byte_offset[0];
v[13] = SHA512Constants::InitializationHashes[5] ^ m_internal_state.message_byte_offset[1];
v[14] = SHA512Constants::InitializationHashes[6] ^ m_internal_state.is_at_last_block;
v[15] = SHA512Constants::InitializationHashes[7];
for (size_t i = 0; i < 12; ++i) {
u64 sigma_selection[16];
for (size_t j = 0; j < 16; ++j)
sigma_selection[j] = BLAKE2bSigma[i % 10][j];
mix(v, 0, 4, 8, 12, m[sigma_selection[0]], m[sigma_selection[1]]);
mix(v, 1, 5, 9, 13, m[sigma_selection[2]], m[sigma_selection[3]]);
mix(v, 2, 6, 10, 14, m[sigma_selection[4]], m[sigma_selection[5]]);
mix(v, 3, 7, 11, 15, m[sigma_selection[6]], m[sigma_selection[7]]);
mix(v, 0, 5, 10, 15, m[sigma_selection[8]], m[sigma_selection[9]]);
mix(v, 1, 6, 11, 12, m[sigma_selection[10]], m[sigma_selection[11]]);
mix(v, 2, 7, 8, 13, m[sigma_selection[12]], m[sigma_selection[13]]);
mix(v, 3, 4, 9, 14, m[sigma_selection[14]], m[sigma_selection[15]]);
}
for (size_t i = 0; i < 8; ++i)
m_internal_state.hash_state[i] = m_internal_state.hash_state[i] ^ v[i] ^ v[i + 8];
}
}

View file

@ -7,82 +7,23 @@
#pragma once #pragma once
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <LibCrypto/Hash/HashFunction.h> #include <LibCrypto/Hash/OpenSSLHashFunction.h>
#include <LibCrypto/Hash/SHA2.h>
namespace Crypto::Hash { namespace Crypto::Hash {
namespace BLAKE2bConstants { class BLAKE2b final : public OpenSSLHashFunction<BLAKE2b, 1024, 512> {
static constexpr auto blockbytes { 128 }; AK_MAKE_NONCOPYABLE(BLAKE2b);
static constexpr auto hash_length { 64 };
};
class BLAKE2b final : public HashFunction<1024, 512> {
public: public:
using HashFunction::update; explicit BLAKE2b(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_blake2b512(), context)
BLAKE2b()
{ {
reset();
} }
virtual void update(u8 const*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
static DigestType hash(u8 const* data, size_t length)
{
BLAKE2b blake2b;
blake2b.update(data, length);
return blake2b.digest();
}
static DigestType hash(ByteBuffer const& buffer) { return hash(buffer.data(), buffer.size()); }
static DigestType hash(StringView buffer) { return hash((u8 const*)buffer.characters_without_null_termination(), buffer.length()); }
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return "BLAKE2b"; return "BLAKE2b";
} }
virtual void reset() override
{
m_internal_state = {};
// BLAKE2b uses the same initialization vector as SHA512.
for (size_t i = 0; i < 8; ++i)
m_internal_state.hash_state[i] = SHA512Constants::InitializationHashes[i];
m_internal_state.hash_state[0] ^= 0x01010000 ^ (0 << 8) ^ BLAKE2bConstants::hash_length;
}
private:
static constexpr u8 BLAKE2bSigma[12][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
};
struct BLAKE2bState {
u64 hash_state[8] {};
u64 message_byte_offset[2] {};
u64 is_at_last_block { 0 };
u8 buffer[BLAKE2bConstants::blockbytes] = {};
size_t buffer_length { 0 };
};
BLAKE2bState m_internal_state {};
void mix(u64* work_vector, u64 a, u64 b, u64 c, u64 d, u64 x, u64 y);
void increment_counter_by(u64 const amount);
void transform(u8 const*);
}; };
}; };

View file

@ -90,6 +90,11 @@ class Manager final : public HashFunction<0, 0, MultiHashDigestVariant> {
public: public:
using HashFunction::update; using HashFunction::update;
static NonnullOwnPtr<Manager> create(HashKind kind)
{
return make<Manager>(kind);
}
Manager() Manager()
{ {
m_pre_init_buffer = ByteBuffer(); m_pre_init_buffer = ByteBuffer();
@ -116,14 +121,14 @@ public:
{ {
return m_algorithm.visit( return m_algorithm.visit(
[&](Empty const&) -> size_t { return 0; }, [&](Empty const&) -> size_t { return 0; },
[&](auto const& hash) { return hash.digest_size(); }); [&](auto const& hash) { return hash->digest_size(); });
} }
inline size_t block_size() const inline size_t block_size() const
{ {
return m_algorithm.visit( return m_algorithm.visit(
[&](Empty const&) -> size_t { return 0; }, [&](Empty const&) -> size_t { return 0; },
[&](auto const& hash) { return hash.block_size(); }); [&](auto const& hash) { return hash->block_size(); });
} }
inline void initialize(HashKind kind) inline void initialize(HashKind kind)
@ -135,22 +140,22 @@ public:
m_kind = kind; m_kind = kind;
switch (kind) { switch (kind) {
case HashKind::BLAKE2b: case HashKind::BLAKE2b:
m_algorithm = BLAKE2b(); m_algorithm = BLAKE2b::create();
break; break;
case HashKind::MD5: case HashKind::MD5:
m_algorithm = MD5(); m_algorithm = MD5::create();
break; break;
case HashKind::SHA1: case HashKind::SHA1:
m_algorithm = SHA1(); m_algorithm = SHA1::create();
break; break;
case HashKind::SHA256: case HashKind::SHA256:
m_algorithm = SHA256(); m_algorithm = SHA256::create();
break; break;
case HashKind::SHA384: case HashKind::SHA384:
m_algorithm = SHA384(); m_algorithm = SHA384::create();
break; break;
case HashKind::SHA512: case HashKind::SHA512:
m_algorithm = SHA512(); m_algorithm = SHA512::create();
break; break;
default: default:
case HashKind::None: case HashKind::None:
@ -165,11 +170,11 @@ public:
if (size) { if (size) {
m_algorithm.visit( m_algorithm.visit(
[&](Empty&) {}, [&](Empty&) {},
[&](auto& hash) { hash.update(m_pre_init_buffer); }); [&](auto& hash) { hash->update(m_pre_init_buffer); });
} }
m_algorithm.visit( m_algorithm.visit(
[&](Empty&) { m_pre_init_buffer.append(data, length); }, [&](Empty&) { m_pre_init_buffer.append(data, length); },
[&](auto& hash) { hash.update(data, length); }); [&](auto& hash) { hash->update(data, length); });
if (size && m_kind != HashKind::None) if (size && m_kind != HashKind::None)
m_pre_init_buffer.clear(); m_pre_init_buffer.clear();
} }
@ -178,14 +183,14 @@ public:
{ {
return m_algorithm.visit( return m_algorithm.visit(
[&](Empty&) -> DigestType { VERIFY_NOT_REACHED(); }, [&](Empty&) -> DigestType { VERIFY_NOT_REACHED(); },
[&](auto& hash) -> DigestType { return hash.peek(); }); [&](auto& hash) -> DigestType { return hash->peek(); });
} }
virtual DigestType digest() override virtual DigestType digest() override
{ {
auto digest = peek(); return m_algorithm.visit(
reset(); [&](Empty&) -> DigestType { VERIFY_NOT_REACHED(); },
return digest; [&](auto& hash) -> DigestType { return hash->digest(); });
} }
virtual void reset() override virtual void reset() override
@ -193,14 +198,14 @@ public:
m_pre_init_buffer.clear(); m_pre_init_buffer.clear();
m_algorithm.visit( m_algorithm.visit(
[&](Empty&) {}, [&](Empty&) {},
[&](auto& hash) { hash.reset(); }); [&](auto& hash) { hash->reset(); });
} }
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return m_algorithm.visit( return m_algorithm.visit(
[&](Empty const&) -> ByteString { return "UninitializedHashManager"; }, [&](Empty const&) -> ByteString { return "UninitializedHashManager"; },
[&](auto const& hash) { return hash.class_name(); }); [&](auto const& hash) { return hash->class_name(); });
} }
inline HashKind kind() const inline HashKind kind() const
@ -215,15 +220,19 @@ public:
inline Manager copy() const inline Manager copy() const
{ {
auto algorithm = m_algorithm.visit(
[&](Empty const&) -> AlgorithmVariant { VERIFY_NOT_REACHED(); },
[&](auto const& hash) -> AlgorithmVariant { return hash->copy(); });
Manager result; Manager result;
result.m_algorithm = m_algorithm; result.m_algorithm = move(algorithm);
result.m_kind = m_kind; result.m_kind = m_kind;
result.m_pre_init_buffer = m_pre_init_buffer; result.m_pre_init_buffer = m_pre_init_buffer;
return result; return result;
} }
private: private:
using AlgorithmVariant = Variant<Empty, BLAKE2b, MD5, SHA1, SHA256, SHA384, SHA512>; using AlgorithmVariant = Variant<Empty, NonnullOwnPtr<BLAKE2b>, NonnullOwnPtr<MD5>, NonnullOwnPtr<SHA1>, NonnullOwnPtr<SHA256>, NonnullOwnPtr<SHA384>, NonnullOwnPtr<SHA512>>;
AlgorithmVariant m_algorithm {}; AlgorithmVariant m_algorithm {};
HashKind m_kind { HashKind::None }; HashKind m_kind { HashKind::None };
ByteBuffer m_pre_init_buffer; ByteBuffer m_pre_init_buffer;

View file

@ -1,205 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Memory.h>
#include <AK/Types.h>
#include <LibCrypto/Hash/MD5.h>
static constexpr u32 F(u32 x, u32 y, u32 z) { return (x & y) | ((~x) & z); }
static constexpr u32 G(u32 x, u32 y, u32 z) { return (x & z) | ((~z) & y); }
static constexpr u32 H(u32 x, u32 y, u32 z) { return x ^ y ^ z; }
static constexpr u32 I(u32 x, u32 y, u32 z) { return y ^ (x | ~z); }
static constexpr u32 ROTATE_LEFT(u32 x, size_t n)
{
return (x << n) | (x >> (32 - n));
}
static constexpr void round_1(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += F(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
static constexpr void round_2(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += G(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
static constexpr void round_3(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += H(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
static constexpr void round_4(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += I(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
namespace Crypto::Hash {
void MD5::update(u8 const* input, size_t length)
{
auto index = (u32)(m_count[0] >> 3) & 0x3f;
size_t offset { 0 };
m_count[0] += (u32)length << 3;
if (m_count[0] < ((u32)length << 3)) {
++m_count[1];
}
m_count[1] += (u32)length >> 29;
auto part_length = 64 - index;
auto buffer = Bytes { m_data_buffer, sizeof(m_data_buffer) };
if (length >= part_length) {
buffer.overwrite(index, input, part_length);
transform(buffer.data());
for (offset = part_length; offset + 63 < length; offset += 64)
transform(&input[offset]);
index = 0;
}
VERIFY(length < part_length || length - offset <= 64);
buffer.overwrite(index, &input[offset], length - offset);
}
MD5::DigestType MD5::digest()
{
auto digest = peek();
reset();
return digest;
}
MD5::DigestType MD5::peek()
{
DigestType digest;
u8 bits[8];
encode(m_count, bits, 8);
// pad the data to 56%64
u32 index = (u32)((m_count[0] >> 3) & 0x3f);
u32 pad_length = index < 56 ? 56 - index : 120 - index;
update(MD5Constants::PADDING, pad_length);
// append length
update(bits, 8);
// store state (4 registers ABCD)
encode(&m_A, digest.data, 4 * sizeof(m_A));
return digest;
}
void MD5::encode(u32 const* from, u8* to, size_t length)
{
for (size_t i = 0, j = 0; j < length; ++i, j += 4) {
to[j] = (u8)(from[i] & 0xff);
to[j + 1] = (u8)((from[i] >> 8) & 0xff);
to[j + 2] = (u8)((from[i] >> 16) & 0xff);
to[j + 3] = (u8)((from[i] >> 24) & 0xff);
}
}
void MD5::decode(u8 const* from, u32* to, size_t length)
{
for (size_t i = 0, j = 0; j < length; ++i, j += 4)
to[i] = (((u32)from[j]) | (((u32)from[j + 1]) << 8) | (((u32)from[j + 2]) << 16) | (((u32)from[j + 3]) << 24));
}
void MD5::transform(u8 const* block)
{
auto a = m_A;
auto b = m_B;
auto c = m_C;
auto d = m_D;
u32 x[16];
decode(block, x, 64);
round_1(a, b, c, d, x[0], MD5Constants::S11, 0xd76aa478); // 1
round_1(d, a, b, c, x[1], MD5Constants::S12, 0xe8c7b756); // 2
round_1(c, d, a, b, x[2], MD5Constants::S13, 0x242070db); // 3
round_1(b, c, d, a, x[3], MD5Constants::S14, 0xc1bdceee); // 4
round_1(a, b, c, d, x[4], MD5Constants::S11, 0xf57c0faf); // 5
round_1(d, a, b, c, x[5], MD5Constants::S12, 0x4787c62a); // 6
round_1(c, d, a, b, x[6], MD5Constants::S13, 0xa8304613); // 7
round_1(b, c, d, a, x[7], MD5Constants::S14, 0xfd469501); // 8
round_1(a, b, c, d, x[8], MD5Constants::S11, 0x698098d8); // 9
round_1(d, a, b, c, x[9], MD5Constants::S12, 0x8b44f7af); // 10
round_1(c, d, a, b, x[10], MD5Constants::S13, 0xffff5bb1); // 11
round_1(b, c, d, a, x[11], MD5Constants::S14, 0x895cd7be); // 12
round_1(a, b, c, d, x[12], MD5Constants::S11, 0x6b901122); // 13
round_1(d, a, b, c, x[13], MD5Constants::S12, 0xfd987193); // 14
round_1(c, d, a, b, x[14], MD5Constants::S13, 0xa679438e); // 15
round_1(b, c, d, a, x[15], MD5Constants::S14, 0x49b40821); // 16
round_2(a, b, c, d, x[1], MD5Constants::S21, 0xf61e2562); // 17
round_2(d, a, b, c, x[6], MD5Constants::S22, 0xc040b340); // 18
round_2(c, d, a, b, x[11], MD5Constants::S23, 0x265e5a51); // 19
round_2(b, c, d, a, x[0], MD5Constants::S24, 0xe9b6c7aa); // 20
round_2(a, b, c, d, x[5], MD5Constants::S21, 0xd62f105d); // 21
round_2(d, a, b, c, x[10], MD5Constants::S22, 0x2441453); // 22
round_2(c, d, a, b, x[15], MD5Constants::S23, 0xd8a1e681); // 23
round_2(b, c, d, a, x[4], MD5Constants::S24, 0xe7d3fbc8); // 24
round_2(a, b, c, d, x[9], MD5Constants::S21, 0x21e1cde6); // 25
round_2(d, a, b, c, x[14], MD5Constants::S22, 0xc33707d6); // 26
round_2(c, d, a, b, x[3], MD5Constants::S23, 0xf4d50d87); // 27
round_2(b, c, d, a, x[8], MD5Constants::S24, 0x455a14ed); // 28
round_2(a, b, c, d, x[13], MD5Constants::S21, 0xa9e3e905); // 29
round_2(d, a, b, c, x[2], MD5Constants::S22, 0xfcefa3f8); // 30
round_2(c, d, a, b, x[7], MD5Constants::S23, 0x676f02d9); // 31
round_2(b, c, d, a, x[12], MD5Constants::S24, 0x8d2a4c8a); // 32
round_3(a, b, c, d, x[5], MD5Constants::S31, 0xfffa3942); // 33
round_3(d, a, b, c, x[8], MD5Constants::S32, 0x8771f681); // 34
round_3(c, d, a, b, x[11], MD5Constants::S33, 0x6d9d6122); // 35
round_3(b, c, d, a, x[14], MD5Constants::S34, 0xfde5380c); // 36
round_3(a, b, c, d, x[1], MD5Constants::S31, 0xa4beea44); // 37
round_3(d, a, b, c, x[4], MD5Constants::S32, 0x4bdecfa9); // 38
round_3(c, d, a, b, x[7], MD5Constants::S33, 0xf6bb4b60); // 39
round_3(b, c, d, a, x[10], MD5Constants::S34, 0xbebfbc70); // 40
round_3(a, b, c, d, x[13], MD5Constants::S31, 0x289b7ec6); // 41
round_3(d, a, b, c, x[0], MD5Constants::S32, 0xeaa127fa); // 42
round_3(c, d, a, b, x[3], MD5Constants::S33, 0xd4ef3085); // 43
round_3(b, c, d, a, x[6], MD5Constants::S34, 0x4881d05); // 44
round_3(a, b, c, d, x[9], MD5Constants::S31, 0xd9d4d039); // 45
round_3(d, a, b, c, x[12], MD5Constants::S32, 0xe6db99e5); // 46
round_3(c, d, a, b, x[15], MD5Constants::S33, 0x1fa27cf8); // 47
round_3(b, c, d, a, x[2], MD5Constants::S34, 0xc4ac5665); // 48
round_4(a, b, c, d, x[0], MD5Constants::S41, 0xf4292244); // 49
round_4(d, a, b, c, x[7], MD5Constants::S42, 0x432aff97); // 50
round_4(c, d, a, b, x[14], MD5Constants::S43, 0xab9423a7); // 51
round_4(b, c, d, a, x[5], MD5Constants::S44, 0xfc93a039); // 52
round_4(a, b, c, d, x[12], MD5Constants::S41, 0x655b59c3); // 53
round_4(d, a, b, c, x[3], MD5Constants::S42, 0x8f0ccc92); // 54
round_4(c, d, a, b, x[10], MD5Constants::S43, 0xffeff47d); // 55
round_4(b, c, d, a, x[1], MD5Constants::S44, 0x85845dd1); // 56
round_4(a, b, c, d, x[8], MD5Constants::S41, 0x6fa87e4f); // 57
round_4(d, a, b, c, x[15], MD5Constants::S42, 0xfe2ce6e0); // 58
round_4(c, d, a, b, x[6], MD5Constants::S43, 0xa3014314); // 59
round_4(b, c, d, a, x[13], MD5Constants::S44, 0x4e0811a1); // 60
round_4(a, b, c, d, x[4], MD5Constants::S41, 0xf7537e82); // 61
round_4(d, a, b, c, x[11], MD5Constants::S42, 0xbd3af235); // 62
round_4(c, d, a, b, x[2], MD5Constants::S43, 0x2ad7d2bb); // 63
round_4(b, c, d, a, x[9], MD5Constants::S44, 0xeb86d391); // 64
m_A += a;
m_B += b;
m_C += c;
m_D += d;
secure_zero(x, sizeof(x));
}
}

View file

@ -7,88 +7,23 @@
#pragma once #pragma once
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/Types.h> #include <LibCrypto/Hash/OpenSSLHashFunction.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto::Hash { namespace Crypto::Hash {
namespace MD5Constants { class MD5 final : public OpenSSLHashFunction<MD5, 512, 128> {
AK_MAKE_NONCOPYABLE(MD5);
constexpr u32 init_A = 0x67452301;
constexpr u32 init_B = 0xefcdab89;
constexpr u32 init_C = 0x98badcfe;
constexpr u32 init_D = 0x10325476;
constexpr u32 S11 = 7;
constexpr u32 S12 = 12;
constexpr u32 S13 = 17;
constexpr u32 S14 = 22;
constexpr u32 S21 = 5;
constexpr u32 S22 = 9;
constexpr u32 S23 = 14;
constexpr u32 S24 = 20;
constexpr u32 S31 = 4;
constexpr u32 S32 = 11;
constexpr u32 S33 = 16;
constexpr u32 S34 = 23;
constexpr u32 S41 = 6;
constexpr u32 S42 = 10;
constexpr u32 S43 = 15;
constexpr u32 S44 = 21;
constexpr u8 PADDING[] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
};
}
class MD5 final : public HashFunction<512, 128> {
public: public:
using HashFunction::update; explicit MD5(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_md5(), context)
virtual void update(u8 const*, size_t) override; {
virtual DigestType digest() override; }
virtual DigestType peek() override;
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return "MD5"; return "MD5";
} }
static DigestType hash(u8 const* data, size_t length)
{
MD5 md5;
md5.update(data, length);
return md5.digest();
}
static DigestType hash(ByteBuffer const& buffer) { return hash(buffer.data(), buffer.size()); }
static DigestType hash(StringView buffer) { return hash((u8 const*)buffer.characters_without_null_termination(), buffer.length()); }
virtual void reset() override
{
m_A = MD5Constants::init_A;
m_B = MD5Constants::init_B;
m_C = MD5Constants::init_C;
m_D = MD5Constants::init_D;
m_count[0] = 0;
m_count[1] = 0;
__builtin_memset(m_data_buffer, 0, sizeof(m_data_buffer));
}
private:
inline void transform(u8 const*);
static void encode(u32 const* from, u8* to, size_t length);
static void decode(u8 const* from, u32* to, size_t length);
u32 m_A { MD5Constants::init_A }, m_B { MD5Constants::init_B }, m_C { MD5Constants::init_C }, m_D { MD5Constants::init_D };
u32 m_count[2] { 0, 0 };
u8 m_data_buffer[64] {};
}; };
} }

View file

@ -19,9 +19,9 @@ public:
static ErrorOr<ByteBuffer> mgf1(ReadonlyBytes seed, size_t length) static ErrorOr<ByteBuffer> mgf1(ReadonlyBytes seed, size_t length)
requires requires { HashFunction::digest_size(); } requires requires { HashFunction::digest_size(); }
{ {
HashFunction hash; auto hash = HashFunction::create();
size_t h_len = hash.digest_size(); size_t h_len = hash->digest_size();
// 1. If length > 2^32(hLen), output "mask too long" and stop. // 1. If length > 2^32(hLen), output "mask too long" and stop.
if constexpr (sizeof(size_t) > 32) { if constexpr (sizeof(size_t) > 32) {
@ -42,9 +42,9 @@ public:
ByteReader::store(static_cast<u8*>(c.data()), AK::convert_between_host_and_big_endian(static_cast<u32>(counter))); ByteReader::store(static_cast<u8*>(c.data()), AK::convert_between_host_and_big_endian(static_cast<u32>(counter)));
// b. Concatenate the hash of the seed Z and C to the octet string T: T = T || Hash (Z || C) // b. Concatenate the hash of the seed Z and C to the octet string T: T = T || Hash (Z || C)
hash.update(seed); hash->update(seed);
hash.update(c); hash->update(c);
auto digest = hash.digest(); auto digest = hash->digest();
TRY(t.try_append(digest.bytes())); TRY(t.try_append(digest.bytes()));
} }

View file

@ -1,146 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2023, Jelle Raaijmakers <jelle@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Endian.h>
#include <AK/Memory.h>
#include <AK/Types.h>
#include <LibCrypto/Hash/SHA1.h>
namespace Crypto::Hash {
static constexpr auto ROTATE_LEFT(u32 value, size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
inline void SHA1::transform(u8 const* data)
{
u32 blocks[80];
for (size_t i = 0; i < 16; ++i)
blocks[i] = AK::convert_between_host_and_network_endian(((u32 const*)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;
secure_zero(blocks, 16 * sizeof(u32));
}
void SHA1::update(u8 const* message, size_t length)
{
while (length > 0) {
size_t copy_bytes = AK::min(length, BlockSize - m_data_length);
__builtin_memcpy(m_data_buffer + m_data_length, message, copy_bytes);
message += copy_bytes;
length -= copy_bytes;
m_data_length += copy_bytes;
if (m_data_length == BlockSize) {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
m_data_length = 0;
}
}
}
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 {
// 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);
for (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;
}
}

View file

@ -7,71 +7,23 @@
#pragma once #pragma once
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <LibCrypto/Hash/HashFunction.h> #include <LibCrypto/Hash/OpenSSLHashFunction.h>
namespace Crypto::Hash { namespace Crypto::Hash {
namespace SHA1Constants { class SHA1 final : public OpenSSLHashFunction<SHA1, 512, 160> {
AK_MAKE_NONCOPYABLE(SHA1);
constexpr static u32 InitializationHashes[5] { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
constexpr static u32 RoundConstants[4] {
0X5a827999,
0X6ed9eba1,
0X8f1bbcdc,
0Xca62c1d6,
};
}
class SHA1 final : public HashFunction<512, 160> {
public: public:
using HashFunction::update; explicit SHA1(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_sha1(), context)
SHA1()
{ {
reset();
} }
virtual void update(u8 const*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
static DigestType hash(u8 const* data, size_t length)
{
SHA1 sha;
sha.update(data, length);
return sha.digest();
}
static DigestType hash(ByteBuffer const& buffer) { return hash(buffer.data(), buffer.size()); }
static DigestType hash(StringView buffer) { return hash((u8 const*)buffer.characters_without_null_termination(), buffer.length()); }
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return "SHA1"; return "SHA1";
} }
virtual void reset() override
{
m_data_length = 0;
m_bit_length = 0;
for (auto i = 0; i < 5; ++i)
m_state[i] = SHA1Constants::InitializationHashes[i];
}
private:
inline void transform(u8 const*);
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;
}; };
} }

View file

@ -1,375 +0,0 @@
/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2023, Jelle Raaijmakers <jelle@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Types.h>
#include <LibCrypto/Hash/SHA2.h>
namespace Crypto::Hash {
constexpr static auto ROTRIGHT(u32 a, size_t b) { return (a >> b) | (a << (32 - b)); }
constexpr static auto CH(u32 x, u32 y, u32 z) { return (x & y) ^ (z & ~x); }
constexpr static auto MAJ(u32 x, u32 y, u32 z) { return (x & y) ^ (x & z) ^ (y & z); }
constexpr static auto EP0(u32 x) { return ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22); }
constexpr static auto EP1(u32 x) { return ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25); }
constexpr static auto SIGN0(u32 x) { return ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ (x >> 3); }
constexpr static auto SIGN1(u32 x) { return ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ (x >> 10); }
constexpr static auto ROTRIGHT(u64 a, size_t b) { return (a >> b) | (a << (64 - b)); }
constexpr static auto CH(u64 x, u64 y, u64 z) { return (x & y) ^ (z & ~x); }
constexpr static auto MAJ(u64 x, u64 y, u64 z) { return (x & y) ^ (x & z) ^ (y & z); }
constexpr static auto EP0(u64 x) { return ROTRIGHT(x, 28) ^ ROTRIGHT(x, 34) ^ ROTRIGHT(x, 39); }
constexpr static auto EP1(u64 x) { return ROTRIGHT(x, 14) ^ ROTRIGHT(x, 18) ^ ROTRIGHT(x, 41); }
constexpr static auto SIGN0(u64 x) { return ROTRIGHT(x, 1) ^ ROTRIGHT(x, 8) ^ (x >> 7); }
constexpr static auto SIGN1(u64 x) { return ROTRIGHT(x, 19) ^ ROTRIGHT(x, 61) ^ (x >> 6); }
inline void SHA256::transform(u8 const* data)
{
u32 m[64];
size_t i = 0;
for (size_t j = 0; i < 16; ++i, j += 4) {
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | data[j + 3];
}
for (; i < BlockSize; ++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 (i = 0; i < Rounds; ++i) {
auto temp0 = h + EP1(e) + CH(e, f, g) + SHA256Constants::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;
}
template<size_t BlockSize, typename Callback>
void update_buffer(u8* buffer, u8 const* input, size_t length, size_t& data_length, Callback callback)
{
while (length > 0) {
size_t copy_bytes = AK::min(length, BlockSize - data_length);
__builtin_memcpy(buffer + data_length, input, copy_bytes);
input += copy_bytes;
length -= copy_bytes;
data_length += copy_bytes;
if (data_length == BlockSize) {
callback();
data_length = 0;
}
}
}
void SHA256::update(u8 const* message, size_t length)
{
update_buffer<BlockSize>(m_data_buffer, message, length, m_data_length, [&]() {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
});
}
SHA256::DigestType SHA256::digest()
{
auto digest = peek();
reset();
return digest;
}
SHA256::DigestType SHA256::peek()
{
DigestType digest;
size_t i = m_data_length;
if (i < 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 that doesn't support shifting operations
for (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;
digest.data[i + 20] = (m_state[5] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 24] = (m_state[6] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 28] = (m_state[7] >> (24 - i * 8)) & 0x000000ff;
}
return digest;
}
inline void SHA384::transform(u8 const* 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 (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(u8 const* message, size_t length)
{
update_buffer<BlockSize>(m_data_buffer, message, length, m_data_length, [&]() {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
});
}
SHA384::DigestType SHA384::digest()
{
auto digest = peek();
reset();
return digest;
}
SHA384::DigestType SHA384::peek()
{
DigestType digest;
size_t i = m_data_length;
if (i < 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;
// FIXME: Theoretically we should keep track of the number of bits as a u128, now we can only hash up to 2 EiB.
m_data_buffer[BlockSize - 9] = 0;
m_data_buffer[BlockSize - 10] = 0;
m_data_buffer[BlockSize - 11] = 0;
m_data_buffer[BlockSize - 12] = 0;
m_data_buffer[BlockSize - 13] = 0;
m_data_buffer[BlockSize - 14] = 0;
m_data_buffer[BlockSize - 15] = 0;
m_data_buffer[BlockSize - 16] = 0;
transform(m_data_buffer);
// SHA uses big-endian and we assume little-endian
// FIXME: looks like a thing for AK::NetworkOrdered,
// but that doesn't support shifting operations
for (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(u8 const* 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 (i = 0; i < Rounds; ++i) {
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 SHA512::update(u8 const* message, size_t length)
{
update_buffer<BlockSize>(m_data_buffer, message, length, m_data_length, [&]() {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
});
}
SHA512::DigestType SHA512::digest()
{
auto digest = peek();
reset();
return digest;
}
SHA512::DigestType SHA512::peek()
{
DigestType digest;
size_t i = m_data_length;
if (i < 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;
// FIXME: Theoretically we should keep track of the number of bits as a u128, now we can only hash up to 2 EiB.
m_data_buffer[BlockSize - 9] = 0;
m_data_buffer[BlockSize - 10] = 0;
m_data_buffer[BlockSize - 11] = 0;
m_data_buffer[BlockSize - 12] = 0;
m_data_buffer[BlockSize - 13] = 0;
m_data_buffer[BlockSize - 14] = 0;
m_data_buffer[BlockSize - 15] = 0;
m_data_buffer[BlockSize - 16] = 0;
transform(m_data_buffer);
// SHA uses big-endian and we assume little-endian
// FIXME: looks like a thing for AK::NetworkOrdered,
// but that doesn't support shifting operations
for (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;
digest.data[i + 48] = (m_state[6] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 56] = (m_state[7] >> (56 - i * 8)) & 0x000000ff;
}
return digest;
}
}

View file

@ -7,219 +7,53 @@
#pragma once #pragma once
#include <AK/ByteString.h> #include <AK/ByteString.h>
#include <AK/StringBuilder.h> #include <LibCrypto/Hash/OpenSSLHashFunction.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto::Hash { namespace Crypto::Hash {
namespace SHA256Constants { class SHA256 final : public OpenSSLHashFunction<SHA256, 512, 256> {
constexpr static u32 RoundConstants[64] { AK_MAKE_NONCOPYABLE(SHA256);
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
constexpr static u32 InitializationHashes[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
}
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,
0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
constexpr static u64 InitializationHashes[8] = {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
};
}
// FIXME: I want template<size_t BlockSize> but the compiler gets confused
class SHA256 final : public HashFunction<512, 256> {
public: public:
using HashFunction::update; explicit SHA256(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_sha256(), context)
SHA256()
{ {
reset();
} }
virtual void update(u8 const*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
static DigestType hash(u8 const* data, size_t length)
{
SHA256 sha;
sha.update(data, length);
return sha.digest();
}
static DigestType hash(ByteBuffer const& buffer) { return hash(buffer.data(), buffer.size()); }
static DigestType hash(StringView buffer) { return hash((u8 const*)buffer.characters_without_null_termination(), buffer.length()); }
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return ByteString::formatted("SHA{}", DigestSize * 8); return "SHA256";
} }
virtual void reset() override
{
m_data_length = 0;
m_bit_length = 0;
for (size_t i = 0; i < 8; ++i)
m_state[i] = SHA256Constants::InitializationHashes[i];
}
private:
inline void transform(u8 const*);
u8 m_data_buffer[BlockSize] {};
size_t m_data_length { 0 };
u64 m_bit_length { 0 };
u32 m_state[8];
constexpr static auto FinalBlockDataSize = BlockSize - 8;
constexpr static auto Rounds = 64;
}; };
class SHA384 final : public HashFunction<1024, 384> { class SHA384 final : public OpenSSLHashFunction<SHA384, 1024, 384> {
AK_MAKE_NONCOPYABLE(SHA384);
public: public:
using HashFunction::update; explicit SHA384(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_sha384(), context)
SHA384()
{ {
reset();
} }
virtual void update(u8 const*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
static DigestType hash(u8 const* data, size_t length)
{
SHA384 sha;
sha.update(data, length);
return sha.digest();
}
static DigestType hash(ByteBuffer const& buffer) { return hash(buffer.data(), buffer.size()); }
static DigestType hash(StringView buffer) { return hash((u8 const*)buffer.characters_without_null_termination(), buffer.length()); }
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return ByteString::formatted("SHA{}", DigestSize * 8); return "SHA384";
} }
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(u8 const*);
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 - 16;
constexpr static auto Rounds = 80;
}; };
class SHA512 final : public HashFunction<1024, 512> { class SHA512 final : public OpenSSLHashFunction<SHA512, 1024, 512> {
AK_MAKE_NONCOPYABLE(SHA512);
public: public:
using HashFunction::update; explicit SHA512(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_sha512(), context)
SHA512()
{ {
reset();
} }
virtual void update(u8 const*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
static DigestType hash(u8 const* data, size_t length)
{
SHA512 sha;
sha.update(data, length);
return sha.digest();
}
static DigestType hash(ByteBuffer const& buffer) { return hash(buffer.data(), buffer.size()); }
static DigestType hash(StringView buffer) { return hash((u8 const*)buffer.characters_without_null_termination(), buffer.length()); }
virtual ByteString class_name() const override virtual ByteString class_name() const override
{ {
return ByteString::formatted("SHA{}", DigestSize * 8); return "SHA512";
} }
virtual void reset() override
{
m_data_length = 0;
m_bit_length = 0;
for (size_t i = 0; i < 8; ++i)
m_state[i] = SHA512Constants::InitializationHashes[i];
}
private:
inline void transform(u8 const*);
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 - 16;
constexpr static auto Rounds = 80;
}; };
} }

View file

@ -37,9 +37,9 @@ public:
auto ps = TRY(ByteBuffer::create_zeroed(padding_size)); auto ps = TRY(ByteBuffer::create_zeroed(padding_size));
// 4. Let pHash = Hash(P), an octet string of length hLen. // 4. Let pHash = Hash(P), an octet string of length hLen.
HashFunction hash; auto hash = HashFunction::create();
hash.update(parameters); hash->update(parameters);
auto digest = hash.digest(); auto digest = hash->digest();
auto p_hash = digest.bytes(); auto p_hash = digest.bytes();
// 5. Concatenate pHash, PS, the message M, and other padding to form a data block DB as: DB = pHash || PS || 01 || M // 5. Concatenate pHash, PS, the message M, and other padding to form a data block DB as: DB = pHash || PS || 01 || M
@ -92,9 +92,9 @@ public:
return Error::from_string_view("message too long"sv); return Error::from_string_view("message too long"sv);
// 3. If the label L is not provided, let L be the empty string. Let lHash = Hash(L), an octet string of length hLen. // 3. If the label L is not provided, let L be the empty string. Let lHash = Hash(L), an octet string of length hLen.
HashFunction hash; auto hash = HashFunction::create();
hash.update(label); hash->update(label);
auto digest = hash.digest(); auto digest = hash->digest();
auto l_hash = digest.bytes(); auto l_hash = digest.bytes();
// 4. Generate an octet string PS consisting of k - mLen - 2hLen - 2 zero octets. The length of PS may be zero. // 4. Generate an octet string PS consisting of k - mLen - 2hLen - 2 zero octets. The length of PS may be zero.
@ -207,9 +207,9 @@ public:
// 1. If the label L is not provided, let L be the empty string. // 1. If the label L is not provided, let L be the empty string.
// Let lHash = Hash(L), an octet string of length hLen (see the note in Section 7.1.1). // Let lHash = Hash(L), an octet string of length hLen (see the note in Section 7.1.1).
HashFunction hash; auto hash = HashFunction::create();
hash.update(label); hash->update(label);
auto digest = hash.digest(); auto digest = hash->digest();
auto l_hash = digest.bytes(); auto l_hash = digest.bytes();
// 2. Separate the encoded message EM into // 2. Separate the encoded message EM into

View file

@ -437,15 +437,14 @@ static void hmac_pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 co
HMACType hmac(secret); HMACType hmac(secret);
append_label_seed(hmac); append_label_seed(hmac);
constexpr auto digest_size = hmac.digest_size(); auto digest_size = hmac.digest_size();
u8 digest[digest_size]; auto digest_0 = MUST(ByteBuffer::create_uninitialized(digest_size));
auto digest_0 = Bytes { digest, digest_size };
digest_0.overwrite(0, hmac.digest().immutable_data(), digest_size); digest_0.overwrite(0, hmac.digest().immutable_data(), digest_size);
size_t index = 0; size_t index = 0;
while (index < output.size()) { while (index < output.size()) {
hmac.update(digest_0); hmac.update(digest_0.bytes());
append_label_seed(hmac); append_label_seed(hmac);
auto digest_1 = hmac.digest(); auto digest_1 = hmac.digest();
@ -454,7 +453,7 @@ static void hmac_pseudorandom_function(Bytes output, ReadonlyBytes secret, u8 co
output.overwrite(index, digest_1.immutable_data(), copy_size); output.overwrite(index, digest_1.immutable_data(), copy_size);
index += copy_size; index += copy_size;
digest_0.overwrite(0, hmac.process(digest_0).immutable_data(), digest_size); digest_0.overwrite(0, hmac.process(digest_0.bytes()).immutable_data(), digest_size);
} }
} }

View file

@ -15,8 +15,8 @@
TEST_CASE(test_BLAKE2b_name) TEST_CASE(test_BLAKE2b_name)
{ {
Crypto::Hash::BLAKE2b blake2b; auto blake2b = Crypto::Hash::BLAKE2b::create();
EXPECT_EQ(blake2b.class_name(), "BLAKE2b"sv); EXPECT_EQ(blake2b->class_name(), "BLAKE2b"sv);
} }
TEST_CASE(test_BLAKE2b_hash_string) TEST_CASE(test_BLAKE2b_hash_string)
@ -42,37 +42,37 @@ TEST_CASE(test_BLAKE2b_consecutive_multiple_updates)
u8 result[] { u8 result[] {
0x9d, 0xaa, 0x2e, 0x57, 0xc4, 0x94, 0xb6, 0xfd, 0x61, 0x6e, 0x39, 0x0b, 0x71, 0xf4, 0x19, 0x03, 0x41, 0x5c, 0x5c, 0x61, 0x7e, 0x30, 0x0a, 0xf0, 0x0b, 0x3e, 0x9c, 0x77, 0x23, 0x1f, 0x11, 0x4d, 0x83, 0x9d, 0xd6, 0xe0, 0x4a, 0x92, 0x19, 0xae, 0xec, 0xc9, 0x13, 0x57, 0xc6, 0xf1, 0x06, 0x92, 0xb9, 0xf9, 0x97, 0x3e, 0xfd, 0xb3, 0x6f, 0xc8, 0xe1, 0x94, 0xad, 0x8e, 0x33, 0xc2, 0x66, 0x3f 0x9d, 0xaa, 0x2e, 0x57, 0xc4, 0x94, 0xb6, 0xfd, 0x61, 0x6e, 0x39, 0x0b, 0x71, 0xf4, 0x19, 0x03, 0x41, 0x5c, 0x5c, 0x61, 0x7e, 0x30, 0x0a, 0xf0, 0x0b, 0x3e, 0x9c, 0x77, 0x23, 0x1f, 0x11, 0x4d, 0x83, 0x9d, 0xd6, 0xe0, 0x4a, 0x92, 0x19, 0xae, 0xec, 0xc9, 0x13, 0x57, 0xc6, 0xf1, 0x06, 0x92, 0xb9, 0xf9, 0x97, 0x3e, 0xfd, 0xb3, 0x6f, 0xc8, 0xe1, 0x94, 0xad, 0x8e, 0x33, 0xc2, 0x66, 0x3f
}; };
Crypto::Hash::BLAKE2b blake2b; auto blake2b = Crypto::Hash::BLAKE2b::create();
blake2b.update("Well"sv); blake2b->update("Well"sv);
blake2b.update(" hello "sv); blake2b->update(" hello "sv);
blake2b.update("friends"sv); blake2b->update("friends"sv);
auto digest = blake2b.digest(); auto digest = blake2b->digest();
EXPECT(memcmp(result, digest.data, Crypto::Hash::BLAKE2b::digest_size()) == 0); EXPECT(memcmp(result, digest.data, Crypto::Hash::BLAKE2b::digest_size()) == 0);
} }
TEST_CASE(test_BLAKE2b_consecutive_updates_reuse) TEST_CASE(test_BLAKE2b_consecutive_updates_reuse)
{ {
Crypto::Hash::BLAKE2b blake2b; auto blake2b = Crypto::Hash::BLAKE2b::create();
blake2b.update("Well"sv); blake2b->update("Well"sv);
blake2b.update(" hello "sv); blake2b->update(" hello "sv);
blake2b.update("friends"sv); blake2b->update("friends"sv);
auto digest0 = blake2b.digest(); auto digest0 = blake2b->digest();
blake2b.update("Well"sv); blake2b->update("Well"sv);
blake2b.update(" hello "sv); blake2b->update(" hello "sv);
blake2b.update("friends"sv); blake2b->update("friends"sv);
auto digest1 = blake2b.digest(); auto digest1 = blake2b->digest();
EXPECT(memcmp(digest0.data, digest1.data, Crypto::Hash::BLAKE2b::digest_size()) == 0); EXPECT(memcmp(digest0.data, digest1.data, Crypto::Hash::BLAKE2b::digest_size()) == 0);
} }
TEST_CASE(test_MD5_name) TEST_CASE(test_MD5_name)
{ {
Crypto::Hash::MD5 md5; auto md5 = Crypto::Hash::MD5::create();
EXPECT(md5.class_name() == "MD5"); EXPECT(md5->class_name() == "MD5");
} }
TEST_CASE(test_MD5_hash_string) TEST_CASE(test_MD5_hash_string)
@ -125,37 +125,37 @@ TEST_CASE(test_MD5_consecutive_multiple_updates)
u8 result[] { u8 result[] {
0xaf, 0x04, 0x3a, 0x08, 0x94, 0x38, 0x6e, 0x7f, 0xbf, 0x73, 0xe4, 0xaa, 0xf0, 0x8e, 0xee, 0x4c 0xaf, 0x04, 0x3a, 0x08, 0x94, 0x38, 0x6e, 0x7f, 0xbf, 0x73, 0xe4, 0xaa, 0xf0, 0x8e, 0xee, 0x4c
}; };
Crypto::Hash::MD5 md5; auto md5 = Crypto::Hash::MD5::create();
md5.update("Well"sv); md5->update("Well"sv);
md5.update(" hello "sv); md5->update(" hello "sv);
md5.update("friends"sv); md5->update("friends"sv);
auto digest = md5.digest(); auto digest = md5->digest();
EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0); EXPECT(memcmp(result, digest.data, Crypto::Hash::MD5::digest_size()) == 0);
} }
TEST_CASE(test_MD5_consecutive_updates_reuse) TEST_CASE(test_MD5_consecutive_updates_reuse)
{ {
Crypto::Hash::MD5 md5; auto md5 = Crypto::Hash::MD5::create();
md5.update("Well"sv); md5->update("Well"sv);
md5.update(" hello "sv); md5->update(" hello "sv);
md5.update("friends"sv); md5->update("friends"sv);
auto digest0 = md5.digest(); auto digest0 = md5->digest();
md5.update("Well"sv); md5->update("Well"sv);
md5.update(" hello "sv); md5->update(" hello "sv);
md5.update("friends"sv); md5->update("friends"sv);
auto digest1 = md5.digest(); auto digest1 = md5->digest();
EXPECT(memcmp(digest0.data, digest1.data, Crypto::Hash::MD5::digest_size()) == 0); EXPECT(memcmp(digest0.data, digest1.data, Crypto::Hash::MD5::digest_size()) == 0);
} }
TEST_CASE(test_SHA1_name) TEST_CASE(test_SHA1_name)
{ {
Crypto::Hash::SHA1 sha; auto sha = Crypto::Hash::SHA1::create();
EXPECT(sha.class_name() == "SHA1"sv); EXPECT(sha->class_name() == "SHA1"sv);
} }
TEST_CASE(test_SHA1_hash_empty_string) TEST_CASE(test_SHA1_hash_empty_string)
@ -181,21 +181,21 @@ TEST_CASE(test_SHA1_hash_successive_updates)
u8 result[] { u8 result[] {
0xd6, 0x6e, 0xce, 0xd1, 0xf4, 0x08, 0xc6, 0xd8, 0x35, 0xab, 0xf0, 0xc9, 0x05, 0x26, 0xa4, 0xb2, 0xb8, 0xa3, 0x7c, 0xd3 0xd6, 0x6e, 0xce, 0xd1, 0xf4, 0x08, 0xc6, 0xd8, 0x35, 0xab, 0xf0, 0xc9, 0x05, 0x26, 0xa4, 0xb2, 0xb8, 0xa3, 0x7c, 0xd3
}; };
auto hasher = Crypto::Hash::SHA1 {}; auto hasher = Crypto::Hash::SHA1::create();
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaaaaaaaa"sv); hasher->update("aaaaaaaaaaaaaaa"sv);
hasher.update("aaaaaaaaa"sv); hasher->update("aaaaaaaaa"sv);
auto digest = hasher.digest(); auto digest = hasher->digest();
EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA1::digest_size()) == 0); EXPECT(memcmp(result, digest.data, Crypto::Hash::SHA1::digest_size()) == 0);
} }
@ -210,8 +210,8 @@ TEST_CASE(test_SHA1_hash_split_into_blocks)
TEST_CASE(test_SHA256_name) TEST_CASE(test_SHA256_name)
{ {
Crypto::Hash::SHA256 sha; auto sha = Crypto::Hash::SHA256::create();
EXPECT_EQ(sha.class_name(), "SHA256"sv); EXPECT_EQ(sha->class_name(), "SHA256"sv);
} }
TEST_CASE(test_SHA256_hash_string) TEST_CASE(test_SHA256_hash_string)
@ -243,8 +243,8 @@ TEST_CASE(test_SHA256_hash_split_into_blocks)
TEST_CASE(test_SHA384_name) TEST_CASE(test_SHA384_name)
{ {
Crypto::Hash::SHA384 sha; auto sha = Crypto::Hash::SHA384::create();
EXPECT_EQ(sha.class_name(), "SHA384"sv); EXPECT_EQ(sha->class_name(), "SHA384"sv);
} }
TEST_CASE(test_SHA384_hash_string) TEST_CASE(test_SHA384_hash_string)
@ -268,8 +268,8 @@ TEST_CASE(test_SHA384_hash_bug)
TEST_CASE(test_SHA512_name) TEST_CASE(test_SHA512_name)
{ {
Crypto::Hash::SHA512 sha; auto sha = Crypto::Hash::SHA512::create();
EXPECT_EQ(sha.class_name(), "SHA512"sv); EXPECT_EQ(sha->class_name(), "SHA512"sv);
} }
TEST_CASE(test_SHA512_hash_string) TEST_CASE(test_SHA512_hash_string)