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