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