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

randombit / botan / 16581714815

28 Jul 2025 10:25PM UTC coverage: 90.475% (-0.2%) from 90.685%
16581714815

Pull #5021

github

web-flow
Merge 072983077 into 1eacc5b05
Pull Request #5021: Add PK_Signature_Options

99366 of 109827 relevant lines covered (90.48%)

12349417.34 hits per line

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

93.43
/src/lib/pubkey/pk_ops.cpp
1
/*
2
* PK Operation Types
3
* (C) 2010,2015,2023 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/internal/pk_ops_impl.h>
9

10
#include <botan/hash.h>
11
#include <botan/kdf.h>
12
#include <botan/rng.h>
13
#include <botan/internal/bit_ops.h>
14
#include <botan/internal/eme.h>
15
#include <botan/internal/fmt.h>
16
#include <botan/internal/parsing.h>
17
#include <botan/internal/scan_name.h>
18

19
#if defined(BOTAN_HAS_RAW_HASH_FN)
20
   #include <botan/internal/raw_hash.h>
21
#endif
22

23
namespace Botan {
24

25
AlgorithmIdentifier PK_Ops::Signature::algorithm_identifier() const {
×
26
   throw Not_Implemented("This signature scheme does not have an algorithm identifier available");
×
27
}
28

29
PK_Ops::Encryption_with_EME::Encryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}
291✔
30

31
PK_Ops::Encryption_with_EME::~Encryption_with_EME() = default;
291✔
32

33
size_t PK_Ops::Encryption_with_EME::max_input_bits() const {
204✔
34
   return 8 * m_eme->maximum_input_size(max_ptext_input_bits());
204✔
35
}
36

37
std::vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
359✔
38
   const size_t max_input_bits = max_ptext_input_bits();
359✔
39
   const size_t max_input_bytes = (max_input_bits + 7) / 8;
359✔
40
   BOTAN_ARG_CHECK(msg.size() <= max_input_bytes, "Plaintext too large");
359✔
41

42
   secure_vector<uint8_t> eme_output(max_input_bits);
359✔
43
   const size_t written = m_eme->pad(eme_output, msg, max_input_bits, rng);
359✔
44
   return raw_encrypt(std::span{eme_output}.first(written), rng);
359✔
45
}
718✔
46

47
PK_Ops::Decryption_with_EME::Decryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}
314✔
48

49
PK_Ops::Decryption_with_EME::~Decryption_with_EME() = default;
314✔
50

51
secure_vector<uint8_t> PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) {
5,659✔
52
   const secure_vector<uint8_t> raw = raw_decrypt(ctext);
5,659✔
53

54
   secure_vector<uint8_t> ptext(raw.size());
5,641✔
55
   auto len = m_eme->unpad(ptext, raw);
5,641✔
56

57
   valid_mask = CT::Mask<uint8_t>::from_choice(len.has_value()).if_set_return(0xFF);
5,641✔
58

59
   /*
60
   This is potentially not const time, depending on how std::vector is
61
   implemented. But since we are always reducing length, it should
62
   just amount to setting the member var holding the length. Resizing
63
   downwards is guaranteed to not change the capacity, and since we
64
   set ctext to the maximum possible size (equal to the raw input) we
65
   know that this is always, if anything, resizing smaller than the
66
   capacity, so no reallocation occurs.
67
   */
68

69
   ptext.resize(len.value_or(0));
5,641✔
70
   return ptext;
5,641✔
71
}
5,641✔
72

73
PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) {
8,850✔
74
   if(kdf != "Raw") {
17,662✔
75
      m_kdf = KDF::create_or_throw(kdf);
38✔
76
   }
77
}
8,850✔
78

79
PK_Ops::Key_Agreement_with_KDF::~Key_Agreement_with_KDF() = default;
8,850✔
80

81
secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
10,431✔
82
                                                             std::span<const uint8_t> other_key,
