OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/cert/ct_objects_extractor.h" | |
6 | |
7 #include <string.h> | |
8 | |
9 #include <openssl/bytestring.h> | |
10 #include <openssl/obj.h> | |
11 #include <openssl/x509.h> | |
12 | |
13 #include "base/logging.h" | |
14 #include "base/sha1.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "crypto/scoped_openssl_types.h" | |
17 #include "crypto/sha2.h" | |
18 #include "net/cert/asn1_util.h" | |
19 #include "net/cert/signed_certificate_timestamp.h" | |
20 | |
21 namespace net { | |
22 | |
23 namespace ct { | |
24 | |
25 namespace { | |
26 | |
27 typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509; | |
28 | |
29 void FreeX509_EXTENSIONS(X509_EXTENSIONS* ptr) { | |
30 sk_X509_EXTENSION_pop_free(ptr, X509_EXTENSION_free); | |
31 } | |
32 | |
33 typedef crypto::ScopedOpenSSL<X509_EXTENSIONS, FreeX509_EXTENSIONS>::Type | |
34 ScopedX509_EXTENSIONS; | |
35 | |
36 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of | |
37 // RFC6962. | |
38 const uint8_t kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, | |
39 0x02, 0x04, 0x02}; | |
40 | |
41 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for | |
42 // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see | |
43 // Section 3.3 of RFC6962. | |
44 const uint8_t kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, | |
45 0x02, 0x04, 0x05}; | |
46 | |
47 bool StringEqualToCBS(const std::string& value1, const CBS* value2) { | |
48 if (CBS_len(value2) != value1.size()) | |
49 return false; | |
50 return memcmp(value1.data(), CBS_data(value2), CBS_len(value2)) == 0; | |
51 } | |
52 | |
53 ScopedX509 OSCertHandleToOpenSSL(X509Certificate::OSCertHandle os_handle) { | |
54 #if defined(USE_OPENSSL_CERTS) | |
55 return ScopedX509(X509Certificate::DupOSCertHandle(os_handle)); | |
56 #else | |
57 std::string der_encoded; | |
58 if (!X509Certificate::GetDEREncoded(os_handle, &der_encoded)) | |
59 return ScopedX509(); | |
60 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data()); | |
61 return ScopedX509(d2i_X509(NULL, &bytes, der_encoded.size())); | |
62 #endif | |
63 } | |
64 | |
65 // Finds the SignedCertificateTimestampList in an extension with OID |oid| in | |
66 // |x509_exts|. If found, returns true and sets |*out_sct_list| to the encoded | |
67 // SCT list. |out_sct_list| may be NULL. | |
68 bool GetSCTListFromX509_EXTENSIONS(const X509_EXTENSIONS* x509_exts, | |
69 const uint8_t* oid, | |
70 size_t oid_len, | |
71 std::string* out_sct_list) { | |
72 for (size_t i = 0; i < sk_X509_EXTENSION_num(x509_exts); i++) { | |
73 X509_EXTENSION* x509_ext = sk_X509_EXTENSION_value(x509_exts, i); | |
74 if (static_cast<size_t>(x509_ext->object->length) == oid_len && | |
75 memcmp(x509_ext->object->data, oid, oid_len) == 0) { | |
76 // The SCT list is an OCTET STRING inside the extension. | |
77 CBS ext_value, sct_list; | |
78 CBS_init(&ext_value, x509_ext->value->data, x509_ext->value->length); | |
79 if (!CBS_get_asn1(&ext_value, &sct_list, CBS_ASN1_OCTETSTRING) || | |
80 CBS_len(&ext_value) != 0) { | |
81 return false; | |
82 } | |
83 if (out_sct_list) { | |
84 *out_sct_list = std::string( | |
85 reinterpret_cast<const char*>(CBS_data(&sct_list)), | |
86 CBS_len(&sct_list)); | |
87 } | |
88 return true; | |
89 } | |
90 } | |
91 return false; | |
92 } | |
93 | |
94 // Finds the SingleResponse in |responses| which matches |issuer| and | |
95 // |cert_serial_number|. On success, returns true and sets | |
96 // |*out_single_response| to the body of the SingleResponse starting at the | |
97 // |certStatus| field. | |
98 bool FindMatchingSingleResponse(CBS* responses, | |
99 X509Certificate::OSCertHandle issuer, | |
100 const std::string& cert_serial_number, | |
101 CBS* out_single_response) { | |
102 std::string issuer_der; | |
103 if (!X509Certificate::GetDEREncoded(issuer, &issuer_der)) | |
104 return false; | |
105 | |
106 base::StringPiece issuer_spki; | |
107 if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_spki)) | |
108 return false; | |
109 | |
110 // In OCSP, only the key itself is under hash. | |
111 base::StringPiece issuer_spk; | |
112 if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk)) | |
113 return false; | |
114 | |
115 // ExtractSubjectPublicKey... does not remove the initial octet encoding the | |
116 // number of unused bits in the ASN.1 BIT STRING so we do it here. For public | |
117 // keys, the bitstring is in practice always byte-aligned. | |
118 if (issuer_spk.empty() || issuer_spk[0] != 0) | |
119 return false; | |
120 issuer_spk.remove_prefix(1); | |
121 | |
122 // TODO(ekasper): add SHA-384 to crypto/sha2.h and here if it proves | |
123 // necessary. | |
124 // TODO(ekasper): only compute the hashes on demand. | |
125 std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk); | |
126 std::string issuer_key_sha1_hash = base::SHA1HashString( | |
127 issuer_spk.as_string()); | |
128 | |
129 while (CBS_len(responses) > 0) { | |
130 CBS single_response, cert_id; | |
131 if (!CBS_get_asn1(responses, &single_response, CBS_ASN1_SEQUENCE) || | |
132 !CBS_get_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE)) { | |
133 return false; | |
134 } | |
135 | |
136 CBS hash_algorithm, hash, serial_number, issuer_name_hash, issuer_key_hash; | |
137 if (!CBS_get_asn1(&cert_id, &hash_algorithm, CBS_ASN1_SEQUENCE) || | |
138 !CBS_get_asn1(&hash_algorithm, &hash, CBS_ASN1_OBJECT) || | |
139 !CBS_get_asn1(&cert_id, &issuer_name_hash, CBS_ASN1_OCTETSTRING) || | |
140 !CBS_get_asn1(&cert_id, &issuer_key_hash, CBS_ASN1_OCTETSTRING) || | |
141 !CBS_get_asn1(&cert_id, &serial_number, CBS_ASN1_INTEGER) || | |
142 CBS_len(&cert_id) != 0) { | |
143 return false; | |
144 } | |
145 | |
146 // Check the serial number matches. | |
147 if (!StringEqualToCBS(cert_serial_number, &serial_number)) | |
148 continue; | |
149 | |
150 // Check if the issuer_key_hash matches. | |
151 // TODO(ekasper): also use the issuer name hash in matching. | |
152 switch (OBJ_cbs2nid(&hash)) { | |
153 case NID_sha1: | |
154 if (StringEqualToCBS(issuer_key_sha1_hash, &issuer_key_hash)) { | |
155 *out_single_response = single_response; | |
156 return true; | |
157 } | |
158 break; | |
159 case NID_sha256: | |
160 if (StringEqualToCBS(issuer_key_sha256_hash, &issuer_key_hash)) { | |
161 *out_single_response = single_response; | |
162 return true; | |
163 } | |
164 break; | |
165 } | |
166 } | |
167 | |
168 return false; | |
169 } | |
170 | |
171 } // namespace | |
172 | |
173 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert, | |
174 std::string* sct_list) { | |
175 ScopedX509 x509(OSCertHandleToOpenSSL(cert)); | |
176 if (!x509) | |
177 return false; | |
178 X509_EXTENSIONS* x509_exts = x509->cert_info->extensions; | |
179 if (!x509_exts) | |
180 return false; | |
181 return GetSCTListFromX509_EXTENSIONS( | |
182 x509->cert_info->extensions, kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), | |
183 sct_list); | |
184 } | |
185 | |
186 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, | |
187 X509Certificate::OSCertHandle issuer, | |
188 LogEntry* result) { | |
189 result->Reset(); | |
190 | |
191 ScopedX509 leaf_x509(OSCertHandleToOpenSSL(leaf)); | |
192 if (!leaf_x509) | |
193 return false; | |
194 | |
195 // XXX(rsleevi): This check may be overkill, since we should be able to | |
196 // generate precerts for certs without the extension. For now, just a sanity | |
197 // check to match the reference implementation. | |
198 if (!leaf_x509->cert_info->extensions || | |
199 !GetSCTListFromX509_EXTENSIONS(leaf_x509->cert_info->extensions, | |
200 kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), | |
201 NULL)) { | |
202 return false; | |
203 } | |
204 | |
205 // The Precertificate log entry is the final certificate's TBSCertificate | |
206 // without the SCT extension (RFC6962, section 3.2). | |
207 ScopedX509 leaf_copy(X509_dup(leaf_x509.get())); | |
208 if (!leaf_copy || !leaf_copy->cert_info->extensions) { | |
209 NOTREACHED(); | |
210 return false; | |
211 } | |
212 X509_EXTENSIONS* leaf_copy_exts = leaf_copy->cert_info->extensions; | |
213 for (size_t i = 0; i < sk_X509_EXTENSION_num(leaf_copy_exts); i++) { | |
214 X509_EXTENSION* ext = sk_X509_EXTENSION_value(leaf_copy_exts, i); | |
215 if (static_cast<size_t>(ext->object->length) == sizeof(kEmbeddedSCTOid) && | |
216 memcmp(ext->object->data, | |
217 kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid)) == 0) { | |
218 X509_EXTENSION_free(sk_X509_EXTENSION_delete(leaf_copy_exts, i)); | |
219 X509_CINF_set_modified(leaf_copy->cert_info); | |
220 break; | |
221 } | |
222 } | |
223 | |
224 std::string to_be_signed; | |
225 int len = i2d_X509_CINF(leaf_copy->cert_info, NULL); | |
226 if (len < 0) | |
227 return false; | |
228 uint8_t* ptr = reinterpret_cast<uint8_t*>(WriteInto(&to_be_signed, len + 1)); | |
229 if (i2d_X509_CINF(leaf_copy->cert_info, &ptr) < 0) | |
230 return false; | |
231 | |
232 // Extract the issuer's public key. | |
233 std::string issuer_der; | |
234 if (!X509Certificate::GetDEREncoded(issuer, &issuer_der)) | |
235 return ScopedX509(); | |
236 base::StringPiece issuer_key; | |
237 if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_key)) | |
238 return false; | |
239 | |
240 // Fill in the LogEntry. | |
241 result->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT; | |
242 result->tbs_certificate.swap(to_be_signed); | |
243 crypto::SHA256HashString(issuer_key, | |
244 result->issuer_key_hash.data, | |
245 sizeof(result->issuer_key_hash.data)); | |
246 | |
247 return true; | |
248 } | |
249 | |
250 bool GetX509LogEntry(X509Certificate::OSCertHandle leaf, LogEntry* result) { | |
251 DCHECK(leaf); | |
252 | |
253 std::string encoded; | |
254 if (!X509Certificate::GetDEREncoded(leaf, &encoded)) | |
255 return false; | |
256 | |
257 result->Reset(); | |
258 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; | |
259 result->leaf_certificate.swap(encoded); | |
260 return true; | |
261 } | |
262 | |
263 bool ExtractSCTListFromOCSPResponse(X509Certificate::OSCertHandle issuer, | |
264 const std::string& cert_serial_number, | |
265 const std::string& ocsp_response, | |
266 std::string* sct_list) { | |
267 // The input is an OCSPResponse. See RFC2560, section 4.2.1. The SCT list is | |
268 // in the extensions field of the SingleResponse which matches the input | |
269 // certificate. | |
270 CBS cbs; | |
271 CBS_init(&cbs, | |
272 reinterpret_cast<const uint8_t*>(ocsp_response.data()), | |
273 ocsp_response.size()); | |
274 | |
275 // Parse down to the ResponseBytes. The ResponseBytes is optional, but if it's | |
276 // missing, this can't include an SCT list. | |
277 CBS sequence, response_status, tagged_response_bytes, response_bytes; | |
278 CBS response_type, response; | |
279 if (!CBS_get_asn1(&cbs, &sequence, CBS_ASN1_SEQUENCE) || | |
280 CBS_len(&cbs) != 0 || | |
281 !CBS_get_asn1(&sequence, &response_status, CBS_ASN1_ENUMERATED) || | |
282 !CBS_get_asn1(&sequence, &tagged_response_bytes, | |
283 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || | |
284 CBS_len(&sequence) != 0 || | |
285 !CBS_get_asn1(&tagged_response_bytes, &response_bytes, | |
286 CBS_ASN1_SEQUENCE) || | |
287 CBS_len(&tagged_response_bytes) != 0 || | |
288 !CBS_get_asn1(&response_bytes, &response_type, CBS_ASN1_OBJECT) || | |
289 !CBS_get_asn1(&response_bytes, &response, CBS_ASN1_OCTETSTRING) || | |
290 CBS_len(&response_bytes) != 0) { | |
291 return false; | |
292 } | |
293 | |
294 // The only relevant ResponseType is id-pkix-ocsp-basic. | |
295 if (OBJ_cbs2nid(&response_type) != NID_id_pkix_OCSP_basic) | |
296 return false; | |
297 | |
298 // Parse the ResponseData out of the BasicOCSPResponse. Ignore the rest. | |
299 CBS basic_response, response_data, responses; | |
300 if (!CBS_get_asn1(&response, &basic_response, CBS_ASN1_SEQUENCE) || | |
301 CBS_len(&response) != 0 || | |
302 !CBS_get_asn1(&basic_response, &response_data, CBS_ASN1_SEQUENCE)) { | |
303 } | |
304 | |
305 // Skip the optional version. | |
306 const int kVersionTag = | |
307 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0; | |
308 if (CBS_len(&response_data) > 0 && | |
309 CBS_data(&response_data)[0] == kVersionTag && | |
310 !CBS_get_asn1(&response_data, NULL /* version */, kVersionTag)) { | |
311 return false; | |
312 } | |
313 | |
314 // Extract the list of SingleResponses. | |
315 if (!CBS_get_any_asn1_element(&response_data, | |
316 NULL /* responderID */, NULL, NULL) || | |
317 !CBS_get_any_asn1_element(&response_data, | |
318 NULL /* producedAt */, NULL, NULL) || | |
319 !CBS_get_asn1(&response_data, &responses, CBS_ASN1_SEQUENCE)) { | |
320 return false; | |
321 } | |
322 | |
323 CBS single_response; | |
324 if (!FindMatchingSingleResponse(&responses, issuer, cert_serial_number, | |
325 &single_response)) { | |
326 return false; | |
327 } | |
328 | |
329 // Skip the certStatus and thisUpdate fields. | |
330 if (!CBS_get_any_asn1_element(&single_response, | |
331 NULL /* certStatus */, NULL, NULL) || | |
332 !CBS_get_any_asn1_element(&single_response, | |
333 NULL /* thisUpdate */, NULL, NULL)) { | |
334 return false; | |
335 } | |
336 | |
337 const int kNextUpdateTag = | |
338 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0; | |
339 const int kSingleExtensionsTag = | |
340 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1; | |
341 | |
342 // Skip the optional nextUpdate field. | |
343 if (CBS_len(&single_response) > 0 && | |
344 CBS_data(&single_response)[0] == kNextUpdateTag && | |
345 !CBS_get_asn1(&single_response, NULL /* nextUpdate */, kNextUpdateTag)) { | |
346 return false; | |
347 } | |
348 | |
349 CBS extensions; | |
350 if (!CBS_get_asn1(&single_response, &extensions, kSingleExtensionsTag)) | |
351 return false; | |
352 const uint8_t* ptr = CBS_data(&extensions); | |
353 ScopedX509_EXTENSIONS x509_exts( | |
354 d2i_X509_EXTENSIONS(NULL, &ptr, CBS_len(&extensions))); | |
355 if (!x509_exts || ptr != CBS_data(&extensions) + CBS_len(&extensions)) | |
356 return false; | |
357 | |
358 return GetSCTListFromX509_EXTENSIONS( | |
359 x509_exts.get(), kOCSPExtensionOid, sizeof(kOCSPExtensionOid), sct_list); | |
360 } | |
361 | |
362 } // namespace ct | |
363 | |
364 } // namespace net | |
OLD | NEW |