Botan 3.9.0
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/mem_utils.h>
16#include <botan/internal/stl_util.h>
17
18namespace Botan {
19
20std::unique_ptr<KDF> HKDF::new_object() const {
21 return std::make_unique<HKDF>(m_prf->new_object());
22}
23
24std::string HKDF::name() const {
25 return fmt("HKDF({})", m_prf->name());
26}
27
28void HKDF::perform_kdf(std::span<uint8_t> key,
29 std::span<const uint8_t> secret,
30 std::span<const uint8_t> salt,
31 std::span<const uint8_t> label) const {
32 HKDF_Extract extract(m_prf->new_object());
33 HKDF_Expand expand(m_prf->new_object());
34 secure_vector<uint8_t> prk(m_prf->output_length());
35
36 extract.derive_key(prk, secret, salt, {});
37 expand.derive_key(key, prk, {}, label);
38}
39
40std::unique_ptr<KDF> HKDF_Extract::new_object() const {
41 return std::make_unique<HKDF_Extract>(m_prf->new_object());
42}
43
44std::string HKDF_Extract::name() const {
45 return fmt("HKDF-Extract({})", m_prf->name());
46}
47
48void HKDF_Extract::perform_kdf(std::span<uint8_t> key,
49 std::span<const uint8_t> secret,
50 std::span<const uint8_t> salt,
51 std::span<const uint8_t> label) const {
52 const size_t prf_output_len = m_prf->output_length();
53 BOTAN_ARG_CHECK(key.size() <= prf_output_len, "HKDF-Extract maximum output length exceeeded");
54 BOTAN_ARG_CHECK(label.empty(), "HKDF-Extract does not support a label input");
55
56 if(key.empty()) {
57 return;
58 }
59
60 if(salt.empty()) {
61 m_prf->set_key(std::vector<uint8_t>(prf_output_len));
62 } else {
63 m_prf->set_key(salt);
64 }
65
66 m_prf->update(secret);
67
68 if(key.size() == prf_output_len) {
69 m_prf->final(key);
70 } else {
71 const auto prk = m_prf->final();
72 copy_mem(key, std::span{prk}.first(key.size()));
73 }
74}
75
76std::unique_ptr<KDF> HKDF_Expand::new_object() const {
77 return std::make_unique<HKDF_Expand>(m_prf->new_object());
78}
79
80std::string HKDF_Expand::name() const {
81 return fmt("HKDF-Expand({})", m_prf->name());
82}
83
84void HKDF_Expand::perform_kdf(std::span<uint8_t> key,
85 std::span<const uint8_t> secret,
86 std::span<const uint8_t> salt,
87 std::span<const uint8_t> label) const {
88 const auto prf_output_length = m_prf->output_length();
89 BOTAN_ARG_CHECK(key.size() <= prf_output_length * 255, "HKDF-Expand maximum output length exceeeded");
90
91 if(key.empty()) {
92 return;
93 }
94
95 // Keep a reference to the previous PRF output (empty by default).
96 std::span<uint8_t> h = {};
97
98 BufferStuffer k(key);
99 m_prf->set_key(secret);
100 for(uint8_t counter = 1; !k.full(); ++counter) {
101 m_prf->update(h);
102 m_prf->update(label);
103 m_prf->update(salt);
104 m_prf->update(counter);
105
106 // Write straight into the output buffer, except if the PRF output needs
107 // a truncation in the final iteration.
108 if(k.remaining_capacity() >= prf_output_length) {
109 h = k.next(prf_output_length);
110 m_prf->final(h);
111 } else {
112 const auto full_prf_output = m_prf->final();
113 h = {}; // this is the final iteration!
114 k.append(std::span{full_prf_output}.first(k.remaining_capacity()));
115 }
116 }
117}
118
120 std::span<const uint8_t> secret,
121 std::string_view label,
122 std::span<const uint8_t> hash_val,
123 size_t length) {
124 BOTAN_ARG_CHECK(length <= 0xFFFF, "HKDF-Expand-Label requested output too large");
125 BOTAN_ARG_CHECK(label.size() <= 0xFF, "HKDF-Expand-Label label too long");
126 BOTAN_ARG_CHECK(hash_val.size() <= 0xFF, "HKDF-Expand-Label hash too long");
127
129
130 const auto prefix = concat<std::vector<uint8_t>>(store_be(static_cast<uint16_t>(length)),
131 store_be(static_cast<uint8_t>(label.size())),
132 as_span_of_bytes(label),
133 store_be(static_cast<uint8_t>(hash_val.size())));
134
135 /*
136 * We do something a little dirty here to avoid copying the hash_val,
137 * making use of the fact that Botan's KDF interface supports label+salt,
138 * and knowing that our HKDF hashes first param label then param salt.
139 */
140 return hkdf.derive_key(length, secret, hash_val, prefix);
141}
142
143} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
std::string name() const override
Definition hkdf.cpp:80
std::unique_ptr< KDF > new_object() const override
Definition hkdf.cpp:76
std::unique_ptr< KDF > new_object() const override
Definition hkdf.cpp:40
std::string name() const override
Definition hkdf.cpp:44
std::string name() const override
Definition hkdf.cpp:24
std::unique_ptr< KDF > new_object() const override
Definition hkdf.cpp:20
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::span< const uint8_t > as_span_of_bytes(const char *s, size_t len)
Definition mem_utils.h:28
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:145
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:119
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:255
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745