/src/botan/src/lib/x509/x509_obj.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * X.509 SIGNED Object |
3 | | * (C) 1999-2007,2020 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/x509_obj.h> |
9 | | #include <botan/pubkey.h> |
10 | | #include <botan/der_enc.h> |
11 | | #include <botan/ber_dec.h> |
12 | | #include <botan/internal/parsing.h> |
13 | | #include <botan/pem.h> |
14 | | #include <botan/internal/emsa.h> |
15 | | #include <algorithm> |
16 | | |
17 | | namespace Botan { |
18 | | |
19 | | namespace { |
20 | | struct Pss_params |
21 | | { |
22 | | AlgorithmIdentifier hash_algo; |
23 | | AlgorithmIdentifier mask_gen_algo; |
24 | | AlgorithmIdentifier mask_gen_hash; // redundant: decoded mask_gen_algo.parameters |
25 | | size_t salt_len; |
26 | | size_t trailer_field; |
27 | | }; |
28 | | |
29 | | Pss_params decode_pss_params(const std::vector<uint8_t>& encoded_pss_params) |
30 | 75 | { |
31 | 75 | const AlgorithmIdentifier default_hash("SHA-160", AlgorithmIdentifier::USE_NULL_PARAM); |
32 | 75 | const AlgorithmIdentifier default_mgf("MGF1", default_hash.BER_encode()); |
33 | | |
34 | 75 | Pss_params pss_parameter; |
35 | 75 | BER_Decoder(encoded_pss_params) |
36 | 75 | .start_sequence() |
37 | 75 | .decode_optional(pss_parameter.hash_algo, ASN1_Type(0), ASN1_Class::ExplicitContextSpecific, default_hash) |
38 | 75 | .decode_optional(pss_parameter.mask_gen_algo, ASN1_Type(1), ASN1_Class::ExplicitContextSpecific, default_mgf) |
39 | 75 | .decode_optional(pss_parameter.salt_len, ASN1_Type(2), ASN1_Class::ExplicitContextSpecific, size_t(20)) |
40 | 75 | .decode_optional(pss_parameter.trailer_field, ASN1_Type(3), ASN1_Class::ExplicitContextSpecific, size_t(1)) |
41 | 75 | .end_cons(); |
42 | | |
43 | 75 | BER_Decoder(pss_parameter.mask_gen_algo.get_parameters()).decode(pss_parameter.mask_gen_hash); |
44 | | |
45 | 75 | return pss_parameter; |
46 | 75 | } |
47 | | } |
48 | | |
49 | | /* |
50 | | * Read a PEM or BER X.509 object |
51 | | */ |
52 | | void X509_Object::load_data(DataSource& in) |
53 | 20.2k | { |
54 | 20.2k | try { |
55 | 20.2k | if(ASN1::maybe_BER(in) && !PEM_Code::matches(in)) |
56 | 13.9k | { |
57 | 13.9k | BER_Decoder dec(in); |
58 | 13.9k | decode_from(dec); |
59 | 13.9k | } |
60 | 6.29k | else |
61 | 6.29k | { |
62 | 6.29k | std::string got_label; |
63 | 6.29k | DataSource_Memory ber(PEM_Code::decode(in, got_label)); |
64 | | |
65 | 6.29k | if(got_label != PEM_label()) |
66 | 165 | { |
67 | 165 | bool is_alternate = false; |
68 | 165 | for(std::string alt_label : alternate_PEM_labels()) |
69 | 165 | { |
70 | 165 | if(got_label == alt_label) |
71 | 1 | { |
72 | 1 | is_alternate = true; |
73 | 1 | break; |
74 | 1 | } |
75 | 165 | } |
76 | | |
77 | 165 | if(!is_alternate) |
78 | 164 | throw Decoding_Error("Unexpected PEM label for " + PEM_label() + " of " + got_label); |
79 | 6.13k | } |
80 | | |
81 | 6.13k | BER_Decoder dec(ber); |
82 | 6.13k | decode_from(dec); |
83 | 6.13k | } |
84 | 20.2k | } |
85 | 20.2k | catch(Decoding_Error& e) |
86 | 7.38k | { |
87 | 7.38k | throw Decoding_Error(PEM_label() + " decoding", e); |
88 | 7.38k | } |
89 | 20.2k | } |
90 | | |
91 | | |
92 | | void X509_Object::encode_into(DER_Encoder& to) const |
93 | 11.7k | { |
94 | 11.7k | to.start_sequence() |
95 | 11.7k | .start_sequence() |
96 | 11.7k | .raw_bytes(signed_body()) |
97 | 11.7k | .end_cons() |
98 | 11.7k | .encode(signature_algorithm()) |
99 | 11.7k | .encode(signature(), ASN1_Type::BitString) |
100 | 11.7k | .end_cons(); |
101 | 11.7k | } |
102 | | |
103 | | /* |
104 | | * Read a BER encoded X.509 object |
105 | | */ |
106 | | void X509_Object::decode_from(BER_Decoder& from) |
107 | 19.3k | { |
108 | 19.3k | from.start_sequence() |
109 | 19.3k | .start_sequence() |
110 | 19.3k | .raw_bytes(m_tbs_bits) |
111 | 19.3k | .end_cons() |
112 | 19.3k | .decode(m_sig_algo) |
113 | 19.3k | .decode(m_sig, ASN1_Type::BitString) |
114 | 19.3k | .end_cons(); |
115 | | |
116 | 19.3k | force_decode(); |
117 | 19.3k | } |
118 | | |
119 | | /* |
120 | | * Return a PEM encoded X.509 object |
121 | | */ |
122 | | std::string X509_Object::PEM_encode() const |
123 | 0 | { |
124 | 0 | return PEM_Code::encode(BER_encode(), PEM_label()); |
125 | 0 | } |
126 | | |
127 | | /* |
128 | | * Return the TBS data |
129 | | */ |
130 | | std::vector<uint8_t> X509_Object::tbs_data() const |
131 | 7.41k | { |
132 | 7.41k | return ASN1::put_in_sequence(m_tbs_bits); |
133 | 7.41k | } |
134 | | |
135 | | /* |
136 | | * Return the hash used in generating the signature |
137 | | */ |
138 | | std::string X509_Object::hash_used_for_signature() const |
139 | 0 | { |
140 | 0 | const OID& oid = m_sig_algo.get_oid(); |
141 | 0 | const std::vector<std::string> sig_info = split_on(oid.to_formatted_string(), '/'); |
142 | |
|
143 | 0 | if(sig_info.size() == 1 && sig_info[0] == "Ed25519") |
144 | 0 | return "SHA-512"; |
145 | 0 | else if(sig_info.size() != 2) |
146 | 0 | throw Internal_Error("Invalid name format found for " + oid.to_string()); |
147 | | |
148 | 0 | if(sig_info[1] == "EMSA4") |
149 | 0 | { |
150 | 0 | const OID hash_oid = decode_pss_params(signature_algorithm().get_parameters()).hash_algo.get_oid(); |
151 | 0 | return hash_oid.to_formatted_string(); |
152 | 0 | } |
153 | 0 | else |
154 | 0 | { |
155 | 0 | const std::vector<std::string> pad_and_hash = |
156 | 0 | parse_algorithm_name(sig_info[1]); |
157 | |
|
158 | 0 | if(pad_and_hash.size() != 2) |
159 | 0 | { |
160 | 0 | throw Internal_Error("Invalid name format " + sig_info[1]); |
161 | 0 | } |
162 | | |
163 | 0 | return pad_and_hash[1]; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | /* |
168 | | * Check the signature on an object |
169 | | */ |
170 | | bool X509_Object::check_signature(const Public_Key* pub_key) const |
171 | 0 | { |
172 | 0 | if(!pub_key) |
173 | 0 | throw Invalid_Argument("No key provided for " + PEM_label() + " signature check"); |
174 | 0 | std::unique_ptr<const Public_Key> key(pub_key); |
175 | 0 | return check_signature(*key); |
176 | 0 | } |
177 | | |
178 | | bool X509_Object::check_signature(const Public_Key& pub_key) const |
179 | 0 | { |
180 | 0 | const Certificate_Status_Code code = verify_signature(pub_key); |
181 | 0 | return (code == Certificate_Status_Code::VERIFIED); |
182 | 0 | } |
183 | | |
184 | | Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key) const |
185 | 7.50k | { |
186 | 7.50k | const std::vector<std::string> sig_info = |
187 | 7.50k | split_on(m_sig_algo.get_oid().to_formatted_string(), '/'); |
188 | | |
189 | 7.50k | if(sig_info.size() < 1 || sig_info.size() > 2 || sig_info[0] != pub_key.algo_name()) |
190 | 71 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
191 | | |
192 | 7.43k | const std::string pub_key_algo = sig_info[0]; |
193 | 7.43k | std::string padding; |
194 | 7.43k | if(sig_info.size() == 2) |
195 | 7.33k | padding = sig_info[1]; |
196 | 97 | else if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS") |
197 | 95 | padding = "Pure"; |
198 | 2 | else |
199 | 2 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
200 | | |
201 | 7.43k | const Signature_Format format = pub_key.default_x509_signature_format(); |
202 | | |
203 | 7.43k | if(padding == "EMSA4") |
204 | 75 | { |
205 | | // "MUST contain RSASSA-PSS-params" |
206 | 75 | if(signature_algorithm().get_parameters().empty()) |
207 | 0 | { |
208 | 0 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
209 | 0 | } |
210 | | |
211 | 75 | Pss_params pss_parameter = decode_pss_params(signature_algorithm().get_parameters()); |
212 | | |
213 | | // hash_algo must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512 |
214 | 75 | const std::string hash_algo = pss_parameter.hash_algo.get_oid().to_formatted_string(); |
215 | 75 | if(hash_algo != "SHA-160" && |
216 | 68 | hash_algo != "SHA-224" && |
217 | 68 | hash_algo != "SHA-256" && |
218 | 2 | hash_algo != "SHA-384" && |
219 | 1 | hash_algo != "SHA-512") |
220 | 1 | { |
221 | 1 | return Certificate_Status_Code::UNTRUSTED_HASH; |
222 | 1 | } |
223 | | |
224 | 74 | const std::string mgf_algo = pss_parameter.mask_gen_algo.get_oid().to_formatted_string(); |
225 | 74 | if(mgf_algo != "MGF1") |
226 | 1 | { |
227 | 1 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
228 | 1 | } |
229 | | |
230 | | // For MGF1, it is strongly RECOMMENDED that the underlying hash function be the same as the one identified by hashAlgorithm |
231 | | // Must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512 |
232 | 73 | if(pss_parameter.mask_gen_hash.get_oid() != pss_parameter.hash_algo.get_oid()) |
233 | 1 | { |
234 | 1 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
235 | 1 | } |
236 | | |
237 | 72 | if(pss_parameter.trailer_field != 1) |
238 | 1 | { |
239 | 1 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
240 | 1 | } |
241 | | |
242 | 71 | padding += "(" + hash_algo + "," + mgf_algo + "," + std::to_string(pss_parameter.salt_len) + ")"; |
243 | 71 | } |
244 | 7.35k | else |
245 | 7.35k | { |
246 | | /* |
247 | | * For all other signature types the signature parameters should |
248 | | * be either NULL or empty. In theory there is some distinction between |
249 | | * these but in practice they seem to be used somewhat interchangeably. |
250 | | * |
251 | | * The various RFCs all have prescriptions of what is allowed: |
252 | | * RSA - NULL (RFC 3279) |
253 | | * DSA - empty (RFC 3279) |
254 | | * ECDSA - empty (RFC 3279) |
255 | | * GOST - empty (RFC 4491) |
256 | | * Ed25519 - empty (RFC 8410) |
257 | | * XMSS - empty (draft-vangeest-x509-hash-sigs) |
258 | | * |
259 | | * But in practice we find RSA with empty and ECDSA will NULL all |
260 | | * over the place so it's not really possible to enforce. For Ed25519 |
261 | | * and XMSS because they are new we attempt to enforce. |
262 | | */ |
263 | 7.35k | if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS") |
264 | 95 | { |
265 | 95 | if(!signature_algorithm().parameters_are_empty()) |
266 | 0 | { |
267 | 0 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
268 | 0 | } |
269 | 7.26k | } |
270 | 7.26k | else |
271 | 7.26k | { |
272 | 7.26k | if(!signature_algorithm().parameters_are_null_or_empty()) |
273 | 6 | { |
274 | 6 | return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS; |
275 | 6 | } |
276 | 7.42k | } |
277 | 7.35k | } |
278 | | |
279 | 7.42k | try |
280 | 7.42k | { |
281 | 7.42k | PK_Verifier verifier(pub_key, padding, format); |
282 | 7.42k | const bool valid = verifier.verify_message(tbs_data(), signature()); |
283 | | |
284 | 7.42k | if(valid) |
285 | 5.31k | return Certificate_Status_Code::VERIFIED; |
286 | 2.10k | else |
287 | 2.10k | return Certificate_Status_Code::SIGNATURE_ERROR; |
288 | 0 | } |
289 | 0 | catch(Algorithm_Not_Found&) |
290 | 0 | { |
291 | 0 | return Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN; |
292 | 0 | } |
293 | 7 | catch(...) |
294 | 7 | { |
295 | | // This shouldn't happen, fallback to generic signature error |
296 | 7 | return Certificate_Status_Code::SIGNATURE_ERROR; |
297 | 7 | } |
298 | 7.42k | } |
299 | | |
300 | | /* |
301 | | * Apply the X.509 SIGNED macro |
302 | | */ |
303 | | std::vector<uint8_t> X509_Object::make_signed(PK_Signer* signer, |
304 | | RandomNumberGenerator& rng, |
305 | | const AlgorithmIdentifier& algo, |
306 | | const secure_vector<uint8_t>& tbs_bits) |
307 | 0 | { |
308 | 0 | const std::vector<uint8_t> signature = signer->sign_message(tbs_bits, rng); |
309 | |
|
310 | 0 | std::vector<uint8_t> output; |
311 | 0 | DER_Encoder(output) |
312 | 0 | .start_sequence() |
313 | 0 | .raw_bytes(tbs_bits) |
314 | 0 | .encode(algo) |
315 | 0 | .encode(signature, ASN1_Type::BitString) |
316 | 0 | .end_cons(); |
317 | |
|
318 | 0 | return output; |
319 | 0 | } |
320 | | |
321 | | namespace { |
322 | | |
323 | | std::string choose_sig_algo(AlgorithmIdentifier& sig_algo, |
324 | | const Private_Key& key, |
325 | | const std::string& hash_fn, |
326 | | const std::string& user_specified) |
327 | 0 | { |
328 | 0 | const std::string algo_name = key.algo_name(); |
329 | 0 | std::string padding; |
330 | | |
331 | | // check algo_name and set default |
332 | 0 | if(algo_name == "RSA") |
333 | 0 | { |
334 | | // set to EMSA3 for compatibility reasons, originally it was the only option |
335 | 0 | padding = "EMSA3(" + hash_fn + ")"; |
336 | 0 | } |
337 | 0 | else if(algo_name == "DSA" || |
338 | 0 | algo_name == "ECDSA" || |
339 | 0 | algo_name == "ECGDSA" || |
340 | 0 | algo_name == "ECKCDSA" || |
341 | 0 | algo_name == "GOST-34.10" || |
342 | 0 | algo_name == "GOST-34.10-2012-256" || |
343 | 0 | algo_name == "GOST-34.10-2012-512") |
344 | 0 | { |
345 | 0 | padding = "EMSA1(" + hash_fn + ")"; |
346 | 0 | } |
347 | 0 | else if(algo_name == "Ed25519") |
348 | 0 | { |
349 | 0 | padding = "Pure"; |
350 | 0 | } |
351 | 0 | else if(algo_name == "XMSS") |
352 | 0 | { |
353 | 0 | if(user_specified.empty() == true) |
354 | 0 | { |
355 | 0 | throw Invalid_Argument("XMSS requires padding scheme"); |
356 | 0 | } |
357 | 0 | padding = user_specified; |
358 | 0 | sig_algo = AlgorithmIdentifier(OID::from_string("XMSS"), AlgorithmIdentifier::USE_EMPTY_PARAM); |
359 | 0 | return padding; |
360 | 0 | } |
361 | 0 | else |
362 | 0 | { |
363 | 0 | throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); |
364 | 0 | } |
365 | | |
366 | 0 | if(user_specified.empty() == false) |
367 | 0 | { |
368 | 0 | padding = user_specified; |
369 | 0 | } |
370 | |
|
371 | 0 | if(padding != "Pure") |
372 | 0 | { |
373 | | // try to construct an EMSA object from the padding options or default |
374 | 0 | std::unique_ptr<EMSA> emsa; |
375 | 0 | try |
376 | 0 | { |
377 | 0 | emsa.reset(get_emsa(padding)); |
378 | 0 | } |
379 | | /* |
380 | | * get_emsa will throw if opts contains {"padding",<valid_padding>} but |
381 | | * <valid_padding> does not specify a hash function. |
382 | | * Omitting it is valid since it needs to be identical to hash_fn. |
383 | | * If it still throws, something happened that we cannot repair here, |
384 | | * e.g. the algorithm/padding combination is not supported. |
385 | | */ |
386 | 0 | catch(...) |
387 | 0 | { |
388 | 0 | emsa.reset(get_emsa(padding + "(" + hash_fn + ")")); |
389 | 0 | } |
390 | |
|
391 | 0 | if(!emsa) |
392 | 0 | { |
393 | 0 | throw Invalid_Argument("Could not parse padding scheme " + padding); |
394 | 0 | } |
395 | | |
396 | 0 | sig_algo = emsa->config_for_x509(key, hash_fn); |
397 | 0 | return emsa->name(); |
398 | 0 | } |
399 | 0 | else |
400 | 0 | { |
401 | 0 | sig_algo = AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM); |
402 | 0 | return "Pure"; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | } |
407 | | |
408 | | /* |
409 | | * Choose a signing format for the key |
410 | | */ |
411 | | std::unique_ptr<PK_Signer> X509_Object::choose_sig_format(AlgorithmIdentifier& sig_algo, |
412 | | const Private_Key& key, |
413 | | RandomNumberGenerator& rng, |
414 | | const std::string& hash_fn, |
415 | | const std::string& padding_algo) |
416 | 0 | { |
417 | 0 | const Signature_Format format = key.default_x509_signature_format(); |
418 | |
|
419 | 0 | const std::string emsa = choose_sig_algo(sig_algo, key, hash_fn, padding_algo); |
420 | |
|
421 | 0 | return std::unique_ptr<PK_Signer>(new PK_Signer(key, rng, emsa, format)); |
422 | 0 | } |
423 | | |
424 | | } |