diff --git a/Libraries/LibCrypto/CMakeLists.txt b/Libraries/LibCrypto/CMakeLists.txt index 7a659f3c04d..7f65b3111c9 100644 --- a/Libraries/LibCrypto/CMakeLists.txt +++ b/Libraries/LibCrypto/CMakeLists.txt @@ -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 diff --git a/Libraries/LibCrypto/Hash/HKDF.cpp b/Libraries/LibCrypto/Hash/HKDF.cpp new file mode 100644 index 00000000000..c3c9df2b67d --- /dev/null +++ b/Libraries/LibCrypto/Hash/HKDF.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#include +#include +#include + +namespace Crypto::Hash { + +HKDF::HKDF(HashKind hash_kind) + : m_kdf(EVP_KDF_fetch(nullptr, "HKDF", nullptr)) + , m_hash_kind(hash_kind) +{ +} + +ErrorOr HKDF::derive_key(Optional 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(hash_name.characters_without_null_termination()), hash_name.length()), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, const_cast(key.data()), key.size()), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, const_cast(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(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; +} + +} diff --git a/Libraries/LibCrypto/Hash/HKDF.h b/Libraries/LibCrypto/Hash/HKDF.h index e44d0307719..1ac1851c72b 100644 --- a/Libraries/LibCrypto/Hash/HKDF.h +++ b/Libraries/LibCrypto/Hash/HKDF.h @@ -1,88 +1,33 @@ /* * Copyright (c) 2023, stelar7 * Copyright (c) 2024, Ben Wiederhake + * Copyright (c) 2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include +#include namespace Crypto::Hash { -// https://www.rfc-editor.org/rfc/rfc5869#section-2 -template class HKDF { public: - using HashType = HashT; - using DigestType = typename HashType::DigestType; - using HMACType = typename Crypto::Authentication::HMAC; + 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 really is the correct type. - static ErrorOr derive_key(Optional 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(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(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 derive_key(Optional maybe_salt, ReadonlyBytes input_keying_material, ReadonlyBytes info, u32 key_length_bytes); private: - HKDF() = delete; + EVP_KDF* m_kdf; + HashKind m_hash_kind; }; } diff --git a/Libraries/LibCrypto/Hash/PBKDF2.cpp b/Libraries/LibCrypto/Hash/PBKDF2.cpp new file mode 100644 index 00000000000..803218ddabe --- /dev/null +++ b/Libraries/LibCrypto/Hash/PBKDF2.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#include +#include +#include + +namespace Crypto::Hash { + +PBKDF2::PBKDF2(HashKind hash_kind) + : m_kdf(EVP_KDF_fetch(nullptr, "PBKDF2", nullptr)) + , m_hash_kind(hash_kind) +{ +} + +ErrorOr 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(hash_name.characters_without_null_termination()), hash_name.length()), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, const_cast(password.data()), password.size()), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, const_cast(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; +} + +} diff --git a/Libraries/LibCrypto/Hash/PBKDF2.h b/Libraries/LibCrypto/Hash/PBKDF2.h index 9b917d130be..95220f7c636 100644 --- a/Libraries/LibCrypto/Hash/PBKDF2.h +++ b/Libraries/LibCrypto/Hash/PBKDF2.h @@ -1,84 +1,30 @@ /* * Copyright (c) 2023, stelar7 + * Copyright (c) 2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include -#include +#include namespace Crypto::Hash { -// https://www.rfc-editor.org/rfc/rfc2898#section-5.2 class PBKDF2 { public: - template - static ErrorOr 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 derive_key(ReadonlyBytes password, ReadonlyBytes salt, u32 iterations, u32 key_length_bytes); + +private: + EVP_KDF* m_kdf; + HashKind m_hash_kind; }; } diff --git a/Libraries/LibCrypto/OpenSSL.cpp b/Libraries/LibCrypto/OpenSSL.cpp index e7b4a529cac..dddf321992f 100644 --- a/Libraries/LibCrypto/OpenSSL.cpp +++ b/Libraries/LibCrypto/OpenSSL.cpp @@ -44,4 +44,20 @@ ErrorOr openssl_bignum_to_unsigned_big_integer(OpenSSL_BN co return UnsignedBigInteger::import_data(buf.bytes().data(), size); } +ErrorOr 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"); + } +} + } diff --git a/Libraries/LibCrypto/OpenSSL.h b/Libraries/LibCrypto/OpenSSL.h index 390f08c5bee..392baf12bcd 100644 --- a/Libraries/LibCrypto/OpenSSL.h +++ b/Libraries/LibCrypto/OpenSSL.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Altomani Gianluca + * Copyright (c) 2024-2025, Altomani Gianluca * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,6 +9,7 @@ #include #include #include +#include #include inline int openssl_print_errors(char const* str, size_t len, [[maybe_unused]] void* u) @@ -106,9 +107,15 @@ public: static ErrorOr create(); }; +class OpenSSL_KDF_CTX { + OPENSSL_WRAPPER_CLASS(OpenSSL_KDF_CTX, EVP_KDF_CTX, EVP_KDF_CTX); +}; + #undef OPENSSL_WRAPPER_CLASS ErrorOr unsigned_big_integer_to_openssl_bignum(UnsignedBigInteger const& integer); ErrorOr openssl_bignum_to_unsigned_big_integer(OpenSSL_BN const& bn); +ErrorOr hash_kind_to_openssl_digest_name(Hash::HashKind hash); + } diff --git a/Libraries/LibCrypto/OpenSSLForward.h b/Libraries/LibCrypto/OpenSSLForward.h index 9122be5a50e..14167d54f94 100644 --- a/Libraries/LibCrypto/OpenSSLForward.h +++ b/Libraries/LibCrypto/OpenSSLForward.h @@ -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*); } diff --git a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp index de8ea53c89f..95330956d82 100644 --- a/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp +++ b/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp @@ -6657,26 +6657,34 @@ WebIDL::ExceptionOr> 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 result = Error::from_string_literal("noop error"); - if (hash_algorithm == "SHA-1") { - result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA1>::derive_key(Optional(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(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(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(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(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(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 HKDF::get_key_length(AlgorithmParams const&) @@ -6708,32 +6716,32 @@ WebIDL::ExceptionOr> 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 result = Error::from_string_literal("noop error"); - auto password = key->handle().get(); - 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 diff --git a/Tests/LibCrypto/TestHKDF.cpp b/Tests/LibCrypto/TestHKDF.cpp index 4593310cc72..83adac89df9 100644 --- a/Tests/LibCrypto/TestHKDF.cpp +++ b/Tests/LibCrypto/TestHKDF.cpp @@ -6,15 +6,13 @@ */ #include -#include -#include #include TEST_CASE(test_error_extreme_output_key_length) { - auto result = Crypto::Hash::HKDF::derive_key(Optional(), ReadonlyBytes(), ReadonlyBytes(), 999999); + Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1); + auto result = hkdf.derive_key(Optional(), 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::derive_key(Optional(salt), ikm, info, output_key_length)); + Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA256); + auto result = TRY_OR_FAIL(hkdf.derive_key(Optional(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::derive_key(Optional(salt), ikm, info, output_key_length)); + Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA256); + auto result = TRY_OR_FAIL(hkdf.derive_key(Optional(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::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[] = { @@ -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::derive_key(Optional(salt), ikm, info, output_key_length)); + Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1); + auto result = TRY_OR_FAIL(hkdf.derive_key(Optional(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::derive_key(Optional(salt), ikm, info, output_key_length)); + Crypto::Hash::HKDF hkdf(Crypto::Hash::HashKind::SHA1); + auto result = TRY_OR_FAIL(hkdf.derive_key(Optional(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::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[] = { @@ -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::derive_key(Optional(), ikm, ReadonlyBytes(), output_key_length)); + auto result = TRY_OR_FAIL(hkdf.derive_key(Optional(), 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[] = { diff --git a/Tests/LibCrypto/TestPBKDF2.cpp b/Tests/LibCrypto/TestPBKDF2.cpp index be3400c4148..f9475ab4f88 100644 --- a/Tests/LibCrypto/TestPBKDF2.cpp +++ b/Tests/LibCrypto/TestPBKDF2.cpp @@ -4,10 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include -#include -#include #include // 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>(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>(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>(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>(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>(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>(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>(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()); }