Coverage Report

Created: 2026-02-14 06:48

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/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
0
AlgorithmIdentifier PK_Ops::Signature::algorithm_identifier() const {
26
0
   throw Not_Implemented("This signature scheme does not have an algorithm identifier available");
27
0
}
28
29
0
PK_Ops::Encryption_with_EME::Encryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}
30
31
0
PK_Ops::Encryption_with_EME::~Encryption_with_EME() = default;
32
33
0
size_t PK_Ops::Encryption_with_EME::max_input_bits() const {
34
0
   return 8 * m_eme->maximum_input_size(max_ptext_input_bits());
35
0
}
36
37
0
std::vector<uint8_t> PK_Ops::Encryption_with_EME::encrypt(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
38
0
   const size_t max_input_bits = max_ptext_input_bits();
39
0
   const size_t max_input_bytes = (max_input_bits + 7) / 8;
40
0
   BOTAN_ARG_CHECK(msg.size() <= max_input_bytes, "Plaintext too large");
41
42
0
   secure_vector<uint8_t> eme_output(max_input_bits);
43
0
   const size_t written = m_eme->pad(eme_output, msg, max_input_bits, rng);
44
0
   return raw_encrypt(std::span{eme_output}.first(written), rng);
45
0
}
46
47
0
PK_Ops::Decryption_with_EME::Decryption_with_EME(std::string_view eme) : m_eme(EME::create(eme)) {}
48
49
0
PK_Ops::Decryption_with_EME::~Decryption_with_EME() = default;
50
51
0
secure_vector<uint8_t> PK_Ops::Decryption_with_EME::decrypt(uint8_t& valid_mask, std::span<const uint8_t> ctext) {
52
0
   const secure_vector<uint8_t> raw = raw_decrypt(ctext);
53
54
0
   secure_vector<uint8_t> ptext(raw.size());
55
0
   auto len = m_eme->unpad(ptext, raw);
56
57
0
   valid_mask = CT::Mask<uint8_t>::from_choice(len.has_value()).if_set_return(0xFF);
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
0
   ptext.resize(len.value_or(0));
70
0
   return ptext;
71
0
}
72
73
0
PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) {
74
0
   if(kdf != "Raw") {
75
0
      m_kdf = KDF::create_or_throw(kdf);
76
0
   }
77
0
}
78
79
0
PK_Ops::Key_Agreement_with_KDF::~Key_Agreement_with_KDF() = default;
80
81
secure_vector<uint8_t> PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len,
82
                                                             std::span<const uint8_t> other_key,
83
0
                                                             std::span<const uint8_t> salt) {
84
0
   if(!salt.empty() && m_kdf == nullptr) {
85
0
      throw Invalid_Argument("PK_Key_Agreement::derive_key requires a KDF to use a salt");
86
0
   }
87
88
0
   secure_vector<uint8_t> z = raw_agree(other_key.data(), other_key.size());
89
0
   if(m_kdf) {
90
0
      return m_kdf->derive_key(key_len, z, salt.data(), salt.size());
91
0
   }
92
0
   return z;
93
0
}
94
95
namespace {
96
97
0
std::unique_ptr<HashFunction> create_signature_hash(std::string_view padding) {
98
0
   if(auto hash = HashFunction::create(padding)) {
99
0
      return hash;
100
0
   }
101
102
0
   SCAN_Name req(padding);
103
104
0
   if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
105
0
      if(auto hash = HashFunction::create(req.arg(0))) {
106
0
         return hash;
107
0
      }
108
0
   }
109
110
0
#if defined(BOTAN_HAS_RAW_HASH_FN)
111
0
   if(req.algo_name() == "Raw") {
112
0
      if(req.arg_count() == 0) {
113
0
         return std::make_unique<RawHashFunction>("Raw", 0);
114
0
      }
115
116
0
      if(req.arg_count() == 1) {
117
0
         if(auto hash = HashFunction::create(req.arg(0))) {
118
0
            return std::make_unique<RawHashFunction>(std::move(hash));
119
0
         }
120
0
      }
121
0
   }
122
0
#endif
123
124
0
   throw Algorithm_Not_Found(padding);
125
0
}
126
127
}  // namespace
128
129
PK_Ops::Signature_with_Hash::Signature_with_Hash(std::string_view hash) :
130
0
      Signature(), m_hash(create_signature_hash(hash)) {}
131
132
0
PK_Ops::Signature_with_Hash::~Signature_with_Hash() = default;
133
134
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
135
0
std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const {
136
0
   std::string hash = m_hash->name();
137
0
   if(hash != "Raw") {
138
0
      return hash;
139
0
   }
140
0
   return "SHA-512";
141
0
}
142
#endif
143
144
0
std::string PK_Ops::Signature_with_Hash::hash_function() const {
145
0
   return m_hash->name();
146
0
}
147
148
0
void PK_Ops::Signature_with_Hash::update(std::span<const uint8_t> msg) {
149
0
   m_hash->update(msg);
150
0
}
151
152
0
std::vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
153
0
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
154
0
   return raw_sign(msg, rng);
