Botan 3.10.0
Crypto and TLS for C&
sp800_108.cpp
Go to the documentation of this file.
1/*
2* KDFs defined in NIST SP 800-108
3* (C) 2016 Kai Michaelis
4* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity
5* (C) 2025 René Meusel, Amos Treiber, Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/sp800_108.h>
11
12#include <botan/internal/bit_ops.h>
13#include <botan/internal/fmt.h>
14#include <botan/internal/loadstor.h>
15#include <botan/internal/stl_util.h>
16
17#include <limits>
18
19namespace Botan {
20
21namespace {
22
23class CounterParams {
24 public:
25 constexpr static void validate_bit_lengths(size_t counter_bits, size_t output_length_bits) {
26 BOTAN_ARG_CHECK(counter_bits % 8 == 0 && counter_bits <= 32,
27 "SP.800-108 counter length may be one of {8, 16, 24, 32} only");
28 BOTAN_ARG_CHECK(output_length_bits % 8 == 0 && output_length_bits <= 32,
29 "SP.800-108 output length encoding may be one of {8, 16, 24, 32} only");
30 }
31
32 static CounterParams create_or_throw(size_t output_bytes,
33 size_t output_length_bits,
34 size_t counter_bits,
35 size_t prf_output_bytes) {
36 // The maximum legal output bit length is limited by the requested encoding
37 // bit length of the "L" field.
38 BOTAN_ARG_CHECK(static_cast<uint64_t>(output_bytes) * 8 <= std::numeric_limits<uint32_t>::max(),
39 "SP.800-108 output size in bits does not fit into 32-bits");
40 const auto output_bits = static_cast<uint32_t>(output_bytes * 8);
41 const auto max_output_bits = (uint64_t(1) << output_length_bits) - 1;
42 BOTAN_ARG_CHECK(output_bits <= max_output_bits,
43 "SP.800-108 output size does not fit into the requested field length");
44
45 // Calculate the number of blocks needed to serve the requested bytes
46 const auto blocks_required = ceil_division<uint64_t /* for 32bit systems */>(output_bytes, prf_output_bytes);
47
48 // The maximal legal invocation count of the PRF is limited by the requested
49 // encoding bit length of the counter ("r"). The counter starts at "1", so the
50 // maximum number of usable blocks is 2^r - 1.
51 const auto max_blocks = (uint64_t(1) << counter_bits) - 1;
52 BOTAN_ARG_CHECK(blocks_required < max_blocks, "SP.800-108 output size too large");
53
54 return CounterParams(
55 output_bits, output_length_bits / 8, counter_bits / 8, static_cast<uint32_t>(blocks_required));
56 }
57
58 template <std::invocable<std::span<const uint8_t>, std::span<const uint8_t>> Fn>
59 constexpr void generate_blocks(Fn fn) const {
60 const auto outlen_encoded = store_be(m_output_length_bits);
61 for(uint32_t counter = 1; counter <= m_blocks_required; ++counter) {
62 const auto counter_encoded = store_be(counter);
63 fn(std::span{counter_encoded}.last(m_counter_bytes),
64 std::span{outlen_encoded}.last(m_output_length_encoding_bytes));
65 }
66 }
67
68 private:
69 CounterParams(uint32_t output_length_bits,
70 size_t output_length_encoding_bytes,
71 size_t counter_bytes,
72 uint32_t blocks_required) :
73 m_output_length_bits(output_length_bits),
74 m_output_length_encoding_bytes(output_length_encoding_bytes),
75 m_counter_bytes(counter_bytes),
76 m_blocks_required(blocks_required) {}
77
78 private:
79 uint32_t m_output_length_bits;
80 size_t m_output_length_encoding_bytes;
81 size_t m_counter_bytes;
82 uint32_t m_blocks_required;
83};
84
85} // namespace
86
87SP800_108_Counter::SP800_108_Counter(std::unique_ptr<MessageAuthenticationCode> mac, size_t r, size_t L) :
88 m_prf(std::move(mac)), m_counter_bits(r), m_output_length_bits(L) {
89 CounterParams::validate_bit_lengths(m_counter_bits, m_output_length_bits);
90}
91
92std::string SP800_108_Counter::name() const {
93 // TODO(Botan4): make this always return the explicit lengths
94 return (m_counter_bits == 32 && m_output_length_bits == 32)
95 ? fmt("SP800-108-Counter({})", m_prf->name()) // keep the name consistent with previous versions
96 : fmt("SP800-108-Counter({},{},{})", m_prf->name(), m_counter_bits, m_output_length_bits);
97}
98
99std::unique_ptr<KDF> SP800_108_Counter::new_object() const {
100 return std::make_unique<SP800_108_Counter>(m_prf->new_object(), m_counter_bits, m_output_length_bits);
101}
102
103void SP800_108_Counter::perform_kdf(std::span<uint8_t> key,
104 std::span<const uint8_t> secret,
105 std::span<const uint8_t> salt,
106 std::span<const uint8_t> label) const {
107 if(key.empty()) {
108 return;
109 }
110
111 const auto prf_len = m_prf->output_length();
112 const auto params = CounterParams::create_or_throw(key.size(), m_output_length_bits, m_counter_bits, prf_len);
113
114 constexpr uint8_t delim = 0;
115
116 BufferStuffer k(key);
117 m_prf->set_key(secret);
118
119 params.generate_blocks([&](auto counter_encoded, auto outlen_encoded) {
120 m_prf->update(counter_encoded);
121 m_prf->update(label);
122 m_prf->update(delim);
123 m_prf->update(salt);
124 m_prf->update(outlen_encoded);
125
126 // Write straight into the output buffer, except if the PRF output needs
127 // a truncation in the final iteration.
128 if(k.remaining_capacity() >= prf_len) {
129 m_prf->final(k.next(prf_len));
130 } else {
131 const auto h = m_prf->final();
132 k.append(std::span{h}.first(k.remaining_capacity()));
133 }
134 });
135
136 BOTAN_ASSERT_NOMSG(k.full());
137}
138
139SP800_108_Feedback::SP800_108_Feedback(std::unique_ptr<MessageAuthenticationCode> mac, size_t r, size_t L) :
140 m_prf(std::move(mac)), m_counter_bits(r), m_output_length_bits(L) {
141 CounterParams::validate_bit_lengths(m_counter_bits, m_output_length_bits);
142}
143
144std::string SP800_108_Feedback::name() const {
145 // TODO(Botan4): make this always return the explicit lengths
146 return (m_counter_bits == 32 && m_output_length_bits == 32)
147 ? fmt("SP800-108-Feedback({})", m_prf->name()) // keep the name consistent with previous versions
148 : fmt("SP800-108-Feedback({},{},{})", m_prf->name(), m_counter_bits, m_output_length_bits);
149}
150
151std::unique_ptr<KDF> SP800_108_Feedback::new_object() const {
152 return std::make_unique<SP800_108_Feedback>(m_prf->new_object(), m_counter_bits, m_output_length_bits);
153}
154
155void SP800_108_Feedback::perform_kdf(std::span<uint8_t> key,
156 std::span<const uint8_t> secret,
157 std::span<const uint8_t> salt,
158 std::span<const uint8_t> label) const {
159 if(key.empty()) {
160 return;
161 }
162
163 const auto prf_len = m_prf->output_length();
164 const auto iv_len = (salt.size() >= prf_len ? prf_len : 0);
165 constexpr uint8_t delim = 0;
166 const auto params = CounterParams::create_or_throw(key.size(), m_output_length_bits, m_counter_bits, prf_len);
167
168 BufferSlicer s(salt);
169 auto prev = s.copy_as_secure_vector(iv_len);
170 const auto ctx = s.take(s.remaining());
171 BOTAN_ASSERT_NOMSG(s.empty());
172
173 BufferStuffer k(key);
174 m_prf->set_key(secret);
175
176 params.generate_blocks([&](auto counter_encoded, auto outlen_encoded) {
177 m_prf->update(prev);
178 m_prf->update(counter_encoded);
179 m_prf->update(label);
180 m_prf->update(delim);
181 m_prf->update(ctx);
182 m_prf->update(outlen_encoded);
183 m_prf->final(prev);
184
185 const auto bytes_to_write = std::min(prev.size(), k.remaining_capacity());
186 k.append(std::span{prev}.first(bytes_to_write));
187 });
188
189 BOTAN_ASSERT_NOMSG(k.full());
190}
191
192SP800_108_Pipeline::SP800_108_Pipeline(std::unique_ptr<MessageAuthenticationCode> mac, size_t r, size_t L) :
193 m_prf(std::move(mac)), m_counter_bits(r), m_output_length_bits(L) {
194 CounterParams::validate_bit_lengths(m_counter_bits, m_output_length_bits);
195}
196
197std::string SP800_108_Pipeline::name() const {
198 // TODO(Botan4): make this always return the explicit lengths
199 return (m_counter_bits == 32 && m_output_length_bits == 32)
200 ? fmt("SP800-108-Pipeline({})", m_prf->name()) // keep the name consistent with previous versions
201 : fmt("SP800-108-Pipeline({},{},{})", m_prf->name(), m_counter_bits, m_output_length_bits);
202}
203
204std::unique_ptr<KDF> SP800_108_Pipeline::new_object() const {
205 return std::make_unique<SP800_108_Pipeline>(m_prf->new_object(), m_counter_bits, m_output_length_bits);
206}
207
208void SP800_108_Pipeline::perform_kdf(std::span<uint8_t> key,
209 std::span<const uint8_t> secret,
210 std::span<const uint8_t> salt,
211 std::span<const uint8_t> label) const {
212 if(key.empty()) {
213 return;
214 }
215
216 const auto prf_len = m_prf->output_length();
217 constexpr uint8_t delim = 0;
218 const auto params = CounterParams::create_or_throw(key.size(), m_output_length_bits, m_counter_bits, prf_len);
219
220 BufferStuffer k(key);
221 m_prf->set_key(secret);
223
224 params.generate_blocks([&](auto counter_encoded, auto outlen_encoded) {
225 auto incorporate_constant_input = [label, salt, outlen_encoded](MessageAuthenticationCode* prf) {
226 prf->update(label);
227 prf->update(delim);
228 prf->update(salt);
229 prf->update(outlen_encoded);
230 };
231
232 if(scratch.empty()) {
233 // A(0)
234 incorporate_constant_input(m_prf.get());
235 scratch = m_prf->final();
236 } else {
237 // A(i)
238 m_prf->update(scratch);
239 m_prf->final(scratch);
240 }
241
242 m_prf->update(scratch);
243 m_prf->update(counter_encoded);
244 incorporate_constant_input(m_prf.get());
245
246 // Write straight into the output buffer, except if the PRF output needs
247 // a truncation in the final iteration.
248 if(k.remaining_capacity() >= prf_len) {
249 m_prf->final(k.next(prf_len));
250 } else {
251 // This must be the final iteration anyway, so we can just reuse the
252 // 'ai' scratch space to avoid allocating another short-lived buffer.
253 m_prf->final(scratch);
254 k.append(std::span{scratch}.first(k.remaining_capacity()));
255 }
256 });
257
258 BOTAN_ASSERT_NOMSG(k.full());
259}
260
261} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
SP800_108_Counter(std::unique_ptr< MessageAuthenticationCode > mac, size_t r, size_t L)
Definition sp800_108.cpp:87
std::string name() const override
Definition sp800_108.cpp:92
std::unique_ptr< KDF > new_object() const override
Definition sp800_108.cpp:99
std::string name() const override
SP800_108_Feedback(std::unique_ptr< MessageAuthenticationCode > mac, size_t r, size_t L)
std::unique_ptr< KDF > new_object() const override
std::unique_ptr< KDF > new_object() const override
std::string name() const override
SP800_108_Pipeline(std::unique_ptr< MessageAuthenticationCode > mac, size_t r, size_t L)
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
BOTAN_FORCE_INLINE constexpr T ceil_division(T a, T b)
Definition bit_ops.h:160
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745