• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 11213759155

07 Oct 2024 10:37AM UTC coverage: 91.273% (-0.003%) from 91.276%
11213759155

push

github

web-flow
Merge pull request #4350 from Rohde-Schwarz/chore/response_files_for_link_commands

Use @response_files.txt for linking in Ninja

87852 of 96252 relevant lines covered (91.27%)

9056282.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.32
/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp
1
/*
2
* Crystals Dilithium Digital Signature Algorithms
3
* Based on the public domain reference implementation by the
4
* designers (https://github.com/pq-crystals/dilithium)
5
*
6
* Further changes
7
* (C) 2021-2023 Jack Lloyd
8
* (C) 2021-2022 Manuel Glaser - Rohde & Schwarz Cybersecurity
9
* (C) 2021-2023 Michael Boric, René Meusel - Rohde & Schwarz Cybersecurity
10
* (C) 2024      René Meusel - Rohde & Schwarz Cybersecurity
11
*
12
* Botan is released under the Simplified BSD License (see license.txt)
13
*/
14

15
#include <botan/dilithium.h>
16

17
#include <botan/exceptn.h>
18
#include <botan/rng.h>
19

20
#include <botan/internal/dilithium_algos.h>
21
#include <botan/internal/dilithium_symmetric_primitives.h>
22
#include <botan/internal/dilithium_types.h>
23
#include <botan/internal/fmt.h>
24
#include <botan/internal/pk_ops_impl.h>
25
#include <botan/internal/stl_util.h>
26

