mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 09:34:57 +09:00
LibCrypto: Refactor HMAC
implementations with OpenSSL
This commit is contained in:
parent
c5d0af54d0
commit
80fe259dab
Notes:
github-actions[bot]
2025-03-02 14:12:54 +00:00
Author: https://github.com/devgianlu
Commit: 80fe259dab
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3749
Reviewed-by: https://github.com/konradekk
7 changed files with 153 additions and 123 deletions
69
Libraries/LibCrypto/Authentication/HMAC.cpp
Normal file
69
Libraries/LibCrypto/Authentication/HMAC.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/Authentication/HMAC.h>
|
||||
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
namespace Crypto::Authentication {
|
||||
|
||||
HMAC::HMAC(Hash::HashKind hash_kind, ReadonlyBytes key)
|
||||
: m_hash_kind(hash_kind)
|
||||
, m_key(key)
|
||||
, m_mac(EVP_MAC_fetch(nullptr, "HMAC", nullptr))
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
HMAC::~HMAC()
|
||||
{
|
||||
EVP_MAC_free(m_mac);
|
||||
EVP_MAC_CTX_free(m_ctx);
|
||||
}
|
||||
|
||||
size_t HMAC::digest_size() const
|
||||
{
|
||||
return EVP_MAC_CTX_get_mac_size(m_ctx);
|
||||
}
|
||||
|
||||
void HMAC::update(u8 const* message, size_t length)
|
||||
{
|
||||
if (EVP_MAC_update(m_ctx, message, length) != 1) {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuffer HMAC::digest()
|
||||
{
|
||||
auto buf = MUST(ByteBuffer::create_uninitialized(digest_size()));
|
||||
|
||||
auto size = digest_size();
|
||||
if (EVP_MAC_final(m_ctx, buf.data(), &size, size) != 1) {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
return MUST(buf.slice(0, size));
|
||||
}
|
||||
|
||||
void HMAC::reset()
|
||||
{
|
||||
EVP_MAC_CTX_free(m_ctx);
|
||||
m_ctx = EVP_MAC_CTX_new(m_mac);
|
||||
|
||||
auto hash_name = MUST(hash_kind_to_openssl_digest_name(m_hash_kind));
|
||||
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, const_cast<char*>(hash_name.characters_without_null_termination()), hash_name.length()),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
|
||||
if (EVP_MAC_init(m_ctx, m_key.data(), m_key.size(), params) != 1) {
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -8,110 +9,51 @@
|
|||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
constexpr static auto IPAD = 0x36;
|
||||
constexpr static auto OPAD = 0x5c;
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
#include <LibCrypto/OpenSSL.h>
|
||||
#include <LibCrypto/OpenSSLForward.h>
|
||||
|
||||
namespace Crypto::Authentication {
|
||||
|
||||
template<typename HashT>
|
||||
class HMAC {
|
||||
public:
|
||||
using HashType = HashT;
|
||||
using TagType = typename HashType::DigestType;
|
||||
explicit HMAC(Hash::HashKind hash, ReadonlyBytes key);
|
||||
~HMAC();
|
||||
|
||||
size_t digest_size() const { return m_inner_hasher->digest_size(); }
|
||||
size_t digest_size() const;
|
||||
|
||||
template<typename KeyBufferType, typename... Args>
|
||||
HMAC(KeyBufferType key, Args... args)
|
||||
: m_inner_hasher(move(HashT::create(args...)))
|
||||
, m_outer_hasher(move(HashT::create(args...)))
|
||||
{
|
||||
derive_key(key);
|
||||
reset();
|
||||
}
|
||||
void update(u8 const* message, size_t length);
|
||||
void update(ReadonlyBytes span) { return update(span.data(), span.size()); }
|
||||
void update(StringView string) { return update((u8 const*)string.characters_without_null_termination(), string.length()); }
|
||||
|
||||
TagType process(u8 const* message, size_t length)
|
||||
ByteBuffer process(u8 const* message, size_t length)
|
||||
{
|
||||
reset();
|
||||
update(message, length);
|
||||
return digest();
|
||||
}
|
||||
ByteBuffer process(ReadonlyBytes span) { return process(span.data(), span.size()); }
|
||||
ByteBuffer process(StringView string) { return process((u8 const*)string.characters_without_null_termination(), string.length()); }
|
||||
|
||||
void update(u8 const* message, size_t length)
|
||||
{
|
||||
m_inner_hasher->update(message, length);
|
||||
}
|
||||
ByteBuffer digest();
|
||||
|
||||
TagType process(ReadonlyBytes span) { return process(span.data(), span.size()); }
|
||||
TagType process(StringView string) { return process((u8 const*)string.characters_without_null_termination(), string.length()); }
|
||||
|
||||
void update(ReadonlyBytes span) { return update(span.data(), span.size()); }
|
||||
void update(StringView string) { return update((u8 const*)string.characters_without_null_termination(), string.length()); }
|
||||
|
||||
TagType 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());
|
||||
}
|
||||
void reset();
|
||||
|
||||
ByteString class_name() const
|
||||
{
|
||||
auto hash_name = MUST(hash_kind_to_openssl_digest_name(m_hash_kind));
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append("HMAC-"sv);
|
||||
builder.append(m_inner_hasher->class_name());
|
||||
builder.append(hash_name);
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
private:
|
||||
void derive_key(u8 const* key, size_t length)
|
||||
{
|
||||
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);
|
||||
auto key_buffer = v_key.span();
|
||||
// m_key_data is zero'd, so copying the data in
|
||||
// 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();
|
||||
// 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());
|
||||
} else if (length > 0) {
|
||||
key_buffer.overwrite(0, key, length);
|
||||
}
|
||||
|
||||
// fill out the inner and outer padded keys
|
||||
auto* i_key = m_key_data;
|
||||
auto* o_key = m_key_data + block_size;
|
||||
for (size_t i = 0; i < block_size; ++i) {
|
||||
auto key_byte = key_buffer[i];
|
||||
i_key[i] = key_byte ^ IPAD;
|
||||
o_key[i] = key_byte ^ OPAD;
|
||||
}
|
||||
}
|
||||
|
||||
void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); }
|
||||
void derive_key(StringView key) { derive_key(key.bytes()); }
|
||||
|
||||
NonnullOwnPtr<HashType> m_inner_hasher, m_outer_hasher;
|
||||
u8 m_key_data[2048];
|
||||
Hash::HashKind m_hash_kind;
|
||||
ReadonlyBytes m_key;
|
||||
EVP_MAC* m_mac { nullptr };
|
||||
EVP_MAC_CTX* m_ctx { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue