Coverage Report

Created: 2026-06-08 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/pubkey/pk_ops.cpp
Line
Count
Source
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/assert.h>
11
#include <botan/hash.h>
12
#include <botan/kdf.h>
13
#include <botan/rng.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/enc_padding.h>
16
#include <botan/internal/fmt.h>
17
#include <botan/internal/parsing.h>
18
#include <botan/internal/scan_name.h>
19
20
#if defined(BOTAN_HAS_RAW_HASH_FN)
21
   #include <botan/internal/raw_hash.h>
22
#endif
23
24
namespace Botan {
25
26
0
AlgorithmIdentifier PK_Ops::Signature::algorithm_identifier() const {
27
0
   throw Not_Implemented("This signature scheme does not have an algorithm identifier available");
28
0
}
29
30
PK_Ops::Encryption_with_Padding::Encryption_with_Padding(std::string_view padding) :
31
0
      m_padding(EncryptionPaddingScheme::create(padding)) {}
32
33
0
PK_Ops::Encryption_with_Padding::~Encryption_with_Padding() = default;
34
35
0
size_t PK_Ops::Encryption_with_Padding::max_input_bits() const {
36
0
   return 8 * m_padding->maximum_input_size(max_ptext_input_bits());
37
0
}
38
39
std::vector<uint8_t> PK_Ops::Encryption_with_Padding::encrypt(std::span<const uint8_t> msg,
40
0
                                                              RandomNumberGenerator& rng) {
41
0
   const size_t max_input_bits = max_ptext_input_bits();
42
0
   const size_t max_input_bytes = (max_input_bits + 7) / 8;
43
0
   BOTAN_ARG_CHECK(msg.size() <= max_input_bytes, "Plaintext too large");
44
45
0
   secure_vector<uint8_t> padded_ptext(max_input_bits);
46
0
   const size_t written = m_padding->pad(padded_ptext, msg, max_input_bits, rng);
47
0
   return raw_encrypt(std::span{padded_ptext}.first(written), rng);
48
0
}
49
50
PK_Ops::Decryption_with_Padding::Decryption_with_Padding(std::string_view padding) :
51
0
      m_padding(EncryptionPaddingScheme::create(padding)) {}
52
53
0
PK_Ops::Decryption_with_Padding::~Decryption_with_Padding() = default;
54
55
0
secure_vector<uint8_t> PK_Ops::Decryption_with_Padding::decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) {
56
0
   const secure_vector<uint8_t> raw = raw_decrypt(ctext);
57
58
0
   secure_vector<uint8_t> ptext(raw.size());
59
0
   auto len = m_padding->unpad(ptext, raw);
60
61
0
   valid_mask = CT::Mask<uint8_t>::from_choice(len.has_value()).if_set_return(0xFF);
62
63
   /*
64
   This is potentially not const time, depending on how std::vector is
65
   implemented. But since we are always reducing length, it should
66
   just amount to setting the member var holding the length. Resizing
67
   downwards is guaranteed to not change the capacity, and since we
68
   set ctext to the maximum possible size (equal to the raw input) we
69
   know that this is always, if anything, resizing smaller than the
70
   capacity, so no reallocation occurs.
71
   */
72
73
0
   ptext.resize(len.value_or(0));
74
0
   return ptext;
75
0
}
76
77
0
PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) {
78
0
   if(kdf != "Raw") {
79
0
      m_kdf = KDF::create_or_throw(kdf);
80
0
   }
81
0
}
82
83
0
PK_Ops::Key_Agreement_with_KDF::~Key_Agreement_with_KDF() = default;
84
85
secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
86
                                                             std::span<const uint8_t> other_key,
87
0
                                                             std::span<const uint8_t> salt) {
88
0
   if(!salt.empty() && m_kdf == nullptr) {
89
0
      throw Invalid_Argument("PK_Key_Agreement::derive_key requires a KDF to use a salt");
90
0
   }
91
92
0
   secure_vector<uint8_t> z = raw_agree(other_key.data(), other_key.size());
93
0
   if(m_kdf) {
94
0
      return m_kdf->derive_key(key_len, z, salt.data(), salt.size());
95
0
   }
96
0
   return z;
97
0
}
98
99
namespace {
100
101
4.32k
std::unique_ptr<HashFunction> create_signature_hash(std::string_view padding) {
102
4.32k
   if(auto hash = HashFunction::create(padding)) {
103
0
      return hash;
104
0
   }
105
106
4.32k
   SCAN_Name req(padding);
107
108
4.32k
   if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
109
433
      if(auto hash = HashFunction::create(req.arg(0))) {
110
433
         return hash;
111
433
      }
112
433
   }
113
114
3.89k
#if defined(BOTAN_HAS_RAW_HASH_FN)
115
3.89k
   if(req.algo_name() == "Raw") {
116
3.89k
      if(req.arg_count() == 0) {
117
3.89k
         return std::make_unique<RawHashFunction>("Raw", 0);
118
3.89k
      }
119
120
0
      if(req.arg_count() == 1) {
121
0
         if(auto hash = HashFunction::create(req.arg(0))) {
122
0
            return std::make_unique<RawHashFunction>(std::move(hash));
123
0
         }
124
0
      }
125
0
   }
126
0
#endif
127
128
0
   throw Algorithm_Not_Found(padding);
129
3.89k
}
130
131
}  // namespace
132
133
PK_Ops::Signature_with_Hash::Signature_with_Hash(std::string_view hash) :
134
433
      Signature(), m_hash(create_signature_hash(hash)) {}