27
namespace Botan {
28
namespace {
29

30
DilithiumMode::Mode dilithium_mode_from_string(std::string_view str) {
156✔
31
   if(str == "Dilithium-4x4-r3") {
156✔
32
      return DilithiumMode::Dilithium4x4;
16✔
33
   }
34
   if(str == "Dilithium-4x4-AES-r3") {
140✔
35
      return DilithiumMode::Dilithium4x4_AES;
14✔
36
   }
37
   if(str == "Dilithium-6x5-r3") {
126✔
38
      return DilithiumMode::Dilithium6x5;
67✔
39
   }
40
   if(str == "Dilithium-6x5-AES-r3") {
59✔
41
      return DilithiumMode::Dilithium6x5_AES;
14✔
42
   }
43
   if(str == "Dilithium-8x7-r3") {
45✔
44
      return DilithiumMode::Dilithium8x7;
14✔
45
   }
46
   if(str == "Dilithium-8x7-AES-r3") {
31✔
47
      return DilithiumMode::Dilithium8x7_AES;
31✔
48
   }
49

50
   throw Invalid_Argument(fmt("'{}' is not a valid Dilithium mode name", str));
×
51
}
52

53
}  // namespace
54

55
DilithiumMode::DilithiumMode(const OID& oid) : m_mode(dilithium_mode_from_string(oid.to_formatted_string())) {}
139✔
56

57
DilithiumMode::DilithiumMode(std::string_view str) : m_mode(dilithium_mode_from_string(str)) {}
17✔
58

59
OID DilithiumMode::object_identifier() const {
461✔
60
   return OID::from_string(to_string());
922✔
61
}
62

63
std::string DilithiumMode::to_string() const {
473✔
64
   switch(m_mode) {
473✔
65
      case DilithiumMode::Dilithium4x4:
55✔
66
         return "Dilithium-4x4-r3";
55✔
67
      case DilithiumMode::Dilithium4x4_AES:
54✔
68
         return "Dilithium-4x4-AES-r3";
54✔
69
      case DilithiumMode::Dilithium6x5:
179✔
70
         return "Dilithium-6x5-r3";
179✔
71
      case DilithiumMode::Dilithium6x5_AES:
54✔
72
         return "Dilithium-6x5-AES-r3";
54✔
73
      case DilithiumMode::Dilithium8x7:
54✔
74
         return "Dilithium-8x7-r3";
54✔
75
      case DilithiumMode::Dilithium8x7_AES:
77✔
76
         return "Dilithium-8x7-AES-r3";
77✔
77
   }
78

79
   BOTAN_ASSERT_UNREACHABLE();
×
80
}
81

82
class Dilithium_PublicKeyInternal {
83
   public:
84
      static std::shared_ptr<Dilithium_PublicKeyInternal> decode(
1,302✔
85
         DilithiumConstants mode, StrongSpan<const DilithiumSerializedPublicKey> raw_pk) {
86
         auto [rho, t1] = Dilithium_Algos::decode_public_key(raw_pk, mode);
1,302✔
87
         return std::make_shared<Dilithium_PublicKeyInternal>(std::move(mode), std::move(rho), std::move(t1));
2,604✔
88
      }
1,302✔
89

90
      Dilithium_PublicKeyInternal(DilithiumConstants mode, DilithiumSeedRho rho, DilithiumPolyVec t1) :
2,612✔
91
            m_mode(std::move(mode)),
2,612✔
92
            m_rho(std::move(rho)),
2,612✔
93
            m_t1(std::move(t1)),
2,612✔
94
            m_tr(m_mode.symmetric_primitives().H(raw_pk())) {
5,224✔
95
         BOTAN_ASSERT_NOMSG(!m_rho.empty());
2,612✔
96
         BOTAN_ASSERT_NOMSG(m_t1.size() > 0);
2,612✔
97
      }
2,612✔
98

99
   public:
100
      DilithiumSerializedPublicKey raw_pk() const { return Dilithium_Algos::encode_public_key(m_rho, m_t1, m_mode); }
5,104✔
101

102
      const DilithiumHashedPublicKey& tr() const { return m_tr; }
2,672✔
103

104
      const DilithiumPolyVec& t1() const { return m_t1; }
1,429✔
105

106
      const DilithiumSeedRho& rho() const { return m_rho; }
1,429✔
107

108
      const DilithiumConstants& mode() const { return m_mode; }
3,281✔
109

110
   private:
111
      const DilithiumConstants m_mode;
112
      DilithiumSeedRho m_rho;
113
      DilithiumPolyVec m_t1;
114
      DilithiumHashedPublicKey m_tr;
115
};
116

117
class Dilithium_PrivateKeyInternal {
118
   public:
119
      static std::shared_ptr<Dilithium_PrivateKeyInternal> decode(DilithiumConstants mode,
67✔
120
                                                                  StrongSpan<const DilithiumSerializedPrivateKey> sk) {
121
         auto [rho, signing_seed, tr, s1, s2, t0] = Dilithium_Algos::decode_private_key(sk, mode);
67✔
122
         return std::make_shared<Dilithium_PrivateKeyInternal>(std::move(mode),
67✔
123
                                                               std::move(rho),
124
                                                               std::move(signing_seed),
125
                                                               std::move(tr),
126
                                                               std::move(s1),
127
                                                               std::move(s2),
128
                                                               std::move(t0));
67✔
129
      }
67✔
130

131
      Dilithium_PrivateKeyInternal(DilithiumConstants mode,
1,310✔
132
                                   DilithiumSeedRho rho,
133
                                   DilithiumSigningSeedK signing_seed,
134
                                   DilithiumHashedPublicKey tr,
135
                                   DilithiumPolyVec s1,
136
                                   DilithiumPolyVec s2,
137
                                   DilithiumPolyVec t0) :
1,310✔
138
            m_mode(std::move(mode)),
1,310✔
139
            m_rho(std::move(rho)),
1,310✔
140
            m_signing_seed(std::move(signing_seed)),
1,310✔
141
            m_tr(std::move(tr)),
1,310✔
142
            m_t0(std::move(t0)),
1,310✔
143
            m_s1(std::move(s1)),
1,310✔
144
            m_s2(std::move(s2)) {}
1,310✔
145

146
   public:
147
      DilithiumSerializedPrivateKey raw_sk() const {
1,306✔
148
         auto scope = CT::scoped_poison(*this);
1,306✔
149
         auto result = Dilithium_Algos::encode_private_key(m_rho, m_tr, m_signing_seed, m_s1, m_s2, m_t0, m_mode);
1,306✔
150
         CT::unpoison(result);
1,306✔
151
         return result;
1,306✔
152
      }
1,306✔
153

154
      const DilithiumConstants& mode() const { return m_mode; }
1,331✔
155

156
      const DilithiumSeedRho& rho() const { return m_rho; }
1,421✔
157

158
      const DilithiumSigningSeedK& signing_seed() const { return m_signing_seed; }
626✔
159

160
      const DilithiumHashedPublicKey& tr() const { return m_tr; }
1,354✔
161

162
      const DilithiumPolyVec& s1() const { return m_s1; }
1,287✔
163

164
      const DilithiumPolyVec& s2() const { return m_s2; }
1,354✔
165

166
      const DilithiumPolyVec& t0() const { return m_t0; }
1,287✔
167

168
      void _const_time_poison() const {
2,602✔
169
         // Note: m_rho and m_tr is public knowledge
170
         CT::poison_all(m_signing_seed, m_s1, m_s2, m_t0);
2,602✔
171
      }
172

173
      void _const_time_unpoison() const { CT::unpoison_all(m_rho, m_signing_seed, m_tr, m_s1, m_s2, m_t0); }
67✔
174

175
   private:
176
      const DilithiumConstants m_mode;
177
      DilithiumSeedRho m_rho;
178
      DilithiumSigningSeedK m_signing_seed;
179
      DilithiumHashedPublicKey m_tr;
180
      DilithiumPolyVec m_t0;
181
      DilithiumPolyVec m_s1;
182
      DilithiumPolyVec m_s2;
183
};
184

185
class Dilithium_Signature_Operation final : public PK_Ops::Signature {
×
186
   public:
187
      Dilithium_Signature_Operation(std::shared_ptr<Dilithium_PrivateKeyInternal> sk, bool randomized) :
1,287✔
188
            m_priv_key(std::move(sk)),
1,287✔
189
            m_randomized(randomized),
1,287✔
190
            m_h(m_priv_key->mode().symmetric_primitives().get_message_hash(m_priv_key->tr())),
2,574✔
191
            m_s1(ntt(m_priv_key->s1().clone())),
1,287✔
192
            m_s2(ntt(m_priv_key->s2().clone())),
1,287✔
193
            m_t0(ntt(m_priv_key->t0().clone())),
1,287✔
194
            m_A(Dilithium_Algos::expand_A(m_priv_key->rho(), m_priv_key->mode())) {}
2,574✔
195

196
      void update(std::span<const uint8_t> input) override { m_h.update(input); }
1,296✔
197

198
      /**
199
       * NIST FIPS 204 IPD, Algorithm 2 (ML-DSA.Sign)
200
       *
201
       * Note that the private key decoding is done ahead of time. Also, the
202
       * matrix expansion of A from 'rho' along with the NTT-transforms of s1,
203
       * s2 and t0 are done in the constructor of this class, as a 'signature
204
       * operation' may be used to sign multiple messages.
205
       */
206
      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override {
1,296✔
207
         auto scope = CT::scoped_poison(*m_priv_key);
1,296✔
208

209
         const auto mu = m_h.final();
1,296✔
210
         const auto& mode = m_priv_key->mode();
1,296✔
211
         const auto& sympri = mode.symmetric_primitives();
1,296✔
212

213
         // TODO: ML-DSA generates rhoprime differently, namely
214
         //       rhoprime = H(K, rnd, mu) with rnd being 32 random bytes or 32 zero bytes
215
         const auto rhoprime = (m_randomized)
1,296✔
216
                                  ? rng.random_vec<DilithiumSeedRhoPrime>(DilithiumConstants::SEED_RHOPRIME_BYTES)
1,296✔
217
                                  : sympri.H(m_priv_key->signing_seed(), mu);
1,296✔
218
         CT::poison(rhoprime);
219

220
         // Note: nonce (as requested by `polyvecl_uniform_gamma1`) is actually just uint16_t
221
         //       but to avoid an integer overflow, we use uint32_t as the loop variable.
222
         for(uint32_t nonce = 0; nonce <= std::numeric_limits<uint16_t>::max(); nonce += mode.l()) {
5,704✔
223
            const auto y = Dilithium_Algos::expand_mask(rhoprime, static_cast<uint16_t>(nonce), mode);
5,704✔
224

225
            auto w_ntt = m_A * ntt(y.clone());
5,704✔
226
            w_ntt.reduce();
5,704✔
227
            auto w = inverse_ntt(std::move(w_ntt));
5,704✔
228
            w.conditional_add_q();
5,704✔
229

230
            auto [w1, w0] = Dilithium_Algos::decompose(w, mode);
5,704✔
231
            const auto ch = sympri.H(mu, Dilithium_Algos::encode_commitment(w1, mode));
5,704✔
232
            CT::unpoison(ch);  // part of the signature
5,704✔
233

234
            StrongSpan<const DilithiumCommitmentHash> c1(
5,704✔
235
               std::span<const uint8_t>(ch).first(DilithiumConstants::COMMITMENT_HASH_C1_BYTES));
5,704✔
236
            const auto c = ntt(Dilithium_Algos::sample_in_ball(c1, mode));
5,704✔
237
            const auto cs1 = inverse_ntt(c * m_s1);
5,704✔
238
            auto z = y + cs1;
5,704✔
239
            z.reduce();
5,704✔
240
            if(!Dilithium_Algos::infinity_norm_within_bound(z, to_underlying(mode.gamma1()) - mode.beta())) {
5,704✔
241
               continue;
2,249✔
242
            }
243
            CT::unpoison(z);  // part of the signature
3,455✔
244

245
            const auto cs2 = inverse_ntt(c * m_s2);
3,455✔
246
            w0 -= cs2;
3,455✔
247
            w0.reduce();
3,455✔
248
            if(!Dilithium_Algos::infinity_norm_within_bound(w0, to_underlying(mode.gamma2()) - mode.beta())) {
3,455✔
249
               continue;
2,145✔
250
            }
251

252
            auto ct0 = inverse_ntt(c * m_t0);
1,310✔
253
            ct0.reduce();
1,310✔
254
            if(!Dilithium_Algos::infinity_norm_within_bound(ct0, mode.gamma2())) {
1,310✔
255
               continue;
×
256
            }
257

258
            w0 += ct0;
1,310✔
259
            w0.conditional_add_q();
1,310✔
260

261
            const auto hint = Dilithium_Algos::make_hint(w0, w1, mode);
1,310✔
262
            const auto hamming_weight = hint.hamming_weight();
1,310✔
263
            CT::unpoison(hamming_weight);
1,310✔
264
            if(hamming_weight > mode.omega()) {
1,310✔
265
               continue;
14✔
266
            }
267
            CT::unpoison(hint);  // part of the signature
1,296✔
268

269
            return Dilithium_Algos::encode_signature(ch, z, hint, mode).get();
2,592✔
270
         }
22,816✔
271

272
         throw Internal_Error("Dilithium signature loop did not terminate");
×
273
      }
8,296✔
274

275
      size_t signature_length() const override { return m_priv_key->mode().signature_bytes(); }
×
276

277
      AlgorithmIdentifier algorithm_identifier() const override {
44✔
278
         return AlgorithmIdentifier(m_priv_key->mode().mode().object_identifier(),
88✔
279
                                    AlgorithmIdentifier::USE_EMPTY_PARAM);
88✔
280
      }
281

282
      std::string hash_function() const override { return m_h.name(); }
66✔
283

284
   private:
285
      std::shared_ptr<Dilithium_PrivateKeyInternal> m_priv_key;
286
      bool m_randomized;
287
      DilithiumMessageHash m_h;
288

289
      const DilithiumPolyVecNTT m_s1;
290
      const DilithiumPolyVecNTT m_s2;
291
      const DilithiumPolyVecNTT m_t0;
292
      const DilithiumPolyMatNTT m_A;
293
};
294

295
class Dilithium_Verification_Operation final : public PK_Ops::Verification {
×
296
   public:
297
      Dilithium_Verification_Operation(std::shared_ptr<Dilithium_PublicKeyInternal> pubkey) :
1,429✔
298
            m_pub_key(std::move(pubkey)),
1,429✔
299
            m_A(Dilithium_Algos::expand_A(m_pub_key->rho(), m_pub_key->mode())),
1,429✔
300
            m_t1_ntt_shifted(ntt(m_pub_key->t1() << DilithiumConstants::D)),
1,429✔
301
            m_h(m_pub_key->mode().symmetric_primitives().get_message_hash(m_pub_key->tr())) {}
5,716✔
302

303
      void update(std::span<const uint8_t> input) override { m_h.update(input); }
2,657✔
304

305
      /**
306
       * NIST FIPS 204 IPD, Algorithm 3 (ML-DSA.Verify)
307
       *
308
       * Note that the public key decoding is done ahead of time. Also, the
309
       * matrix A is expanded from 'rho' in the constructor of this class, as
310
       * a 'verification operation' may be used to verify multiple signatures.
311
       */
312
      bool is_valid_signature(std::span<const uint8_t> sig) override {
3,857✔
313
         const auto& mode = m_pub_key->mode();
3,857✔
314
         const auto& sympri = mode.symmetric_primitives();
3,857✔
315
         StrongSpan<const DilithiumSerializedSignature> sig_bytes(sig);
3,857✔
316

317
         if(sig_bytes.size() != mode.signature_bytes()) {
3,857✔
318
            return false;
319
         }
320

321
         const auto mu = m_h.final();
3,857✔
322

323
         auto signature = Dilithium_Algos::decode_signature(sig_bytes, mode);
3,857✔
324
         if(!signature.has_value()) {
3,857✔
325
            return false;
326
         }
327
         auto [ch, z, h] = std::move(signature.value());
3,857✔
328
         StrongSpan<const DilithiumCommitmentHash> c1(
3,857✔
329
            std::span<uint8_t>(ch).first(DilithiumConstants::COMMITMENT_HASH_C1_BYTES));
3,857✔
330

331
         if(h.hamming_weight() > mode.omega() ||
11,571✔
332
            !Dilithium_Algos::infinity_norm_within_bound(z, to_underlying(mode.gamma1()) - mode.beta())) {
3,857✔
333
            return false;
×
334
         }
335

336
         const auto c_hat = ntt(Dilithium_Algos::sample_in_ball(c1, mode));
3,857✔
337
         auto w_approx = m_A * ntt(std::move(z));
3,857✔
338
         w_approx -= c_hat * m_t1_ntt_shifted;
3,857✔
339
         w_approx.reduce();
3,857✔
340
         auto w1 = inverse_ntt(std::move(w_approx));
3,857✔
341
         w1.conditional_add_q();
3,857✔
342
         Dilithium_Algos::use_hint(w1, h, mode);
3,857✔
343

344
         const auto chprime = sympri.H(mu, Dilithium_Algos::encode_commitment(w1, mode));
3,857✔
345

346
         BOTAN_ASSERT_NOMSG(ch.size() == chprime.size());
3,857✔
347
         return std::equal(ch.begin(), ch.end(), chprime.begin());
7,714✔
348
      }
19,285✔
349

350
      std::string hash_function() const override { return m_h.name(); }
44✔
351

352
   private:
353
      std::shared_ptr<Dilithium_PublicKeyInternal> m_pub_key;
354
      DilithiumPolyMatNTT m_A;
355
      DilithiumPolyVecNTT m_t1_ntt_shifted;
356
      DilithiumMessageHash m_h;
357
};
358

359
Dilithium_PublicKey::Dilithium_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> pk) :
84✔
360
      Dilithium_PublicKey(pk, DilithiumMode(alg_id.oid())) {}
