Coverage Report

Created: 2025-12-31 06:08

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
1.25k
std::unique_ptr<HashFunction> create_signature_hash(std::string_view padding) {
102
1.25k
   if(auto hash = HashFunction::create(padding)) {
103
0
      return hash;
104
0
   }
105
106
1.25k
   const SCAN_Name req(padding);
107
108
1.25k
   if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
109
484
      if(auto hash = HashFunction::create(req.arg(0))) {
110
484
         return hash;
111
484
      }
112
484
   }
113
114
772
#if defined(BOTAN_HAS_RAW_HASH_FN)
115
772
   if(req.algo_name() == "Raw") {
116
772
      if(req.arg_count() == 0) {
117
772
         return std::make_unique<RawHashFunction>("Raw", 0);
118
772
      }
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
772
}
130
131
}  // namespace
132
133
PK_Ops::Signature_with_Hash::Signature_with_Hash(std::string_view hash) :
134
484
      Signature(), m_hash(create_signature_hash(hash)) {}
135
136
484
PK_Ops::Signature_with_Hash::~Signature_with_Hash() = default;
137
138
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
139
484
std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const {
140
484
   std::string hash = m_hash->name();
141
484
   if(hash != "Raw") {
142
484
      return hash;
143
484
   }
144
0
   return "SHA-512";
145
484
}
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
484
void PK_Ops::Signature_with_Hash::update(std::span<const uint8_t> msg) {
153
484
   m_hash->update(msg);
154
484
}
155
156
484
std::vector<uint8_t> PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rng) {
157
484
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
158
484
   return raw_sign(msg, rng);
159
484
}
160
161
PK_Ops::Verification_with_Hash::Verification_with_Hash(std::string_view padding) :
162
772
      Verification(), m_hash(create_signature_hash(padding)) {}
163
164
772
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
772
void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) {
194
772
   m_hash->update(msg);
195
772
}
196
197
772
bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) {
198
772
   const std::vector<uint8_t> msg = m_hash->final_stdvec();
199
772
   return verify(msg, sig);
200
772
}
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