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