/src/botan/src/lib/pubkey/pk_ops.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 2.31k | PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(std::string_view kdf) { |
74 | 2.31k | if(kdf != "Raw") { |
75 | 0 | m_kdf = KDF::create_or_throw(kdf); |
76 | 0 | } |
77 | 2.31k | } |
78 | | |
79 | 2.31k | 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 | 2.31k | std::span<const uint8_t> salt) { |
84 | 2.31k | 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 | 2.31k | secure_vector<uint8_t> z = raw_agree(other_key.data(), other_key.size()); |
89 | 2.31k | if(m_kdf) { |
90 | 0 | return m_kdf->derive_key(key_len, z, salt.data(), salt.size()); |
91 | 0 | } |
92 | 2.31k | return z; |
93 | 2.31k | } |
94 | | |
95 | | namespace { |
96 | | |
97 | 190 | std::unique_ptr<HashFunction> create_signature_hash(std::string_view padding) { |
98 | 190 | if(auto hash = HashFunction::create(padding)) { |
99 | 190 | return hash; |
100 | 190 | } |
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 | 190 | Verification(), m_hash(create_signature_hash(padding)) {} |
159 | | |
160 | 821 | PK_Ops::Verification_with_Hash::~Verification_with_Hash() = default; |
161 | | |
162 | 36 | std::string PK_Ops::Verification_with_Hash::hash_function() const { |
163 | 36 | return m_hash->name(); |
164 | 36 | } |
165 | | |
166 | | PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier& alg_id, |
167 | | std::string_view pk_algo, |
168 | 685 | bool allow_null_parameters) { |
169 | 685 | const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/'); |
170 | | |
171 | 685 | if(oid_info.size() != 2 || oid_info[0] != pk_algo) { |
172 | 17 | throw Decoding_Error( |
173 | 17 | fmt("Unexpected AlgorithmIdentifier OID {} in association with {} key", alg_id.oid(), pk_algo)); |
174 | 17 | } |
175 | | |
176 | 668 | if(!alg_id.parameters_are_empty()) { |
177 | 37 | if(alg_id.parameters_are_null()) { |
178 | 30 | if(!allow_null_parameters) { |
179 | 30 | throw Decoding_Error(fmt("Unexpected NULL AlgorithmIdentifier parameters for {}", pk_algo)); |
180 | 30 | } |
181 | 30 | } else { |
182 | 7 | throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier parameters for {}", pk_algo)); |
183 | 7 | } |
184 | 37 | } |
185 | | |
186 | 631 | m_hash = HashFunction::create_or_throw(oid_info[1]); |
187 | 631 | } |
188 | | |
189 | 821 | void PK_Ops::Verification_with_Hash::update(std::span<const uint8_t> msg) { |
190 | 821 | m_hash->update(msg); |
191 | 821 | } |
192 | | |
193 | 802 | bool PK_Ops::Verification_with_Hash::is_valid_signature(std::span<const uint8_t> sig) { |
194 | 802 | const std::vector<uint8_t> msg = m_hash->final_stdvec(); |
195 | 802 | return verify(msg, sig); |
196 | 802 | } |
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 |