83
                                                             std::span<const uint8_t> salt) {
84
   if(!salt.empty() && m_kdf == nullptr) {
10,431✔
85
      throw Invalid_Argument("PK_Key_Agreement::derive_key requires a KDF to use a salt");
×
86
   }
87

88
   secure_vector<uint8_t> z = raw_agree(other_key.data(), other_key.size());
10,431✔
89
   if(m_kdf) {
10,370✔
90
      return m_kdf->derive_key(key_len, z, salt.data(), salt.size());
100✔
91
   }
92
   return z;
10,320✔
93
}
10,370✔
94

95
namespace {
96

97
std::unique_ptr<HashFunction> validate_options_returning_hash(const PK_Signature_Options& options) {
15,489✔
98
   BOTAN_ARG_CHECK(!options.hash_function_name().empty(), "This algorithm requires a hash function for signing");
15,489✔
99
   BOTAN_ARG_CHECK(!options.using_padding(), "This algorithm does not support padding modes");
15,489✔
100
   BOTAN_ARG_CHECK(!options.using_salt_size(), "This algorithm does not support a salt");
15,489✔
101

102
   BOTAN_ARG_CHECK(!options.using_explicit_trailer_field(), "This algorithm does not support a padding trailer field");
15,489✔
103

104
   /*
105
   * In a sense ECDSA/DSA are *always* in prehashing mode, so we accept the case
106
   * where prehashing is requested as long as the prehash hash matches the signature hash.
107
   */
108
   if(options.prehash_fn().has_value()) {
15,489✔
109
      if(options.prehash_fn().value() != options.hash_function_name()) {
×
110
         throw Invalid_Argument("This algorithm does not support prehashing with a different hash");
×
111
      }
112
   }
113

114
#if defined(BOTAN_HAS_RAW_HASH_FN)
115
   if(options.hash_function_name().starts_with("Raw")) {
30,978✔
116
      if(options.hash_function_name() == "Raw") {
129✔
117
         return std::make_unique<RawHashFunction>("Raw", 0);
114✔
118
      }
119

120
      SCAN_Name req(options.hash_function_name());
15✔
121
      if(req.arg_count() == 1) {
15✔
122
         if(auto hash = HashFunction::create(req.arg(0))) {
30✔
123
            return std::make_unique<RawHashFunction>(std::move(hash));
15✔
124
         }
×
125
      }
126
   }
15✔
127
#endif
128

129
   return HashFunction::create_or_throw(options.hash_function_name());
26,470✔
130
}
131

132
}  // namespace
133

134
PK_Ops::Signature_with_Hash::Signature_with_Hash(const PK_Signature_Options& options) :
2,327✔
135
      Signature(), m_hash(validate_options_returning_hash(options)) {}
2,327✔
136

137
PK_Ops::Signature_with_Hash::~Signature_with_Hash() = default;
2,327✔
138

139
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
140
std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const {
2,238✔
141
   std::string hash = m_hash->name();
2,238✔
142
   if(hash != "Raw") {
2,238✔
143
      return hash;
2,226✔
144
   }
145
   return "SHA-512";
12✔
146
}
2,238✔
147
#endif
148

149
std::string PK_Ops::Signature_with_Hash::hash_function() const {
3,822✔
150
   return m_hash->name();
3,822✔
151
}
152

153
void PK_Ops::Signature_with_Hash::update(std::span<const uint8_t> msg) {
6,138✔
154
   m_hash->update(msg);
6,138✔
155
}
6,138✔
156

157
std::vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
6,139✔
158
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
6,139✔
159
   return raw_sign(msg, rng);
6,139✔
160
}
6,139✔
161

162
PK_Ops::Verification_with_Hash::Verification_with_Hash(const PK_Signature_Options& options) :
13,162✔
163
      Verification(), m_hash(validate_options_returning_hash(options)) {}
13,162✔
164

165
PK_Ops::Verification_with_Hash::~Verification_with_Hash() = default;
13,429✔
166

167
std::string PK_Ops::Verification_with_Hash::hash_function() const {
4,437✔
168
   return m_hash->name();
4,437✔
169
}
170

171
PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier& alg_id,
4,558✔
172
                                                       std::string_view pk_algo,
