mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibCrypto: Add support for BLAKE2b
This commit is contained in:
parent
da2c18d1f9
commit
1b3ad1c721
Notes:
sideshowbarker
2024-07-17 09:56:35 +09:00
Author: https://github.com/implicitfield Commit: https://github.com/SerenityOS/serenity/commit/1b3ad1c721 Pull-request: https://github.com/SerenityOS/serenity/pull/21112 Reviewed-by: https://github.com/alimpfard
8 changed files with 302 additions and 2 deletions
15
Meta/Lagom/Fuzzers/FuzzBLAKE2b.cpp
Normal file
15
Meta/Lagom/Fuzzers/FuzzBLAKE2b.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/Hash/BLAKE2b.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
|
||||
{
|
||||
Crypto::Hash::BLAKE2b::hash(data, size);
|
||||
return 0;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
set(FUZZER_TARGETS
|
||||
ASN1
|
||||
BLAKE2b
|
||||
BMPLoader
|
||||
Brotli
|
||||
CyrillicDecoder
|
||||
|
@ -70,6 +71,7 @@ if (TARGET LibWeb)
|
|||
endif()
|
||||
|
||||
set(FUZZER_DEPENDENCIES_ASN1 LibCrypto LibTLS)
|
||||
set(FUZZER_DEPENDENCIES_BLAKE2b LibCrypto)
|
||||
set(FUZZER_DEPENDENCIES_BMPLoader LibGfx)
|
||||
set(FUZZER_DEPENDENCIES_Brotli LibCompress)
|
||||
set(FUZZER_DEPENDENCIES_CSSParser LibWeb)
|
||||
|
|
|
@ -6,12 +6,69 @@
|
|||
|
||||
#include <LibCrypto/Authentication/GHash.h>
|
||||
#include <LibCrypto/Authentication/HMAC.h>
|
||||
#include <LibCrypto/Hash/BLAKE2b.h>
|
||||
#include <LibCrypto/Hash/MD5.h>
|
||||
#include <LibCrypto/Hash/SHA1.h>
|
||||
#include <LibCrypto/Hash/SHA2.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <cstring>
|
||||
|
||||
TEST_CASE(test_BLAKE2b_name)
|
||||
{
|
||||
Crypto::Hash::BLAKE2b blake2b;
|
||||
EXPECT_EQ(blake2b.class_name(), "BLAKE2b"sv);
|
||||
}
|
||||
|
||||
TEST_CASE(test_BLAKE2b_hash_string)
|
||||
{
|
||||
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
|
||||
};
|
||||
auto digest = Crypto::Hash::BLAKE2b::hash("Well hello friends"sv);
|
||||
EXPECT(memcmp(result, digest.data, Crypto::Hash::BLAKE2b::digest_size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_BLAKE2b_hash_empty_string)
|
||||
{
|
||||
u8 result[] {
|
||||
0x78, 0x6a, 0x02, 0xf7, 0x42, 0x01, 0x59, 0x03, 0xc6, 0xc6, 0xfd, 0x85, 0x25, 0x52, 0xd2, 0x72, 0x91, 0x2f, 0x47, 0x40, 0xe1, 0x58, 0x47, 0x61, 0x8a, 0x86, 0xe2, 0x17, 0xf7, 0x1f, 0x54, 0x19, 0xd2, 0x5e, 0x10, 0x31, 0xaf, 0xee, 0x58, 0x53, 0x13, 0x89, 0x64, 0x44, 0x93, 0x4e, 0xb0, 0x4b, 0x90, 0x3a, 0x68, 0x5b, 0x14, 0x48, 0xb7, 0x55, 0xd5, 0x6f, 0x70, 0x1a, 0xfe, 0x9b, 0xe2, 0xce
|
||||
};
|
||||
auto digest = Crypto::Hash::BLAKE2b::hash(""sv);
|
||||
EXPECT(memcmp(result, digest.data, Crypto::Hash::BLAKE2b::digest_size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_BLAKE2b_consecutive_multiple_updates)
|
||||
{
|
||||
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
|
||||
};
|
||||
Crypto::Hash::BLAKE2b blake2b;
|
||||
|
||||
blake2b.update("Well"sv);
|
||||
blake2b.update(" hello "sv);
|
||||
blake2b.update("friends"sv);
|
||||
auto digest = blake2b.digest();
|
||||
|
||||
EXPECT(memcmp(result, digest.data, Crypto::Hash::BLAKE2b::digest_size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_BLAKE2b_consecutive_updates_reuse)
|
||||
{
|
||||
Crypto::Hash::BLAKE2b blake2b;
|
||||
|
||||
blake2b.update("Well"sv);
|
||||
blake2b.update(" hello "sv);
|
||||
blake2b.update("friends"sv);
|
||||
auto digest0 = blake2b.digest();
|
||||
|
||||
blake2b.update("Well"sv);
|
||||
blake2b.update(" hello "sv);
|
||||
blake2b.update("friends"sv);
|
||||
auto digest1 = blake2b.digest();
|
||||
|
||||
EXPECT(memcmp(digest0.data, digest1.data, Crypto::Hash::BLAKE2b::digest_size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE(test_MD5_name)
|
||||
{
|
||||
Crypto::Hash::MD5 md5;
|
||||
|
|
|
@ -25,6 +25,7 @@ set(SOURCES
|
|||
Curves/SECP256r1.cpp
|
||||
Curves/X25519.cpp
|
||||
Curves/X448.cpp
|
||||
Hash/BLAKE2b.cpp
|
||||
Hash/MD5.cpp
|
||||
Hash/SHA1.cpp
|
||||
Hash/SHA2.cpp
|
||||
|
|
126
Userland/Libraries/LibCrypto/Hash/BLAKE2b.cpp
Normal file
126
Userland/Libraries/LibCrypto/Hash/BLAKE2b.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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(const u64 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];
|
||||
}
|
||||
|
||||
}
|
93
Userland/Libraries/LibCrypto/Hash/BLAKE2b.h
Normal file
93
Userland/Libraries/LibCrypto/Hash/BLAKE2b.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCrypto/Hash/HashFunction.h>
|
||||
#include <LibCrypto/Hash/SHA2.h>
|
||||
|
||||
#ifndef KERNEL
|
||||
# include <AK/DeprecatedString.h>
|
||||
#endif
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
namespace BLAKE2bConstants {
|
||||
static constexpr auto blockbytes { 128 };
|
||||
static constexpr auto hash_length { 64 };
|
||||
};
|
||||
|
||||
class BLAKE2b final : public HashFunction<1024, 512> {
|
||||
public:
|
||||
using HashFunction::update;
|
||||
|
||||
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()); }
|
||||
|
||||
#ifndef KERNEL
|
||||
virtual DeprecatedString class_name() const override
|
||||
{
|
||||
return "BLAKE2b";
|
||||
}
|
||||
#endif
|
||||
|
||||
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(const u64 amount);
|
||||
void transform(u8 const*);
|
||||
};
|
||||
|
||||
};
|
|
@ -9,6 +9,7 @@
|
|||
#include <AK/Optional.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCrypto/Hash/BLAKE2b.h>
|
||||
#include <LibCrypto/Hash/HashFunction.h>
|
||||
#include <LibCrypto/Hash/MD5.h>
|
||||
#include <LibCrypto/Hash/SHA1.h>
|
||||
|
@ -19,11 +20,12 @@ namespace Crypto::Hash {
|
|||
enum class HashKind {
|
||||
Unknown,
|
||||
None,
|
||||
BLAKE2b,
|
||||
MD5,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
MD5,
|
||||
};
|
||||
|
||||
struct MultiHashDigestVariant {
|
||||
|
@ -132,6 +134,9 @@ public:
|
|||
|
||||
m_kind = kind;
|
||||
switch (kind) {
|
||||
case HashKind::BLAKE2b:
|
||||
m_algorithm = BLAKE2b();
|
||||
break;
|
||||
case HashKind::MD5:
|
||||
m_algorithm = MD5();
|
||||
break;
|
||||
|
@ -211,7 +216,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
using AlgorithmVariant = Variant<Empty, MD5, SHA1, SHA256, SHA384, SHA512>;
|
||||
using AlgorithmVariant = Variant<Empty, BLAKE2b, MD5, SHA1, SHA256, SHA384, SHA512>;
|
||||
AlgorithmVariant m_algorithm {};
|
||||
HashKind m_kind { HashKind::None };
|
||||
ByteBuffer m_pre_init_buffer;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// TODO: Look into generating this from the authoritative list of fuzzing targets in fuzzer.cmake.
|
||||
#define ENUMERATE_TARGETS(T) \
|
||||
T(ASN1) \
|
||||
T(BLAKE2b) \
|
||||
T(BMPLoader) \
|
||||
T(Brotli) \
|
||||
T(CSSParser) \
|
||||
|
|
Loading…
Reference in a new issue