Botan 3.12.0
Crypto and TLS for C&
sp800_56c_one_step.cpp
Go to the documentation of this file.
1/*
2* KDF defined in NIST SP 800-56a revision 2 (Single-step key-derivation function)
3* or in NIST SP 800-56C revision 2 (Section 4 - One-Step KDM)
4*
5* (C) 2017 Ribose Inc. Written by Krzysztof Kwiatkowski.
6* (C) 2024 Fabian Albert - Rohde & Schwarz Cybersecurity
7* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity
8*
9* Botan is released under the Simplified BSD License (see license.txt)
10*/
11
12#include <botan/internal/sp800_56c_one_step.h>
13
14#include <botan/exceptn.h>
15#include <botan/mem_ops.h>
16#include <botan/internal/bit_ops.h>
17#include <botan/internal/buffer_stuffer.h>
18#include <botan/internal/fmt.h>
19#include <botan/internal/kmac.h>
20
21#include <functional>
22
23namespace Botan {
24
25namespace {
26template <typename T>
27concept hash_or_mac_type = std::is_same_v<T, HashFunction> || std::is_same_v<T, MessageAuthenticationCode>;
28
29/**
30 * @brief One-Step Key Derivation as defined in SP800-56Cr2 Section 4
31 */
32template <hash_or_mac_type HashOrMacType>
33void kdm_internal(std::span<uint8_t> output_buffer,
34 std::span<const uint8_t> z,
35 std::span<const uint8_t> fixed_info,
36 HashOrMacType& hash_or_mac,
37 const std::function<void(HashOrMacType&)>& init_h_callback) {
38 // 1. If L > 0, then set reps = ceil(L / H_outputBits); otherwise,
39 // output an error indicator and exit this process without
40 // performing the remaining actions (i.e., omit steps 2 through 8).
41 //
42 // We follow the usual convention within the library that a KDF request for
43 // zero bytes is valid and, exactly as requested, outputs nothing.
44 if(output_buffer.empty()) {
45 return;
46 }
47
48 const size_t output_len = output_buffer.size();
49 const size_t h_output_len = hash_or_mac.output_length();
50 const size_t reps = ceil_division(output_len, h_output_len);
51
52 // 2. If reps > (2^32 − 1), then output an error indicator and exit this
53 // process without performing the remaining actions
54 // (i.e., omit steps 3 through 8).
55 BOTAN_ARG_CHECK(reps <= 0xFFFFFFFF, "Too large KDM output length");
56
57 // 3. Initialize a big-endian 4-byte unsigned integer counter as
58 // 0x00000000, corresponding to a 32-bit binary representation of
59 // the number zero.
60 uint32_t counter = 0;
61
62 // 4. If counter || Z || FixedInfo is more than max_H_inputBits bits
63 // long, then output an error indicator and exit this process
64 // without performing any of the remaining actions (i.e., omit
65 // steps 5 through 8). => SHA3 and KMAC are unlimited
66
67 // 5-7. Derive keying material directly into the output buffer.
68 BufferStuffer k(output_buffer);
69 for(size_t i = 1; i <= reps; i++) {
70 // 6.1. Increment counter by 1.
71 counter++;
72 // Reset the hash/MAC object. For MAC, also set the key (salt) and IV.
73 hash_or_mac.clear();
74 init_h_callback(hash_or_mac);
75
76 // 6.2 Compute K(i) = H(counter || Z || FixedInfo).
77 hash_or_mac.update_be(counter);
78 hash_or_mac.update(z);
79 hash_or_mac.update(fixed_info);
80
81 // 6.3. Set Result(i) = Result(i−1) || K(i).
82 if(k.remaining_capacity() >= h_output_len) {
83 hash_or_mac.final(k.next(h_output_len));
84 } else {
85 // Needs truncation so can't write directly to the output buffer
86 const auto k_i = hash_or_mac.final();
87 k.append(std::span{k_i}.first(k.remaining_capacity()));
88 }
89 }
90
91 BOTAN_ASSERT_NOMSG(k.full());
92}
93
94} // namespace
95
96void SP800_56C_One_Step_Hash::perform_kdf(std::span<uint8_t> key,
97 std::span<const uint8_t> secret,
98 std::span<const uint8_t> salt,
99 std::span<const uint8_t> label) const {
100 BOTAN_ARG_CHECK(salt.empty(), "SP800-56C KDF with hash does not support using a salt parameter");
101 kdm_internal<HashFunction>(key, secret, label, *m_hash, [](HashFunction&) { /* NOP */ });
102}
103
104std::string SP800_56C_One_Step_Hash::name() const {
105 return fmt("SP800-56A({})", m_hash->name());
106}
107
108std::unique_ptr<KDF> SP800_56C_One_Step_Hash::new_object() const {
109 return std::make_unique<SP800_56C_One_Step_Hash>(m_hash->new_object());
110}
111
112SP800_56C_One_Step_HMAC::SP800_56C_One_Step_HMAC(std::unique_ptr<MessageAuthenticationCode> mac) :
113 m_mac(std::move(mac)) {
114 // TODO: we need a MessageAuthenticationCode::is_hmac
115 if(!m_mac->name().starts_with("HMAC(")) {
116 throw Algorithm_Not_Found("Only HMAC can be used with SP800_56A_HMAC");
117 }
118}
119
120void SP800_56C_One_Step_HMAC::perform_kdf(std::span<uint8_t> key,
121 std::span<const uint8_t> secret,
122 std::span<const uint8_t> salt,
123 std::span<const uint8_t> label) const {
124 kdm_internal<MessageAuthenticationCode>(key, secret, label, *m_mac, [&](MessageAuthenticationCode& kdf_mac) {
125 // 4.1 Option 2 and 3 - An implementation dependent byte string, salt,
126 // whose (non-null) value may be optionally provided in
127 // OtherInput, serves as the HMAC# key ..
128
129 // SP 800-56Cr2 specifies if the salt is empty then a block of zeros
130 // equal to the hash's underlying block size are used. However for HMAC
131 // this is equivalent to setting a zero-length key, so the same call
132 // works for either case.
133 kdf_mac.set_key(salt);
134 });
135}
136
137std::string SP800_56C_One_Step_HMAC::name() const {
138 return fmt("SP800-56A({})", m_mac->name());
139}
140
141std::unique_ptr<KDF> SP800_56C_One_Step_HMAC::new_object() const {
142 return std::make_unique<SP800_56C_One_Step_HMAC>(m_mac->new_object());
143}
144
145// Option 3 - KMAC
146void SP800_56A_One_Step_KMAC_Abstract::perform_kdf(std::span<uint8_t> key,
147 std::span<const uint8_t> secret,
148 std::span<const uint8_t> salt,
149 std::span<const uint8_t> label) const {
150 auto mac = create_kmac_instance(key.size());
151 kdm_internal<MessageAuthenticationCode>(key, secret, label, *mac, [&](MessageAuthenticationCode& kdf_mac) {
152 // 4.1 Option 2 and 3 - An implementation dependent byte string, salt,
153 // whose (non-null) value may be optionally provided in
154 // OtherInput, serves as the KMAC# key ...
155 if(salt.empty()) {
156 // 4.1 Implementation-Dependent Parameters 3
157 // If H(x) = KMAC128[or 256](salt, x, H_outputBits, "KDF"),
158 // then – in the absence of an agreed-upon alternative –
159 // the default_salt shall be an all - zero string of
160 // 164 bytes [or 132 bytes]
161 kdf_mac.set_key(std::vector<uint8_t>(default_salt_length(), 0));
162 } else {
163 kdf_mac.set_key(salt);
164 }
165
166 // 4.1 Option 3 - The "customization string" S shall be the byte string
167 // 01001011 || 01000100 || 01000110, which represents the sequence
168 // of characters 'K', 'D', and 'F' in 8-bit ASCII.
169 kdf_mac.start(std::array<uint8_t, 3>{'K', 'D', 'F'});
170 });
171}
172
173std::unique_ptr<MessageAuthenticationCode> SP800_56C_One_Step_KMAC128::create_kmac_instance(
174 size_t output_byte_len) const {
175 return std::make_unique<KMAC128>(output_byte_len * 8);
176}
177
178std::unique_ptr<MessageAuthenticationCode> SP800_56C_One_Step_KMAC256::create_kmac_instance(
179 size_t output_byte_len) const {
180 return std::make_unique<KMAC256>(output_byte_len * 8);
181}
182
183} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
Helper class to ease in-place marshalling of concatenated fixed-length values.
void start(std::span< const uint8_t > nonce)
Definition mac.h:63
virtual std::unique_ptr< MessageAuthenticationCode > create_kmac_instance(size_t output_byte_len) const =0
virtual size_t default_salt_length() const =0
See SP800-56C Section 4.1 - Implementation-Dependent Parameters 3.
SP800_56C_One_Step_HMAC(std::unique_ptr< MessageAuthenticationCode > mac)
std::string name() const override
std::unique_ptr< KDF > new_object() const override
std::string name() const override
std::unique_ptr< KDF > new_object() const override
void set_key(const OctetString &key)
Definition sym_algo.cpp:14
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:167