135
136
433
PK_Ops::Signature_with_Hash::~Signature_with_Hash() = default;
137
138
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
139
414
std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const {
140
414
   std::string hash = m_hash->name();
141
414
   if(hash != "Raw") {
142
414
      return hash;
143
414
   }
144
0
   return "SHA-512";
145
414
}
146
#endif
147
148
0
std::string PK_Ops::Signature_with_Hash::hash_function() const {
149
0
   return m_hash->name();
150
0
}
151
152
414
void PK_Ops::Signature_with_Hash::update(std::span<const uint8_t> msg) {
153
414
   m_hash->update(msg);
154
414
}
155
156
414
std::vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
157
414
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
158
414
   return raw_sign(msg, rng);
159
414
}
160
161
PK_Ops::Verification_with_Hash::Verification_with_Hash(std::string_view padding) :
162
3.89k
      Verification(), m_hash(create_signature_hash(padding)) {}
163
164
3.89k
PK_Ops::Verification_with_Hash::~Verification_with_Hash() = default;
165
166
0
std::string PK_Ops::Verification_with_Hash::hash_function() const {
167
0
   return m_hash->name();
168
0
}
169
170
PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier& alg_id,
171
                                                       std::string_view pk_algo,
172
0
                                                       bool allow_null_parameters) {
173
0
   const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/');
174
175
0
   if(oid_info.size() != 2 || oid_info[0] != pk_algo) {
176
0
      throw Decoding_Error(
177
0
         fmt("Unexpected AlgorithmIdentifier OID {} in association with {} key", alg_id.oid(), pk_algo));
178
0
   }
179
180
0
   if(!alg_id.parameters_are_empty()) {
181
0
      if(alg_id.parameters_are_null()) {
182
0
         if(!allow_null_parameters) {
183
0
            throw Decoding_Error(fmt("Unexpected NULL AlgorithmIdentifier parameters for {}", pk_algo));
184
0
         }
185
0
      } else {
186
0
         throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier parameters for {}", pk_algo));
187
0
      }
188
0
   }
189
190
0
   m_hash = HashFunction::create_or_throw(oid_info[1]);
191
0
}
192
193
3.89k
void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) {
194
3.89k
   m_hash->update(msg);
195
3.89k
}
196
197
3.89k
bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) {
198
3.89k
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
199
3.89k
   return verify(msg, sig);
200
3.89k
}
201
202
0
size_t PK_Ops::KEM_Encryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
203
0
   if(m_kdf) {
204
0
      return desired_shared_key_len;
205
0
   } else {
206
0
      return this->raw_kem_shared_key_length();
207
0
   }
208
0
}
209
210
void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(std::span<uint8_t> out_encapsulated_key,
211
                                                  std::span<uint8_t> out_shared_key,
212
                                                  RandomNumberGenerator& rng,
213
                                                  size_t desired_shared_key_len,
214
0
                                                  std::span<const uint8_t> salt) {
215
0
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Encryptor::encrypt requires a KDF to use a salt");
216
0
   BOTAN_ASSERT_NOMSG(out_encapsulated_key.size() == encapsulated_key_length());
217
218
0
   if(m_kdf) {
219
0
      BOTAN_ASSERT_EQUAL(
220
0
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");
221
222
0
      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
223
0
      this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng);
224
0
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
225
0
   } else {
226
0
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
227
0
      this->raw_kem_encrypt(out_encapsulated_key, out_shared_key, rng);
228
0
   }
229
0
}
230
231
0
PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(std::string_view kdf) {
232
0
   if(kdf != "Raw") {
233
0
      m_kdf = KDF::create_or_throw(kdf);
234
0
   }
235
0
}
236
237
0
PK_Ops::KEM_Encryption_with_KDF::~KEM_Encryption_with_KDF() = default;
238
239
0
size_t PK_Ops::KEM_Decryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
240
0
   if(m_kdf) {
241
0
      return desired_shared_key_len;
242
0
   } else {
243
0
      return this->raw_kem_shared_key_length();
244
0
   }
245
0
}
246
247
void PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(std::span<uint8_t> out_shared_key,
248
                                                  std::span<const uint8_t> encapsulated_key,
249
                                                  size_t desired_shared_key_len,
250
0
                                                  std::span<const uint8_t> salt) {
251
0
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Decryptor::decrypt requires a KDF to use a salt");
252
253
0
   if(m_kdf) {
254
0
      BOTAN_ASSERT_EQUAL(
255
0
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");
256
257
0
      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
258
0
      this->raw_kem_decrypt(raw_shared, encapsulated_key);
259
0
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
260
0
   } else {
261
0
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
262
0
      this->raw_kem_decrypt(out_shared_key, encapsulated_key);
263
0
   }
264
0
}
265
266
0
PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(std::string_view kdf) {
267
0
   if(kdf != "Raw") {
268
0
      m_kdf = KDF::create_or_throw(kdf);
269
0
   }
270
0
}
271
272
0
PK_Ops::KEM_Decryption_with_KDF::~KEM_Decryption_with_KDF() = default;
273
274
}  // namespace Botan