Botan 3.6.0
Crypto and TLS for C&
dilithium_symmetric_primitives.h
Go to the documentation of this file.
1/*
2* Symmetric primitives for dilithium
3*
4* (C) 2022-2023 Jack Lloyd
5* (C) 2022-2023 Michael Boric, René Meusel - Rohde & Schwarz Cybersecurity
6* (C) 2022 Manuel Glaser - Rohde & Schwarz Cybersecurity
7*
8* Botan is released under the Simplified BSD License (see license.txt)
9*/
10
11#ifndef BOTAN_DILITHIUM_ASYM_PRIMITIVES_H_
12#define BOTAN_DILITHIUM_ASYM_PRIMITIVES_H_
13
14#include <botan/dilithium.h>
15
16#include <botan/internal/dilithium_types.h>
17#include <botan/internal/fmt.h>
18#include <botan/internal/shake_xof.h>
19#include <botan/internal/stl_util.h>
20
21namespace Botan {
22
23class RandomNumberGenerator;
24
25/**
26 * Wrapper type for the H() function calculating the message representative for
27 * the Dilithium signature scheme. This wrapper may be used multiple times.
28 *
29 * Namely: mu = H(tr || M)
30 */
32 public:
33 DilithiumMessageHash(DilithiumHashedPublicKey tr) : m_tr(std::move(tr)) { clear(); }
34
35 virtual ~DilithiumMessageHash() = default;
36
37 std::string name() const {
38 return Botan::fmt("{}({})", m_shake.name(), DilithiumConstants::MESSAGE_HASH_BYTES * 8);
39 }
40
41 virtual bool is_valid_user_context(std::span<const uint8_t> user_context) const {
42 // Only ML-DSA supports user contexts, for all other modes it must be empty.
43 return user_context.empty();
44 }
45
46 virtual void start(std::span<const uint8_t> user_context) {
47 BOTAN_STATE_CHECK(!m_was_started);
48 BOTAN_ARG_CHECK(is_valid_user_context(user_context), "Invalid user context");
49 m_was_started = true;
50 update(m_tr); // see calculation of mu in FIPS 204, Algorithm 7, line 6
51 }
52
53 void update(std::span<const uint8_t> data) {
54 ensure_started();
55 m_shake.update(data);
56 }
57
59 ensure_started();
60 scoped_cleanup clean([this]() { clear(); });
62 }
63
64 private:
65 void clear() {
66 m_shake.clear();
67 m_was_started = false;
68 }
69
70 void ensure_started() {
71 if(!m_was_started) {
72 // FIPS 204, page 17, footnote 4: By default, the context is the empty string [...]
73 start({});
74 }
75 }
76
77 private:
79 bool m_was_started;
80 SHAKE_256_XOF m_shake;
81};
82
83/**
84* Implemented by the derived classes to create the correct XOF instance. This is
85* a customization point to enable support for the AES variant of Dilithium. This
86* was not standardized in the FIPS 204; ML-DSA always uses SHAKE. Once we decide
87* to remove the AES variant, this can be removed.
88*/
90 public:
91 virtual ~DilithiumXOF() = default;
92
93 virtual Botan::XOF& XOF128(std::span<const uint8_t> seed, uint16_t nonce) const = 0;
94 virtual Botan::XOF& XOF256(std::span<const uint8_t> seed, uint16_t nonce) const = 0;
95};
96
97/**
98* Adapter class that uses polymorphy to distinguish
99* Dilithium "common" from Dilithium "AES" modes.
100*/
102 protected:
103 Dilithium_Symmetric_Primitives_Base(const DilithiumConstants& mode, std::unique_ptr<DilithiumXOF> xof_adapter) :
104 m_commitment_hash_length_bytes(mode.commitment_hash_full_bytes()),
105 m_public_key_hash_bytes(mode.public_key_hash_bytes()),
106 m_mode(mode.mode()),
107 m_xof_adapter(std::move(xof_adapter)) {}
108
109 public:
110 static std::unique_ptr<Dilithium_Symmetric_Primitives_Base> create(const DilithiumConstants& mode);
111
117
118 virtual std::unique_ptr<DilithiumMessageHash> get_message_hash(DilithiumHashedPublicKey tr) const {
119 return std::make_unique<DilithiumMessageHash>(std::move(tr));
120 }
121
122 /// Computes the private random seed rho prime used for signing
123 /// if a @p rng is given, the seed is randomized
127 std::optional<std::reference_wrapper<RandomNumberGenerator>> rng) const = 0;
128
132
133 std::tuple<DilithiumSeedRho, DilithiumSeedRhoPrime, DilithiumSigningSeedK> H(
135 m_xof.update(seed);
136 if(auto domsep = seed_expansion_domain_separator()) {
137 m_xof.update(domsep.value());
138 }
139
140 // Note: The order of invocations in an initializer list is not
141 // guaranteed by the C++ standard. Hence, we have to store the
142 // results in variables to ensure the correct order of execution.
146 m_xof.clear();
147
148 return {std::move(rho), std::move(rhoprime), std::move(k)};
149 }
150
155
157 m_xof_external.clear();
158 m_xof_external.update(truncate_commitment_hash(seed));
159 return m_xof_external;
160 }
161
162 // Once Dilithium AES is removed, this could return a SHAKE_256_XOF and
163 // avoid the virtual method call.
164 Botan::XOF& H(StrongSpan<const DilithiumSeedRho> seed, uint16_t nonce) const {
165 return m_xof_adapter->XOF128(seed, nonce);
166 }
167
168 // Once Dilithium AES is removed, this could return a SHAKE_128_XOF and
169 // avoid the virtual method call.
171 return m_xof_adapter->XOF256(seed, nonce);
172 }
173
174 protected:
175 /**
176 * Implemented by the derived classes to truncate the commitment hash
177 * to the correct length. This is a customization point to enable support
178 * for the final ML-DSA standard.
179 */
182
183 /**
184 * Creates the domain separator for the initial seed expansion.
185 * The return value may be std::nullopt meaning that no domain separation
186 * is required (for Dilithium).
187 */
188 virtual std::optional<std::array<uint8_t, 2>> seed_expansion_domain_separator() const = 0;
189
191 OutT H_256(size_t outbytes, InTs&&... ins) const {
192 scoped_cleanup clean([this]() { m_xof.clear(); });
193 (m_xof.update(ins), ...);
194 return m_xof.output<OutT>(outbytes);
195 }
196
197 private:
198 size_t m_commitment_hash_length_bytes;
199 size_t m_public_key_hash_bytes;
200 DilithiumMode m_mode;
201
202 std::unique_ptr<DilithiumXOF> m_xof_adapter;
203 mutable SHAKE_256_XOF m_xof;
204 mutable SHAKE_256_XOF m_xof_external;
205};
206
207} // namespace Botan
208
209#endif
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
static constexpr size_t MESSAGE_HASH_BYTES
static constexpr size_t SEED_SIGNING_KEY_BYTES
static constexpr size_t SEED_RHOPRIME_BYTES
static constexpr size_t SEED_RHO_BYTES
virtual void start(std::span< const uint8_t > user_context)
DilithiumMessageHash(DilithiumHashedPublicKey tr)
void update(std::span< const uint8_t > data)
virtual ~DilithiumMessageHash()=default
virtual bool is_valid_user_context(std::span< const uint8_t > user_context) const
virtual ~DilithiumXOF()=default
virtual Botan::XOF & XOF256(std::span< const uint8_t > seed, uint16_t nonce) const =0
virtual Botan::XOF & XOF128(std::span< const uint8_t > seed, uint16_t nonce) const =0
Dilithium_Symmetric_Primitives_Base(Dilithium_Symmetric_Primitives_Base &&)=delete
SHAKE_256_XOF & H(StrongSpan< const DilithiumCommitmentHash > seed) const
Dilithium_Symmetric_Primitives_Base & operator=(const Dilithium_Symmetric_Primitives_Base &)=delete
static std::unique_ptr< Dilithium_Symmetric_Primitives_Base > create(const DilithiumConstants &mode)
Dilithium_Symmetric_Primitives_Base(const Dilithium_Symmetric_Primitives_Base &)=delete
Botan::XOF & H(StrongSpan< const DilithiumSeedRho > seed, uint16_t nonce) const
Botan::XOF & H(StrongSpan< const DilithiumSeedRhoPrime > seed, uint16_t nonce) const
DilithiumHashedPublicKey H(StrongSpan< const DilithiumSerializedPublicKey > pk) const
virtual ~Dilithium_Symmetric_Primitives_Base()=default
virtual std::optional< std::array< uint8_t, 2 > > seed_expansion_domain_separator() const =0
std::tuple< DilithiumSeedRho, DilithiumSeedRhoPrime, DilithiumSigningSeedK > H(StrongSpan< const DilithiumSeedRandomness > seed) const
OutT H_256(size_t outbytes, InTs &&... ins) const
virtual DilithiumSeedRhoPrime H_maybe_randomized(StrongSpan< const DilithiumSigningSeedK > k, StrongSpan< const DilithiumMessageRepresentative > mu, std::optional< std::reference_wrapper< RandomNumberGenerator > > rng) const =0
Dilithium_Symmetric_Primitives_Base(const DilithiumConstants &mode, std::unique_ptr< DilithiumXOF > xof_adapter)
virtual StrongSpan< const DilithiumCommitmentHash > truncate_commitment_hash(StrongSpan< const DilithiumCommitmentHash > seed) const =0
DilithiumCommitmentHash H(StrongSpan< const DilithiumMessageRepresentative > mu, StrongSpan< const DilithiumSerializedCommitment > w1) const
virtual std::unique_ptr< DilithiumMessageHash > get_message_hash(DilithiumHashedPublicKey tr) const
Dilithium_Symmetric_Primitives_Base & operator=(Dilithium_Symmetric_Primitives_Base &&)=delete
std::string name() const final
Definition shake_xof.h:70
void clear()
Definition xof.h:66
T output(size_t bytes)
Definition xof.h:155
void update(std::span< const uint8_t > input)
Definition xof.h:142
Helper class to create a RAII-style cleanup callback.
Definition stl_util.h:354
int(* update)(CTX *, const void *, CC_LONG len)
constexpr T rho(T x)
Definition rotate.h:51
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
Strong< std::vector< uint8_t >, struct DilithiumHashedPublicKey_ > DilithiumHashedPublicKey