Botan 3.12.0
Crypto and TLS for C&
xmss_wots.cpp
Go to the documentation of this file.
1/*
2 * XMSS WOTS Public and Private Key
3 *
4 * A Winternitz One Time Signature public/private key for use with
5 * Extended Hash-Based Signatures.
6 *
7 * (C) 2016,2017,2018 Matthias Gierlings
8 * 2023 René Meusel - Rohde & Schwarz Cybersecurity
9 * 2026 Jack Lloyd
10 *
11 * Botan is released under the Simplified BSD License (see license.txt)
12 **/
13
14#include <botan/internal/xmss_wots.h>
15
16#include <botan/mem_ops.h>
17#include <botan/internal/concat_util.h>
18#include <botan/internal/xmss_address.h>
19#include <botan/internal/xmss_hash.h>
20#include <botan/internal/xmss_tools.h>
21
22namespace Botan {
23
24namespace {
25
26/**
27* Algorithm 1 (base_w) followed by the WOTS+ checksum, as used by the
28* signing and verification routines in RFC 8391. The result is a single
29* buffer of length params.len() holding the len_1 base-w digits of the
30* message followed by the len_2 base-w digits of the checksum.
31*/
32secure_vector<uint8_t> base_w_with_checksum(const XMSS_WOTS_Parameters& params, std::span<const uint8_t> input) {
33 const size_t len_1 = params.len_1();
34 const size_t len_2 = params.len_2();
35 const size_t lg_w = params.lg_w();
36 const uint8_t mask = static_cast<uint8_t>(params.wots_parameter() - 1);
37
38 BOTAN_ASSERT_NOMSG(input.size() * 8 >= len_1 * lg_w);
39
40 secure_vector<uint8_t> result(len_1 + len_2);
41
42 size_t in = 0;
43 size_t total = 0;
44 size_t bits = 0;
45 for(size_t i = 0; i < len_1; ++i) {
46 if(bits == 0) {
47 total = input[in++];
48 bits = 8;
49 }
50 bits -= lg_w;
51 result[i] = static_cast<uint8_t>((total >> bits) & mask);
52 }
53
54 size_t csum = 0;
55 for(size_t i = 0; i < len_1; ++i) {
56 csum += params.wots_parameter() - 1 - result[i];
57 }
58
59 for(size_t i = 0; i < len_2; ++i) {
60 const size_t shift = lg_w * (len_2 - 1 - i);
61 result[len_1 + i] = static_cast<uint8_t>((csum >> shift) & mask);
62 }
63
64 return result;
65}
66
67/**
68 * Algorithm 2: Chaining Function.
69 *
70 * Takes an n-byte input string and transforms it into a the function
71 * result iterating the cryptographic hash function "F" steps times on
72 * the input x using the outputs of the PRNG "G".
73 *
74 * This overload is used in multithreaded scenarios, where it is
75 * required to provide separate instances of XMSS_Hash to each
76 * thread.
77 *
78 * @param params The WOTS parameters to use
79 * @param[out] result An n-byte input string, that will be transformed into
80 * the chaining function result.
81 * @param start_idx The start index.
82 * @param steps A number of steps.
83 * @param adrs An OTS Hash Address.
84 * @param seed A seed.
85 * @param hash Instance of XMSS_Hash, that may only by the thread
86 * executing chain.
87 **/
88void chain(const XMSS_WOTS_Parameters& params,
90 size_t start_idx,
91 size_t steps,
92 XMSS_Address adrs,
93 std::span<const uint8_t> seed,
94 XMSS_Hash& hash) {
95 BOTAN_ASSERT_NOMSG(result.size() == hash.output_length());
96 BOTAN_ASSERT_NOMSG(start_idx + steps < params.wots_parameter());
97 secure_vector<uint8_t> prf_output(hash.output_length());
98
99 // Note that RFC 8391 defines this algorithm recursively (building up the
100 // iterations before any calculation) using 'steps' as the iterator and a
101 // recursion base with 'steps == 0'.
102 // Instead, we implement it iteratively using 'i' as iterator. This makes
103 // 'adrs.set_hash_address(i)' equivalent to 'ADRS.setHashAddress(i + s - 1)'.
104 for(size_t i = start_idx; i < (start_idx + steps) && i < params.wots_parameter(); i++) {
105 adrs.set_hash_address(static_cast<uint32_t>(i));
106
107 // Calculate tmp XOR bitmask
108 adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Mask_Mode);
109 hash.prf(prf_output, seed, adrs.bytes());
110 xor_buf(result.data(), prf_output.data(), result.size());
111
112 // Calculate key
113 adrs.set_key_mask_mode(XMSS_Address::Key_Mask::Key_Mode);
114
115 // Calculate f(key, tmp XOR bitmask)
116 hash.prf(prf_output, seed, adrs.bytes());
117 hash.f(result, prf_output, result);
118 }
119}
120
121} // namespace
122
124 std::span<const uint8_t> public_seed,
125 const XMSS_WOTS_PrivateKey& private_key,
126 XMSS_Address adrs,
127 XMSS_Hash& hash) :
128 XMSS_WOTS_Base(params, private_key.key_data()) {
129 for(size_t i = 0; i < m_params.len(); ++i) {
130 adrs.set_chain_address(static_cast<uint32_t>(i));
131 chain(m_params, m_key_data[i], 0, m_params.wots_parameter() - 1, adrs, public_seed, hash);
132 }
133}
134
136 std::span<const uint8_t> public_seed,
137 wots_keysig_t signature,
138 const secure_vector<uint8_t>& msg,
139 XMSS_Address adrs,
140 XMSS_Hash& hash) :
141 XMSS_WOTS_Base(params, std::move(signature)) {
142 const secure_vector<uint8_t> msg_digest = base_w_with_checksum(m_params, msg);
143
144 for(size_t i = 0; i < m_params.len(); i++) {
145 adrs.set_chain_address(static_cast<uint32_t>(i));
146 chain(m_params,
147 m_key_data[i],
148 msg_digest[i],
149 m_params.wots_parameter() - 1 - msg_digest[i],
150 adrs,
151 public_seed,
152 hash);
153 }
154}
155
157 std::span<const uint8_t> public_seed,
158 XMSS_Address adrs,
159 XMSS_Hash& hash) {
160 const secure_vector<uint8_t> msg_digest = base_w_with_checksum(m_params, msg);
161 auto sig = this->key_data();
162
163 for(size_t i = 0; i < m_params.len(); i++) {
164 adrs.set_chain_address(static_cast<uint32_t>(i));
165 chain(m_params, sig[i], 0, msg_digest[i], adrs, public_seed, hash);
166 }
167
168 return sig;
169}
170
172 std::span<const uint8_t> public_seed,
173 std::span<const uint8_t> private_seed,
174 XMSS_Address adrs,
175 XMSS_Hash& hash) :
176 XMSS_WOTS_Base(params) {
177 m_key_data.resize(m_params.len());
178 for(size_t i = 0; i < m_params.len(); ++i) {
179 adrs.set_chain_address(static_cast<uint32_t>(i));
180 const auto data = concat<std::vector<uint8_t>>(public_seed, adrs.bytes());
181 hash.prf_keygen(m_key_data[i], private_seed, data);
182 }
183}
184
185// Constructor for legacy XMSS_PrivateKeys
187 std::span<const uint8_t> private_seed,
188 XMSS_Address adrs,
189 XMSS_Hash& hash) :
190 XMSS_WOTS_Base(params) {
191 m_key_data.resize(m_params.len());
192
194 hash.prf(r, private_seed, adrs.bytes());
195
196 for(size_t i = 0; i < m_params.len(); ++i) {
197 xmss_concat<size_t>(m_key_data[i], i, 32);
198 hash.prf(m_key_data[i], r, m_key_data[i]);
199 }
200}
201
202} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
void set_chain_address(uint32_t value)
std::span< const uint8_t > bytes() const
void prf(secure_vector< uint8_t > &result, std::span< const uint8_t > key, std::span< const uint8_t > data)
Definition xmss_hash.h:57
const wots_keysig_t & key_data() const
Definition xmss_wots.h:31
XMSS_WOTS_Base(XMSS_WOTS_Parameters params)
Definition xmss_wots.h:26
wots_keysig_t m_key_data
Definition xmss_wots.h:35
XMSS_WOTS_Parameters m_params
Definition xmss_wots.h:34
XMSS_WOTS_PrivateKey(XMSS_WOTS_Parameters params, std::span< const uint8_t > public_seed, std::span< const uint8_t > private_seed, XMSS_Address adrs, XMSS_Hash &hash)
wots_keysig_t sign(const secure_vector< uint8_t > &msg, std::span< const uint8_t > public_seed, XMSS_Address adrs, XMSS_Hash &hash)
XMSS_WOTS_PublicKey(XMSS_WOTS_Parameters params, std::span< const uint8_t > public_seed, const XMSS_WOTS_PrivateKey &private_key, XMSS_Address adrs, XMSS_Hash &hash)
std::vector< secure_vector< uint8_t > > wots_keysig_t
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:341
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68