Coverage Report

Created: 2024-11-29 06:10

/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