84✔
361

362
Dilithium_PublicKey::Dilithium_PublicKey(std::span<const uint8_t> pk, DilithiumMode m) {
1,302✔
363
   DilithiumConstants mode(m);
1,302✔
364
   BOTAN_ARG_CHECK(pk.empty() || pk.size() == mode.public_key_bytes(),
1,302✔
365
                   "dilithium public key does not have the correct byte count");
366

367
   m_public = Dilithium_PublicKeyInternal::decode(std::move(mode), StrongSpan<const DilithiumSerializedPublicKey>(pk));
1,302✔
368
}
1,302✔
369

370
std::string Dilithium_PublicKey::algo_name() const {
201✔
371
   return object_identifier().to_formatted_string();
201✔
372
}
373

374
AlgorithmIdentifier Dilithium_PublicKey::algorithm_identifier() const {
210✔
375
   return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
210✔
376
}
377

378
OID Dilithium_PublicKey::object_identifier() const {
417✔
379
   return m_public->mode().mode().object_identifier();
417✔
380
}
381

382
size_t Dilithium_PublicKey::key_length() const {
25✔
383
   return m_public->mode().canonical_parameter_set_identifier();
25✔
384
}
385

386
size_t Dilithium_PublicKey::estimated_strength() const {
79✔
387
   return m_public->mode().lambda();
79✔
388
}
389

