Skip to content

Commit 0daf11c

Browse files
committed
LibCrypto: Refactor HMAC implementations with OpenSSL
1 parent 48d5849 commit 0daf11c

File tree

7 files changed

+139
-122
lines changed

7 files changed

+139
-122
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2025, Altomani Gianluca <[email protected]>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <LibCrypto/Authentication/HMAC.h>
8+
9+
#include <openssl/core_names.h>
10+
#include <openssl/evp.h>
11+
12+
namespace Crypto::Authentication {
13+
14+
HMAC::HMAC(Hash::HashKind hash_kind, ReadonlyBytes key)
15+
: m_hash_kind(hash_kind)
16+
, m_key(key)
17+
, m_mac(EVP_MAC_fetch(nullptr, "HMAC", nullptr))
18+
{
19+
reset();
20+
}
21+
22+
HMAC::~HMAC()
23+
{
24+
EVP_MAC_free(m_mac);
25+
EVP_MAC_CTX_free(m_ctx);
26+
}
27+
28+
size_t HMAC::digest_size() const
29+
{
30+
return EVP_MAC_CTX_get_mac_size(m_ctx);
31+
}
32+
33+
void HMAC::update(u8 const* message, size_t length)
34+
{
35+
if (EVP_MAC_update(m_ctx, message, length) != 1) {
36+
VERIFY_NOT_REACHED();
37+
}
38+
}
39+
40+
ByteBuffer HMAC::digest()
41+
{
42+
auto buf = MUST(ByteBuffer::create_uninitialized(digest_size()));
43+
44+
auto size = digest_size();
45+
if (EVP_MAC_final(m_ctx, buf.data(), &size, size) != 1) {
46+
VERIFY_NOT_REACHED();
47+
}
48+
49+
return MUST(buf.slice(0, size));
50+
}
51+
52+
void HMAC::reset()
53+
{
54+
EVP_MAC_CTX_free(m_ctx);
55+
m_ctx = EVP_MAC_CTX_new(m_mac);
56+
57+
auto hash_name = MUST(hash_kind_to_openssl_digest_name(m_hash_kind));
58+
59+
OSSL_PARAM params[] = {
60+
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, const_cast<char*>(hash_name.characters_without_null_termination()), hash_name.length()),
61+
OSSL_PARAM_END
62+
};
63+
64+
if (EVP_MAC_init(m_ctx, m_key.data(), m_key.size(), params) != 1) {
65+
VERIFY_NOT_REACHED();
66+
}
67+
}
68+
69+
}
Lines changed: 22 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2020, Ali Mohammad Pur <[email protected]>
3+
* Copyright (c) 2025, Altomani Gianluca <[email protected]>
34
*
45
* SPDX-License-Identifier: BSD-2-Clause
56
*/
@@ -8,110 +9,51 @@
89

910
#include <AK/ByteBuffer.h>
1011
#include <AK/ByteString.h>
11-
#include <AK/StringBuilder.h>
12-
#include <AK/StringView.h>
13-
#include <AK/Types.h>
14-
#include <AK/Vector.h>
15-
16-
constexpr static auto IPAD = 0x36;
17-
constexpr static auto OPAD = 0x5c;
12+
#include <LibCrypto/Hash/HashManager.h>
13+
#include <LibCrypto/OpenSSL.h>
14+
#include <LibCrypto/OpenSSLForward.h>
1815

