Botan 3.11.0
Crypto and TLS for C&
lms.cpp
Go to the documentation of this file.
1/**
2 * LMS - Leighton-Micali Hash-Based Signatures (RFC 8554)
3 * (C) 2023 Jack Lloyd
4 * 2023 Fabian Albert, Philippe Lieser - Rohde & Schwarz Cybersecurity GmbH
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8
9#include <botan/internal/lms.h>
10
11#include <botan/exceptn.h>
12#include <botan/hash.h>
13#include <botan/internal/buffer_slicer.h>
14#include <botan/internal/buffer_stuffer.h>
15#include <botan/internal/concat_util.h>
16#include <botan/internal/loadstor.h>
17#include <botan/internal/tree_hash.h>
18
19namespace Botan {
20namespace {
21
22/**
23 * @brief Domain-separation parameter when computing the hash of the leaf of an LMS tree.
24 */
25constexpr uint16_t D_LEAF = 0x8282;
26
27/**
28 * @brief Domain-separation parameter when computing the hash of an interior node of an LMS tree.
29 */
30constexpr uint16_t D_INTR = 0x8383;
31
32/// Index of the layer in a tree starting with 0 for the bottom level to the root layer
34
35class TreeAddress final {
36 public:
37 explicit TreeAddress(uint32_t total_tree_height) : m_h(total_tree_height), m_r(0) {
38 BOTAN_ARG_CHECK(total_tree_height > 0 && total_tree_height < 32, "Invalid tree height");
39 }
40
41 TreeAddress& set_address(LMS_TreeLayerIndex tree_layer, LMS_Tree_Node_Idx tree_index) {
42 BOTAN_ARG_CHECK(tree_index.get() < (1 << m_h), "Invalid tree index");
43 BOTAN_ARG_CHECK(tree_layer.get() <= m_h, "Invalid tree index");
44 m_r = (1 << (m_h - tree_layer)).get() + tree_index.get();
45 return *this;
46 }
47
48 uint32_t r() const { return m_r; }
49
50 bool is_leaf() const { return m_r >= (1 << m_h); }
51
52 LMS_Tree_Node_Idx q() const {
53 BOTAN_STATE_CHECK(is_leaf());
54 return LMS_Tree_Node_Idx(m_r - (1 << m_h.get()));
55 }
56
57 private:
58 LMS_TreeLayerIndex m_h;
59 uint32_t m_r;
60};
61
62auto get_hash_pair_func_for_identifier(const LMS_Params& lms_params, LMS_Identifier identifier) {
63 return [hash = lms_params.hash(), I = std::move(identifier)](StrongSpan<LMS_Tree_Node> out,
64 const TreeAddress& address,
67 hash->update(I);
68 hash->update(store_be(address.r()));
69 hash->update(store_be(D_INTR));
70 hash->update(left);
71 hash->update(right);
72 hash->final(out);
73 };
74}
75
76void lms_gen_leaf(StrongSpan<LMS_Tree_Node> out,
77 const LMOTS_Public_Key& lmots_pk,
78 const TreeAddress& tree_address,
79 HashFunction& hash) {
80 hash.update(lmots_pk.identifier());
81 hash.update(store_be(tree_address.r()));
82 hash.update(store_be(D_LEAF));
83 hash.update(lmots_pk.K());
84 hash.final(out);
85}
86
87auto lms_gen_leaf_func(const LMS_PrivateKey& lms_sk) {
88 return [hash = lms_sk.lms_params().hash(), lms_sk](StrongSpan<LMS_Tree_Node> out, const TreeAddress& tree_address) {
89 auto lmots_sk = LMOTS_Private_Key(lms_sk.lmots_params(), lms_sk.identifier(), tree_address.q(), lms_sk.seed());
90 auto lmots_pk = LMOTS_Public_Key(lmots_sk);
91 lms_gen_leaf(out, lmots_pk, tree_address, *hash);
92 };
93}
94
95void lms_treehash(StrongSpan<LMS_Tree_Node> out_root,
96 std::optional<StrongSpan<LMS_AuthenticationPath>> out_auth_path,
97 std::optional<LMS_Tree_Node_Idx> leaf_idx,
98 const LMS_PrivateKey& lms_sk) {
99 auto hash_pair_func = get_hash_pair_func_for_identifier(lms_sk.lms_params(), lms_sk.identifier());
100 auto gen_leaf = lms_gen_leaf_func(lms_sk);
101 TreeAddress lms_tree_address(lms_sk.lms_params().h());
102
103 treehash(out_root,
104 out_auth_path,
105 leaf_idx,
106 lms_sk.lms_params().m(),
107 LMS_TreeLayerIndex(lms_sk.lms_params().h()),
108 0,
109 std::move(hash_pair_func),
110 std::move(gen_leaf),
111 lms_tree_address);
112}
113
114} // namespace
115
116std::unique_ptr<HashFunction> LMS_Params::hash() const {
118}
119
121 auto [hash_name, height] = [](const LMS_Algorithm_Type& lms_type) -> std::pair<std::string_view, uint8_t> {
122 switch(lms_type) {
124 return {"SHA-256", static_cast<uint8_t>(5)};
126 return {"SHA-256", static_cast<uint8_t>(10)};
128 return {"SHA-256", static_cast<uint8_t>(15)};
130 return {"SHA-256", static_cast<uint8_t>(20)};
132 return {"SHA-256", static_cast<uint8_t>(25)};
134 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(5)};
136 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(10)};
138 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(15)};
140 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(20)};
142 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(25)};
144 return {"SHAKE-256(256)", static_cast<uint8_t>(5)};
146 return {"SHAKE-256(256)", static_cast<uint8_t>(10)};
148 return {"SHAKE-256(256)", static_cast<uint8_t>(15)};
150 return {"SHAKE-256(256)", static_cast<uint8_t>(20)};
152 return {"SHAKE-256(256)", static_cast<uint8_t>(25)};
154 return {"SHAKE-256(192)", static_cast<uint8_t>(5)};
156 return {"SHAKE-256(192)", static_cast<uint8_t>(10)};
158 return {"SHAKE-256(192)", static_cast<uint8_t>(15)};
160 return {"SHAKE-256(192)", static_cast<uint8_t>(20)};
162 return {"SHAKE-256(192)", static_cast<uint8_t>(25)};
163 default:
164 throw Decoding_Error("Unsupported LMS algorithm type");
165 }
166 }(type);
167
168 return LMS_Params(type, hash_name, height);
169}
170
171LMS_Params LMS_Params::create_or_throw(std::string_view hash_name, uint8_t height) {
172 const LMS_Algorithm_Type type = [](std::string_view hash, uint8_t h) -> LMS_Algorithm_Type {
173 if(hash == "SHA-256") {
174 switch(h) {
175 case 5:
177 case 10:
179 case 15:
181 case 20:
183 case 25:
185 default:
186 throw Decoding_Error("Unsupported height for hash function");
187 }
188 }
189 if(hash == "Truncated(SHA-256,192)") {
190 switch(h) {
191 case 5:
193 case 10:
195 case 15:
197 case 20:
199 case 25:
201 default:
202 throw Decoding_Error("Unsupported height for hash function");
203 }
204 }
205 if(hash == "SHAKE-256(256)") {
206 switch(h) {
207 case 5:
209 case 10:
211 case 15:
213 case 20:
215 case 25:
217 default:
218 throw Decoding_Error("Unsupported height for hash function");
219 }
220 }
221 if(hash == "SHAKE-256(192)") {
222 switch(h) {
223 case 5:
225 case 10:
227 case 15:
229 case 20:
231 case 25:
233 default:
234 throw Decoding_Error("Unsupported height for hash function");
235 }
236 }
237 throw Decoding_Error("Unsupported hash function");
238 }(hash_name, height);
239
240 return LMS_Params(type, hash_name, height);
241}
242
243LMS_Params::LMS_Params(LMS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t h) :
244 m_algorithm_type(algorithm_type), m_h(h), m_hash_name(hash_name) {
245 const auto hash = HashFunction::create_or_throw(m_hash_name);
246 m_m = hash->output_length();
247}
248
251 const LMS_Message& msg) const {
252 // Pre-alloc space for the signature
253 BOTAN_ARG_CHECK(out_sig.size() == LMS_Signature::size(lms_params(), lmots_params()), "Invalid output buffer size");
254
255 BufferStuffer sig_stuffer(out_sig);
256 sig_stuffer.append(store_be(q));
257 const LMOTS_Private_Key lmots_sk(lmots_params(), identifier(), q, seed());
258 lmots_sk.sign(sig_stuffer.next<LMOTS_Signature_Bytes>(LMOTS_Signature::size(lmots_params())), msg);
259 sig_stuffer.append(store_be(lms_params().algorithm_type()));
260 const auto auth_path_buffer = sig_stuffer.next<LMS_AuthenticationPath>(lms_params().m() * lms_params().h());
261
262 BOTAN_ASSERT_NOMSG(sig_stuffer.full());
263
264 const TreeAddress lms_tree_address(lms_params().h());
265 LMS_Tree_Node pk_buffer(lms_params().m());
266 lms_treehash(StrongSpan<LMS_Tree_Node>(pk_buffer.get()), auth_path_buffer, q, *this);
267
268 CT::unpoison(pk_buffer);
269 return LMS_PublicKey(lms_params(), lmots_params(), identifier(), std::move(pk_buffer));
270}
271
273 const size_t total_remaining_bytes = slicer.remaining();
274 // Alg. 6. 1. (4 bytes are sufficient until the next check)
275 if(total_remaining_bytes < sizeof(LMS_Algorithm_Type)) {
276 throw Decoding_Error("Too few bytes while parsing LMS public key.");
277 }
278 // Alg. 6. 2.a.
279 auto lms_type = load_be<LMS_Algorithm_Type>(slicer.take<sizeof(LMS_Algorithm_Type)>());
280 // Alg. 6. 2.c.
282 // Alg. 6. 2.d.
283 if(total_remaining_bytes < LMS_PublicKey::size(lms_params)) {
284 throw Decoding_Error("Too few bytes while parsing LMS public key.");
285 }
286 // Alg. 6. 2.b.
287 auto lmots_type = load_be<LMOTS_Algorithm_Type>(slicer.take<sizeof(LMOTS_Algorithm_Type)>());
289
290 if(lms_params.hash_name() != lmots_params.hash_name()) {
291 throw Decoding_Error("No support for HSS-LMS instances with multiple hash functions.");
292 }
293
294 // Alg. 6. 2.e.
295 auto I = slicer.copy<LMS_Identifier>(LMS_IDENTIFIER_LEN);
296 // Alg. 6. 2.f.
297 auto lms_root = slicer.copy<LMS_Tree_Node>(lms_params.m());
298
299 return LMS_PublicKey(std::move(lms_params), std::move(lmots_params), std::move(I), std::move(lms_root));
300}
301
302std::vector<uint8_t> LMS_PublicKey::to_bytes() const {
303 // clang-format off
305 store_be(lms_params().algorithm_type()),
306 store_be(lmots_params().algorithm_type()),
307 identifier(),
308 m_lms_root);
309 // clang-format on
310}
311
315 LMS_Tree_Node lms_root) :
316 LMS_Instance(std::move(lms_params), std::move(lmots_params), std::move(I)), m_lms_root(std::move(lms_root)) {
317 BOTAN_ARG_CHECK(identifier().size() == LMS_IDENTIFIER_LEN, "Invalid LMS identifier");
318 BOTAN_ARG_CHECK(m_lms_root.size() == this->lms_params().m(), "Invalid LMS root");
319}
320
324
326 const size_t total_remaining_bytes = slicer.remaining();
327 // Alg. 6a 1. (next 4 bytes are checked in LMOTS_Signature::from_bytes_or_throw)
328 if(total_remaining_bytes < sizeof(LMS_Tree_Node_Idx)) {
329 throw Decoding_Error("Too few signature bytes while parsing LMS signature.");
330 }
331 // Alg. 6a 2.a.
332 auto q = load_be<LMS_Tree_Node_Idx>(slicer.take<sizeof(LMS_Tree_Node_Idx)>());
333
334 // Alg. 6a 2.b.-e.
336 const LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type());
337
338 if(slicer.remaining() < sizeof(LMS_Algorithm_Type)) {
339 throw Decoding_Error("Too few signature bytes while parsing LMS signature.");
340 }
341 // Alg. 6a 2.f.
343 // Alg. 6a 2.h.
345 // Alg. 6a 2.i. (signature is not exactly [...] bytes long)
346 if(total_remaining_bytes < size(lms_params, lmots_params)) {
347 throw Decoding_Error("Too few signature bytes while parsing LMS signature.");
348 }
349
350 // Alg. 6a 2.j.
351 auto auth_path = slicer.copy<LMS_AuthenticationPath>(lms_params.m() * lms_params.h());
352
353 return LMS_Signature(q, std::move(lmots_sig), lms_type, std::move(auth_path));
354}
355
357 /* NOLINT(*-slicing) */ LMS_Instance(sk), m_lms_root(sk.lms_params().m()) {
358 lms_treehash(StrongSpan<LMS_Tree_Node>(m_lms_root), std::nullopt, std::nullopt, sk);
359}
360
362 if(lms_root().size() != lms_params().m()) {
363 // LMS public key (T[1] part) has unexpected length
364 return false;
365 }
366 if(lms_params().algorithm_type() != sig.lms_type()) {
367 // LMS algorithm type does not match with the signature's
368 return false;
369 }
370 // Alg. 6a 2.g.
371 if(lmots_params().algorithm_type() != sig.lmots_sig().algorithm_type()) {
372 // LMOTS algorithm type does not match with the signature's
373 return false;
374 }
375 // Alg. 6a 2.i.
376 if(sig.q() >= (1ULL << uint64_t(lms_params().h()))) {
377 return false;
378 }
379 // Alg 6. 3.
380 std::optional<LMS_Tree_Node> Tc = lms_compute_root_from_sig(msg, sig);
381 if(!Tc.has_value()) {
382 return false;
383 }
384 // Alg 6. 4.
385 return Tc.value() == lms_root();
386}
387
388std::optional<LMS_Tree_Node> LMS_PublicKey::lms_compute_root_from_sig(const LMS_Message& msg,
389 const LMS_Signature& sig) const {
390 // Alg. 6a 2.c, 2.g
391 if(lms_params().algorithm_type() != sig.lms_type() ||
392 lmots_params().algorithm_type() != sig.lmots_sig().algorithm_type()) {
393 return std::nullopt;
394 }
395 try {
396 const LMS_Params lms_params = LMS_Params::create_or_throw(sig.lms_type());
397 const LMOTS_Signature& lmots_sig = sig.lmots_sig();
398 const LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type());
399 const LMOTS_K Kc = lmots_compute_pubkey_from_sig(lmots_sig, msg, identifier(), sig.q());
400 const auto hash = lms_params.hash();
401
402 auto hash_pair_func = get_hash_pair_func_for_identifier(lms_params, identifier());
403
404 auto lms_address = TreeAddress(lms_params.h());
405 lms_address.set_address(LMS_TreeLayerIndex(0), LMS_Tree_Node_Idx(sig.q().get()));
406
407 const LMOTS_Public_Key pk_candidate(lmots_params, identifier(), sig.q(), Kc);
409 lms_gen_leaf(tmp, pk_candidate, lms_address, *hash);
410
412
413 compute_root(StrongSpan<LMS_Tree_Node>(root),
414 sig.auth_path(),
415 sig.q(),
416 StrongSpan<const LMS_Tree_Node>(tmp),
417 lms_params.m(),
418 LMS_TreeLayerIndex(lms_params.h()),
419 0,
420 std::move(hash_pair_func),
421 lms_address);
422 return LMS_Tree_Node(root);
423 } catch(const Decoding_Error&) {
424 return std::nullopt;
425 }
426}
427
428size_t LMS_Signature::size(const LMS_Params& lms_params, const LMOTS_Params& lmots_params) {
429 return sizeof(uint32_t) + LMOTS_Signature::size(lmots_params) + sizeof(uint32_t) + lms_params.h() * lms_params.m();
430}
431
432} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
size_t remaining() const
auto copy(const size_t count)
std::span< const uint8_t > take(const size_t count)
Helper class to ease in-place marshalling of concatenated fixed-length values.
constexpr void append(std::span< const uint8_t > buffer)
constexpr std::span< uint8_t > next(size_t bytes)
constexpr bool full() const
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:308
The LM-OTS parameters.
Definition lm_ots.h:103
static LMOTS_Params create_or_throw(LMOTS_Algorithm_Type type)
Create the LM-OTS parameters from a known algorithm type.
Definition lm_ots.cpp:106
Representation of an LMOTS private key.
Definition lm_ots.h:260
void sign(StrongSpan< LMOTS_Signature_Bytes > out_sig, const LMS_Message &msg) const
Generate a new LMOTS signature.
Definition lm_ots.cpp:282
Representation of an OTS public key.
Definition lm_ots.h:309
static size_t size(const LMOTS_Params &params)
The expected size of the signature.
Definition lm_ots.h:210
LMOTS_Algorithm_Type algorithm_type() const
Returns the LM-OTS algorithm type.
Definition lm_ots.h:195
static LMOTS_Signature from_bytes_or_throw(BufferSlicer &slicer)
Parse a LM-OTS signature.
Definition lm_ots.cpp:241
const LMS_Params & lms_params() const
The LMS parameters for this LMS instance.
Definition lms.h:166
const LMOTS_Params & lmots_params() const
The LMOTS parameters used for OTS instances of this LMS instance.
Definition lms.h:171
const LMS_Identifier & identifier() const
The identifier of this LMS tree ('I' in RFC 8554).
Definition lms.h:176
LMS_Instance(LMS_Params lms_params, LMOTS_Params lmots_params, LMS_Identifier identifier)
Constructor storing the provided LMS data.
Definition lms.h:158
The LMS parameters.
Definition lms.h:91
const std::string & hash_name() const
Returns the name of the hash function to use.
Definition lms.h:126
size_t m() const
Returns the number of bytes associated with each node.
Definition lms.h:121
std::unique_ptr< HashFunction > hash() const
Construct a new hash instance for the LMS instance.
Definition lms.cpp:116
uint8_t h() const
Returns the height of the LMS tree.
Definition lms.h:116
static LMS_Params create_or_throw(LMS_Algorithm_Type type)
Create the LMS parameters from a known algorithm type.
Definition lms.cpp:120
Representation of an LMS Private key.
Definition lms.h:192
LMS_PublicKey sign_and_get_pk(StrongSpan< LMS_Signature_Bytes > out_sig, LMS_Tree_Node_Idx q, const LMS_Message &msg) const
Sign a message using an LMS_PrivateKey and the used leaf index (RFC 8554 5.4.1).
Definition lms.cpp:249
const LMS_Seed & seed() const
The secret seed used for LMOTS' WOTS chain input creation (RFC 8554 Appendix A).
Definition lms.h:203
The LMS public key.
Definition lms.h:227
static size_t size(const LMS_Params &lms_params)
The expected size of an LMS public key for given lms_params.
Definition lms.cpp:321
std::vector< uint8_t > to_bytes() const
Bytes of the full lms public key according to 8554 5.3.
Definition lms.cpp:302
static LMS_PublicKey from_bytes_or_throw(BufferSlicer &slicer)
Parse a public LMS key.
Definition lms.cpp:272
LMS_PublicKey(LMS_Params lms_params, LMOTS_Params lmots_params, LMS_Identifier I, LMS_Tree_Node lms_root)
Construct a public key for given public key data.
Definition lms.cpp:312
bool verify_signature(const LMS_Message &msg, const LMS_Signature &sig) const
Verify a LMS signature.
Definition lms.cpp:361
Container for LMS Signature data.
Definition lms.h:295
const LMOTS_Signature & lmots_sig() const
The LMOTS signature object containing the parsed LMOTS signature bytes contained in the LMS signature...
Definition lms.h:315
LMS_Tree_Node_Idx q() const
The index of the signing leaf given by the signature.
Definition lms.h:309
static LMS_Signature from_bytes_or_throw(BufferSlicer &slicer)
Parse the bytes of a lms signature into a LMS Signature object.
Definition lms.cpp:325
LMS_Algorithm_Type lms_type() const
The LMS algorithm type given by the signature.
Definition lms.h:320
StrongSpan< const LMS_AuthenticationPath > auth_path() const
The authentication path bytes given by the signature.
Definition lms.h:327
static size_t size(const LMS_Params &lms_params, const LMOTS_Params &lmots_params)
Definition lms.cpp:428
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
constexpr T & get() &
Definition strong_type.h:85
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:67
LMOTS_K lmots_compute_pubkey_from_sig(const LMOTS_Signature &sig, const LMS_Message &msg, const LMS_Identifier &identifier, LMS_Tree_Node_Idx q)
Compute a public key candidate for an OTS-signature-message pair and the OTS instance parameters.
Definition lm_ots.cpp:333
Strong< std::vector< uint8_t >, struct LMOTS_K_ > LMOTS_K
The K value from the LM-OTS public key.
Definition lm_ots.h:38
Gf448Elem root(const Gf448Elem &elem)
Compute the root of elem in the field.
LMS_Algorithm_Type
Enum of available LMS algorithm types.
Definition lms.h:32
constexpr size_t LMS_IDENTIFIER_LEN
The length in bytes of the LMS identifier (I).
Definition lms.h:69
Strong< std::vector< uint8_t >, struct LMS_Tree_Node_ > LMS_Tree_Node
A node with the LMS tree.
Definition lms.h:79
Strong< std::vector< uint8_t >, struct LMS_AuthenticationPath_ > LMS_AuthenticationPath
The authentication path of an LMS signature.
Definition lms.h:74
Strong< std::vector< uint8_t >, struct LMS_Identifier_ > LMS_Identifier
The identifier of an LMS tree (I in RFC 8554).
Definition lm_ots.h:53
LMOTS_Algorithm_Type
Enum of available LM-OTS algorithm types.
Definition lm_ots.h:68
Strong< std::vector< uint8_t >, struct LMS_Message_ > LMS_Message
A message that is signed with an LMS tree.
Definition lm_ots.h:58
constexpr auto concat(Rs &&... ranges)
Definition concat_util.h:90
Strong< uint32_t, struct LMS_Tree_Node_Idx_, EnableArithmeticWithPlainNumber > LMS_Tree_Node_Idx
The index of a node within a specific LMS tree layer.
Definition lm_ots.h:48
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504
void treehash(StrongSpan< SphincsTreeNode > out_root, StrongSpan< SphincsAuthenticationPath > out_auth_path, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes, std::optional< TreeNodeIndex > leaf_idx, uint32_t idx_offset, uint32_t total_tree_height, const GenerateLeafFunction &gen_leaf, Sphincs_Address &tree_address)
void compute_root(StrongSpan< SphincsTreeNode > out, const Sphincs_Parameters &params, Sphincs_Hash_Functions &hashes, const SphincsTreeNode &leaf, TreeNodeIndex leaf_idx, uint32_t idx_offset, StrongSpan< const SphincsAuthenticationPath > authentication_path, uint32_t total_tree_height, Sphincs_Address &tree_address)
Strong< std::vector< uint8_t >, struct LMOTS_Signature_Bytes_ > LMOTS_Signature_Bytes
Byte vector of an LM-OTS signature.
Definition lm_ots.h:43