Skip to content

Commit

Permalink
LibCrypto: Replace all hashes implementation with OpenSSL
Browse files Browse the repository at this point in the history
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!
  • Loading branch information
devgianlu committed Dec 20, 2024
1 parent 2e5de2d commit 1d91da6
Show file tree
Hide file tree
Showing 16 changed files with 164 additions and 1,350 deletions.
32 changes: 16 additions & 16 deletions Libraries/LibCrypto/Authentication/HMAC.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ class HMAC {
using HashType = HashT;
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>
HMAC(KeyBufferType key, Args... args)
: m_inner_hasher(args...)
, m_outer_hasher(args...)
: m_inner_hasher(move(HashT::create(args...)))
, m_outer_hasher(move(HashT::create(args...)))
{
derive_key(key);
reset();
Expand All @@ -44,7 +44,7 @@ class HMAC {

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()); }
Expand All @@ -55,32 +55,32 @@ class HMAC {

TagType digest()
{
m_outer_hasher.update(m_inner_hasher.digest().immutable_data(), m_inner_hasher.digest_size());
auto result = m_outer_hasher.digest();
m_outer_hasher->update(m_inner_hasher->digest().immutable_data(), m_inner_hasher->digest_size());
auto result = m_outer_hasher->digest();
reset();
return result;
}

void reset()
{
m_inner_hasher.reset();
m_outer_hasher.reset();
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_inner_hasher->reset();
m_outer_hasher->reset();
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());
}

ByteString class_name() const
{
StringBuilder builder;
builder.append("HMAC-"sv);
builder.append(m_inner_hasher.class_name());
builder.append(m_inner_hasher->class_name());
return builder.to_byte_string();
}

private:
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.
Vector<u8, 64> v_key;
v_key.resize(block_size);
Expand All @@ -89,10 +89,10 @@ class HMAC {
// the first few bytes leaves the rest zero, which
// is exactly what we want (zero padding)
if (length > block_size) {
m_inner_hasher.update(key, length);
auto digest = m_inner_hasher.digest();
m_inner_hasher->update(key, length);
auto digest = m_inner_hasher->digest();
// 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) {
key_buffer.overwrite(0, key, length);
}
Expand All @@ -110,7 +110,7 @@ class HMAC {
void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); }
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];
};

Expand Down
4 changes: 0 additions & 4 deletions Libraries/LibCrypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ set(SOURCES
Curves/Ed25519.cpp
Curves/X25519.cpp
Curves/X448.cpp
Hash/BLAKE2b.cpp
Hash/MD5.cpp
Hash/SHA1.cpp
Hash/SHA2.cpp
NumberTheory/ModularFunctions.cpp
PK/RSA.cpp
PK/EC.cpp
Expand Down
26 changes: 13 additions & 13 deletions Libraries/LibCrypto/Curves/Ed25519.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ ErrorOr<ByteBuffer> Ed25519::sign(ReadonlyBytes public_key, ReadonlyBytes privat
memcpy(p, h.data + 32, 32);

// 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
hash.update(p, 32);
hash->update(p, 32);
// 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.
// 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);

// 3. Compute the point [r]B.
Expand All @@ -100,13 +100,13 @@ ErrorOr<ByteBuffer> Ed25519::sign(ReadonlyBytes public_key, ReadonlyBytes privat
// 4. Compute SHA512(dom2(F, C) || R || A || PH(M)),
// NOTE: We can reuse hash here, since digest() calls reset()
// 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
hash.update(public_key.data(), public_key.size());
hash->update(public_key.data(), public_key.size());
// 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.
memcpy(k, digest.data, 64);

Expand Down Expand Up @@ -160,15 +160,15 @@ bool Ed25519::verify(ReadonlyBytes public_key, ReadonlyBytes signature, Readonly
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.
Crypto::Hash::SHA512 hash;
auto hash = Hash::SHA512::create();
// 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
hash.update(public_key.data(), key_size());
hash->update(public_key.data(), key_size());
// 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;

// 3. Check the group equation [8][S]B = [8]R + [8][k]A'.
Expand Down
126 changes: 0 additions & 126 deletions Libraries/LibCrypto/Hash/BLAKE2b.cpp

This file was deleted.

69 changes: 5 additions & 64 deletions Libraries/LibCrypto/Hash/BLAKE2b.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,82 +7,23 @@
#pragma once

#include <AK/ByteString.h>
#include <LibCrypto/Hash/HashFunction.h>
#include <LibCrypto/Hash/SHA2.h>
#include <LibCrypto/Hash/OpenSSLHashFunction.h>

namespace Crypto::Hash {

namespace BLAKE2bConstants {
static constexpr auto blockbytes { 128 };
static constexpr auto hash_length { 64 };
};
class BLAKE2b final : public OpenSSLHashFunction<BLAKE2b, 1024, 512> {
AK_MAKE_NONCOPYABLE(BLAKE2b);

class BLAKE2b final : public HashFunction<1024, 512> {
public:
using HashFunction::update;

BLAKE2b()
explicit BLAKE2b(EVP_MD_CTX* context)
: OpenSSLHashFunction(EVP_blake2b512(), context)
{
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
{
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*);
};

};
Loading

0 comments on commit 1d91da6

Please sign in to comment.