Botan 3.11.0
Crypto and TLS for C&
lm_ots.cpp
Go to the documentation of this file.
1/**
2 * LM-OTS - Leighton-Micali One-Time Signatures
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/lm_ots.h>
10
11#include <botan/exceptn.h>
12#include <botan/strong_type.h>
13#include <botan/internal/bit_ops.h>
14#include <botan/internal/buffer_slicer.h>
15#include <botan/internal/buffer_stuffer.h>
16#include <botan/internal/ct_utils.h>
17#include <botan/internal/hss_lms_utils.h>
18#include <botan/internal/int_utils.h>
19
20namespace Botan {
21
22namespace {
23constexpr uint16_t D_PBLC = 0x8080;
24constexpr uint16_t D_MESG = 0x8181;
25/// For derivation of C as in https://github.com/cisco/hash-sigs
26constexpr uint16_t C_INDEX = 0xFFFD;
27
28class Chain_Generator {
29 public:
30 Chain_Generator(const LMS_Identifier& identifier, LMS_Tree_Node_Idx q) : m_gen(identifier) {
31 m_gen.set_q(q.get());
32 }
33
34 void process(HashFunction& hash,
35 uint16_t chain_idx,
36 uint8_t start,
37 uint8_t end,
38 std::span<const uint8_t> in,
39 std::span<uint8_t> out) {
40 BOTAN_ARG_CHECK(start <= end, "Start value is bigger than end value");
41
42 copy_mem(out, in);
43 m_gen.set_i(chain_idx);
44
45 for(uint8_t j = start; j < end; ++j) {
46 m_gen.set_j(j);
47 m_gen.gen(out, hash, out);
48 }
49 }
50
51 private:
52 PseudorandomKeyGeneration m_gen;
53};
54
55// RFC 8554 3.1.1
56uint8_t byte(std::span<const uint8_t> S, uint32_t i) {
57 BOTAN_ARG_CHECK(i < S.size(), "Index out of range");
58 return S[i];
59}
60
61// RFC 8554 3.1.3
62uint8_t coef(std::span<const uint8_t> S, uint32_t i, const LMOTS_Params& params) {
63 const uint8_t w_bit_mask = params.coef_max();
64 const uint8_t coef_byte = byte(S, (i * params.w()) / 8);
65 const uint8_t shift = 8 - (params.w() * (i % (8 / params.w())) + params.w());
66
67 return w_bit_mask & (coef_byte >> shift);
68}
69
70// RFC 8554 4.4
71uint16_t checksum(const LMOTS_Params& params, std::span<const uint8_t> S) {
72 size_t sum = 0;
73 for(uint32_t i = 0; i < (params.n() * 8 / params.w()); ++i) {
74 sum += params.coef_max() - coef(S, i, params);
75 }
76 return checked_cast_to<uint16_t>(sum << params.ls());
77}
78
79std::vector<uint8_t> gen_Q_with_cksm(const LMOTS_Params& params,
80 const LMS_Identifier& identifier,
81 const LMS_Tree_Node_Idx& q,
82 std::span<const uint8_t> C,
83 const LMS_Message& msg) {
84 std::vector<uint8_t> Q_with_cksm(params.n() + sizeof(uint16_t));
85 BufferStuffer qwc_stuffer(Q_with_cksm);
86 const auto hash = params.hash();
87 hash->update(identifier);
88 hash->update(store_be(q));
89 hash->update(store_be(D_MESG));
90 hash->update(C);
91 hash->update(msg);
92 auto Q_span = qwc_stuffer.next(params.n());
93 hash->final(Q_span);
94
95 qwc_stuffer.append(store_be(checksum(params, Q_span)));
96
97 return Q_with_cksm;
98}
99
100} // namespace
101
102std::unique_ptr<HashFunction> LMOTS_Params::hash() const {
104}
105
107 auto [hash_name, w] = [](const LMOTS_Algorithm_Type& lmots_type) -> std::pair<std::string_view, uint8_t> {
108 switch(lmots_type) {
110 return {"SHA-256", static_cast<uint8_t>(1)};
112 return {"SHA-256", static_cast<uint8_t>(2)};
114 return {"SHA-256", static_cast<uint8_t>(4)};
116 return {"SHA-256", static_cast<uint8_t>(8)};
118 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(1)};
120 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(2)};
122 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(4)};
124 return {"Truncated(SHA-256,192)", static_cast<uint8_t>(8)};
126 return {"SHAKE-256(256)", static_cast<uint8_t>(1)};
128 return {"SHAKE-256(256)", static_cast<uint8_t>(2)};
130 return {"SHAKE-256(256)", static_cast<uint8_t>(4)};
132 return {"SHAKE-256(256)", static_cast<uint8_t>(8)};
134 return {"SHAKE-256(192)", static_cast<uint8_t>(1)};
136 return {"SHAKE-256(192)", static_cast<uint8_t>(2)};
138 return {"SHAKE-256(192)", static_cast<uint8_t>(4)};
140 return {"SHAKE-256(192)", static_cast<uint8_t>(8)};
142 throw Decoding_Error("Unsupported LMS algorithm type");
143 }
144 throw Decoding_Error("Unsupported LMS algorithm type");
145 }(type);
146
147 return LMOTS_Params(type, hash_name, w);
148}
149
150LMOTS_Params LMOTS_Params::create_or_throw(std::string_view hash_name, uint8_t w) {
151 if(w != 1 && w != 2 && w != 4 && w != 8) {
152 throw Decoding_Error("Invalid Winternitz parameter");
153 }
154 const LMOTS_Algorithm_Type type = [](std::string_view hash, uint8_t w_p) -> LMOTS_Algorithm_Type {
155 if(hash == "SHA-256") {
156 switch(w_p) {
157 case 1:
159 case 2:
161 case 4:
163 case 8:
165 default:
166 throw Decoding_Error("Unsupported Winternitz parameter");
167 }
168 }
169 if(hash == "Truncated(SHA-256,192)") {
170 switch(w_p) {
171 case 1:
173 case 2:
175 case 4:
177 case 8:
179 default:
180 throw Decoding_Error("Unsupported Winternitz parameter");
181 }
182 }
183 if(hash == "SHAKE-256(256)") {
184 switch(w_p) {
185 case 1:
187 case 2:
189 case 4:
191 case 8:
193 default:
194 throw Decoding_Error("Unsupported Winternitz parameter");
195 }
196 }
197 if(hash == "SHAKE-256(192)") {
198 switch(w_p) {
199 case 1:
201 case 2:
203 case 4:
205 case 8:
207 default:
208 throw Decoding_Error("Unsupported Winternitz parameter");
209 }
210 }
211 throw Decoding_Error("Unsupported hash function");
212 }(hash_name, w);
213
214 return LMOTS_Params(type, hash_name, w);
215}
216
217LMOTS_Params::LMOTS_Params(LMOTS_Algorithm_Type algorithm_type, std::string_view hash_name, uint8_t w) :
218 m_algorithm_type(algorithm_type), m_w(w), m_hash_name(hash_name) {
219 const auto hash = HashFunction::create_or_throw(m_hash_name);
220 m_n = hash->output_length();
221 // RFC 8553 Appendix B - Parameter Computation
222 auto u = ceil_division<size_t>(8 * m_n, m_w); // ceil(8*n/w)
223 auto v = ceil_division<size_t>(high_bit(((1 << m_w) - 1) * u), m_w); // ceil((floor(lg[(2^w - 1) * u]) + 1) / w)
224 m_ls = checked_cast_to<uint8_t>(16 - (v * w));
225 m_p = checked_cast_to<uint16_t>(u + v);
226}
227
228LMOTS_Signature::LMOTS_Signature(LMOTS_Algorithm_Type lmots_type,
229 std::vector<uint8_t> C,
230 std::vector<uint8_t> y_buffer) :
231 m_algorithm_type(lmots_type), m_C(std::move(C)), m_y_buffer(std::move(y_buffer)) {
232 const LMOTS_Params params = LMOTS_Params::create_or_throw(m_algorithm_type);
233
234 BufferSlicer y_slicer(m_y_buffer);
235 for(uint16_t i = 0; i < params.p(); ++i) {
236 m_y.push_back(y_slicer.take<LMOTS_Node>(params.n()));
237 }
238 BOTAN_ASSERT_NOMSG(y_slicer.empty());
239}
240
242 const size_t total_remaining_bytes = slicer.remaining();
243 // Alg. 6a. 1. (last 4 bytes) / Alg. 4b. 1.
244 if(total_remaining_bytes < sizeof(LMOTS_Algorithm_Type)) {
245 throw Decoding_Error("Too few signature bytes while parsing LMOTS signature.");
246 }
247 // Alg. 6a. 2.b. / Alg. 4b. 2.a.
249
250 // Alg. 6a. 2.d. / Alg. 4b. 2.c.
252
253 if(total_remaining_bytes < size(params)) {
254 throw Decoding_Error("Too few signature bytes while parsing LMOTS signature.");
255 }
256
257 // Alg. 4b. 2.d.
258 auto C = slicer.copy_as_vector(params.n());
259 // Alg. 4b. 2.e.
260 auto m_y_buffer = slicer.copy_as_vector(params.p() * params.n());
261
262 return LMOTS_Signature(algorithm_type, std::move(C), std::move(m_y_buffer));
263}
264
268 const LMS_Seed& seed) :
269 OTS_Instance(params, identifier, q), m_seed(seed) {
271 const auto hash = params.hash();
272
273 gen.set_q(q.get());
274 gen.set_j(0xff);
275
276 for(uint16_t i = 0; i < params.p(); ++i) {
277 gen.set_i(i);
278 m_ots_sk.push_back(gen.gen<LMOTS_Node>(*hash, seed));
279 }
280}
281
283 BOTAN_ARG_CHECK(out_sig.size() == LMOTS_Signature::size(params()), "Invalid output buffer size");
284 BufferStuffer sig_stuffer(out_sig);
285 const auto hash = params().hash();
286 sig_stuffer.append(store_be(params().algorithm_type()));
287 const auto C = sig_stuffer.next(params().n());
288
289 // Since we do not store the signatures of the lms trees in the HSS sk,
290 // we need deterministic signatures to avoid reusing a OTS key to generate multiple signatures.
291 // See also: https://github.com/cisco/hash-sigs/blob/b0631b8891295bf2929e68761205337b7c031726/lm_ots_sign.c#L110-L115
292 derive_random_C(C, *hash);
293 CT::unpoison(C); // contained in signature
294
295 const auto Q_with_cksm = gen_Q_with_cksm(params(), identifier(), q(), C, msg);
296
297 Chain_Generator chain_gen(identifier(), q());
298 for(uint16_t i = 0; i < params().p(); ++i) {
299 const auto y_i = sig_stuffer.next(params().n());
300 const uint8_t a = coef(Q_with_cksm, i, params());
301 chain_gen.process(*hash, i, 0, a, chain_input(i), y_i);
302 }
303 BOTAN_ASSERT_NOMSG(sig_stuffer.full());
304}
305
306void LMOTS_Private_Key::derive_random_C(std::span<uint8_t> out, HashFunction& hash) const {
308
309 gen.set_q(q().get());
310 gen.set_i(C_INDEX);
311 gen.set_j(0xff);
312
313 gen.gen(out, hash, m_seed);
314}
315
316LMOTS_Public_Key::LMOTS_Public_Key(const LMOTS_Private_Key& lmots_sk) : /* NOLINT(*-slicing) */ OTS_Instance(lmots_sk) {
317 const auto pk_hash = lmots_sk.params().hash();
318 pk_hash->update(lmots_sk.identifier());
319 pk_hash->update(store_be(lmots_sk.q()));
320 pk_hash->update(store_be(D_PBLC));
321
322 Chain_Generator chain_gen(lmots_sk.identifier(), lmots_sk.q());
323 const auto hash = lmots_sk.params().hash();
324 LMOTS_Node tmp(lmots_sk.params().n());
325 for(uint16_t i = 0; i < lmots_sk.params().p(); ++i) {
326 chain_gen.process(*hash, i, 0, lmots_sk.params().coef_max(), lmots_sk.chain_input(i), tmp);
327 pk_hash->update(tmp);
328 }
329
330 m_K = pk_hash->final<LMOTS_K>();
331}
332
334 const LMS_Message& msg,
335 const LMS_Identifier& identifier,
338
339 // Alg. 4b 3.
340
341 const auto Q_with_cksm = gen_Q_with_cksm(params, identifier, q, sig.C(), msg);
342
343 // Prefill the final hash object
344 const auto pk_hash = params.hash();
345 pk_hash->update(identifier);
346 pk_hash->update(store_be(q));
347 pk_hash->update(store_be(D_PBLC));
348
349 Chain_Generator chain_gen(identifier, q);
350 const auto hash = params.hash();
351 LMOTS_Node tmp(params.n());
352 for(uint16_t i = 0; i < params.p(); ++i) {
353 const uint8_t a = coef(Q_with_cksm, i, params);
354 chain_gen.process(*hash, i, a, params.coef_max(), sig.y(i), tmp);
355 pk_hash->update(tmp);
356 }
357 // Alg. 4b 4.
358 return pk_hash->final<LMOTS_K>();
359}
360
361} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
size_t remaining() const
std::span< const uint8_t > take(const size_t count)
auto copy_as_vector(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
void update(const uint8_t in[], size_t length)
Definition buf_comp.h:34
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
uint8_t coef_max() const
The maximum the winternitz coefficients can have.
Definition lm_ots.h:138
std::unique_ptr< HashFunction > hash() const
Construct a new hash instance for the OTS instance.
Definition lm_ots.cpp:102
size_t n() const
The number of bytes of the output of the hash function.
Definition lm_ots.h:128
uint8_t w() const
The width (in bits) of the Winternitz coefficients.
Definition lm_ots.h:133
const std::string & hash_name() const
Name of the hash function to use.
Definition lm_ots.h:153
uint16_t p() const
The number of n-byte string elements that make up the LM-OTS signature.
Definition lm_ots.h:143
Representation of an LMOTS private key.
Definition lm_ots.h:260
const LMOTS_Node & chain_input(uint16_t chain_idx) const
The secret chain input at a given chain index. (x[] in RFC 8554 4.2).
Definition lm_ots.h:275
void sign(StrongSpan< LMOTS_Signature_Bytes > out_sig, const LMS_Message &msg) const
Generate a new LMOTS signature.
Definition lm_ots.cpp:282
LMOTS_Private_Key(const LMOTS_Params &params, const LMS_Identifier &identifier, LMS_Tree_Node_Idx q, const LMS_Seed &seed)
Derive a LMOTS private key for a given seed.
Definition lm_ots.cpp:265
LMOTS_Public_Key(const LMOTS_Private_Key &lmots_sk)
Derivivation of an LMOTS public key using an LMOTS_Private_Key as defined in RFC 8554 4....
Definition lm_ots.cpp:316
Representation of a LM-OTS signature.
Definition lm_ots.h:181
static size_t size(const LMOTS_Params &params)
The expected size of the signature.
Definition lm_ots.h:210
StrongSpan< const LMOTS_Node > y(uint16_t chain_idx) const
Returns the part of the signature for chain_idx.
Definition lm_ots.h:205
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
std::span< const uint8_t > C() const
The n-byte randomizer of the signature.
Definition lm_ots.h:200
const LMS_Identifier & identifier() const
The LMS identifier of the LMS tree containing this OTS instance ('I' in RFC 8554).
Definition lm_ots.h:241
OTS_Instance(const LMOTS_Params &params, const LMS_Identifier &identifier, LMS_Tree_Node_Idx q)
Constructor storing the specific OTS parameters.
Definition lm_ots.h:230
LMS_Tree_Node_Idx q() const
The index of the LMS tree leaf associated with this OTS instance.
Definition lm_ots.h:246
const LMOTS_Params & params() const
The LMOTS parameters.
Definition lm_ots.h:236
Helper class used to derive secret values based in the pseudorandom key generation described in RFC 8...
void set_i(uint16_t i)
Specify the value for the u16str(i) hash input field.
void set_j(uint8_t j)
Specify the value for the u8str(j) hash input field.
T gen(HashFunction &hash, std::span< const uint8_t > seed) const
Create a hash value using the preconfigured prefix and a seed.
void set_q(uint32_t q)
Specify the value for the u32str(q) hash input field.
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
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
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
BOTAN_FORCE_INLINE constexpr T ceil_division(T a, T b)
Definition bit_ops.h:167
constexpr RT checked_cast_to(AT i)
Definition int_utils.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< secure_vector< uint8_t >, struct LMS_SEED_ > LMS_Seed
Seed of the LMS tree, used to generate the LM-OTS private keys.
Definition lm_ots.h:28
Strong< secure_vector< uint8_t >, struct LMOTS_Node_ > LMOTS_Node
One node within one LM-OTS hash chain.
Definition lm_ots.h:33
Strong< std::vector< uint8_t >, struct LMS_Message_ > LMS_Message
A message that is signed with an LMS tree.
Definition lm_ots.h:58
BOTAN_FORCE_INLINE constexpr size_t high_bit(T n)
Definition bit_ops.h:73
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
std::uint8_t byte
Definition types.h:110
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504