Botan 3.10.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/internal/loadstor.h>
12#include <botan/internal/tree_hash.h>
13
14namespace Botan {
15namespace {
16
17/**
18 * @brief Domain-separation parameter when computing the hash of the leaf of an LMS tree.
19 */
20constexpr uint16_t D_LEAF = 0x8282;
21
22/**
23 * @brief Domain-separation parameter when computing the hash of an interior node of an LMS tree.
24 */
25constexpr uint16_t D_INTR = 0x8383;
26
27/// Index of the layer in a tree starting with 0 for the bottom level to the root layer
29
30class TreeAddress final {
31 public:
32 explicit TreeAddress(uint32_t total_tree_height) : m_h(total_tree_height), m_r(0) {
33 BOTAN_ARG_CHECK(total_tree_height > 0 && total_tree_height < 32, "Invalid tree height");
34 }
35
36 TreeAddress& set_address(LMS_TreeLayerIndex tree_layer, LMS_Tree_Node_Idx tree_index) {
37 BOTAN_ARG_CHECK(tree_index.get() < (1 << m_h), "Invalid tree index");
38 BOTAN_ARG_CHECK(tree_layer.get() <= m_h, "Invalid tree index");
39 m_r = (1 << (m_h - tree_layer)).get() + tree_index.get();
40 return *this;
41 }
42
43 uint32_t r() const { return m_r; }
44
45 bool is_leaf() const { return m_r >= (1 << m_h); }
46
47 LMS_Tree_Node_Idx q() const {
48 BOTAN_STATE_CHECK(is_leaf());
49 return LMS_Tree_Node_Idx(m_r - (1 << m_h.get()));
50 }
51
52 private:
53 LMS_TreeLayerIndex m_h;
54 uint32_t m_r;
55};
56
57auto get_hash_pair_func_for_identifier(const LMS_Params& lms_params, LMS_Identifier identifier) {
58 return [hash = lms_params.hash(), I = std::move(identifier)](StrongSpan<LMS_Tree_Node> out,
59 const TreeAddress& address,
62 hash->update(I);
63 hash->update(store_be(address.r()));
64 hash->update(store_be(D_INTR));
65 hash->update(left);
66 hash->update(right);
67 hash->final(out);
68 };
69}
70
71void lms_gen_leaf(StrongSpan<LMS_Tree_Node> out,
72 const LMOTS_Public_Key& lmots_pk,
73 const TreeAddress& tree_address,
74 HashFunction& hash) {
75 hash.update(lmots_pk.identifier());
76 hash.update(store_be(tree_address.r()));
77 hash.update(store_be(D_LEAF));
78 hash.update(lmots_pk.K());
79 hash.final(out);
80}
81
82auto lms_gen_leaf_func(const LMS_PrivateKey& lms_sk) {
83 return [hash = lms_sk.lms_params().hash(), lms_sk](StrongSpan<LMS_Tree_Node> out, const TreeAddress& tree_address) {
84 auto lmots_sk = LMOTS_Private_Key(lms_sk.lmots_params(), lms_sk.identifier(), tree_address.q(), lms_sk.seed());
85 auto lmots_pk = LMOTS_Public_Key(lmots_sk);
86 lms_gen_leaf(out, lmots_pk, tree_address, *hash);
87 };
88}
89
90void lms_treehash(StrongSpan<LMS_Tree_Node> out_root,
91 std::optional<StrongSpan<LMS_AuthenticationPath>> out_auth_path,
92 std::optional<LMS_Tree_Node_Idx> leaf_idx,
93 const LMS_PrivateKey& lms_sk) {
94 auto hash_pair_func = get_hash_pair_func_for_identifier(lms_sk.lms_params(), lms_sk.identifier());
95 auto gen_leaf = lms_gen_leaf_func(lms_sk);
96 TreeAddress lms_tree_address(lms_sk.lms_params().h());
97
98 treehash(out_root,
99 out_auth_path,
100 leaf_idx,
101 lms_sk.lms_params().m(),
102 LMS_TreeLayerIndex(lms_sk.lms_params().h()),
103 0,
104 std::move(hash_pair_func),
105 std::move(gen_leaf),
106 lms_tree_address);
107}
108
109} // namespace
110
112 auto [hash_name, height] = [](const LMS_Algorithm_Type& lms_type) -> std::pair<std::string_view, uint8_t> {
113 switch(lms_type) {
115 return {"SHA-256", static_cast<uint8_t>(5)};
117 return {"SHA-256", static_cast<uint8_t>(10)};
119 return {"SHA-256", static_cast<uint8_t>(15)};
121 return {"SHA-256", static_cast<uint8_t>(20)};
123 return {"SHA-256", static_cast<uint8_t>(25)};
125 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(5)};
127 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(10)};
129 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(15)};
131 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(20)};
133 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(25)};
135 return {"SHAKE-256(256)", static_cast<uint8_t>(5)};
137 return {"SHAKE-256(256)", static_cast<uint8_t>(10)};
139 return {"SHAKE-256(256)", static_cast<uint8_t>(15)};
141 return {"SHAKE-256(256)", static_cast<uint8_t>(20)};
143 return {"SHAKE-256(256)", static_cast<uint8_t>(25)};
145 return {"SHAKE-256(192)", static_cast<uint8_t>(5)};
147 return {"SHAKE-256(192)", static_cast<uint8_t>(10)};
149 return {"SHAKE-256(192)", static_cast<uint8_t>(15)};
151 return {"SHAKE-256(192)", static_cast<uint8_t>(20)};
153 return {"SHAKE-256(192)", static_cast<uint8_t>(25)};
154 default:
155 throw Decoding_Error("Unsupported LMS algorithm type");
156 }
157 }(type);
158
159 return LMS_Params(type, hash_name, height);
160}
161
162LMS_Params LMS_Params::create_or_throw(std::string_view hash_name, uint8_t height) {
163 LMS_Algorithm_Type type = [](std::string_view hash, uint8_t h) -> LMS_Algorithm_Type {
164 if(hash == "SHA-256") {
165 switch(h) {
166 case 5:
168 case 10:
170 case 15:
172 case 20:
174 case 25:
176 default:
177 throw Decoding_Error("Unsupported height for hash function");
178 }
179 }
180 if(hash == "Truncated(SHA-256,192)") {
181 switch(h) {
182 case 5:
184 case 10:
186 case 15:
188 case 20:
190 case 25:
192 default:
193 throw Decoding_Error("Unsupported height for hash function");
194 }
195 }
196 if(hash == "SHAKE-256(256)") {
197 switch(h) {
198 case 5:
200 case 10:
202 case 15:
204 case 20:
206 case 25:
208 default:
209 throw Decoding_Error("Unsupported height for hash function");
210 }
211 }
212 if(hash == "SHAKE-256(192)") {
213 switch(h) {
214 case 5:
216 case 10:
218 case 15:
220 case 20:
222 case 25:
224 default:
225 throw Decoding_Error("Unsupported height for hash function");
226 }
227 }
228 throw Decoding_Error("Unsupported hash function");
229 }(hash_name, height);
230
231 return LMS_Params(type, hash_name, height);
232}
233
234LMS_Params::LMS_Params(LMS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t h) :
235 m_algorithm_type(algorithm_type), m_h(h), m_hash_name(hash_name) {
236 const auto hash = HashFunction::create_or_throw(m_hash_name);
237 m_m = hash->output_length();
238}
239
242 const LMS_Message& msg) const {
243 // Pre-alloc space for the signature
244 BOTAN_ARG_CHECK(out_sig.size() == LMS_Signature::size(lms_params(), lmots_params()), "Invalid output buffer size");
245
246 BufferStuffer sig_stuffer(out_sig);
247 sig_stuffer.append(store_be(q));
248 const LMOTS_Private_Key lmots_sk(lmots_params(), identifier(), q, seed());
249 lmots_sk.sign(sig_stuffer.next<LMOTS_Signature_Bytes>(LMOTS_Signature::size(lmots_params())), msg);
250 sig_stuffer.append(store_be(lms_params().algorithm_type()));
251 const auto auth_path_buffer = sig_stuffer.next<LMS_AuthenticationPath>(lms_params().m() * lms_params().h());
252
253 BOTAN_ASSERT_NOMSG(sig_stuffer.full());
254
255 TreeAddress lms_tree_address(lms_params().h());
256 LMS_Tree_Node pk_buffer(lms_params().m());
257 lms_treehash(StrongSpan<LMS_Tree_Node>(pk_buffer.get()), auth_path_buffer, q, *this);
258
259 CT::unpoison(pk_buffer);
260 return LMS_PublicKey(lms_params(), lmots_params(), identifier(), std::move(pk_buffer));
261}
262
264 size_t total_remaining_bytes = slicer.remaining();
265 // Alg. 6. 1. (4 bytes are sufficient until the next check)
266 if(total_remaining_bytes < sizeof(LMS_Algorithm_Type)) {
267 throw Decoding_Error("Too few bytes while parsing LMS public key.");
268 }
269 // Alg. 6. 2.a.
270 auto lms_type = load_be<LMS_Algorithm_Type>(slicer.take<sizeof(LMS_Algorithm_Type)>());
271 // Alg. 6. 2.c.
273 // Alg. 6. 2.d.
274 if(total_remaining_bytes < LMS_PublicKey::size(lms_params)) {
275 throw Decoding_Error("Too few bytes while parsing LMS public key.");
276 }
277 // Alg. 6. 2.b.
278 auto lmots_type = load_be<LMOTS_Algorithm_Type>(slicer.take<sizeof(LMOTS_Algorithm_Type)>());
280
281 if(lms_params.hash_name() != lmots_params.hash_name()) {
282 throw Decoding_Error("No support for HSS-LMS instances with multiple hash functions.");
283 }
284
285 // Alg. 6. 2.e.
286 auto I = slicer.copy<LMS_Identifier>(LMS_IDENTIFIER_LEN);
287 // Alg. 6. 2.f.
288 auto lms_root = slicer.copy<LMS_Tree_Node>(lms_params.m());
289
290 return LMS_PublicKey(std::move(lms_params), std::move(lmots_params), std::move(I), std::move(lms_root));
291}
292
293std::vector<uint8_t> LMS_PublicKey::to_bytes() const {
294 // clang-format off
296 store_be(lms_params().algorithm_type()),
297 store_be(lmots_params().algorithm_type()),
298 identifier(),
299 m_lms_root);
300 // clang-format on
301}
302
306 LMS_Tree_Node lms_root) :
307 LMS_Instance(std::move(lms_params), std::move(lmots_params), std::move(I)), m_lms_root(std::move(lms_root)) {
308 BOTAN_ARG_CHECK(identifier().size() == LMS_IDENTIFIER_LEN, "Invalid LMS identifier");
309 BOTAN_ARG_CHECK(m_lms_root.size() == this->lms_params().m(), "Invalid LMS root");
310}
311
315
317 size_t total_remaining_bytes = slicer.remaining();
318 // Alg. 6a 1. (next 4 bytes are checked in LMOTS_Signature::from_bytes_or_throw)
319 if(total_remaining_bytes < sizeof(LMS_Tree_Node_Idx)) {
320 throw Decoding_Error("Too few signature bytes while parsing LMS signature.");
321 }
322 // Alg. 6a 2.a.
323 auto q = load_be<LMS_Tree_Node_Idx>(slicer.take<sizeof(LMS_Tree_Node_Idx)>());
324
325 // Alg. 6a 2.b.-e.
327 LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type());
328
329 if(slicer.remaining() < sizeof(LMS_Algorithm_Type)) {
330 throw Decoding_Error("Too few signature bytes while parsing LMS signature.");
331 }
332 // Alg. 6a 2.f.
334 // Alg. 6a 2.h.
336 // Alg. 6a 2.i. (signature is not exactly [...] bytes long)
337 if(total_remaining_bytes < size(lms_params, lmots_params)) {
338 throw Decoding_Error("Too few signature bytes while parsing LMS signature.");
339 }
340
341 // Alg. 6a 2.j.
342 auto auth_path = slicer.copy<LMS_AuthenticationPath>(lms_params.m() * lms_params.h());
343
344 return LMS_Signature(q, std::move(lmots_sig), lms_type, std::move(auth_path));
345}
346
348 /* NOLINT(*-slicing) */ LMS_Instance(sk), m_lms_root(sk.lms_params().m()) {
349 lms_treehash(StrongSpan<LMS_Tree_Node>(m_lms_root), std::nullopt, std::nullopt, sk);
350}
351
353 if(lms_root().size() != lms_params().m()) {
354 // LMS public key (T[1] part) has unexpected length
355 return false;
356 }
357 if(lms_params().algorithm_type() != sig.lms_type()) {
358 // LMS algorithm type does not match with the signature's
359 return false;
360 }
361 // Alg. 6a 2.g.
362 if(lmots_params().algorithm_type() != sig.lmots_sig().algorithm_type()) {
363 // LMOTS algorithm type does not match with the signature's
364 return false;
365 }
366 // Alg. 6a 2.i.
367 if(sig.q() >= (1ULL << uint64_t(lms_params().h()))) {
368 return false;
369 }
370 // Alg 6. 3.
371 std::optional<LMS_Tree_Node> Tc = lms_compute_root_from_sig(msg, sig);
372 if(!Tc.has_value()) {
373 return false;
374 }
375 // Alg 6. 4.
376 return Tc.value() == lms_root();
377}
378
379std::optional<LMS_Tree_Node> LMS_PublicKey::lms_compute_root_from_sig(const LMS_Message& msg,
380 const LMS_Signature& sig) const {
381 // Alg. 6a 2.c, 2.g
382 if(lms_params().algorithm_type() != sig.lms_type() ||
383 lmots_params().algorithm_type() != sig.lmots_sig().algorithm_type()) {
384 return std::nullopt;
385 }
386 try {
387 const LMS_Params lms_params = LMS_Params::create_or_throw(sig.lms_type());
388 const LMOTS_Signature& lmots_sig = sig.lmots_sig();
389 const LMOTS_Params lmots_params = LMOTS_Params::create_or_throw(lmots_sig.algorithm_type());
390 const LMOTS_K Kc = lmots_compute_pubkey_from_sig(lmots_sig, msg, identifier(), sig.q());
391 const auto hash = lms_params.hash();
392
393 auto hash_pair_func = get_hash_pair_func_for_identifier(lms_params, identifier());
394
395 auto lms_address = TreeAddress(lms_params.h());
396 lms_address.set_address(LMS_TreeLayerIndex(0), LMS_Tree_Node_Idx(sig.q().get()));
397
398 LMOTS_Public_Key pk_candidate(lmots_params, identifier(), sig.q(), Kc);
400 lms_gen_leaf(tmp, pk_candidate, lms_address, *hash);
401
403
404 compute_root(StrongSpan<LMS_Tree_Node>(root),
405 sig.auth_path(),
406 sig.q(),
407 StrongSpan<const LMS_Tree_Node>(tmp),
408 lms_params.m(),
409 LMS_TreeLayerIndex(lms_params.h()),
410 0,
411 std::move(hash_pair_func),
412 lms_address);
413 return LMS_Tree_Node(root);
414 } catch(const Decoding_Error&) {
415 return std::nullopt;
416 }
417}
418
419size_t LMS_Signature::size(const LMS_Params& lms_params, const LMOTS_Params& lmots_params) {
420 return sizeof(uint32_t) + LMOTS_Signature::size(lmots_params) + sizeof(uint32_t) + lms_params.h() * lms_params.m();
421}
422
423} // 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
Definition stl_util.h:118
auto copy(const size_t count)
Definition stl_util.h:80
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:89
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:133
constexpr void append(std::span< const uint8_t > buffer)
Definition stl_util.h:168
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:141
constexpr bool full() const
Definition stl_util.h:178
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:100
static LMOTS_Params create_or_throw(LMOTS_Algorithm_Type type)
Create the LM-OTS parameters from a known algorithm type.
Definition lm_ots.cpp:100
Representation of an LMOTS private key.
Definition lm_ots.h:257
void sign(StrongSpan< LMOTS_Signature_Bytes > out_sig, const LMS_Message &msg) const
Generate a new LMOTS signature.
Definition lm_ots.cpp:276
Representation of an OTS public key.
Definition lm_ots.h:306
static size_t size(const LMOTS_Params &params)
The expected size of the signature.
Definition lm_ots.h:207
LMOTS_Algorithm_Type algorithm_type() const
Returns the LM-OTS algorithm type.
Definition lm_ots.h:192
static LMOTS_Signature from_bytes_or_throw(BufferSlicer &slicer)
Parse a LM-OTS signature.
Definition lm_ots.cpp:235
const LMS_Params & lms_params() const
The LMS parameters for this LMS instance.
Definition lms.h:164
const LMOTS_Params & lmots_params() const
The LMOTS parameters used for OTS instances of this LMS instance.
Definition lms.h:169
const LMS_Identifier & identifier() const
The identifier of this LMS tree ('I' in RFC 8554).
Definition lms.h:174
LMS_Instance(LMS_Params lms_params, LMOTS_Params lmots_params, LMS_Identifier identifier)
Constructor storing the provided LMS data.
Definition lms.h:156
The LMS parameters.
Definition lms.h:89
const std::string & hash_name() const
Returns the name of the hash function to use.
Definition lms.h:124
size_t m() const
Returns the number of bytes associated with each node.
Definition lms.h:119
std::unique_ptr< HashFunction > hash() const
Construct a new hash instance for the LMS instance.
Definition lms.h:129
uint8_t h() const
Returns the height of the LMS tree.
Definition lms.h:114
static LMS_Params create_or_throw(LMS_Algorithm_Type type)
Create the LMS parameters from a known algorithm type.
Definition lms.cpp:111
Representation of an LMS Private key.
Definition lms.h:190
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:240
const LMS_Seed & seed() const
The secret seed used for LMOTS' WOTS chain input creation (RFC 8554 Appendix A).
Definition lms.h:201
The LMS public key.
Definition lms.h:225
static size_t size(const LMS_Params &lms_params)
The expected size of an LMS public key for given lms_params.
Definition lms.cpp:312
std::vector< uint8_t > to_bytes() const
Bytes of the full lms public key according to 8554 5.3.
Definition lms.cpp:293
static LMS_PublicKey from_bytes_or_throw(BufferSlicer &slicer)
Parse a public LMS key.
Definition lms.cpp:263
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:303
bool verify_signature(const LMS_Message &msg, const LMS_Signature &sig) const
Verify a LMS signature.
Definition lms.cpp:352
Container for LMS Signature data.
Definition lms.h:293
const LMOTS_Signature & lmots_sig() const
The LMOTS signature object containing the parsed LMOTS signature bytes contained in the LMS signature...
Definition lms.h:313
LMS_Tree_Node_Idx q() const
The index of the signing leaf given by the signature.
Definition lms.h:307
static LMS_Signature from_bytes_or_throw(BufferSlicer &slicer)
Parse the bytes of a lms signature into a LMS Signature object.
Definition lms.cpp:316
LMS_Algorithm_Type lms_type() const
The LMS algorithm type given by the signature.
Definition lms.h:318
StrongSpan< const LMS_AuthenticationPath > auth_path() const
The authentication path bytes given by the signature.
Definition lms.h:325
static size_t size(const LMS_Params &lms_params, const LMOTS_Params &lmots_params)
Definition lms.cpp:419
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
constexpr T & get() &
Definition strong_type.h:52
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:65
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:327
Strong< std::vector< uint8_t >, struct LMOTS_K_ > LMOTS_K
The K value from the LM-OTS public key.
Definition lm_ots.h:35
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:30
constexpr size_t LMS_IDENTIFIER_LEN
The length in bytes of the LMS identifier (I).
Definition lms.h:67
Strong< std::vector< uint8_t >, struct LMS_Tree_Node_ > LMS_Tree_Node
A node with the LMS tree.
Definition lms.h:77
Strong< std::vector< uint8_t >, struct LMS_AuthenticationPath_ > LMS_AuthenticationPath
The authentication path of an LMS signature.
Definition lms.h:72
Strong< std::vector< uint8_t >, struct LMS_Identifier_ > LMS_Identifier
The identifier of an LMS tree (I in RFC 8554).
Definition lm_ots.h:50
LMOTS_Algorithm_Type
Enum of available LM-OTS algorithm types.
Definition lm_ots.h:65
Strong< std::vector< uint8_t >, struct LMS_Message_ > LMS_Message
A message that is signed with an LMS tree.
Definition lm_ots.h:55
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:254
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:45
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:40