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

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: review comments 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
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 <secitem.h> 9 #include <secitem.h>
10 #include <secoid.h> 10 #include <secoid.h>
11 11
12 #include "base/lazy_instance.h" 12 #include "base/lazy_instance.h"
13 #include "base/sha1.h"
13 #include "crypto/scoped_nss_types.h" 14 #include "crypto/scoped_nss_types.h"
14 #include "crypto/sha2.h" 15 #include "crypto/sha2.h"
15 #include "net/cert/asn1_util.h" 16 #include "net/cert/asn1_util.h"
16 #include "net/cert/scoped_nss_types.h" 17 #include "net/cert/scoped_nss_types.h"
17 #include "net/cert/signed_certificate_timestamp.h" 18 #include "net/cert/signed_certificate_timestamp.h"
18 19
19 namespace net { 20 namespace net {
20 21
21 namespace ct { 22 namespace ct {
22 23
23 namespace { 24 namespace {
24 25
26 // NSS black magic to get the address of externally defined template at runtime.
27 SEC_ASN1_MKSUB(SEC_IntegerTemplate)
28 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
29 SEC_ASN1_MKSUB(SEC_GeneralizedTimeTemplate)
30 SEC_ASN1_MKSUB(CERT_SequenceOfCertExtensionTemplate)
31
25 // Wrapper class to convert a X509Certificate::OSCertHandle directly 32 // Wrapper class to convert a X509Certificate::OSCertHandle directly
26 // into a CERTCertificate* usable with other NSS functions. This is used for 33 // into a CERTCertificate* usable with other NSS functions. This is used for
27 // platforms where X509Certificate::OSCertHandle refers to a different type 34 // platforms where X509Certificate::OSCertHandle refers to a different type
28 // than a CERTCertificate*. 35 // than a CERTCertificate*.
29 struct NSSCertWrapper { 36 struct NSSCertWrapper {
30 explicit NSSCertWrapper(X509Certificate::OSCertHandle cert_handle); 37 explicit NSSCertWrapper(X509Certificate::OSCertHandle cert_handle);
31 ~NSSCertWrapper() {} 38 ~NSSCertWrapper() {}
32 39
33 ScopedCERTCertificate cert; 40 ScopedCERTCertificate cert;
34 }; 41 };
(...skipping 20 matching lines...) Expand all
55 } 62 }
56 63
57 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of 64 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of
58 // RFC6962. 65 // RFC6962.
59 const unsigned char kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 66 const unsigned char kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
60 0xD6, 0x79, 0x02, 0x04, 0x02}; 67 0xD6, 0x79, 0x02, 0x04, 0x02};
61 const char kEmbeddedSCTDescription[] = 68 const char kEmbeddedSCTDescription[] =
62 "X.509v3 Certificate Transparency Embedded Signed Certificate Timestamp " 69 "X.509v3 Certificate Transparency Embedded Signed Certificate Timestamp "
63 "List"; 70 "List";
64 71
72 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for
73 // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see
74 // see Section 3.3 of RFC6962.
wtc 2013/12/10 04:23:17 Nit: the "see" on this line is a duplicate and sho
ekasper 2013/12/10 14:45:20 Done.
75 const unsigned char kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
76 0xD6, 0x79, 0x02, 0x04, 0x05};
77
78 const SECItem kOCSPExtensionItem = {
wtc 2013/12/10 04:23:17 Nit: this variable name should also contain "Oid":
ekasper 2013/12/10 14:45:20 Done.
79 siBuffer, const_cast<unsigned char*>(kOCSPExtensionOid),
80 sizeof(kOCSPExtensionOid)
81 };
82
65 // Initializes the necessary NSS internals for use with Certificate 83 // Initializes the necessary NSS internals for use with Certificate
66 // Transparency. 84 // Transparency.
67 class CTInitSingleton { 85 class CTInitSingleton {
68 public: 86 public:
69 SECOidTag embedded_oid() const { return embedded_oid_; } 87 SECOidTag embedded_oid() const { return embedded_oid_; }
70 88
71 private: 89 private:
72 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>; 90 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>;
73 91
74 CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN) { 92 CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN) {
(...skipping 26 matching lines...) Expand all
101 119
102 SECOidTag embedded_oid_; 120 SECOidTag embedded_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 in
112 // updating |ext_data| to be the extension data after removing the DER 130 // the certificate, updating |ext_data| to be the extension data after removing
113 // encoding of OCTET STRING. 131 // the DER encoding of OCTET STRING.
114 bool GetOctetStringExtension(CERTCertificate* cert, 132 bool GetCertOctetStringExtension(CERTCertificate* cert,
115 SECOidTag oid, 133 SECOidTag oid,
116 std::string* extension_data) { 134 std::string* extension_data) {
117 SECItem extension; 135 SECItem extension;
118 SECStatus rv = CERT_FindCertExtension(cert, oid, &extension); 136 SECStatus rv = CERT_FindCertExtension(cert, oid, &extension);
119 if (rv != SECSuccess) 137 if (rv != SECSuccess)
120 return false; 138 return false;
121 139
122 base::StringPiece raw_data(reinterpret_cast<char*>(extension.data), 140 base::StringPiece raw_data(reinterpret_cast<char*>(extension.data),
123 extension.len); 141 extension.len);
124 base::StringPiece parsed_data; 142 base::StringPiece parsed_data;
125 if (!asn1::GetElement(&raw_data, asn1::kOCTETSTRING, &parsed_data) || 143 if (!asn1::GetElement(&raw_data, asn1::kOCTETSTRING, &parsed_data) ||
126 raw_data.size() > 0) { // Decoding failure or raw data left 144 raw_data.size() > 0) { // Decoding failure or raw data left
127 rv = SECFailure; 145 rv = SECFailure;
128 } else { 146 } else {
129 parsed_data.CopyToString(extension_data); 147 parsed_data.CopyToString(extension_data);
130 } 148 }
131 149
132 SECITEM_FreeItem(&extension, PR_FALSE); 150 SECITEM_FreeItem(&extension, PR_FALSE);
133 return rv == SECSuccess; 151 return rv == SECSuccess;
134 } 152 }
135 153
154 // NSS offers CERT_FindCertExtension for certificates, but we also need to
155 // to extract extensions from OCSP responses, so we inspect the extensions
156 // manually.
157 //
158 // Obtains the data for an X.509v3 certificate extension identified by
wtc 2013/12/10 04:23:17 Nit: it seems wrong to call this "an X.509v3 certi
ekasper 2013/12/10 14:45:20 OCSP gets its extension definition from X509, whic
159 // kOCSPExtensionItem and encoded as an OCTET STRING. Returns true if the
160 // extension was found in |extensions|, updating |extension_data| to be the
161 // extension data after removing the DER encoding of OCTET STRING.
162 bool GetSCTListFromOCSPExtension(PLArenaPool* arena,
163 const CERTCertExtension* const* extensions,
164 std::string* extension_data) {
165 if (!extensions)
166 return false;
167
168 const CERTCertExtension* match = NULL;
169
170 for (const CERTCertExtension* const* exts = extensions; *exts; ++exts) {
171 const CERTCertExtension* ext = *exts;
172 if (SECITEM_ItemsAreEqual(&kOCSPExtensionItem, &ext->id)) {
173 match = ext;
174 break;
175 }
176 }
177
178 if (!match)
179 return false;
180
181 SECItem contents;
182 // QuickDER will point to |match|, so we don't have to free |contents|.
183 SECStatus rv = SEC_QuickDERDecodeItem(arena, &contents,
184 SEC_ASN1_GET(SEC_OctetStringTemplate),
185 &match->value);
186 if (rv != SECSuccess)
187 return false;
188
189 base::StringPiece parsed_data(reinterpret_cast<char*>(contents.data),
190 contents.len);
191 parsed_data.CopyToString(extension_data);
192 return true;
193 }
194
136 // Given a |cert|, extract the TBSCertificate from this certificate, also 195 // Given a |cert|, extract the TBSCertificate from this certificate, also
137 // removing the X.509 extension with OID 1.3.6.1.4.1.11129.2.4.2 (that is, 196 // removing the X.509 extension with OID 1.3.6.1.4.1.11129.2.4.2 (that is,
138 // the embedded SCT) 197 // the embedded SCT)
139 bool ExtractTBSCertWithoutSCTs(CERTCertificate* cert, 198 bool ExtractTBSCertWithoutSCTs(CERTCertificate* cert,
140 std::string* to_be_signed) { 199 std::string* to_be_signed) {
141 SECOidData* oid = SECOID_FindOIDByTag(g_ct_singleton.Get().embedded_oid()); 200 SECOidData* oid = SECOID_FindOIDByTag(g_ct_singleton.Get().embedded_oid());
142 if (!oid) 201 if (!oid)
143 return false; 202 return false;
144 203
145 // This is a giant hack, due to the fact that NSS does not expose a good API 204 // This is a giant hack, due to the fact that NSS does not expose a good API
(...skipping 27 matching lines...) Expand all
173 &tbs_data, 232 &tbs_data,
174 &temp_cert, 233 &temp_cert,
175 SEC_ASN1_GET(CERT_CertificateTemplate)); 234 SEC_ASN1_GET(CERT_CertificateTemplate));
176 if (!result) 235 if (!result)
177 return false; 236 return false;
178 237
179 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len); 238 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len);
180 return true; 239 return true;
181 } 240 }
182 241
242 // The following code is adapted from the NSS OCSP module, in order to expose
243 // the internal structure of an OCSP response.
244
245 // ResponseBytes ::= SEQUENCE {
246 // responseType OBJECT IDENTIFIER,
247 // response OCTET STRING }
248 struct ResponseBytes {
249 SECItem response_type;
250 SECItem der_response;
251 };
252
253 const SEC_ASN1Template kResponseBytesTemplate[] = {
254 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseBytes) },
255 { SEC_ASN1_OBJECT_ID, offsetof(ResponseBytes, response_type) },
256 { SEC_ASN1_OCTET_STRING, offsetof(ResponseBytes, der_response) },
257 { 0 }
258 };
259
260 // OCSPResponse ::= SEQUENCE {
261 // responseStatus OCSPResponseStatus,
262 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
263 struct OCSPResponse {
264 SECItem response_status;
265 // This indirection is needed because |response_bytes| is an optional
266 // component and we need a way to determine if it is missing.
267 ResponseBytes* response_bytes;
268 };
269
270 const SEC_ASN1Template kPointerToResponseBytesTemplate[] = {
271 { SEC_ASN1_POINTER, 0, kResponseBytesTemplate }
272 };
273
274 const SEC_ASN1Template kOCSPResponseTemplate[] = {
275 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OCSPResponse) },
276 { SEC_ASN1_ENUMERATED, offsetof(OCSPResponse, response_status) },
277 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
278 SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(OCSPResponse, response_bytes),
279 kPointerToResponseBytesTemplate },
280 { 0 }
281 };
282
283 // CertID ::= SEQUENCE {
284 // hashAlgorithm AlgorithmIdentifier,
285 // issuerNameHash OCTET STRING, -- Hash of Issuer's DN
286 // issuerKeyHash OCTET STRING, -- Hash of Issuers public key
287 // serialNumber CertificateSerialNumber }
288 struct CertID {
289 SECAlgorithmID hash_algorithm;
290 SECItem issuer_name_hash;
291 SECItem issuer_key_hash;
292 SECItem serial_number;
293 };
294
295 const SEC_ASN1Template kCertIDTemplate[] = {
296 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CertID) },
297 { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CertID, hash_algorithm),
298 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
299 { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_name_hash) },
300 { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_key_hash) },
301 { SEC_ASN1_INTEGER, offsetof(CertID, serial_number) },
302 { 0 }
303 };
304
305 // SingleResponse ::= SEQUENCE {
306 // certID CertID,
307 // certStatus CertStatus,
308 // thisUpdate GeneralizedTime,
309 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
310 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
311 struct SingleResponse {
312 CertID cert_id;
313 SECItem der_cert_status;
314 SECItem this_update;
315 SECItem next_update;
wtc 2013/12/10 04:23:17 Should this member be a pointer because it is opti
ekasper 2013/12/10 14:45:20 It should be a pointer if we used this field, but
wtc 2013/12/12 02:31:45 Yes, I remember this now. I am impressed by how mu
ekasper 2013/12/13 13:09:51 Done.
316 CERTCertExtension** single_extensions;
317 };
318
319 const SEC_ASN1Template kSingleResponseTemplate[] = {
320 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SingleResponse) },
321 { SEC_ASN1_INLINE, offsetof(SingleResponse, cert_id), kCertIDTemplate },
322 // ANY because (a) NSS ASN.1 doesn't support automatic CHOICE and
wtc 2013/12/10 04:23:17 Not sure what you meant by "automatic CHOICE". NSS
ekasper 2013/12/10 14:45:20 I assumed the comment in ocsp.c was accurate: * X
wtc 2013/12/12 02:31:45 I see. I found that this comment and the other XXX
323 // (b) we don't care about the contents of this field anyway.
324 { SEC_ASN1_ANY, offsetof(SingleResponse, der_cert_status) },
325 { SEC_ASN1_GENERALIZED_TIME, offsetof(SingleResponse, this_update) },
326 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
327 SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
328 offsetof(SingleResponse, next_update),
329 SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) },
330 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
331 SEC_ASN1_CONTEXT_SPECIFIC | 1,
332 offsetof(SingleResponse, single_extensions),
333 SEC_ASN1_SUB(CERT_SequenceOfCertExtensionTemplate) },
334 { 0 }
335 };
336
337 // ResponseData ::= SEQUENCE {
338 // version [0] EXPLICIT Version DEFAULT v1,
339 // responderID ResponderID,
340 // producedAt GeneralizedTime,
341 // responses SEQUENCE OF SingleResponse,
342 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
343 struct ResponseData {
344 SECItem version;
345 SECItem der_responder_id;
346 SECItem produced_at;
347 SingleResponse** single_responses;
348 // Skip extensions.
349 };
350
351 const SEC_ASN1Template kResponseDataTemplate[] = {
352 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseData) },
353 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
354 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
355 offsetof(ResponseData, version), SEC_ASN1_SUB(SEC_IntegerTemplate) },
356 // ANY because (a) NSS ASN.1 doesn't support automatic CHOICE and
357 // (b) we don't care about the contents of this field anyway.
358 { SEC_ASN1_ANY, offsetof(ResponseData, der_responder_id) },
359 { SEC_ASN1_GENERALIZED_TIME, offsetof(ResponseData, produced_at) },
360 { SEC_ASN1_SEQUENCE_OF, offsetof(ResponseData, single_responses),
361 kSingleResponseTemplate },
362 { SEC_ASN1_SKIP_REST },
363 { 0 }
364 };
365
366 // BasicOCSPResponse ::= SEQUENCE {
367 // tbsResponseData ResponseData,
368 // signatureAlgorithm AlgorithmIdentifier,
369 // signature BIT STRING,
370 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
371 struct BasicOCSPResponse {
372 ResponseData tbs_response_data;
373 // We do not care about the rest.
374 };
375
376 const SEC_ASN1Template kBasicOCSPResponseTemplate[] = {
377 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(BasicOCSPResponse) },
378 { SEC_ASN1_INLINE, offsetof(BasicOCSPResponse, tbs_response_data),
379 kResponseDataTemplate },
380 { SEC_ASN1_SKIP_REST },
381 { 0 }
382 };
383
384 bool StringEqualToSECItem(const std::string& value1, const SECItem& value2) {
385 if (value1.size() != value2.len)
386 return false;
387 return memcmp(value1.data(), value2.data, value2.len) == 0;
388 }
389
390 bool CertIDsMatch(const std::string& serial_number,
391 const std::string& issuer_key_sha1_hash,
392 const std::string& issuer_key_sha256_hash,
393 const CertID& cert_id) {
wtc 2013/12/10 04:23:17 1. The NSS version of this function (ocsp_CertIDsM
ekasper 2013/12/10 14:45:20 1. Added a TODO 2. Not sure why this preference,
wtc 2013/12/12 02:31:45 When I first read the CertIDsMatch, I was confused
ekasper 2013/12/13 13:09:51 Renamed to CertIDMatches.
394 if (!StringEqualToSECItem(serial_number, cert_id.serial_number))
395 return false;
396
397 SECOidTag hash_alg = SECOID_FindOIDTag(&cert_id.hash_algorithm.algorithm);
398 switch (hash_alg) {
399 case SEC_OID_SHA1:
400 return StringEqualToSECItem(issuer_key_sha1_hash,
401 cert_id.issuer_key_hash);
402 case SEC_OID_SHA256:
403 return StringEqualToSECItem(issuer_key_sha256_hash,
404 cert_id.issuer_key_hash);
405 default:
406 return false;
407 }
408 }
409
183 } // namespace 410 } // namespace
184 411
185 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert, 412 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert,
186 std::string* sct_list) { 413 std::string* sct_list) {
187 DCHECK(cert); 414 DCHECK(cert);
188 415
189 NSSCertWrapper leaf_cert(cert); 416 NSSCertWrapper leaf_cert(cert);
190 if (!leaf_cert.cert) 417 if (!leaf_cert.cert)
191 return false; 418 return false;
192 419
193 return GetOctetStringExtension( 420 return GetCertOctetStringExtension(leaf_cert.cert.get(),
194 leaf_cert.cert.get(), g_ct_singleton.Get().embedded_oid(), sct_list); 421 g_ct_singleton.Get().embedded_oid(),
422 sct_list);
195 } 423 }
196 424
197 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, 425 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf,
198 X509Certificate::OSCertHandle issuer, 426 X509Certificate::OSCertHandle issuer,
199 LogEntry* result) { 427 LogEntry* result) {
200 DCHECK(leaf); 428 DCHECK(leaf);
201 DCHECK(issuer); 429 DCHECK(issuer);
202 430
203 NSSCertWrapper leaf_cert(leaf); 431 NSSCertWrapper leaf_cert(leaf);
204 NSSCertWrapper issuer_cert(issuer); 432 NSSCertWrapper issuer_cert(issuer);
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 std::string encoded; 490 std::string encoded;
263 if (!X509Certificate::GetDEREncoded(leaf, &encoded)) 491 if (!X509Certificate::GetDEREncoded(leaf, &encoded))
264 return false; 492 return false;
265 493
266 result->Reset(); 494 result->Reset();
267 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; 495 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509;
268 result->leaf_certificate.swap(encoded); 496 result->leaf_certificate.swap(encoded);
269 return true; 497 return true;
270 } 498 }
271 499
500 bool ExtractSCTListFromOCSPResponse(X509Certificate::OSCertHandle issuer,
501 const std::string& cert_serial_number,
502 const std::string& ocsp_response,
503 std::string* sct_list) {
504 DCHECK(issuer);
505 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
506
507 // QuickDERDecodeItem returns data that points into |src|. This is
508 // ok for us as the decoded items never leave the method scope.
509 SECItem src = { siBuffer,
510 reinterpret_cast<unsigned char*>(const_cast<char*>(
511 ocsp_response.data())), ocsp_response.size() };
512
513 OCSPResponse response;
514 memset(&response, 0, sizeof(response));
515
516 SECStatus rv = SEC_QuickDERDecodeItem(arena.get(), &response,
517 kOCSPResponseTemplate, &src);
518 if (rv != SECSuccess)
519 return false;
520
521 // id-ad-ocsp: 1.3.6.1.5.5.7.48.1.1
522 static const uint8 kBasicOCSPResponseOID[] = { 0x2b, 0x06, 0x01, 0x05, 0x05,
523 0x07, 0x30, 0x01, 0x01 };
524
525 if (!response.response_bytes)
526 return false;
527
528 if (response.response_bytes->response_type.len !=
529 sizeof(kBasicOCSPResponseOID) ||
530 memcmp(response.response_bytes->response_type.data, kBasicOCSPResponseOID,
531 sizeof(kBasicOCSPResponseOID)) != 0) {
532 return false;
533 }
534
535 BasicOCSPResponse basic_response;
536 memset(&basic_response, 0, sizeof(basic_response));
537
538 rv = SEC_QuickDERDecodeItem(arena.get(), &basic_response,
539 kBasicOCSPResponseTemplate,
540 &response.response_bytes->der_response);
541 if (rv != SECSuccess)
542 return false;
543
544 SingleResponse** responses =
545 basic_response.tbs_response_data.single_responses;
546 if (!responses)
547 return false;
548
549 std::string issuer_der;
550 if (!X509Certificate::GetDEREncoded(issuer, &issuer_der))
551 return false;
552
553 base::StringPiece issuer_spki;
554 if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_spki))
555 return false;
556
557 // In OCSP, only the key itself is under hash.
558 base::StringPiece issuer_spk;
559 if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk))
560 return false;
561
562 // ExtractSubjectPublicKey... does not remove the leading pad byte from the
563 // BitString so we do it here. For public keys, the pad byte is in practice
564 // always 0.
wtc 2013/12/10 04:23:17 Do you think this is a bug in asn1::ExtractSubject
ekasper 2013/12/10 14:45:20 Clarified the meaning of the leading byte. I thin
wtc 2013/12/12 02:31:45 I have to admit that when I checked the OCSP RFC 6
ekasper 2013/12/13 13:09:51 Yep, the RFC is ambiguous there.
565 if (issuer_spk.empty() || issuer_spk[0] != 0)
566 return false;
567 issuer_spk.remove_prefix(1);
568
569 // NSS OCSP lib recognizes SHA1, MD5 and MD2; MD5 and MD2 are dead but
570 // https://bugzilla.mozilla.org/show_bug.cgi?id=663315 will add SHA-256
571 // and SHA-384.
572 // TODO(ekasper): add SHA-384 to crypto/sha2.h and here if it proves
573 // necessary.
574 // TODO(ekasper): only compute the hashes on demand.
575 std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk);
576 std::string issuer_key_sha1_hash = base::SHA1HashString(
577 issuer_spk.as_string());
578
579 SingleResponse* match = NULL;
580 for (int i = 0; responses[i] != NULL; ++i) {
581 if (CertIDsMatch(cert_serial_number, issuer_key_sha1_hash,
582 issuer_key_sha256_hash, responses[i]->cert_id)) {
583 match = responses[i];
584 break;
585 }
586 }
587
588 if (!match)
589 return false;
590
591 return GetSCTListFromOCSPExtension(arena.get(), match->single_extensions,
592 sct_list);
593 }
594
272 } // namespace ct 595 } // namespace ct
273 596
274 } // namespace net 597 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698