390
std::vector<uint8_t> Dilithium_PublicKey::raw_public_key_bits() const {
2,492✔
391
   return m_public->raw_pk().get();
2,492✔
392
}
393

394
std::vector<uint8_t> Dilithium_PublicKey::public_key_bits() const {
2,486✔
395
   // Currently, there isn't a finalized definition of an ASN.1 structure for
396
   // Dilithium aka ML-DSA public keys. Therefore, we return the raw public key bits.
397
   return raw_public_key_bits();
2,486✔
398
}
399

400
bool Dilithium_PublicKey::check_key(RandomNumberGenerator&, bool) const {
12✔
401
   return true;  // ???
12✔
402
}
403

404
std::unique_ptr<Private_Key> Dilithium_PublicKey::generate_another(RandomNumberGenerator& rng) const {
6✔
405
   return std::make_unique<Dilithium_PrivateKey>(rng, m_public->mode().mode());
6✔
406
}
407

408
std::unique_ptr<PK_Ops::Verification> Dilithium_PublicKey::create_verification_op(std::string_view params,
1,387✔
409
                                                                                  std::string_view provider) const {
410
   BOTAN_ARG_CHECK(params.empty() || params == "Pure", "Unexpected parameters for verifying with Dilithium");
1,387✔
411
   if(provider.empty() || provider == "base") {
1,388✔
412
      return std::make_unique<Dilithium_Verification_Operation>(m_public);
1,387✔
413
   }
414
   throw Provider_Not_Found(algo_name(), provider);
×
415
}
416

