Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: net/cert/ct_objects_extractor_nss.cc

Issue 92443002: Extract Certificate Transparency SCTs from stapled OCSP responses (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@extract_scts
Patch Set: format Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/cert/ct_objects_extractor.h ('k') | net/cert/ct_objects_extractor_openssl.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/ct_objects_extractor.h" 5 #include "net/cert/ct_objects_extractor.h"
6 6
7 #include <cert.h> 7 #include <cert.h>
8 #include <secasn1.h> 8 #include <secasn1.h>
9 #include <secasn1t.h>
10 #include <secdert.h>
9 #include <secitem.h> 11 #include <secitem.h>
10 #include <secoid.h> 12 #include <secoid.h>
11 13
12 #include "base/lazy_instance.h" 14 #include "base/lazy_instance.h"
15 #include "base/sha1.h"
13 #include "crypto/scoped_nss_types.h" 16 #include "crypto/scoped_nss_types.h"
14 #include "crypto/sha2.h" 17 #include "crypto/sha2.h"
15 #include "net/cert/asn1_util.h" 18 #include "net/cert/asn1_util.h"
16 #include "net/cert/scoped_nss_types.h" 19 #include "net/cert/scoped_nss_types.h"
17 #include "net/cert/signed_certificate_timestamp.h" 20 #include "net/cert/signed_certificate_timestamp.h"
18 21
19 namespace net { 22 namespace net {
20 23
21 namespace ct { 24 namespace ct {
22 25
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 } 58 }
56 59
57 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of 60 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of
58 // RFC6962. 61 // RFC6962.
59 const unsigned char kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 62 const unsigned char kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
60 0xD6, 0x79, 0x02, 0x04, 0x02}; 63 0xD6, 0x79, 0x02, 0x04, 0x02};
61 const char kEmbeddedSCTDescription[] = 64 const char kEmbeddedSCTDescription[] =
62 "X.509v3 Certificate Transparency Embedded Signed Certificate Timestamp " 65 "X.509v3 Certificate Transparency Embedded Signed Certificate Timestamp "
63 "List"; 66 "List";
64 67
68 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of
69 // RFC6962.
70 const unsigned char kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6,
71 0x79, 0x02, 0x04, 0x05};
72
73 const char kOCSPExtensionDescription[] =
74 "OCSP SingleExtension for X.509v3 Certificate Transparency Signed "
75 "Certificate Timestamp List ";
76
65 // Initializes the necessary NSS internals for use with Certificate 77 // Initializes the necessary NSS internals for use with Certificate
66 // Transparency. 78 // Transparency.
67 class CTInitSingleton { 79 class CTInitSingleton {
68 public: 80 public:
69 SECOidTag embedded_oid() const { return embedded_oid_; } 81 SECOidTag embedded_oid() const { return embedded_oid_; }
82 SECOidTag ocsp_extension_oid() const { return ocsp_extension_oid_; }
70 83
71 private: 84 private:
72 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>; 85 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>;
73 86
74 CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN) { 87 CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN),
88 ocsp_extension_oid_(SEC_OID_UNKNOWN) {
75 embedded_oid_ = RegisterOid( 89 embedded_oid_ = RegisterOid(
76 kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), kEmbeddedSCTDescription); 90 kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), kEmbeddedSCTDescription);
91 ocsp_extension_oid_ = RegisterOid(
92 kOCSPExtensionOid, sizeof(kOCSPExtensionOid),
93 kOCSPExtensionDescription);
77 } 94 }
78 95
79 ~CTInitSingleton() {} 96 ~CTInitSingleton() {}
80 97
81 SECOidTag RegisterOid(const unsigned char* oid, 98 SECOidTag RegisterOid(const unsigned char* oid,
82 unsigned int oid_len, 99 unsigned int oid_len,
83 const char* description) { 100 const char* description) {
84 SECOidData oid_data; 101 SECOidData oid_data;
85 oid_data.oid.len = oid_len; 102 oid_data.oid.len = oid_len;
86 oid_data.oid.data = const_cast<unsigned char*>(oid); 103 oid_data.oid.data = const_cast<unsigned char*>(oid);
87 oid_data.offset = SEC_OID_UNKNOWN; 104 oid_data.offset = SEC_OID_UNKNOWN;
88 oid_data.desc = description; 105 oid_data.desc = description;
89 oid_data.mechanism = CKM_INVALID_MECHANISM; 106 oid_data.mechanism = CKM_INVALID_MECHANISM;
90 // Setting this to SUPPORTED_CERT_EXTENSION ensures that if a certificate 107 // Setting this to SUPPORTED_CERT_EXTENSION ensures that if a certificate
91 // contains this extension with the critical bit set, NSS will not reject 108 // contains this extension with the critical bit set, NSS will not reject
92 // it. However, because verification of this extension happens after NSS, 109 // it. However, because verification of this extension happens after NSS,
93 // it is currently left as INVALID_CERT_EXTENSION. 110 // it is currently left as INVALID_CERT_EXTENSION.
94 oid_data.supportedExtension = INVALID_CERT_EXTENSION; 111 oid_data.supportedExtension = INVALID_CERT_EXTENSION;
95 112
96 SECOidTag result = SECOID_AddEntry(&oid_data); 113 SECOidTag result = SECOID_AddEntry(&oid_data);
97 CHECK_NE(SEC_OID_UNKNOWN, result); 114 CHECK_NE(SEC_OID_UNKNOWN, result);
98 115
99 return result; 116 return result;
100 } 117 }
101 118
102 SECOidTag embedded_oid_; 119 SECOidTag embedded_oid_;
120 SECOidTag ocsp_extension_oid_;
103 121
104 DISALLOW_COPY_AND_ASSIGN(CTInitSingleton); 122 DISALLOW_COPY_AND_ASSIGN(CTInitSingleton);
105 }; 123 };
106 124
107 base::LazyInstance<CTInitSingleton>::Leaky g_ct_singleton = 125 base::LazyInstance<CTInitSingleton>::Leaky g_ct_singleton =
108 LAZY_INSTANCE_INITIALIZER; 126 LAZY_INSTANCE_INITIALIZER;
109 127
110 // Obtains the data for an X.509v3 certificate extension identified by |oid| 128 // Obtains the data for an X.509v3 certificate extension identified by |oid|
111 // and encoded as an OCTET STRING. Returns true if the extension was found, 129 // and encoded as an OCTET STRING. Returns true if the extension was found,
112 // updating |ext_data| to be the extension data after removing the DER 130 // updating |ext_data| to be the extension data after removing the DER
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 &tbs_data, 191 &tbs_data,
174 &temp_cert, 192 &temp_cert,
175 SEC_ASN1_GET(CERT_CertificateTemplate)); 193 SEC_ASN1_GET(CERT_CertificateTemplate));
176 if (!result) 194 if (!result)
177 return false; 195 return false;
178 196
179 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len); 197 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len);
180 return true; 198 return true;
181 } 199 }
182 200
201 // The following code is adapted from the NSS OCSP module, in order to expose
202 // the internal structure of an OCSP response.
203
204 // Black magic to get the address of the externally defined template at runtime.
205 SEC_ASN1_MKSUB(SEC_IntegerTemplate)
206 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
207 SEC_ASN1_MKSUB(SEC_GeneralizedTimeTemplate)
208
209 // ResponseBytes ::= SEQUENCE {
210 // responseType OBJECT IDENTIFIER,
211 // response OCTET STRING }
212 struct ResponseBytes {
Ryan Sleevi 2013/12/04 20:30:26 nit: OcspResponseBytes? OCSPResponseBytes?
ekasper 2013/12/05 16:26:48 It's ResponseBytes in the RFC so I'd keep it; ther
213 SECItem response_type;
214 SECItem der_response;
215 };
216
217 const SEC_ASN1Template kResponseBytesTemplate[] = {
218 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseBytes) },
219 { SEC_ASN1_OBJECT_ID, offsetof(ResponseBytes, response_type) },
220 { SEC_ASN1_OCTET_STRING, offsetof(ResponseBytes, der_response) },
221 { 0 }
222 };
223
224 // OCSPResponse ::= SEQUENCE {
225 // responseStatus OCSPResponseStatus,
226 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
227 struct OCSPResponse {
228 SECItem response_status;
229 // This indirection is needed because |response_bytes| is an optional
230 // component and we need a way to determine if it is missing.
231 ResponseBytes *response_bytes;
232 };
233
234 const SEC_ASN1Template kPointerToResponseBytesTemplate[] = {
235 { SEC_ASN1_POINTER, 0, kResponseBytesTemplate }
236 };
237
238 const SEC_ASN1Template kOCSPResponseTemplate[] = {
239 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OCSPResponse) },
240 { SEC_ASN1_ENUMERATED, offsetof(OCSPResponse, response_status) },
241 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
242 SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(OCSPResponse, response_bytes),
243 kPointerToResponseBytesTemplate },
244 { 0 }
245 };
246
247 // CertID ::= SEQUENCE {
248 // hashAlgorithm AlgorithmIdentifier,
249 // issuerNameHash OCTET STRING, -- Hash of Issuer's DN
250 // issuerKeyHash OCTET STRING, -- Hash of Issuers public key
251 // serialNumber CertificateSerialNumber }
252 struct CertID {
253 SECAlgorithmID hash_algorithm;
254 SECItem issuer_name_hash;
255 SECItem issuer_key_hash;
256 SECItem serial_number;
257 };
258
259 const SEC_ASN1Template kCertIDTemplate[] = {
260 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CertID) },
261 { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CertID, hash_algorithm),
262 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
263 { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_name_hash) },
264 { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_key_hash) },
265 { SEC_ASN1_INTEGER, offsetof(CertID, serial_number) },
266 { 0 }
267 };
268
269 // SingleResponse ::= SEQUENCE {
270 // certID CertID,
271 // certStatus CertStatus,
272 // thisUpdate GeneralizedTime,
273 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
274 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
275 struct SingleResponse {
276 CertID cert_id;
277 SECItem der_cert_status;
278 SECItem this_update;
279 SECItem next_update;
280 CERTCertExtension **single_extensions;
281 };
282
283 const SEC_ASN1Template kSingleResponseTemplate[] = {
284 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SingleResponse) },
285 { SEC_ASN1_INLINE, offsetof(SingleResponse, cert_id), kCertIDTemplate },
286 // ANY because (a) NSS ASN.1 doesn't support automatic CHOICE and
287 // (b) we don't care about the contents of this field anyway.
288 { SEC_ASN1_ANY, offsetof(SingleResponse, der_cert_status) },
289 { SEC_ASN1_GENERALIZED_TIME, offsetof(SingleResponse, this_update) },
290 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
291 SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
292 offsetof(SingleResponse, next_update),
293 SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) },
294 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
295 SEC_ASN1_CONTEXT_SPECIFIC | 1,
296 offsetof(SingleResponse, single_extensions),
297 CERT_SequenceOfCertExtensionTemplate },
298 { 0 }
299 };
300
301 // ResponseData ::= SEQUENCE {
302 // version [0] EXPLICIT Version DEFAULT v1,
303 // responderID ResponderID,
304 // producedAt GeneralizedTime,
305 // responses SEQUENCE OF SingleResponse,
306 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
307 struct ResponseData {
308 SECItem version;
309 SECItem der_responder_id;
310 SECItem produced_at;
311 SingleResponse **single_responses;
312 // Skip extensions.
313 };
314
315 const SEC_ASN1Template kResponseDataTemplate[] = {
316 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseData) },
317 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
318 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
319 offsetof(ResponseData, version), SEC_ASN1_SUB(SEC_IntegerTemplate) },
320 // ANY because (a) NSS ASN.1 doesn't support automatic CHOICE and
321 // (b) we don't care about the contents of this field anyway.
322 { SEC_ASN1_ANY, offsetof(ResponseData, der_responder_id) },
323 { SEC_ASN1_GENERALIZED_TIME, offsetof(ResponseData, produced_at) },
324 { SEC_ASN1_SEQUENCE_OF, offsetof(ResponseData, single_responses),
325 kSingleResponseTemplate },
326 { SEC_ASN1_SKIP_REST },
327 { 0 }
328 };
329
330 // BasicOCSPResponse ::= SEQUENCE {
331 // tbsResponseData ResponseData,
332 // signatureAlgorithm AlgorithmIdentifier,
333 // signature BIT STRING,
334 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
335 struct BasicOCSPResponse {
336 ResponseData tbs_response_data;
337 // We do not care about the rest.
338 };
339
340 const SEC_ASN1Template kBasicOCSPResponseTemplate[] = {
341 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(BasicOCSPResponse) },
342 { SEC_ASN1_INLINE, offsetof(BasicOCSPResponse, tbs_response_data),
343 kResponseDataTemplate },
344 { SEC_ASN1_SKIP_REST },
345 { 0 }
346 };
347
348 bool CertIDsMatch(const std::string& serial_number,
349 const std::string& issuer_key_sha1_hash,
350 const CertID& cert_id) {
351 if (serial_number.size() != cert_id.serial_number.len ||
352 memcmp(serial_number.data(), cert_id.serial_number.data,
353 serial_number.size()) != 0) {
354 return false;
355 }
356
357 SECOidTag hash_alg = SECOID_FindOIDTag(&cert_id.hash_algorithm.algorithm);
358 if (hash_alg != SEC_OID_SHA1)
359 return false;
360
361 if (issuer_key_sha1_hash.size() != cert_id.issuer_key_hash.len ||
362 memcmp(issuer_key_sha1_hash.data(), cert_id.issuer_key_hash.data,
363 issuer_key_sha1_hash.size()) != 0) {
364 return false;
365 }
366
367 return true;
368 }
369
183 } // namespace 370 } // namespace
184 371
185 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert, 372 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert,
186 std::string* sct_list) { 373 std::string* sct_list) {
187 DCHECK(cert); 374 DCHECK(cert);
188 375
189 NSSCertWrapper leaf_cert(cert); 376 NSSCertWrapper leaf_cert(cert);
190 if (!leaf_cert.cert) 377 if (!leaf_cert.cert)
191 return false; 378 return false;
192 379
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 std::string encoded; 449 std::string encoded;
263 if (!X509Certificate::GetDEREncoded(leaf, &encoded)) 450 if (!X509Certificate::GetDEREncoded(leaf, &encoded))
264 return false; 451 return false;
265 452
266 result->Reset(); 453 result->Reset();
267 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; 454 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509;
268 result->leaf_certificate.swap(encoded); 455 result->leaf_certificate.swap(encoded);
269 return true; 456 return true;
270 } 457 }
271 458
459 bool ExtractSCTListFromOCSPResponse(const std::string& cert_serial_number,
460 X509Certificate::OSCertHandle issuer,
461 const std::string& ocsp_response,
462 std::string* sct_list) {
463 DCHECK(issuer);
464 sct_list->clear();
Ryan Sleevi 2013/12/04 20:30:26 Just as a minor nit, our normal pattern in net for
ekasper 2013/12/05 16:26:48 Removed the clear() and GetOctetStringExtension do
465 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
466
467 // QuickDERDecodeItem returns data that points into |src|. This is
468 // ok for us as the decoded items never leave the method scope.
469 SECItem src = { siBuffer,
470 reinterpret_cast<unsigned char*>(const_cast<char*>(
471 ocsp_response.data())), ocsp_response.size() };
472
473 OCSPResponse response;
474 memset(&response, 0, sizeof(response));
475
476 SECStatus rv = SEC_QuickDERDecodeItem(arena.get(), &response,
477 kOCSPResponseTemplate, &src);
478 if (rv != SECSuccess)
479 return false;
480
481 // id-ad-ocsp: 1.3.6.1.5.5.7.48.1.1
482 static const uint8 kBasicOCSPResponseOID[] = { 0x2b, 0x06, 0x01, 0x05, 0x05,
483 0x07, 0x30, 0x01, 0x01 };
484
485 if (!response.response_bytes)
486 return false;
487
488 if (response.response_bytes->response_type.len !=
489 sizeof(kBasicOCSPResponseOID) ||
490 memcmp(response.response_bytes->response_type.data, kBasicOCSPResponseOID,
491 sizeof(kBasicOCSPResponseOID)) != 0) {
492 return false;
493 }
494
495 BasicOCSPResponse basic_response;
496 memset(&basic_response, 0, sizeof(basic_response));
497
498 rv = SEC_QuickDERDecodeItem(arena.get(), &basic_response,
499 kBasicOCSPResponseTemplate,
500 &response.response_bytes->der_response);
501 if (rv != SECSuccess)
502 return false;
503
504 SingleResponse** responses =
505 basic_response.tbs_response_data.single_responses;
506 if (responses == NULL)
507 return false;
508
509 NSSCertWrapper issuer_cert(issuer);
510 if (!issuer_cert.cert)
511 return false;
Ryan Sleevi 2013/12/04 20:30:26 If |issuer| has been verified as trusted already (
ekasper 2013/12/05 16:26:48 Right, I forgot about GetDEREncoded. Done, even th
512
513 // Cert_GetSPKIDigest exists but is not exported so we do it by hand.
514 // In OCSP, only the key itself is under hash.
515 SECItem spk = issuer_cert.cert->subjectPublicKeyInfo.subjectPublicKey;
516 DER_ConvertBitString(&spk);
517
518 // NSS OCSP lib recognizes SHA1, MD5 and MD2 here, so seems we can restrict
519 // ourselves to SHA1.
520 std::string issuer_key_sha1_hash = base::SHA1HashString(
521 std::string(reinterpret_cast<char*>(spk.data), spk.len));
Ryan Sleevi 2013/12/04 20:30:26 This may/will change soon ( https://bugzilla.mozil
ekasper 2013/12/05 16:26:48 Aha. Added SHA-256. I hope this code will be comin
522
523 SingleResponse* match = NULL;
524 for (int i = 0; responses[i] != NULL; ++i) {
525 if (CertIDsMatch(cert_serial_number, issuer_key_sha1_hash,
526 responses[i]->cert_id)) {
527 match = responses[i];
528 break;
529 }
530 }
531
532 if (match == NULL)
Ryan Sleevi 2013/12/04 20:30:26 dominant style in net/ and base is if (!match)
ekasper 2013/12/05 16:26:48 Done.
533 return false;
534
535 // Hack: pretend we are a certificate to find the appropriate extension.
536 CERTCertificate wrap_cert;
537 memset(&wrap_cert, 0, sizeof(wrap_cert));
538 // Shouldn't be needed, but just in case...
539 wrap_cert.arena = arena.get();
540 wrap_cert.extensions = match->single_extensions;
Ryan Sleevi 2013/12/04 20:30:26 I'm really nervous about this, even though it will
ekasper 2013/12/05 16:26:48 I have to admit that, at that point yesterday, I w
541
542 return GetOctetStringExtension(&wrap_cert,
543 g_ct_singleton.Get().ocsp_extension_oid(),
544 sct_list);
545 }
546
272 } // namespace ct 547 } // namespace ct
273 548
274 } // namespace net 549 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/ct_objects_extractor.h ('k') | net/cert/ct_objects_extractor_openssl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698