Botan 3.7.1
Crypto and TLS for C&
hkdf.cpp
Go to the documentation of this file.
1/*
2* HKDF
3* (C) 2013,2015,2017 Jack Lloyd
4* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
5* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/hkdf.h>
11
12#include <botan/exceptn.h>
13#include <botan/internal/fmt.h>
14#include <botan/internal/loadstor.h>
15#include <botan/internal/stl_util.h>
16
17namespace Botan {
18
19std::unique_ptr<KDF> HKDF::new_object() const {
20 return std::make_unique<HKDF>(m_prf->new_object());
21}
22
23std::string HKDF::name() const {
24 return fmt("HKDF({})", m_prf->name());
25}
26
27void HKDF::perform_kdf(std::span<uint8_t> key,
28 std::span<const uint8_t> secret,
29 std::span<const uint8_t> salt,
30 std::span<const uint8_t> label) const {
31 HKDF_Extract extract(m_prf->new_object());
32 HKDF_Expand expand(m_prf->new_object());
33 secure_vector<uint8_t> prk(m_prf->output_length());
34
35 extract.derive_key(prk, secret, salt, {});
36 expand.derive_key(key, prk, {}, label);
37}
38
39std::unique_ptr<KDF> HKDF_Extract::new_object() const {
40 return std::make_unique<HKDF_Extract>(m_prf->new_object());
41}
42
43std::string HKDF_Extract::name() const {
44 return fmt("HKDF-Extract({})", m_prf->name());
45}
46
47void HKDF_Extract::perform_kdf(std::span<uint8_t> key,
48 std::span<const uint8_t> secret,
49 std::span<const uint8_t> salt,
50 std::span<const uint8_t> label) const {
51 const size_t prf_output_len = m_prf->output_length();
52 BOTAN_ARG_CHECK(key.size() <= prf_output_len, "HKDF-Extract maximum output length exceeeded");
53 BOTAN_ARG_CHECK(label.empty(), "HKDF-Extract does not support a label input");
54
55 if(key.empty()) {
56 return;
57 }
58
59 if(salt.empty()) {
60 m_prf->set_key(std::vector<uint8_t>(prf_output_len));
61 } else {
62 m_prf->set_key(salt);
63 }
64
65 m_prf->update(secret);
66
67 if(key.size() == prf_output_len) {
68 m_prf->final(key);
69 } else {
70 const auto prk = m_prf->final();
71 copy_mem(key, std::span{prk}.first(key.size()));
72 }
73}
74
75std::unique_ptr<KDF> HKDF_Expand::new_object() const {
76 return std::make_unique<HKDF_Expand>(m_prf->new_object());
77}
78
79std::string HKDF_Expand::name() const {
80 return fmt("HKDF-Expand({})", m_prf->name());
81}
82
83void HKDF_Expand::perform_kdf(std::span<uint8_t> key,
84 std::span<const uint8_t> secret,
85 std::span<const uint8_t> salt,
86 std::span<const uint8_t> label) const {
87 const auto prf_output_length = m_prf->output_length();
88 BOTAN_ARG_CHECK(key.size() <= prf_output_length * 255, "HKDF-Expand maximum output length exceeeded");
89
90 if(key.empty()) {
91 return;
92 }
93
94 // Keep a reference to the previous PRF output (empty by default).
95 std::span<uint8_t> h = {};
96
97 BufferStuffer k(key);
98 m_prf->set_key(secret);
99 for(uint8_t counter = 1; !k.full(); ++counter) {
100 m_prf->update(h);
101 m_prf->update(label);
102 m_prf->update(salt);
103 m_prf->update(counter);
104
105 // Write straight into the output buffer, except if the PRF output needs
106 // a truncation in the final iteration.
107 if(k.remaining_capacity() >= prf_output_length) {
108 h = k.next(prf_output_length);
109 m_prf->final(h);
110 } else {
111 const auto full_prf_output = m_prf->final();
112 h = {}; // this is the final iteration!
113 k.append(std::span{full_prf_output}.first(k.remaining_capacity()));
114 }
115 }
116}
117
119 std::span<const uint8_t> secret,
120 std::string_view label,
121 std::span<const uint8_t> hash_val,
122 size_t length) {
123 BOTAN_ARG_CHECK(length <= 0xFFFF, "HKDF-Expand-Label requested output too large");
124 BOTAN_ARG_CHECK(label.size() <= 0xFF, "HKDF-Expand-Label label too long");
125 BOTAN_ARG_CHECK(hash_val.size() <= 0xFF, "HKDF-Expand-Label hash too long");
126
128
129 const auto prefix = concat<std::vector<uint8_t>>(store_be(static_cast<uint16_t>(length)),
130 store_be(static_cast<uint8_t>(label.size())),
131 std::span{cast_char_ptr_to_uint8(label.data()), label.size()},
132 store_be(static_cast<uint8_t>(hash_val.size())));
133
134 /*
135 * We do something a little dirty here to avoid copying the hash_val,
136 * making use of the fact that Botan's KDF interface supports label+salt,
137 * and knowing that our HKDF hashes first param label then param salt.
138 */
139 return hkdf.derive_key(length, secret, hash_val, prefix);
140}
141
142} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
std::string name() const override
Definition hkdf.cpp:79
std::unique_ptr< KDF > new_object() const override
Definition hkdf.cpp:75
std::unique_ptr< KDF > new_object() const override
Definition hkdf.cpp:39
std::string name() const override
Definition hkdf.cpp:43
std::string name() const override
Definition hkdf.cpp:23
std::unique_ptr< KDF > new_object() const override
Definition hkdf.cpp:19
T derive_key(size_t key_len, const uint8_t secret[], size_t secret_len, const uint8_t salt[], size_t salt_len, const uint8_t label[]=nullptr, size_t label_len=0) const
Definition kdf.h:91
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition mac.cpp:148
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
secure_vector< uint8_t > hkdf_expand_label(std::string_view hash_fn, std::span< const uint8_t > secret, std::string_view label, std::span< const uint8_t > hash_val, size_t length)
Definition hkdf.cpp:118
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:263
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:147
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:773
const uint8_t * cast_char_ptr_to_uint8(const char *s)
Definition mem_ops.h:274