/src/botan/build/include/internal/botan/internal/dilithium_symmetric_primitives.h
Line | Count | Source (jump to first uncovered line) |
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 | | |
21 | | namespace Botan { |
22 | | |
23 | | class 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 | | */ |
31 | | class DilithiumMessageHash { |
32 | | public: |
33 | 0 | DilithiumMessageHash(DilithiumHashedPublicKey tr) : m_tr(std::move(tr)) { clear(); } |
34 | | |
35 | 0 | virtual ~DilithiumMessageHash() = default; |
36 | | |
37 | 0 | std::string name() const { |
38 | 0 | return Botan::fmt("{}({})", m_shake.name(), DilithiumConstants::MESSAGE_HASH_BYTES * 8); |
39 | 0 | } |
40 | | |
41 | 0 | 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 | 0 | return user_context.empty(); |
44 | 0 | } |
45 | | |
46 | 0 | virtual void start(std::span<const uint8_t> user_context) { |
47 | 0 | BOTAN_STATE_CHECK(!m_was_started); |
48 | 0 | BOTAN_ARG_CHECK(is_valid_user_context(user_context), "Invalid user context"); |
49 | 0 | m_was_started = true; |
50 | 0 | update(m_tr); // see calculation of mu in FIPS 204, Algorithm 7, line 6 |
51 | 0 | } |
52 | | |
53 | 0 | void update(std::span<const uint8_t> data) { |
54 | 0 | ensure_started(); |
55 | 0 | m_shake.update(data); |
56 | 0 | } |
57 | | |
58 | 0 | DilithiumMessageRepresentative final() { |
59 | 0 | ensure_started(); |
60 | 0 | scoped_cleanup clean([this]() { clear(); }); |
61 | 0 | return m_shake.output<DilithiumMessageRepresentative>(DilithiumConstants::MESSAGE_HASH_BYTES); |
62 | 0 | } |
63 | | |
64 | | private: |
65 | 0 | void clear() { |
66 | 0 | m_shake.clear(); |
67 | 0 | m_was_started = false; |
68 | 0 | } |
69 | | |
70 | 0 | void ensure_started() { |
71 | 0 | if(!m_was_started) { |
72 | | // FIPS 204, page 17, footnote 4: By default, the context is the empty string [...] |
73 | 0 | start({}); |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | private: |
78 | | DilithiumHashedPublicKey m_tr; |
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 | | */ |
89 | | class DilithiumXOF { |
90 | | public: |
91 | 3 | 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 | | */ |
101 | | class Dilithium_Symmetric_Primitives_Base { |
102 | | protected: |
103 | | Dilithium_Symmetric_Primitives_Base(const DilithiumConstants& mode, std::unique_ptr<DilithiumXOF> xof_adapter) : |
104 | 3 | m_commitment_hash_length_bytes(mode.commitment_hash_full_bytes()), |
105 | 3 | m_public_key_hash_bytes(mode.public_key_hash_bytes()), |
106 | 3 | m_mode(mode.mode()), |
107 | 3 | m_xof_adapter(std::move(xof_adapter)) {} |
108 | | |
109 | | public: |
110 | | static std::unique_ptr<Dilithium_Symmetric_Primitives_Base> create(const DilithiumConstants& mode); |
111 | | |
112 | 3 | virtual ~Dilithium_Symmetric_Primitives_Base() = default; |
113 | | Dilithium_Symmetric_Primitives_Base(const Dilithium_Symmetric_Primitives_Base&) = delete; |
114 | | Dilithium_Symmetric_Primitives_Base& operator=(const Dilithium_Symmetric_Primitives_Base&) = delete; |
115 | | Dilithium_Symmetric_Primitives_Base(Dilithium_Symmetric_Primitives_Base&&) = delete; |
116 | | Dilithium_Symmetric_Primitives_Base& operator=(Dilithium_Symmetric_Primitives_Base&&) = delete; |
117 | | |
118 | 0 | virtual std::unique_ptr<DilithiumMessageHash> get_message_hash(DilithiumHashedPublicKey tr) const { |
119 | 0 | return std::make_unique<DilithiumMessageHash>(std::move(tr)); |
120 | 0 | } |
121 | | |
122 | | /// Computes the private random seed rho prime used for signing |
123 | | /// if a @p rng is given, the seed is randomized |
124 | | virtual DilithiumSeedRhoPrime H_maybe_randomized( |
125 | | StrongSpan<const DilithiumSigningSeedK> k, |
126 | | StrongSpan<const DilithiumMessageRepresentative> mu, |
127 | | std::optional<std::reference_wrapper<RandomNumberGenerator>> rng) const = 0; |
128 | | |
129 | 0 | DilithiumHashedPublicKey H(StrongSpan<const DilithiumSerializedPublicKey> pk) const { |
130 | 0 | return H_256<DilithiumHashedPublicKey>(m_public_key_hash_bytes, pk); |
131 | 0 | } |
132 | | |
133 | | std::tuple<DilithiumSeedRho, DilithiumSeedRhoPrime, DilithiumSigningSeedK> H( |
134 | 0 | StrongSpan<const DilithiumSeedRandomness> seed) const { |
135 | 0 | m_xof.update(seed); |
136 | 0 | if(auto domsep = seed_expansion_domain_separator()) { |
137 | 0 | m_xof.update(domsep.value()); |
138 | 0 | } |
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. |
143 | 0 | auto rho = m_xof.output<DilithiumSeedRho>(DilithiumConstants::SEED_RHO_BYTES); |
144 | 0 | auto rhoprime = m_xof.output<DilithiumSeedRhoPrime>(DilithiumConstants::SEED_RHOPRIME_BYTES); |
145 | 0 | auto k = m_xof.output<DilithiumSigningSeedK>(DilithiumConstants::SEED_SIGNING_KEY_BYTES); |
146 | 0 | m_xof.clear(); |
147 | |
|
148 | 0 | return {std::move(rho), std::move(rhoprime), std::move(k)}; |
149 | 0 | } |
150 | | |
151 | | DilithiumCommitmentHash H(StrongSpan<const DilithiumMessageRepresentative> mu, |
152 | 0 | StrongSpan<const DilithiumSerializedCommitment> w1) const { |
153 | 0 | return H_256<DilithiumCommitmentHash>(m_commitment_hash_length_bytes, mu, w1); |
154 | 0 | } |
155 | | |
156 | 0 | SHAKE_256_XOF& H(StrongSpan<const DilithiumCommitmentHash> seed) const { |
157 | 0 | m_xof_external.clear(); |
158 | 0 | m_xof_external.update(truncate_commitment_hash(seed)); |
159 | 0 | return m_xof_external; |
160 | 0 | } |
161 | | |
162 | | // Once Dilithium AES is removed, this could return a SHAKE_256_XOF and |
163 | | // avoid the virtual method call. |
164 | 0 | Botan::XOF& H(StrongSpan<const DilithiumSeedRho> seed, uint16_t nonce) const { |
165 | 0 | return m_xof_adapter->XOF128(seed, nonce); |
166 | 0 | } |
167 | | |
168 | | // Once Dilithium AES is removed, this could return a SHAKE_128_XOF and |
169 | | // avoid the virtual method call. |
170 | 0 | Botan::XOF& H(StrongSpan<const DilithiumSeedRhoPrime> seed, uint16_t nonce) const { |
171 | 0 | return m_xof_adapter->XOF256(seed, nonce); |
172 | 0 | } |
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 | | */ |
180 | | virtual StrongSpan<const DilithiumCommitmentHash> truncate_commitment_hash( |
181 | | StrongSpan<const DilithiumCommitmentHash> seed) const = 0; |
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 | | |
190 | | template <concepts::resizable_byte_buffer OutT, ranges::spanable_range... InTs> |
191 | 0 | OutT H_256(size_t outbytes, InTs&&... ins) const { |
192 | 0 | scoped_cleanup clean([this]() { m_xof.clear(); }); Unexecuted instantiation: _ZZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS4_9allocatorIhEEEENS_25DilithiumHashedPublicKey_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_29DilithiumSerializedPublicKey_EJEEEEEEEET_mDpOT0_ENKUlvE_clEv Unexecuted instantiation: _ZZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS4_9allocatorIhEEEENS_24DilithiumCommitmentHash_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_31DilithiumMessageRepresentative_EJEEEEERNSC_IKNS3_IS8_NS_30DilithiumSerializedCommitment_EJEEEEEEEET_mDpOT0_ENKUlvE_clEv Unexecuted instantiation: _ZZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS_16secure_allocatorIhEEEENS_22DilithiumSeedRhoPrime_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_15DilithiumSeedK_EJEEEEERNSC_IKNS3_INS5_IhNS4_9allocatorIhEEEENS_31DilithiumMessageRepresentative_EJEEEEEEEET_mDpOT0_ENKUlvE_clEv Unexecuted instantiation: _ZZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS_16secure_allocatorIhEEEENS_22DilithiumSeedRhoPrime_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_15DilithiumSeedK_EJEEEEERNSC_IKNS3_IS8_NS_28DilithiumOptionalRandomness_EJEEEEERNSC_IKNS3_INS5_IhNS4_9allocatorIhEEEENS_31DilithiumMessageRepresentative_EJEEEEEEEET_mDpOT0_ENKUlvE_clEv |
193 | 0 | (m_xof.update(ins), ...); |
194 | 0 | return m_xof.output<OutT>(outbytes); |
195 | 0 | } Unexecuted instantiation: _ZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS4_9allocatorIhEEEENS_25DilithiumHashedPublicKey_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_29DilithiumSerializedPublicKey_EJEEEEEEEET_mDpOT0_ Unexecuted instantiation: _ZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS4_9allocatorIhEEEENS_24DilithiumCommitmentHash_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_31DilithiumMessageRepresentative_EJEEEEERNSC_IKNS3_IS8_NS_30DilithiumSerializedCommitment_EJEEEEEEEET_mDpOT0_ Unexecuted instantiation: _ZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS_16secure_allocatorIhEEEENS_22DilithiumSeedRhoPrime_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_15DilithiumSeedK_EJEEEEERNSC_IKNS3_INS5_IhNS4_9allocatorIhEEEENS_31DilithiumMessageRepresentative_EJEEEEEEEET_mDpOT0_ Unexecuted instantiation: _ZNK5Botan35Dilithium_Symmetric_Primitives_Base5H_256ITkNS_8concepts21resizable_byte_bufferENS_6StrongINSt3__16vectorIhNS_16secure_allocatorIhEEEENS_22DilithiumSeedRhoPrime_EJEEETpTkNS_6ranges14spanable_rangeEJRNS_10StrongSpanIKNS3_IS8_NS_15DilithiumSeedK_EJEEEEERNSC_IKNS3_IS8_NS_28DilithiumOptionalRandomness_EJEEEEERNSC_IKNS3_INS5_IhNS4_9allocatorIhEEEENS_31DilithiumMessageRepresentative_EJEEEEEEEET_mDpOT0_ |
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 |