1916
namespace Crypto::Authentication {
2017

21-
template<typename HashT>
2218
class HMAC {
2319
public:
24-
using HashType = HashT;
25-
using TagType = typename HashType::DigestType;
20+
explicit HMAC(Hash::HashKind hash, ReadonlyBytes key);
21+
~HMAC();
2622

27-
size_t digest_size() const { return m_inner_hasher->digest_size(); }
23+
size_t digest_size() const;
2824

29-
template<typename KeyBufferType, typename... Args>
30-
HMAC(KeyBufferType key, Args... args)
31-
: m_inner_hasher(move(HashT::create(args...)))
32-
, m_outer_hasher(move(HashT::create(args...)))
33-
{
34-
derive_key(key);
35-
reset();
36-
}
25+
void update(u8 const* message, size_t length);
26+
void update(ReadonlyBytes span) { return update(span.data(), span.size()); }
27+
void update(StringView string) { return update((u8 const*)string.characters_without_null_termination(), string.length()); }
3728

38-
TagType process(u8 const* message, size_t length)
29+
ByteBuffer process(u8 const* message, size_t length)
3930
{
4031
reset();
4132
update(message, length);
4233
return digest();
4334
}
35+
ByteBuffer process(ReadonlyBytes span) { return process(span.data(), span.size()); }
36+
ByteBuffer process(StringView string) { return process((u8 const*)string.characters_without_null_termination(), string.length()); }
4437

45-
void update(u8 const* message, size_t length)
46-
{
47-
m_inner_hasher->update(message, length);
48-
}
49-
50-
TagType process(ReadonlyBytes span) { return process(span.data(), span.size()); }
51-
TagType process(StringView string) { return process((u8 const*)string.characters_without_null_termination(), string.length()); }
52-
53-
void update(ReadonlyBytes span) { return update(span.data(), span.size()); }
54-
void update(StringView string) { return update((u8 const*)string.characters_without_null_termination(), string.length()); }
55-
56-
TagType digest()
57-
{
58-
m_outer_hasher->update(m_inner_hasher->digest().immutable_data(), m_inner_hasher->digest_size());
59-
auto result = m_outer_hasher->digest();
60-
reset();
61-
return result;
62-
}
38+
ByteBuffer digest();
6339

64-
void reset()
65-
{
66-
m_inner_hasher->reset();
67-
m_outer_hasher->reset();
68-
m_inner_hasher->update(m_key_data, m_inner_hasher->block_size());
69-
m_outer_hasher->update(m_key_data + m_inner_hasher->block_size(), m_outer_hasher->block_size());
70-
}
40+
void reset();
7141

7242
ByteString class_name() const
7343
{
44+
auto hash_name = MUST(hash_kind_to_openssl_digest_name(m_hash_kind));
45+
7446
StringBuilder builder;
7547
builder.append("HMAC-"sv);
76-
builder.append(m_inner_hasher->class_name());
48+
builder.append(hash_name);
7749
return builder.to_byte_string();
7850
}
7951

8052
private:
81-
void derive_key(u8 const* key, size_t length)
82-
{
83-
auto block_size = m_inner_hasher->block_size();
84-
// Note: The block size of all the current hash functions is 512 bits.
85-
Vector<u8, 64> v_key;
86-
v_key.resize(block_size);
87-
auto key_buffer = v_key.span();
88-
// m_key_data is zero'd, so copying the data in
89-
// the first few bytes leaves the rest zero, which
90-
// is exactly what we want (zero padding)
91-
if (length > block_size) {
92-
m_inner_hasher->update(key, length);
93-
auto digest = m_inner_hasher->digest();
94-
// FIXME: should we check if the hash function creates more data than its block size?
95-
key_buffer.overwrite(0, digest.immutable_data(), m_inner_hasher->digest_size());
96-
} else if (length > 0) {
97-
key_buffer.overwrite(0, key, length);
98-
}
99-
100-
// fill out the inner and outer padded keys
101-
auto* i_key = m_key_data;
102-
auto* o_key = m_key_data + block_size;
103-
for (size_t i = 0; i < block_size; ++i) {
104-
auto key_byte = key_buffer[i];
105-
i_key[i] = key_byte ^ IPAD;
106-
o_key[i] = key_byte ^ OPAD;
107-
}
108-
}
109-
110-
void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); }
111-
void derive_key(StringView key) { derive_key(key.bytes()); }
112-
113-
NonnullOwnPtr<HashType> m_inner_hasher, m_outer_hasher;
114-
u8 m_key_data[2048];
53+
Hash::HashKind m_hash_kind;
54+
ReadonlyBytes m_key;
55+
EVP_MAC* m_mac { nullptr };
56+
EVP_MAC_CTX* m_ctx { nullptr };
11557
};
11658

