Coverage Report

Created: 2024-11-29 06:10

/src/botan/src/lib/pubkey/hss_lms/hss.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * HSS - Hierarchical Signatures System (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/hss.h>
10
11
#include <botan/internal/fmt.h>
12
#include <botan/internal/hss_lms_utils.h>
13
#include <botan/internal/int_utils.h>
14
#include <botan/internal/scan_name.h>
15
#include <botan/internal/stl_util.h>
16
17
#include <algorithm>
18
#include <limits>
19
20
namespace Botan {
21
22
namespace {
23
24
/**
25
 * @brief The maximum number of levels in a HSS-LMS tree.
26
 *
27
 * RFC 8554 Section 6:
28
 *   The number of levels is denoted as L and is between one
29
 *   and eight, inclusive.
30
 */
31
constexpr HSS_Level HSS_MAX_LEVELS(8);
32
33
/**
34
 * @brief Domain-separation parameter for generation the seed of a child LMS tree.
35
 *
36
 * This comes from https://github.com/cisco/hash-sigs.
37
 */
38
constexpr uint16_t SEED_CHILD_SEED = 0xfffe;
39
40
/**
41
 * @brief Domain-separation parameter for generation the identifier of a child LMS tree.
42
 *
43
 * This comes from https://github.com/cisco/hash-sigs.
44
 */
45
constexpr uint16_t SEED_CHILD_I = 0xffff;
46
47
/**
48
 * @brief Check that the given @p hash_name is one of the supported hash functions for HSS-LMS.
49
 */
50
0
constexpr bool is_supported_hash_function(std::string_view hash_name) {
51
0
   return hash_name == "SHA-256" || hash_name == "Truncated(SHA-256,192)" || hash_name == "SHAKE-256(256)" ||
52
0
          hash_name == "SHAKE-256(192)";
53
0
}
54
55
/**
56
 * Given an HSS index, i.e. the number of already created HSS signatures, return the lms leaf indices for
57
 * the different LMS layers from root layer to bottom layer.
58
 */
59
std::vector<LMS_Tree_Node_Idx> derive_lms_leaf_indices_from_hss_index(HSS_Sig_Idx hss_idx,
60
0
                                                                      const HSS_LMS_Params& hss_params) {
61
0
   std::vector<LMS_Tree_Node_Idx> q(hss_params.L().get());
62
0
   for(int32_t layer_ctr = hss_params.L().get() - 1; layer_ctr >= 0; --layer_ctr) {
63
0
      HSS_Level layer(layer_ctr);
64
0
      const HSS_LMS_Params::LMS_LMOTS_Params_Pair& layer_params = hss_params.params_at_level(layer);
65
0
      size_t layer_h = layer_params.lms_params().h();
66
0
      q.at(layer.get()) =
67
0
         checked_cast_to<LMS_Tree_Node_Idx>(hss_idx.get() % checked_cast_to<uint64_t>(1ULL << layer_h));
68
0
      hss_idx = hss_idx >> layer_h;
69
0
   }
70
0
   BOTAN_ARG_CHECK(hss_idx == HSS_Sig_Idx(0), "HSS Tree is exhausted");
71
72
0
   return q;
73
0
}
74
75
}  // namespace
76
77
HSS_LMS_Params::HSS_LMS_Params(std::vector<LMS_LMOTS_Params_Pair> lm_lmots_params) :
78
0
      m_lms_lmots_params(std::move(lm_lmots_params)), m_max_sig_count(calc_max_sig_count()) {
79
0
   BOTAN_ARG_CHECK(!m_lms_lmots_params.empty() && m_lms_lmots_params.size() <= HSS_MAX_LEVELS,
80
0
                   "Invalid number of levels");
81
0
}
82
83
0
HSS_LMS_Params::HSS_LMS_Params(std::string_view algo_params) {
84
0
   const auto wrap_in_hss_lms = [&]() {
85
0
      if(algo_params.starts_with("HSS-LMS(")) {
86
0
         return std::string(algo_params);
87
0
      } else {
88
0
         return fmt("HSS-LMS({})", algo_params);
89
0
      }
90
0
   }();
91
0
   SCAN_Name scan(wrap_in_hss_lms);
92
93
0
   BOTAN_ARG_CHECK(scan.arg_count() >= 2 && scan.arg_count() <= HSS_MAX_LEVELS + 1, "Invalid number of arguments");
94
0
   std::string hash = scan.arg(0);
95
0
   BOTAN_ARG_CHECK(is_supported_hash_function(hash), "Supported HSS-LMS hash function");
96
97
0
   for(size_t i = 1; i < scan.arg_count(); ++i) {
98
0
      SCAN_Name scan_layer(scan.arg(i));
99
0
      BOTAN_ARG_CHECK(scan_layer.algo_name() == "HW", "Invalid name for layer parameters");
100
0
      BOTAN_ARG_CHECK(scan_layer.arg_count() == 2, "Invalid number of layer parameters");
101
0
      const auto h =
102
0
         checked_cast_to_or_throw<uint8_t, Invalid_Argument>(scan_layer.arg_as_integer(0), "Invalid tree height");
103
0
      const auto w = checked_cast_to_or_throw<uint8_t, Invalid_Argument>(scan_layer.arg_as_integer(1),
104
0
                                                                         "Invalid Winternitz parameter");
105
0
      m_lms_lmots_params.push_back({LMS_Params::create_or_throw(hash, h), LMOTS_Params::create_or_throw(hash, w)});
106
0
   }
107
0
   m_max_sig_count = calc_max_sig_count();
108
0
}
109
110
0
HSS_Sig_Idx HSS_LMS_Params::calc_max_sig_count() const {
111
0
   uint32_t total_height_counter = 0;
112
0
   for(HSS_Level level(0); level < L(); level++) {
113
0
      total_height_counter += params_at_level(level).lms_params().h();
114
0
   }
115
0
   if(total_height_counter >= sizeof(HSS_Sig_Idx) * 8) {
116
0
      return HSS_Sig_Idx(std::numeric_limits<HSS_Sig_Idx::wrapped_type>::max());
117
0
   }
118
0
   return HSS_Sig_Idx(1) << total_height_counter;
119
0
}
120
121
HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(const HSS_LMS_Params& hss_params, RandomNumberGenerator& rng) :
122
0
      m_hss_params(hss_params), m_current_idx(0), m_sig_size(HSS_Signature::size(m_hss_params)) {
123
0
   m_hss_seed = rng.random_vec<LMS_Seed>(m_hss_params.params_at_level(HSS_Level(0)).lms_params().m());
124
0
   m_identifier = rng.random_vec<LMS_Identifier>(LMS_IDENTIFIER_LEN);
125
0
}
126
127
std::shared_ptr<HSS_LMS_PrivateKeyInternal> HSS_LMS_PrivateKeyInternal::from_bytes_or_throw(
128
0
   std::span<const uint8_t> key_bytes) {
129
0
   if(key_bytes.size() < sizeof(HSS_Level) + sizeof(HSS_Sig_Idx)) {
130
0
      throw Decoding_Error("Too few private key bytes.");
131
0
   }
132
0
   BufferSlicer slicer(key_bytes);
133
134
0
   const auto L = load_be<HSS_Level>(slicer.take<sizeof(HSS_Level)>());
135
0
   if(L == 0U || L > HSS_MAX_LEVELS) {
136
0
      throw Decoding_Error("Invalid number of HSS layers in private HSS-LMS key.");
137
0
   }
138
139
0
   const auto sig_idx = load_be<HSS_Sig_Idx>(slicer.take<sizeof(HSS_Sig_Idx)>());
140
141
0
   std::vector<HSS_LMS_Params::LMS_LMOTS_Params_Pair> params;
142
0
   for(size_t layer = 1; layer <= L; ++layer) {
143
0
      if(slicer.remaining() < sizeof(LMS_Algorithm_Type) + sizeof(LMOTS_Algorithm_Type)) {
144
0
         throw Decoding_Error("Out of bytes while parsing private HSS-LMS key.");
145
0
      }
146
0
      const auto lms_type = load_be<LMS_Algorithm_Type>(slicer.take<sizeof(LMS_Algorithm_Type)>());
147
0
      const auto lmots_type = load_be<LMOTS_Algorithm_Type>(slicer.take<sizeof(LMOTS_Algorithm_Type)>());
148
0
      params.push_back({LMS_Params::create_or_throw(lms_type), LMOTS_Params::create_or_throw(lmots_type)});
149
0
   }
150
0
   std::string hash_name = params.at(0).lms_params().hash_name();
151
0
   if(std::any_of(params.begin(), params.end(), [&hash_name](HSS_LMS_Params::LMS_LMOTS_Params_Pair& lms_lmots_params) {
152
0
         bool invalid_lmots_hash = lms_lmots_params.lmots_params().hash_name() != hash_name;
153
0
         bool invalid_lms_hash = lms_lmots_params.lms_params().hash_name() != hash_name;
154
0
         return invalid_lmots_hash || invalid_lms_hash;
155
0
      })) {
156
0
      throw Decoding_Error("Inconsistent hash functions are not allowed.");
157
0
   }
158
159
0
   if(slicer.remaining() < params.at(0).lms_params().m() + LMS_IDENTIFIER_LEN) {
160
0
      throw Decoding_Error("Out of bytes while parsing private HSS-LMS key.");
161
0
   }
162
0
   auto hss_seed = slicer.copy<LMS_Seed>(params.at(0).lms_params().m());
163
0
   auto identifier = slicer.copy<LMS_Identifier>(LMS_IDENTIFIER_LEN);
164
165
0
   if(!slicer.empty()) {
166
0
      throw Decoding_Error("Private HSS-LMS key contains more bytes than expected.");
167
0
   }
168
0
   auto sk = std::shared_ptr<HSS_LMS_PrivateKeyInternal>(
169
0
      new HSS_LMS_PrivateKeyInternal(HSS_LMS_Params(std::move(params)), std::move(hss_seed), std::move(identifier)));
170
171
0
   sk->set_idx(sig_idx);
172
0
   return sk;
173
0
}
174
175
0
secure_vector<uint8_t> HSS_LMS_PrivateKeyInternal::to_bytes() const {
176
0
   secure_vector<uint8_t> sk_bytes(size());
177
0
   BufferStuffer stuffer(sk_bytes);
178
179
0
   stuffer.append(store_be(hss_params().L()));
180
0
   stuffer.append(store_be(get_idx()));
181
182
0
   for(HSS_Level layer(1); layer <= hss_params().L(); ++layer) {
183
0
      const auto& params = hss_params().params_at_level(layer - 1);
184
0
      stuffer.append(store_be(params.lms_params().algorithm_type()));
185
0
      stuffer.append(store_be(params.lmots_params().algorithm_type()));
186
0
   }
187
0
   stuffer.append(m_hss_seed);
188
0
   stuffer.append(m_identifier);
189
0
   BOTAN_ASSERT_NOMSG(stuffer.full());
190
191
0
   return sk_bytes;
192
0
}
193
194
0
void HSS_LMS_PrivateKeyInternal::set_idx(HSS_Sig_Idx idx) {
195
0
   m_current_idx = idx;
196
0
}
197
198
0
HSS_Sig_Idx HSS_LMS_PrivateKeyInternal::reserve_next_idx() {
199
0
   HSS_Sig_Idx next_idx = m_current_idx;
200
0
   if(next_idx >= m_hss_params.max_sig_count()) {
201
0
      throw Decoding_Error("HSS private key is exhausted");
202
0
   }
203
0
   set_idx(m_current_idx + 1);
204
0
   return next_idx;
205
0
}
206
207
0
size_t HSS_LMS_PrivateKeyInternal::size() const {
208
0
   size_t sk_size = sizeof(HSS_Level) + sizeof(HSS_Sig_Idx);
209
   // The concatenated algorithm types for all layers
210
0
   sk_size += hss_params().L().get() * (sizeof(LMS_Algorithm_Type) + sizeof(LMOTS_Algorithm_Type));
211
0
   sk_size += m_hss_seed.size() + m_identifier.size();
212
0
   return sk_size;
213
0
}
214
215
HSS_LMS_PrivateKeyInternal::HSS_LMS_PrivateKeyInternal(HSS_LMS_Params hss_params,
216
                                                       LMS_Seed hss_seed,
217
                                                       LMS_Identifier identifier) :
218
0
      m_hss_params(std::move(hss_params)),
219
0
      m_hss_seed(std::move(hss_seed)),
220
0
      m_identifier(std::move(identifier)),
221
0
      m_current_idx(0),
222
0
      m_sig_size(HSS_Signature::size(m_hss_params)) {
223
0
   BOTAN_ARG_CHECK(m_hss_seed.size() == m_hss_params.params_at_level(HSS_Level(0)).lms_params().m(),
224
0
                   "Invalid seed size");
225
0
   BOTAN_ARG_CHECK(m_identifier.size() == LMS_IDENTIFIER_LEN, "Invalid identifier size");
226
0
}
227
228
0
std::vector<uint8_t> HSS_LMS_PrivateKeyInternal::sign(std::span<const uint8_t> msg) {
229
0
   std::vector<uint8_t> sig(HSS_Signature::size(hss_params()));
230
0
   BufferStuffer sig_stuffer(sig);
231
0
   sig_stuffer.append(store_be(hss_params().L() - 1));
232
233
0
   std::vector<LMS_Tree_Node_Idx> q = derive_lms_leaf_indices_from_hss_index(reserve_next_idx(), hss_params());
234
235
   // Derive LMS private keys and compute buffers
236
0
   std::vector<LMS_PrivateKey> lms_key_at_layer;
237
0
   std::vector<StrongSpan<LMS_Signature_Bytes>> out_lms_sig_buffer_at_layer;
238
0
   std::vector<std::span<uint8_t>> out_child_pk_buffer_at_layer;
239
0
   for(HSS_Level layer(0); layer < hss_params().L(); ++layer) {
240
      // Generate key for current layer
241
0
      const HSS_LMS_Params::LMS_LMOTS_Params_Pair& layer_params = hss_params().params_at_level(layer);
242
0
      if(layer == HSS_Level(0)) {
243
0
         lms_key_at_layer.push_back(hss_derive_root_lms_private_key());
244
0
      } else {
245
0
         lms_key_at_layer.push_back(
246
0
            hss_derive_child_lms_private_key(layer_params, lms_key_at_layer.back(), q.at(layer.get() - 1)));
247
0
         out_child_pk_buffer_at_layer.push_back(sig_stuffer.next(LMS_PublicKey::size(layer_params.lms_params())));
248
0
      }
249
0
      out_lms_sig_buffer_at_layer.push_back(sig_stuffer.next<LMS_Signature_Bytes>(
250
0
         LMS_Signature::size(layer_params.lms_params(), layer_params.lmots_params())));
251
0
   }
252
0
   BOTAN_ASSERT_NOMSG(sig_stuffer.full());
253
254
   // Sign and write the signature from bottom layer to root layer
255
0
   std::vector<uint8_t> current_pk;
256
0
   for(int32_t layer_it = hss_params().L().get() - 1; layer_it >= 0; --layer_it) {
257
0
      HSS_Level layer(layer_it);
258
0
      if(layer == hss_params().L() - 1) {
259
0
         current_pk =
260
0
            lms_key_at_layer.at(layer.get())
261
0
               .sign_and_get_pk(out_lms_sig_buffer_at_layer.at(layer.get()), q.at(layer.get()), LMS_Message(msg))
262
0
               .to_bytes();
263
0
      } else {
264
0
         copy_mem(out_child_pk_buffer_at_layer.at(layer.get()), current_pk);
265
0
         current_pk =
266
0
            lms_key_at_layer.at(layer.get())
267
0
               .sign_and_get_pk(out_lms_sig_buffer_at_layer.at(layer.get()), q.at(layer.get()), LMS_Message(current_pk))
268
0
               .to_bytes();
269
0
      }
270
0
   }
271
272
0
   return sig;
273
0
}
274
275
0
LMS_PrivateKey HSS_LMS_PrivateKeyInternal::hss_derive_root_lms_private_key() const {
276
0
   auto& top_params = hss_params().params_at_level(HSS_Level(0));
277
0
   return LMS_PrivateKey(top_params.lms_params(), top_params.lmots_params(), m_identifier, m_hss_seed);
278
0
}
279
280
LMS_PrivateKey HSS_LMS_PrivateKeyInternal::hss_derive_child_lms_private_key(
281
   const HSS_LMS_Params::LMS_LMOTS_Params_Pair& child_lms_lmots_params,
282
   const LMS_PrivateKey& parent_sk,
283
0
   LMS_Tree_Node_Idx parent_q) {
284
0
   const auto hash = HashFunction::create_or_throw(child_lms_lmots_params.lms_params().hash_name());
285
286
   // CHILD_SEED = H( PARENT_I || PARENT_Q || SEED_CHILD_SEED || 0xff || PARENT_SEED )
287
0
   PseudorandomKeyGeneration seed_generator(parent_sk.identifier());
288
0
   seed_generator.set_q(parent_q.get());
289
0
   seed_generator.set_i(SEED_CHILD_SEED);
290
0
   seed_generator.set_j(0xff);
291
0
   auto child_seed = seed_generator.gen<LMS_Seed>(*hash, parent_sk.seed());
292
293
   // CHILD_I = H( PARENT_I || PARENT_Q || SEED_CHILD_I || 0xff || PARENT_SEED )
294
0
   seed_generator.set_i(SEED_CHILD_I);
295
0
   auto child_identifier = seed_generator.gen<LMS_Identifier>(*hash, parent_sk.seed());
296
0
   child_identifier.resize(LMS_IDENTIFIER_LEN);
297
0
   CT::unpoison(child_identifier);  // identifiers are part of the signature
298
299
0
   return LMS_PrivateKey(child_lms_lmots_params.lms_params(),
300
0
                         child_lms_lmots_params.lmots_params(),
301
0
                         std::move(child_identifier),
302
0
                         std::move(child_seed));
303
0
}
304
305
0
HSS_LMS_PublicKeyInternal HSS_LMS_PublicKeyInternal::create(const HSS_LMS_PrivateKeyInternal& hss_sk) {
306
0
   auto& hss_params = hss_sk.hss_params();
307
308
0
   const auto root_sk = hss_sk.hss_derive_root_lms_private_key();
309
0
   LMS_PublicKey top_pub_key = LMS_PublicKey(root_sk);
310
311
0
   return HSS_LMS_PublicKeyInternal(hss_params.L(), std::move(top_pub_key));
312
0
}
313
314
std::shared_ptr<HSS_LMS_PublicKeyInternal> HSS_LMS_PublicKeyInternal::from_bytes_or_throw(
315
0
   std::span<const uint8_t> key_bytes) {
316
0
   if(key_bytes.size() < sizeof(HSS_Level)) {
317
0
      throw Decoding_Error("Too few public key bytes.");
318
0
   }
319
0
   BufferSlicer slicer(key_bytes);
320
321
0
   const auto L = load_be<HSS_Level>(slicer.take<sizeof(HSS_Level)>());
322
0
   if(L > HSS_MAX_LEVELS) {
323
0
      throw Decoding_Error("Invalid number of HSS layers in public HSS-LMS key.");
324
0
   }
325
326
0
   LMS_PublicKey lms_pub_key = LMS_PublicKey::from_bytes_or_throw(slicer);
327
328
0
   if(!slicer.empty()) {
329
0
      throw Decoding_Error("Public HSS-LMS key contains more bytes than expected.");
330
0
   }
331
0
   return std::make_shared<HSS_LMS_PublicKeyInternal>(L, std::move(lms_pub_key));
332
0
}
333
334
0
std::vector<uint8_t> HSS_LMS_PublicKeyInternal::to_bytes() const {
335
0
   return concat<std::vector<uint8_t>>(store_be(m_L), m_top_lms_pub_key.to_bytes());
336
0
}
337
338
0
AlgorithmIdentifier HSS_LMS_PublicKeyInternal::algorithm_identifier() const {
339
0
   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
340
0
}
341
342
0
OID HSS_LMS_PublicKeyInternal::object_identifier() const {
343
0
   return OID::from_string(algo_name());
344
0
}
345
346
0
size_t HSS_LMS_PublicKeyInternal::size() const {
347
0
   return sizeof(m_L) + LMS_PublicKey::size(m_top_lms_pub_key.lms_params());
348
0
}
349
350
0
bool HSS_LMS_PublicKeyInternal::verify_signature(std::span<const uint8_t> msg, const HSS_Signature& sig) const {
351
0
   if(checked_cast_to<HSS_Level>(sig.Nspk()) + 1 != m_L) {
352
      // HSS levels in the public key does not match with the signature's
353
0
      return false;
354
0
   }
355
356
0
   const LMS_PublicKey* lms_pk = &lms_pub_key();
357
0
   const auto hash_name = lms_pk->lms_params().hash_name();
358
359
   // Verify the signature by the above layer over the LMS public keys for layer 1 to Nspk.
360
0
   for(HSS_Level layer(0); layer < sig.Nspk(); ++layer) {
361
0
      const HSS_Signature::Signed_Pub_Key& signed_pub_key = sig.signed_pub_key(layer);
362
0
      if(signed_pub_key.public_key().lms_params().hash_name() != hash_name ||
363
0
         signed_pub_key.public_key().lmots_params().hash_name() != hash_name) {
364
         // We do not allow HSS-LMS instances with multiple different hash functions.
365
0
         return false;
366
0
      }
367
0
      if(!lms_pk->verify_signature(LMS_Message(signed_pub_key.public_key().to_bytes()), signed_pub_key.signature())) {
368
0
         return false;
369
0
      }
370
0
      lms_pk = &signed_pub_key.public_key();
371
0
   }
372
373
   // Verify the signature by the bottom layer over the message.
374
0
   return lms_pk->verify_signature(LMS_Message(msg), sig.bottom_sig());
375
0
}
376
377
HSS_Signature::Signed_Pub_Key::Signed_Pub_Key(LMS_Signature sig, LMS_PublicKey pub) :
378
0
      m_sig(std::move(sig)), m_pub(std::move(pub)) {}
379
380
0
HSS_Signature HSS_Signature::from_bytes_or_throw(std::span<const uint8_t> sig_bytes) {
381
0
   if(sig_bytes.size() < sizeof(uint32_t)) {
382
0
      throw Decoding_Error("Too few HSS signature bytes.");
383
0
   }
384
0
   BufferSlicer slicer(sig_bytes);
385
386
0
   const auto Nspk = load_be(slicer.take<sizeof(uint32_t)>());
387
0
   if(Nspk >= HSS_MAX_LEVELS) {
388
0
      throw Decoding_Error("Invalid number of HSS layers in signature.");
389
0
   }
390
391
0
   std::vector<Signed_Pub_Key> signed_pub_keys;
392
0
   for(size_t i = 0; i < Nspk; ++i) {
393
0
      LMS_Signature sig = LMS_Signature::from_bytes_or_throw(slicer);
394
0
      LMS_PublicKey pub_key = LMS_PublicKey::from_bytes_or_throw(slicer);
395
0
      signed_pub_keys.push_back(Signed_Pub_Key(std::move(sig), std::move(pub_key)));
396
0
   }
397
398
0
   auto sig = LMS_Signature::from_bytes_or_throw(slicer);
399
400
0
   if(!slicer.empty()) {
401
0
      throw Decoding_Error("HSS-LMS signature contains more bytes than expected.");
402
0
   }
403
0
   return HSS_Signature(std::move(signed_pub_keys), std::move(sig));
404
0
}
405
406
0
size_t HSS_Signature::size(const HSS_LMS_Params& params) {
407
0
   size_t size = sizeof(uint32_t);
408
0
   size += LMS_Signature::size(params.params_at_level(HSS_Level(0)).lms_params(),
409
0
                               params.params_at_level(HSS_Level(0)).lmots_params());
410
0
   for(HSS_Level layer(1); layer < params.L(); ++layer) {
411
0
      const auto& param = params.params_at_level(layer);
412
0
      size += LMS_PublicKey::size(param.lms_params());
413
0
      size += LMS_Signature::size(param.lms_params(), param.lmots_params());
414
0
   }
415
0
   return size;
416
0
}
417
418
}  // namespace Botan