mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 11:36:10 +00:00
LibCrypto+LibWeb: Refactor HKDF
and PBKDF2
classes with OpenSSL
This commit is contained in:
parent
6d29a32fad
commit
e90d2a5713
Notes:
github-actions[bot]
2025-02-24 10:12:08 +00:00
Author: https://github.com/devgianlu Commit: https://github.com/LadybirdBrowser/ladybird/commit/e90d2a57137 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3671 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/alimpfard
11 changed files with 210 additions and 185 deletions
|
@ -25,7 +25,9 @@ set(SOURCES
|
|||
Curves/EdwardsCurve.cpp
|
||||
Curves/SECPxxxr1.cpp
|
||||
Hash/BLAKE2b.cpp
|
||||
Hash/HKDF.cpp
|
||||
Hash/MD5.cpp
|
||||
Hash/PBKDF2.cpp
|
||||
Hash/SHA1.cpp
|
||||
Hash/SHA2.cpp
|
||||
NumberTheory/ModularFunctions.cpp
|
||||
|
|
45
Libraries/LibCrypto/Hash/HKDF.cpp
Normal file
45
Libraries/LibCrypto/Hash/HKDF.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/Hash/HKDF.h>
|
||||
#include <LibCrypto/OpenSSL.h>
|
||||
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/params.h>
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
HKDF::HKDF(HashKind hash_kind)
|
||||
: m_kdf(EVP_KDF_fetch(nullptr, "HKDF", nullptr))
|
||||
, m_hash_kind(hash_kind)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> HKDF::derive_key(Optional<ReadonlyBytes> maybe_salt, ReadonlyBytes key, ReadonlyBytes info, u32 key_length_bytes)
|
||||
{
|
||||
auto hash_name = TRY(hash_kind_to_openssl_digest_name(m_hash_kind));
|
||||
|
||||
auto ctx = TRY(OpenSSL_KDF_CTX::wrap(EVP_KDF_CTX_new(m_kdf)));
|
||||
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast<char*>(hash_name.characters_without_null_termination()), hash_name.length()),
|
||||
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, const_cast<u8*>(key.data()), key.size()),
|
||||
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, const_cast<u8*>(info.data()), info.size()),
|
||||
OSSL_PARAM_END,
|
||||
OSSL_PARAM_END,
|
||||
};
|
||||
if (maybe_salt.has_value()) {
|
||||
params[3] = OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, const_cast<u8*>(maybe_salt->data()), maybe_salt->size());
|
||||
}
|
||||
|
||||
auto buf = TRY(ByteBuffer::create_uninitialized(key_length_bytes));
|
||||
OPENSSL_TRY(EVP_KDF_derive(ctx.ptr(), buf.data(), key_length_bytes, params));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2023, stelar7 <dudedbz@gmail.com>
|
||||
* Copyright (c) 2024, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCrypto/Authentication/HMAC.h>
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5869#section-2
|
||||
template<typename HashT>
|
||||
class HKDF {
|
||||
public:
|
||||
using HashType = HashT;
|
||||
using DigestType = typename HashType::DigestType;
|
||||
using HMACType = typename Crypto::Authentication::HMAC<HashType>;
|
||||
HKDF(HashKind hash_kind);
|
||||
|
||||
~HKDF()
|
||||
{
|
||||
EVP_KDF_free(m_kdf);
|
||||
}
|
||||
|
||||
// Note: The output is different for a salt of length zero and an absent salt,
|
||||
// so Optional<ReadonlyBytes> really is the correct type.
|
||||
static ErrorOr<ByteBuffer> derive_key(Optional<ReadonlyBytes> maybe_salt, ReadonlyBytes input_keying_material, ReadonlyBytes info, u32 output_key_length)
|
||||
{
|
||||
if (output_key_length > 255 * DigestType::Size) {
|
||||
return Error::from_string_view("requested output_key_length is too large"sv);
|
||||
}
|
||||
// Note that it feels like we should also refuse to run with output_key_length == 0,
|
||||
// but the spec allows this.
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5869#section-2.1
|
||||
// Note that in the extract step, 'IKM' is used as the HMAC input, not as the HMAC key.
|
||||
|
||||
// salt: optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros.
|
||||
ByteBuffer salt_buffer;
|
||||
auto salt = maybe_salt.value_or_lazy_evaluated([&] {
|
||||
salt_buffer.resize(DigestType::Size, ByteBuffer::ZeroFillNewElements::Yes);
|
||||
return salt_buffer.bytes();
|
||||
});
|
||||
HMACType hmac_salt(salt);
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5869#section-2.2
|
||||
// PRK = HMAC-Hash(salt, IKM)
|
||||
auto prk_digest = hmac_salt.process(input_keying_material);
|
||||
auto prk = prk_digest.bytes();
|
||||
ASSERT(prk.size() == DigestType::Size);
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5869#section-2.3
|
||||
// N = ceil(L/HashLen)
|
||||
auto num_iterations = ceil_div(static_cast<size_t>(output_key_length), DigestType::Size);
|
||||
// T = T(1) | T(2) | T(3) | ... | T(N)
|
||||
ByteBuffer output_buffer;
|
||||
// where:
|
||||
// T(0) = empty string (zero length)
|
||||
// T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
||||
// T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
|
||||
// T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
|
||||
HMACType hmac_prk(prk);
|
||||
// In iteration i we compute T(i), and deduce T(i - 1) from 'output_buffer'.
|
||||
// Hence, we do not need to run i == 0.
|
||||
// INVARIANT: At the beginning of each iteration, hmac_prk is freshly reset.
|
||||
// For the first iteration, this is given by the constructor of HMAC.
|
||||
for (size_t i = 1; i < 1 + num_iterations; ++i) {
|
||||
if (i > 1) {
|
||||
auto t_i_minus_one = output_buffer.bytes().slice_from_end(DigestType::Size);
|
||||
hmac_prk.update(t_i_minus_one);
|
||||
}
|
||||
hmac_prk.update(info);
|
||||
u8 const pad_byte = static_cast<u8>(i & 0xff);
|
||||
hmac_prk.update(ReadonlyBytes(&pad_byte, 1));
|
||||
auto t_i_digest = hmac_prk.digest();
|
||||
output_buffer.append(t_i_digest.bytes());
|
||||
}
|
||||
|
||||
// OKM = first L octets of T
|
||||
ASSERT(output_buffer.size() >= output_key_length);
|
||||
output_buffer.trim(output_key_length, false);
|
||||
|
||||
// 5. Output the derived key DK
|
||||
return { output_buffer };
|
||||
}
|
||||
ErrorOr<ByteBuffer> derive_key(Optional<ReadonlyBytes> maybe_salt, ReadonlyBytes input_keying_material, ReadonlyBytes info, u32 key_length_bytes);
|
||||
|
||||
private:
|
||||
HKDF() = delete;
|
||||
EVP_KDF* m_kdf;
|
||||
HashKind m_hash_kind;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
42
Libraries/LibCrypto/Hash/PBKDF2.cpp
Normal file
42
Libraries/LibCrypto/Hash/PBKDF2.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/Hash/PBKDF2.h>
|
||||
#include <LibCrypto/OpenSSL.h>
|
||||
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/params.h>
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
PBKDF2::PBKDF2(HashKind hash_kind)
|
||||
: m_kdf(EVP_KDF_fetch(nullptr, "PBKDF2", nullptr))
|
||||
, m_hash_kind(hash_kind)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> PBKDF2::derive_key(ReadonlyBytes password, ReadonlyBytes salt, u32 iterations, u32 key_length_bytes)
|
||||
{
|
||||
auto hash_name = TRY(hash_kind_to_openssl_digest_name(m_hash_kind));
|
||||
|
||||
auto ctx = TRY(OpenSSL_KDF_CTX::wrap(EVP_KDF_CTX_new(m_kdf)));
|
||||
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast<char*>(hash_name.characters_without_null_termination()), hash_name.length()),
|
||||
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, const_cast<u8*>(password.data()), password.size()),
|
||||
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, const_cast<u8*>(salt.data()), salt.size()),
|
||||
OSSL_PARAM_uint32(OSSL_KDF_PARAM_ITER, &iterations),
|
||||
OSSL_PARAM_END,
|
||||
};
|
||||
|
||||
auto buf = TRY(ByteBuffer::create_uninitialized(key_length_bytes));
|
||||
OPENSSL_TRY(EVP_KDF_derive(ctx.ptr(), buf.data(), key_length_bytes, params));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,84 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2023, stelar7 <dudedbz@gmail.com>
|
||||
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Math.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
|
||||
namespace Crypto::Hash {
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc2898#section-5.2
|
||||
class PBKDF2 {
|
||||
public:
|
||||
template<typename PRF>
|
||||
static ErrorOr<ByteBuffer> derive_key(ReadonlyBytes password, ReadonlyBytes salt, u32 iterations, u32 key_length_bytes)
|
||||
requires requires(PRF t) {
|
||||
t.digest_size();
|
||||
}
|
||||
PBKDF2(HashKind hash_kind);
|
||||
|
||||
~PBKDF2()
|
||||
{
|
||||
PRF prf(password);
|
||||
|
||||
// Note: hLen denotes the length in octets of the pseudorandom function output
|
||||
u32 h_len = prf.digest_size();
|
||||
|
||||
// 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop.
|
||||
if (key_length_bytes > (AK::pow(2.0, 32.0) - 1) * h_len)
|
||||
return Error::from_string_literal("derived key too long");
|
||||
|
||||
// 2 . Let l be the number of hLen-octet blocks in the derived key rounding up,
|
||||
// and let r be the number of octets in the last block
|
||||
u32 l = AK::ceil_div(key_length_bytes, h_len);
|
||||
u32 r = key_length_bytes - (l - 1) * h_len;
|
||||
|
||||
// 3. For each block of the derived key apply the function F defined
|
||||
// below to the password P, the salt S, the iteration count c, and
|
||||
// the block index to compute the block:
|
||||
|
||||
ByteBuffer ui = TRY(ByteBuffer::create_zeroed(h_len));
|
||||
ByteBuffer ti = TRY(ByteBuffer::create_zeroed(h_len));
|
||||
ByteBuffer key = TRY(ByteBuffer::create_zeroed(key_length_bytes));
|
||||
|
||||
// T_i = F (P, S, c, i)
|
||||
u8 iteration_bytes[4];
|
||||
for (u32 i = 1; i <= l; i++) {
|
||||
iteration_bytes[3] = i;
|
||||
iteration_bytes[2] = ((i >> 8) & 0xff);
|
||||
iteration_bytes[1] = ((i >> 16) & 0xff);
|
||||
iteration_bytes[0] = ((i >> 24) & 0xff);
|
||||
|
||||
prf.update(salt);
|
||||
prf.update(ReadonlyBytes { iteration_bytes, 4 });
|
||||
auto digest = prf.digest();
|
||||
ui.overwrite(0, digest.immutable_data(), h_len);
|
||||
ti.overwrite(0, digest.immutable_data(), h_len);
|
||||
|
||||
// U_1 = PRF (P, S || INT (i))
|
||||
// U_j = PRF (P, U_{j-1})
|
||||
|
||||
// F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
|
||||
for (u32 j = 2; j <= iterations; j++) {
|
||||
prf.update(ui.bytes());
|
||||
auto digest_inner = prf.digest();
|
||||
ui.overwrite(0, digest_inner.immutable_data(), h_len);
|
||||
|
||||
UnsignedBigInteger ti_temp = UnsignedBigInteger::import_data(ti.data(), ti.size());
|
||||
UnsignedBigInteger ui_temp = UnsignedBigInteger::import_data(ui.data(), ui.size());
|
||||
UnsignedBigInteger r_temp = ti_temp.bitwise_xor(ui_temp);
|
||||
|
||||
r_temp.export_data(ti.bytes());
|
||||
}
|
||||
|
||||
// 4. Concatenate the blocks and extract the first dkLen octets to produce a derived key DK:
|
||||
key.overwrite((i - 1) * h_len, ti.data(), i == l ? r : h_len);
|
||||
}
|
||||
|
||||
// 5. Output the derived key DK
|
||||
return key;
|
||||
EVP_KDF_free(m_kdf);
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> derive_key(ReadonlyBytes password, ReadonlyBytes salt, u32 iterations, u32 key_length_bytes);
|
||||
|
||||
private:
|
||||
EVP_KDF* m_kdf;
|
||||
HashKind m_hash_kind;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -44,4 +44,20 @@ ErrorOr<UnsignedBigInteger> openssl_bignum_to_unsigned_big_integer(OpenSSL_BN co
|
|||
return UnsignedBigInteger::import_data(buf.bytes().data(), size);
|
||||
}
|
||||
|
||||
ErrorOr<StringView> hash_kind_to_openssl_digest_name(Hash::HashKind hash)
|
||||
{
|
||||
switch (hash) {
|
||||
case Hash::HashKind::SHA1:
|
||||
return "SHA1"sv;
|
||||
case Hash::HashKind::SHA256:
|
||||
return "SHA256"sv;
|
||||
case Hash::HashKind::SHA384:
|
||||
return "SHA384"sv;
|
||||
case Hash::HashKind::SHA512:
|
||||
return "SHA512"sv;
|
||||
default:
|
||||
return Error::from_string_literal("Unsupported hash kind");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
* Copyright (c) 2024-2025, Altomani Gianluca <altomanigianluca@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -9,6 +9,7 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibCrypto/Hash/HashManager.h>
|
||||
#include <LibCrypto/OpenSSLForward.h>
|
||||
|
||||
inline int openssl_print_errors(char const* str, size_t len, [[maybe_unused]] void* u)
|
||||
|
@ -106,9 +107,15 @@ public:
|
|||
static ErrorOr<OpenSSL_MD_CTX> create();
|
||||
};
|
||||
|
||||
class OpenSSL_KDF_CTX {
|
||||
OPENSSL_WRAPPER_CLASS(OpenSSL_KDF_CTX, EVP_KDF_CTX, EVP_KDF_CTX);
|
||||
};
|
||||
|
||||
#undef OPENSSL_WRAPPER_CLASS
|
||||
|
||||
ErrorOr<OpenSSL_BN> unsigned_big_integer_to_openssl_bignum(UnsignedBigInteger const& integer);
|
||||
ErrorOr<UnsignedBigInteger> openssl_bignum_to_unsigned_big_integer(OpenSSL_BN const& bn);
|
||||
|
||||
ErrorOr<StringView> hash_kind_to_openssl_digest_name(Hash::HashKind hash);
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ typedef struct evp_md_st EVP_MD;
|
|||
typedef struct evp_md_ctx_st EVP_MD_CTX;
|
||||
typedef struct evp_pkey_st EVP_PKEY;
|
||||
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
|
||||
typedef struct evp_kdf_st EVP_KDF;
|
||||
typedef struct evp_kdf_ctx_st EVP_KDF_CTX;
|
||||
|
||||
void ERR_print_errors_cb(int (*cb)(char const* str, size_t len, void* u), void* u);
|
||||
|
||||
|
@ -24,8 +26,8 @@ int EVP_DigestFinal_ex(EVP_MD_CTX*, unsigned char*, unsigned int*);
|
|||
int EVP_MD_CTX_copy_ex(EVP_MD_CTX*, EVP_MD_CTX const*);
|
||||
|
||||
void EVP_PKEY_CTX_free(EVP_PKEY_CTX*);
|
||||
|
||||
void EVP_PKEY_free(EVP_PKEY*);
|
||||
|
||||
void EVP_KDF_CTX_free(EVP_KDF_CTX* ctx);
|
||||
void EVP_KDF_free(EVP_KDF* kdf);
|
||||
void BN_free(BIGNUM*);
|
||||
}
|
||||
|
|
|
@ -6657,26 +6657,34 @@ WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> HKDF::derive_bits(AlgorithmParams
|
|||
// all major browsers instead raise a TypeError, for example:
|
||||
// "Failed to execute 'deriveBits' on 'SubtleCrypto': HkdfParams: salt: Not a BufferSource"
|
||||
// Because we are forced by neither peer pressure nor the spec, we don't support it either.
|
||||
auto const& hash_algorithm = TRY(normalized_algorithm.hash.name(realm.vm()));
|
||||
ErrorOr<ByteBuffer> result = Error::from_string_literal("noop error");
|
||||
if (hash_algorithm == "SHA-1") {
|
||||
result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, *length_optional / 8);
|
||||
} else if (hash_algorithm == "SHA-256") {
|
||||
result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA256>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, *length_optional / 8);
|
||||
} else if (hash_algorithm == "SHA-384") {
|
||||
result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA384>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, *length_optional / 8);
|
||||
} else if (hash_algorithm == "SHA-512") {
|
||||
result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA512>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, *length_optional / 8);
|
||||
} else {
|
||||
return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm)));
|
||||
|
||||
// Note: Check for zero length early because our implementation doesn't support it.
|
||||
if (*length_optional == 0) {
|
||||
return TRY(JS::ArrayBuffer::create(realm, static_cast<size_t>(0)));
|
||||
}
|
||||
|
||||
auto const& hash_algorithm = TRY(normalized_algorithm.hash.name(realm.vm()));
|
||||
auto hash_kind = TRY([&] -> WebIDL::ExceptionOr<::Crypto::Hash::HashKind> {
|
||||
if (hash_algorithm == "SHA-1")
|
||||
return ::Crypto::Hash::HashKind::SHA1;
|
||||
if (hash_algorithm == "SHA-256")
|
||||
return ::Crypto::Hash::HashKind::SHA256;
|
||||
if (hash_algorithm == "SHA-384")
|
||||
return ::Crypto::Hash::HashKind::SHA384;
|
||||
if (hash_algorithm == "SHA-512")
|
||||
return ::Crypto::Hash::HashKind::SHA512;
|
||||
return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm)));
|
||||
}());
|
||||
|
||||
::Crypto::Hash::HKDF hkdf(hash_kind);
|
||||
auto maybe_result = hkdf.derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, *length_optional / 8);
|
||||
|
||||
// 4. If the key derivation operation fails, then throw an OperationError.
|
||||
if (result.is_error())
|
||||
if (maybe_result.is_error())
|
||||
return WebIDL::OperationError::create(realm, "Failed to derive key"_string);
|
||||
|
||||
// 5. Return result
|
||||
return JS::ArrayBuffer::create(realm, result.release_value());
|
||||
return JS::ArrayBuffer::create(realm, maybe_result.release_value());
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<JS::Value> HKDF::get_key_length(AlgorithmParams const&)
|
||||
|
@ -6708,32 +6716,32 @@ WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> PBKDF2::derive_bits(AlgorithmParam
|
|||
// the contents of the salt attribute of normalizedAlgorithm as the salt, S,
|
||||
// the value of the iterations attribute of normalizedAlgorithm as the iteration count, c,
|
||||
// and length divided by 8 as the intended key length, dkLen.
|
||||
ErrorOr<ByteBuffer> result = Error::from_string_literal("noop error");
|
||||
|
||||
auto password = key->handle().get<ByteBuffer>();
|
||||
|
||||
auto salt = normalized_algorithm.salt;
|
||||
auto iterations = normalized_algorithm.iterations;
|
||||
auto derived_key_length_bytes = *length_optional / 8;
|
||||
|
||||
if (hash_algorithm == "SHA-1") {
|
||||
result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA1>>(password, salt, iterations, derived_key_length_bytes);
|
||||
} else if (hash_algorithm == "SHA-256") {
|
||||
result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes);
|
||||
} else if (hash_algorithm == "SHA-384") {
|
||||
result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA384>>(password, salt, iterations, derived_key_length_bytes);
|
||||
} else if (hash_algorithm == "SHA-512") {
|
||||
result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA512>>(password, salt, iterations, derived_key_length_bytes);
|
||||
} else {
|
||||
auto hash_kind = TRY([&] -> WebIDL::ExceptionOr<::Crypto::Hash::HashKind> {
|
||||
if (hash_algorithm == "SHA-1")
|
||||
return ::Crypto::Hash::HashKind::SHA1;
|
||||
if (hash_algorithm == "SHA-256")
|
||||
return ::Crypto::Hash::HashKind::SHA256;
|
||||
if (hash_algorithm == "SHA-384")
|
||||
return ::Crypto::Hash::HashKind::SHA384;
|
||||
if (hash_algorithm == "SHA-512")
|
||||
return ::Crypto::Hash::HashKind::SHA512;
|
||||
return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm)));
|
||||
}
|
||||
}());
|
||||
|
||||
::Crypto::Hash::PBKDF2 pbkdf2(hash_kind);
|
||||
auto maybe_result = pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes);
|
||||
|
||||
// 5. If the key derivation operation fails, then throw an OperationError.
|
||||
if (result.is_error())
|
||||
if (maybe_result.is_error())
|
||||
return WebIDL::OperationError::create(realm, "Failed to derive key"_string);
|
||||
|
||||
// 6. Return result
|
||||
return JS::ArrayBuffer::create(realm, result.release_value());
|
||||
return JS::ArrayBuffer::create(realm, maybe_result.release_value());
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcrypto/#pbkdf2-operations
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
*/
|
||||
|
||||
#include <LibCrypto/Hash/HKDF.h>
|
||||
#include <LibCrypto/Hash/SHA1.h>
|
||||
#include <LibCrypto/Hash/SHA2.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
TEST_CASE(test_error_extreme_output_key_length)
|
||||
{
|
||||
auto result = Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(), ReadonlyBytes(), ReadonlyBytes(), 999999);
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1);
|
||||
auto result = hkdf.derive_key(Optional<ReadonlyBytes>(), ReadonlyBytes(), ReadonlyBytes(), 999999);
|
||||
EXPECT(result.is_error());
|
||||
EXPECT_EQ(result.error().string_literal(), "requested output_key_length is too large");
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5869#appendix-A.1
|
||||
|
@ -35,7 +33,8 @@ TEST_CASE(test_vector_A_1)
|
|||
// L = 42
|
||||
size_t const output_key_length = 42;
|
||||
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA256>::derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
@ -76,7 +75,8 @@ TEST_CASE(test_vector_A_2)
|
|||
static_assert(sizeof(info) == 80);
|
||||
size_t const output_key_length = 82;
|
||||
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA256>::derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
@ -101,8 +101,10 @@ TEST_CASE(test_vector_A_3)
|
|||
static_assert(sizeof(ikm) == 22);
|
||||
size_t const output_key_length = 42;
|
||||
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA256);
|
||||
|
||||
// Note: This creates a salt of zero bytes.
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA256>::derive_key(Optional<ReadonlyBytes>(ReadonlyBytes()), ikm, ReadonlyBytes(), output_key_length));
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(ReadonlyBytes()), ikm, ReadonlyBytes(), output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
@ -127,7 +129,8 @@ TEST_CASE(test_vector_A_4)
|
|||
u8 const info[] = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
|
||||
static_assert(sizeof(info) == 10);
|
||||
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1);
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
@ -168,7 +171,8 @@ TEST_CASE(test_vector_A_5)
|
|||
static_assert(sizeof(info) == 80);
|
||||
size_t const output_key_length = 82;
|
||||
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1);
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(salt), ikm, info, output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
@ -193,8 +197,10 @@ TEST_CASE(test_vector_A_6)
|
|||
static_assert(sizeof(ikm) == 22);
|
||||
size_t const output_key_length = 42;
|
||||
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1);
|
||||
|
||||
// Note: This creates a salt of length zero.
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(ReadonlyBytes()), ikm, ReadonlyBytes(), output_key_length));
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(ReadonlyBytes()), ikm, ReadonlyBytes(), output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
@ -216,8 +222,10 @@ TEST_CASE(test_vector_A_7)
|
|||
static_assert(sizeof(ikm) == 22);
|
||||
size_t const output_key_length = 42;
|
||||
|
||||
Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1);
|
||||
|
||||
// Note: This creates a "None" salt.
|
||||
auto result = TRY_OR_FAIL(Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(), ikm, ReadonlyBytes(), output_key_length));
|
||||
auto result = TRY_OR_FAIL(hkdf.derive_key(Optional<ReadonlyBytes>(), ikm, ReadonlyBytes(), output_key_length));
|
||||
// Intermediate value 'PRK' isn't checked explicitly. However, any bit error would have
|
||||
// an avalanche effect on the output, so if the output is correct then PRK can be presumed correct, too.
|
||||
u8 const expected_output_key[] = {
|
||||
|
|
|
@ -4,10 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/Authentication/HMAC.h>
|
||||
#include <LibCrypto/Hash/PBKDF2.h>
|
||||
#include <LibCrypto/Hash/SHA1.h>
|
||||
#include <LibCrypto/Hash/SHA2.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc6070#section-2
|
||||
|
@ -27,7 +24,8 @@ TEST_CASE(test_vector_1_sha1)
|
|||
u32 iterations = 1;
|
||||
u32 derived_key_length_bytes = 20;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA1>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA1);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
@ -48,7 +46,8 @@ TEST_CASE(test_vector_2_sha1)
|
|||
u32 iterations = 2;
|
||||
u32 derived_key_length_bytes = 20;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA1>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA1);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
@ -69,7 +68,8 @@ TEST_CASE(test_vector_1_sha256)
|
|||
u32 iterations = 1;
|
||||
u32 derived_key_length_bytes = 20;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
@ -90,7 +90,8 @@ TEST_CASE(test_vector_2_sha256)
|
|||
u32 iterations = 2;
|
||||
u32 derived_key_length_bytes = 20;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
@ -111,7 +112,8 @@ TEST_CASE(test_vector_3_sha256)
|
|||
u32 iterations = 4096;
|
||||
u32 derived_key_length_bytes = 20;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
@ -139,7 +141,8 @@ TEST_CASE(test_vector_4_sha256)
|
|||
u32 iterations = 4096;
|
||||
u32 derived_key_length_bytes = 25;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
@ -160,7 +163,8 @@ TEST_CASE(test_vector_5_sha256)
|
|||
u32 iterations = 4096;
|
||||
u32 derived_key_length_bytes = 16;
|
||||
|
||||
auto result = MUST(Crypto::Hash::PBKDF2::derive_key<Crypto::Authentication::HMAC<Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes));
|
||||
Crypto::Hash::PBKDF2 pbkdf2(Crypto::Hash::HashKind::SHA256);
|
||||
auto result = MUST(pbkdf2.derive_key(password, salt, iterations, derived_key_length_bytes));
|
||||
|
||||
EXPECT_EQ(result, expected.span());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue