Coverage Report

Created: 2025-04-11 06:34

/src/botan/build/include/internal/botan/internal/ml_dsa_impl.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
* Asymmetric primitives for ML-DSA
3
* (C) 2024 Jack Lloyd
4
* (C) 2024 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#ifndef BOTAN_ML_DSA_SYM_PRIMITIVES_H_
10
#define BOTAN_ML_DSA_SYM_PRIMITIVES_H_
11
12
#include <botan/internal/dilithium_symmetric_primitives.h>
13
14
#include <botan/rng.h>
15
#include <botan/internal/dilithium_keys.h>
16
#include <botan/internal/dilithium_shake_xof.h>
17
#include <botan/internal/int_utils.h>
18
19
namespace Botan {
20
21
class ML_DSA_Expanding_Keypair_Codec final : public Dilithium_Keypair_Codec {
22
   public:
23
      secure_vector<uint8_t> encode_keypair(DilithiumInternalKeypair keypair) const override;
24
      DilithiumInternalKeypair decode_keypair(std::span<const uint8_t> private_key,
25
                                              DilithiumConstants mode) const override;
26
};
27
28
class ML_DSA_MessageHash final : public DilithiumMessageHash {
29
   public:
30
      using DilithiumMessageHash::DilithiumMessageHash;
31
32
0
      bool is_valid_user_context(std::span<const uint8_t> user_context) const final {
33
0
         return user_context.size() <= 255;
34
0
      }
35
36
0
      void start(std::span<const uint8_t> user_context) final {
37
         // ML-DSA introduced an application-specific context string that is
38
         // empty by default and can be set by the application.
39
         //
40
         // In HashML-DSA, there's an additional domain information, namely
41
         // the serialized OID of the hash function used to hash the message.
42
         //
43
         // See FIPS 204, Algorithm 2, line 10 and Algorithm 7, line 6, and
44
         // FIPS 204, Section 5.4
45
46
0
         DilithiumMessageHash::start(user_context);
47
0
         constexpr uint8_t domain_separator = 0x00;  // HashML-DSA would use 0x01
48
0
         const uint8_t context_length = checked_cast_to<uint8_t>(user_context.size());
49
0
         update(std::array{domain_separator, context_length});
50
0
         update(user_context);
51
0
      }
52
};
53
54
class ML_DSA_Symmetric_Primitives final : public Dilithium_Symmetric_Primitives_Base {
55
   private:
56
      /// Rho prime computation for ML-DSA
57
      DilithiumSeedRhoPrime H(StrongSpan<const DilithiumSigningSeedK> k,
58
                              StrongSpan<const DilithiumOptionalRandomness> rnd,
59
0
                              StrongSpan<const DilithiumMessageRepresentative> mu) const {
60
0
         return H_256<DilithiumSeedRhoPrime>(DilithiumConstants::SEED_RHOPRIME_BYTES, k, rnd, mu);
61
0
      }
62
63
   public:
64
      ML_DSA_Symmetric_Primitives(const DilithiumConstants& mode) :
65
17
            Dilithium_Symmetric_Primitives_Base(mode, std::make_unique<DilithiumShakeXOF>()),
66
17
            m_seed_expansion_domain_separator({mode.k(), mode.l()}) {}
67
68
      DilithiumSeedRhoPrime H_maybe_randomized(
69
         StrongSpan<const DilithiumSigningSeedK> k,
70
         StrongSpan<const DilithiumMessageRepresentative> mu,
71
0
         std::optional<std::reference_wrapper<RandomNumberGenerator>> rng) const override {
72
         // NIST FIPS 204, Algorithm 2 (ML-DSA.Sign), line 5-8:
73
0
         const auto rnd = [&] {
74
0
            DilithiumOptionalRandomness optional_randomness(DilithiumConstants::OPTIONAL_RANDOMNESS_BYTES);
75
0
            if(rng.has_value()) {
76
0
               rng->get().randomize(optional_randomness);
77
0
            }
78
0
            return optional_randomness;
79
0
         }();
80
0
         return H(k, rnd, mu);
81
0
      }
82
83
      StrongSpan<const DilithiumCommitmentHash> truncate_commitment_hash(
84
0
         StrongSpan<const DilithiumCommitmentHash> seed) const override {
85
0
         return seed;
86
0
      }
87
88
0
      std::unique_ptr<DilithiumMessageHash> get_message_hash(DilithiumHashedPublicKey tr) const override {
89
0
         return std::make_unique<ML_DSA_MessageHash>(std::move(tr));
90
0
      }
91
92
0
      std::optional<std::array<uint8_t, 2>> seed_expansion_domain_separator() const override {
93
0
         return m_seed_expansion_domain_separator;
94
0
      }
95
96
   private:
97
      std::array<uint8_t, 2> m_seed_expansion_domain_separator;
98
};
99
100
}  // namespace Botan
101
102
#endif