/src/serenity/Userland/Libraries/LibCrypto/Hash/HKDF.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, stelar7 <dudedbz@gmail.com> |
3 | | * Copyright (c) 2024, Ben Wiederhake <BenWiederhake.GitHub@gmx.de> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #pragma once |
9 | | |
10 | | #include <LibCrypto/Authentication/HMAC.h> |
11 | | |
12 | | namespace Crypto::Hash { |
13 | | |
14 | | // https://www.rfc-editor.org/rfc/rfc5869#section-2 |
15 | | template<typename HashT> |
16 | | class HKDF { |
17 | | public: |
18 | | using HashType = HashT; |
19 | | using DigestType = typename HashType::DigestType; |
20 | | using HMACType = typename Crypto::Authentication::HMAC<HashType>; |
21 | | |
22 | | // Note: The output is different for a salt of length zero and an absent salt, |
23 | | // so Optional<ReadonlyBytes> really is the correct type. |
24 | | static ErrorOr<ByteBuffer> derive_key(Optional<ReadonlyBytes> maybe_salt, ReadonlyBytes input_keying_material, ReadonlyBytes info, u32 output_key_length) |
25 | 0 | { |
26 | 0 | if (output_key_length > 255 * DigestType::Size) { |
27 | 0 | return Error::from_string_view("requested output_key_length is too large"sv); |
28 | 0 | } |
29 | | // Note that it feels like we should also refuse to run with output_key_length == 0, |
30 | | // but the spec allows this. |
31 | | |
32 | | // https://www.rfc-editor.org/rfc/rfc5869#section-2.1 |
33 | | // Note that in the extract step, 'IKM' is used as the HMAC input, not as the HMAC key. |
34 | | |
35 | | // salt: optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros. |
36 | 0 | ByteBuffer salt_buffer; |
37 | 0 | auto salt = maybe_salt.value_or_lazy_evaluated([&] { |
38 | 0 | salt_buffer.resize(DigestType::Size, ByteBuffer::ZeroFillNewElements::Yes); |
39 | 0 | return salt_buffer.bytes(); |
40 | 0 | }); Unexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int)::{lambda()#1}::operator()() constUnexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA256>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int)::{lambda()#1}::operator()() constUnexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA384>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int)::{lambda()#1}::operator()() constUnexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA512>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int)::{lambda()#1}::operator()() const |
41 | 0 | HMACType hmac_salt(salt); |
42 | | |
43 | | // https://www.rfc-editor.org/rfc/rfc5869#section-2.2 |
44 | | // PRK = HMAC-Hash(salt, IKM) |
45 | 0 | auto prk_digest = hmac_salt.process(input_keying_material); |
46 | 0 | auto prk = prk_digest.bytes(); |
47 | 0 | VERIFY(prk.size() == DigestType::Size); |
48 | | |
49 | | // https://www.rfc-editor.org/rfc/rfc5869#section-2.3 |
50 | | // N = ceil(L/HashLen) |
51 | 0 | auto num_iterations = ceil_div(static_cast<size_t>(output_key_length), DigestType::Size); |
52 | | // T = T(1) | T(2) | T(3) | ... | T(N) |
53 | 0 | ByteBuffer output_buffer; |
54 | | // where: |
55 | | // T(0) = empty string (zero length) |
56 | | // T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) |
57 | | // T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) |
58 | | // T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) |
59 | 0 | HMACType hmac_prk(prk); |
60 | | // In iteration i we compute T(i), and deduce T(i - 1) from 'output_buffer'. |
61 | | // Hence, we do not need to run i == 0. |
62 | | // INVARIANT: At the beginning of each iteration, hmac_prk is freshly reset. |
63 | | // For the first iteration, this is given by the constructor of HMAC. |
64 | 0 | for (size_t i = 1; i < 1 + num_iterations; ++i) { |
65 | 0 | if (i > 1) { |
66 | 0 | auto t_i_minus_one = output_buffer.bytes().slice_from_end(DigestType::Size); |
67 | 0 | hmac_prk.update(t_i_minus_one); |
68 | 0 | } |
69 | 0 | hmac_prk.update(info); |
70 | 0 | u8 const pad_byte = static_cast<u8>(i & 0xff); |
71 | 0 | hmac_prk.update(ReadonlyBytes(&pad_byte, 1)); |
72 | 0 | auto t_i_digest = hmac_prk.digest(); |
73 | 0 | output_buffer.append(t_i_digest.bytes()); |
74 | 0 | } |
75 | | |
76 | | // OKM = first L octets of T |
77 | 0 | VERIFY(output_buffer.size() >= output_key_length); |
78 | 0 | output_buffer.trim(output_key_length, false); |
79 | | |
80 | | // 5. Output the derived key DK |
81 | 0 | return { output_buffer }; |
82 | 0 | } Unexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA1>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int) Unexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA256>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int) Unexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA384>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int) Unexecuted instantiation: Crypto::Hash::HKDF<Crypto::Hash::SHA512>::derive_key(AK::Optional<AK::Span<unsigned char const> >, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned int) |
83 | | |
84 | | private: |
85 | | HKDF() = delete; |
86 | | }; |
87 | | |
88 | | } |