417
std::unique_ptr<PK_Ops::Verification> Dilithium_PublicKey::create_x509_verification_op(
42✔
418
   const AlgorithmIdentifier& alg_id, std::string_view provider) const {
419
   if(provider.empty() || provider == "base") {
42✔
420
      if(alg_id != this->algorithm_identifier()) {
42✔
421
         throw Decoding_Error("Unexpected AlgorithmIdentifier for Dilithium X.509 signature");
×
422
      }
423
      return std::make_unique<Dilithium_Verification_Operation>(m_public);
42✔
424
   }
425
   throw Provider_Not_Found(algo_name(), provider);
×
426
}
427

428
namespace Dilithium_Algos {
429

430
namespace {
431

432
std::pair<DilithiumPolyVec, DilithiumPolyVec> compute_t1_and_t0(const DilithiumPolyMatNTT& A,
1,310✔
433
                                                                const DilithiumPolyVec& s1,
434
                                                                const DilithiumPolyVec& s2) {
435
   auto t_hat = A * ntt(s1.clone());
1,310✔
436
   t_hat.reduce();
1,310✔
437
   auto t = inverse_ntt(std::move(t_hat));
1,310✔
438
   t += s2;
1,310✔
439
   t.conditional_add_q();
1,310✔
440

441
   return Dilithium_Algos::power2round(t);
2,620✔
442
}
1,310✔
443

444
}  // namespace
445

446
}  // namespace Dilithium_Algos
447

