/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 |