155
0
}
156
157
PK_Ops::Verification_with_Hash::Verification_with_Hash(std::string_view padding) :
158
0
      Verification(), m_hash(create_signature_hash(padding)) {}
159
160
0
PK_Ops::Verification_with_Hash::~Verification_with_Hash() = default;
161
162
0
std::string PK_Ops::Verification_with_Hash::hash_function() const {
163
0
   return m_hash->name();
164
0
}
165
166
PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier& alg_id,
167
                                                       std::string_view pk_algo,
168
0
                                                       bool allow_null_parameters) {
169
0
   const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/');
170
171
0
   if(oid_info.size() != 2 || oid_info[0] != pk_algo) {
172
0
      throw Decoding_Error(
173
0
         fmt("Unexpected AlgorithmIdentifier OID {} in association with {} key", alg_id.oid(), pk_algo));
174
0
   }
175
176
0
   if(!alg_id.parameters_are_empty()) {
177
0
      if(alg_id.parameters_are_null()) {
178
0
         if(!allow_null_parameters) {
179
0
            throw Decoding_Error(fmt("Unexpected NULL AlgorithmIdentifier parameters for {}", pk_algo));
180
0
         }
181
0
      } else {
182
0
         throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier parameters for {}", pk_algo));
183
0
      }
184
0
   }
185
186
0
   m_hash = HashFunction::create_or_throw(oid_info[1]);
187
0
}
188
189
0
void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) {
190
0
   m_hash->update(msg);
191
0
}
192
193
0
bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) {
194
0
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
195
0
   return verify(msg, sig);
196
0
}
197
198
0
size_t PK_Ops::KEM_Encryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
199
0
   if(m_kdf) {
200
0
      return desired_shared_key_len;
201
0
   } else {
202
0
      return this->raw_kem_shared_key_length();
203
0
   }
204
0
}
205
206
void PK_Ops::KEM_Encryption_with_KDF::kem_encrypt(std::span<uint8_t> out_encapsulated_key,
207
                                                  std::span<uint8_t> out_shared_key,
208
                                                  RandomNumberGenerator& rng,
209
                                                  size_t desired_shared_key_len,
210
0
                                                  std::span<const uint8_t> salt) {
211
0
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Encryptor::encrypt requires a KDF to use a salt");
212
0
   BOTAN_ASSERT_NOMSG(out_encapsulated_key.size() == encapsulated_key_length());
213
214
0
   if(m_kdf) {
215
0
      BOTAN_ASSERT_EQUAL(
216
0
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");
217
218
0
      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
219
0
      this->raw_kem_encrypt(out_encapsulated_key, raw_shared, rng);
220
0
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
221
0
   } else {
222
0
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
223
0
      this->raw_kem_encrypt(out_encapsulated_key, out_shared_key, rng);
224
0
   }
225
0
}
226
227
0
PK_Ops::KEM_Encryption_with_KDF::KEM_Encryption_with_KDF(std::string_view kdf) {
228
0
   if(kdf != "Raw") {
229
0
      m_kdf = KDF::create_or_throw(kdf);
230
0
   }
231
0
}
232
233
0
PK_Ops::KEM_Encryption_with_KDF::~KEM_Encryption_with_KDF() = default;
234
235
0
size_t PK_Ops::KEM_Decryption_with_KDF::shared_key_length(size_t desired_shared_key_len) const {
236
0
   if(m_kdf) {
237
0
      return desired_shared_key_len;
238
0
   } else {
239
0
      return this->raw_kem_shared_key_length();
240
0
   }
241
0
}
242
243
void PK_Ops::KEM_Decryption_with_KDF::kem_decrypt(std::span<uint8_t> out_shared_key,
244
                                                  std::span<const uint8_t> encapsulated_key,
245
                                                  size_t desired_shared_key_len,
246
0
                                                  std::span<const uint8_t> salt) {
247
0
   BOTAN_ARG_CHECK(salt.empty() || m_kdf, "PK_KEM_Decryptor::decrypt requires a KDF to use a salt");
248
249
0
   if(m_kdf) {
250
0
      BOTAN_ASSERT_EQUAL(
251
0
         out_shared_key.size(), desired_shared_key_len, "KDF output length and shared key length match");
252
253
0
      secure_vector<uint8_t> raw_shared(raw_kem_shared_key_length());
254
0
      this->raw_kem_decrypt(raw_shared, encapsulated_key);
255
0
      m_kdf->derive_key(out_shared_key, raw_shared, salt, {});
256
0
   } else {
257
0
      BOTAN_ASSERT_EQUAL(out_shared_key.size(), raw_kem_shared_key_length(), "Shared key has raw KEM output length");
258
0
      this->raw_kem_decrypt(out_shared_key, encapsulated_key);
259
0
   }
260
0
}
261
262
0
PK_Ops::KEM_Decryption_with_KDF::KEM_Decryption_with_KDF(std::string_view kdf) {
263
0
   if(kdf != "Raw") {
264
0
      m_kdf = KDF::create_or_throw(kdf);
265
0
   }
266
0
}
267
268
0
PK_Ops::KEM_Decryption_with_KDF::~KEM_Decryption_with_KDF() = default;
269
270
}  // namespace Botan