448
/**
449
 * NIST FIPS 204 IPD, Algorithm 1 (ML-DSA.KeyGen)
450
 */
451
Dilithium_PrivateKey::Dilithium_PrivateKey(RandomNumberGenerator& rng, DilithiumMode m) {
1,243✔
452
   DilithiumConstants mode(m);
1,243✔
453
   const auto& sympriv = mode.symmetric_primitives();
1,243✔
454

455
   const auto xi = rng.random_vec<DilithiumSeedRandomness>(DilithiumConstants::SEED_RANDOMNESS_BYTES);
1,243✔
456
   CT::poison(xi);
1,243✔
457

458
   auto [rho, rhoprime, key] = sympriv.H(xi);
1,243✔
459
   CT::unpoison(rho);  // rho is public (seed for the public matrix A)
1,243✔
460

461
   const auto A = Dilithium_Algos::expand_A(rho, mode);
1,243✔
462
   auto [s1, s2] = Dilithium_Algos::expand_s(rhoprime, mode);
1,243✔
463
   auto [t1, t0] = Dilithium_Algos::compute_t1_and_t0(A, s1, s2);
1,243✔
464

465
   CT::unpoison_all(t1, key, s1, s2, t0);
1,243✔
466
   m_public = std::make_shared<Dilithium_PublicKeyInternal>(mode, rho, std::move(t1));
1,243✔
467
   m_private = std::make_shared<Dilithium_PrivateKeyInternal>(
1,243✔
468
      std::move(mode), std::move(rho), std::move(key), m_public->tr(), std::move(s1), std::move(s2), std::move(t0));
1,243✔
469
}
3,729✔
470