11759
}

Libraries/LibCrypto/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(SOURCES
66
ASN1/DER.cpp
77
ASN1/PEM.cpp
88
Authentication/GHash.cpp
9+
Authentication/HMAC.cpp
910
BigFraction/BigFraction.cpp
1011
BigInt/Algorithms/BitwiseOperations.cpp
1112
BigInt/Algorithms/Division.cpp

Libraries/LibCrypto/OpenSSL.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ ErrorOr<UnsignedBigInteger> openssl_bignum_to_unsigned_big_integer(OpenSSL_BN co
4747
ErrorOr<StringView> hash_kind_to_openssl_digest_name(Hash::HashKind hash)
4848
{
4949
switch (hash) {
50+
case Hash::HashKind::MD5:
51+
return "MD5"sv;
5052
case Hash::HashKind::SHA1:
5153
return "SHA1"sv;
5254
case Hash::HashKind::SHA256:

Libraries/LibCrypto/OpenSSLForward.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ typedef struct evp_pkey_st EVP_PKEY;
1515
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
1616
typedef struct evp_kdf_st EVP_KDF;
1717
typedef struct evp_kdf_ctx_st EVP_KDF_CTX;
18+
typedef struct evp_mac_st EVP_MAC;
19+
typedef struct evp_mac_ctx_st EVP_MAC_CTX;
1820

1921
void ERR_print_errors_cb(int (*cb)(char const* str, size_t len, void* u), void* u);
2022

Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7781,21 +7781,21 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> X448::import_key(
77817781

77827782
static WebIDL::ExceptionOr<ByteBuffer> hmac_calculate_message_digest(JS::Realm& realm, GC::Ptr<KeyAlgorithm> hash, ReadonlyBytes key, ReadonlyBytes message)
77837783
{
7784-
auto calculate_digest = [&]<typename T>() -> ByteBuffer {
7785-
::Crypto::Authentication::HMAC<T> hmac(key);
7786-
auto digest = hmac.process(message);
7787-
return MUST(ByteBuffer::copy(digest.bytes()));
7788-
};
77897784
auto hash_name = hash->name();
7790-
if (hash_name == "SHA-1")
7791-
return calculate_digest.operator()<::Crypto::Hash::SHA1>();
7792-
if (hash_name == "SHA-256")
7793-
return calculate_digest.operator()<::Crypto::Hash::SHA256>();
7794-
if (hash_name == "SHA-384")
7795-
return calculate_digest.operator()<::Crypto::Hash::SHA384>();
7796-
if (hash_name == "SHA-512")
7797-
return calculate_digest.operator()<::Crypto::Hash::SHA512>();
7798-
return WebIDL::NotSupportedError::create(realm, "Invalid algorithm"_string);
7785+
auto hash_kind = TRY([&] -> WebIDL::ExceptionOr<::Crypto::Hash::HashKind> {
7786+
if (hash_name == "SHA-1")
7787+
return ::Crypto::Hash::HashKind::SHA1;
7788+
if (hash_name == "SHA-256")
7789+
return ::Crypto::Hash::HashKind::SHA256;
7790+
if (hash_name == "SHA-384")
7791+
return ::Crypto::Hash::HashKind::SHA384;
7792+
if (hash_name == "SHA-512")
7793+
return ::Crypto::Hash::HashKind::SHA512;
7794+
return WebIDL::NotSupportedError::create(realm, MUST(String::formatted("Invalid hash function '{}'", hash_name)));
7795+
}());
7796+
7797+
::Crypto::Authentication::HMAC hmac(hash_kind, key);
7798+
return hmac.process(message);
77997799
}
78007800

78017801
static WebIDL::ExceptionOr<WebIDL::UnsignedLong> hmac_hash_block_size(JS::Realm& realm, HashAlgorithmIdentifier hash)

0 commit comments

Comments
 (0)