173
                                                       bool allow_null_parameters) {
4,558✔
174
   const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/');
4,558✔
175

176
   if(oid_info.size() != 2 || oid_info[0] != pk_algo) {
4,558✔
177
      throw Decoding_Error(
×
178
         fmt("Unexpected AlgorithmIdentifier OID {} in association with {} key", alg_id.oid(), pk_algo));
×
179
   }
180

181
   if(!alg_id.parameters_are_empty()) {
4,558✔
182
      if(alg_id.parameters_are_null()) {
44✔
183
         if(!allow_null_parameters) {
44✔
184
            throw Decoding_Error(fmt("Unexpected NULL AlgorithmIdentifier parameters for {}", pk_algo));
41✔
185
         }
186
      } else {
187
         throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier parameters for {}", pk_algo));
×
188
      }
189
   }
190

191
   m_hash = HashFunction::create_or_throw(oid_info[1]);
4,517✔
192
}
4,599✔
193

194
void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) {
36,437✔
195
   m_hash->update(msg);
36,437✔
196
}
36,437✔
197

198
bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) {
36,210✔
199
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
36,210✔
200
   return verify(msg, sig);
36,210✔
201
}
36,210✔
202

203
size_t PK_Ops::KEM_Encryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
6,962✔
204
   if(m_kdf) {
6,962✔
205
      return desired_shared_key_len;
206
   } else {
207
      return this->raw_kem_shared_key_length();
1,744✔
208
   }
209
}
210

211
void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(std::span<uint8_t> out_encapsulated_key,
3,361✔
212
                                                  std::span<uint8_t> out_shared_key,
213
                                                  RandomNumberGenerator& rng,
214
                                                  size_t desired_shared_key_len,
215
                                                  std::span<const uint8_t> salt) {
216
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Encryptor::encrypt requires a KDF to use a salt");
3,361✔
217
   BOTAN_ASSERT_NOMSG(out_encapsulated_key.size() == encapsulated_key_length());
3,361✔
218

219
   if(m_kdf) {
3,361✔
220
      BOTAN_ASSERT_EQUAL(
2,603✔
221
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");
222

223
      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
2,603✔
224
      this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng);
2,603✔
225
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
5,206✔
226
   } else {
2,603✔
227
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
758✔
228
      this->raw_kem_encrypt(out_encapsulated_key, out_shared_key, rng);
758✔
229
   }
230
}
3,359✔
231

232
PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(std::string_view kdf) {
844✔
233
   if(kdf != "Raw") {
1,579✔
234
      m_kdf = KDF::create_or_throw(kdf);
109✔
235
   }
236
}
844✔
237

238
PK_Ops::KEM_Encryption_with_KDF::~KEM_Encryption_with_KDF() = default;
844✔
239

240
size_t PK_Ops::KEM_Decryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
7,112✔
241
   if(m_kdf) {
7,112✔
242
      return desired_shared_key_len;
243
   } else {
244
      return this->raw_kem_shared_key_length();
1,894✔
245
   }
246
}
247

248
void PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(std::span<uint8_t> out_shared_key,
3,430✔
249
                                                  std::span<const uint8_t> encapsulated_key,
250
                                                  size_t desired_shared_key_len,
251
                                                  std::span<const uint8_t> salt) {
252
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Decryptor::decrypt requires a KDF to use a salt");
3,430✔
253

254
   if(m_kdf) {
3,430✔
255
      BOTAN_ASSERT_EQUAL(
2,603✔
256
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");
257

258
      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
2,603✔
259
      this->raw_kem_decrypt(raw_shared, encapsulated_key);
2,603✔
260
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
5,206✔
261
   } else {
2,603✔
262
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
827✔
263
      this->raw_kem_decrypt(out_shared_key, encapsulated_key);
827✔
264
   }
265
}
3,408✔
266

267
PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(std::string_view kdf) {
782✔
268
   if(kdf != "Raw") {
1,455✔
269
      m_kdf = KDF::create_or_throw(kdf);
109✔
270
   }
271
}
782✔
272

273
PK_Ops::KEM_Decryption_with_KDF::~KEM_Decryption_with_KDF() = default;
782✔
274

275
}  // 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