471
Dilithium_PrivateKey::Dilithium_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> sk) :
55✔
472
      Dilithium_PrivateKey(sk, DilithiumMode(alg_id.oid())) {}
55✔
473

474
Dilithium_PrivateKey::Dilithium_PrivateKey(std::span<const uint8_t> sk, DilithiumMode m) {
67✔
475
   auto scope = CT::scoped_poison(sk);
67✔
476

477
   DilithiumConstants mode(m);
67✔
478
   BOTAN_ARG_CHECK(sk.size() == mode.private_key_bytes(), "dilithium private key does not have the correct byte count");
67✔
479
   m_private =
67✔
480
      Dilithium_PrivateKeyInternal::decode(std::move(mode), StrongSpan<const DilithiumSerializedPrivateKey>(sk));
67✔
481

482
   // Currently, Botan's Private_Key class inherits from Public_Key, forcing us
483
   // to derive the public key from the private key here.
484

485
   // rho is public (used in rejection sampling of matrix A)
486
   CT::unpoison(m_private->rho());
67✔
487

488
   const auto A = Dilithium_Algos::expand_A(m_private->rho(), m_private->mode());
67✔
489
   auto [t1, _] = Dilithium_Algos::compute_t1_and_t0(A, m_private->s1(), m_private->s2());
67✔
490
   CT::unpoison(t1);
67✔
491

492
   m_public = std::make_shared<Dilithium_PublicKeyInternal>(m_private->mode(), m_private->rho(), std::move(t1));
67✔
493
   CT::unpoison(*m_private);
67✔
494

495
   if(m_public->tr() != m_private->tr()) {
67✔
496
      throw Decoding_Error("Calculated dilithium public key hash does not match the one stored in the private key");
×
497
   }
498
}
67✔
499

500
secure_vector<uint8_t> Dilithium_PrivateKey::raw_private_key_bits() const {
×
501
   return this->private_key_bits();
×
502
}
503

504
secure_vector<uint8_t> Dilithium_PrivateKey::private_key_bits() const {
1,306✔
505
   return std::move(m_private->raw_sk().get());
1,306✔
506
}
507

508
std::unique_ptr<PK_Ops::Signature> Dilithium_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
1,287✔
509
                                                                             std::string_view params,
510
                                                                             std::string_view provider) const {
511
   BOTAN_UNUSED(rng);
1,287✔
512

513
   BOTAN_ARG_CHECK(params.empty() || params == "Deterministic" || params == "Randomized",
2,567✔
514
                   "Unexpected parameters for signing with Dilithium");
515

516
   // TODO: ML-DSA uses the randomized (hedged) variant by default.
517
   //       We might even drop support for the deterministic variant.
518
   const bool randomized = (params == "Randomized");
1,287✔
519
   if(provider.empty() || provider == "base") {
1,288✔
520
      return std::make_unique<Dilithium_Signature_Operation>(m_private, randomized);
1,287✔
521
   }
522
   throw Provider_Not_Found(algo_name(), provider);
×
523
}
524

525
std::unique_ptr<Public_Key> Dilithium_PrivateKey::public_key() const {
12✔
526
   return std::make_unique<Dilithium_PublicKey>(*this);
12✔
527
}
528
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc