OLD | NEW |
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 Loading... |
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 // Section 3.3 of RFC6962. |
| 75 const unsigned char kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, |
| 76 0xD6, 0x79, 0x02, 0x04, 0x05}; |
| 77 |
| 78 const SECItem kOCSPExtensionOidItem = { |
| 79 siBuffer, const_cast<unsigned char*>(kOCSPExtensionOid), |
| 80 sizeof(kOCSPExtensionOid) |
| 81 }; |
| 82 |
| 83 // id-ad-ocsp: 1.3.6.1.5.5.7.48.1.1 |
| 84 const unsigned char kBasicOCSPResponseOid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, |
| 85 0x07, 0x30, 0x01, 0x01}; |
| 86 |
| 87 const SECItem kBasicOCSPResponseOidItem = { |
| 88 siBuffer, const_cast<unsigned char*>(kBasicOCSPResponseOid), |
| 89 sizeof(kBasicOCSPResponseOid) |
| 90 }; |
| 91 |
| 92 |
65 // Initializes the necessary NSS internals for use with Certificate | 93 // Initializes the necessary NSS internals for use with Certificate |
66 // Transparency. | 94 // Transparency. |
67 class CTInitSingleton { | 95 class CTInitSingleton { |
68 public: | 96 public: |
69 SECOidTag embedded_oid() const { return embedded_oid_; } | 97 SECOidTag embedded_oid() const { return embedded_oid_; } |
70 | 98 |
71 private: | 99 private: |
72 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>; | 100 friend struct base::DefaultLazyInstanceTraits<CTInitSingleton>; |
73 | 101 |
74 CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN) { | 102 CTInitSingleton() : embedded_oid_(SEC_OID_UNKNOWN) { |
(...skipping 26 matching lines...) Expand all Loading... |
101 | 129 |
102 SECOidTag embedded_oid_; | 130 SECOidTag embedded_oid_; |
103 | 131 |
104 DISALLOW_COPY_AND_ASSIGN(CTInitSingleton); | 132 DISALLOW_COPY_AND_ASSIGN(CTInitSingleton); |
105 }; | 133 }; |
106 | 134 |
107 base::LazyInstance<CTInitSingleton>::Leaky g_ct_singleton = | 135 base::LazyInstance<CTInitSingleton>::Leaky g_ct_singleton = |
108 LAZY_INSTANCE_INITIALIZER; | 136 LAZY_INSTANCE_INITIALIZER; |
109 | 137 |
110 // Obtains the data for an X.509v3 certificate extension identified by |oid| | 138 // 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, | 139 // 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 | 140 // the certificate, updating |ext_data| to be the extension data after removing |
113 // encoding of OCTET STRING. | 141 // the DER encoding of OCTET STRING. |
114 bool GetOctetStringExtension(CERTCertificate* cert, | 142 bool GetCertOctetStringExtension(CERTCertificate* cert, |
115 SECOidTag oid, | 143 SECOidTag oid, |
116 std::string* extension_data) { | 144 std::string* extension_data) { |
117 SECItem extension; | 145 SECItem extension; |
118 SECStatus rv = CERT_FindCertExtension(cert, oid, &extension); | 146 SECStatus rv = CERT_FindCertExtension(cert, oid, &extension); |
119 if (rv != SECSuccess) | 147 if (rv != SECSuccess) |
120 return false; | 148 return false; |
121 | 149 |
122 base::StringPiece raw_data(reinterpret_cast<char*>(extension.data), | 150 base::StringPiece raw_data(reinterpret_cast<char*>(extension.data), |
123 extension.len); | 151 extension.len); |
124 base::StringPiece parsed_data; | 152 base::StringPiece parsed_data; |
125 if (!asn1::GetElement(&raw_data, asn1::kOCTETSTRING, &parsed_data) || | 153 if (!asn1::GetElement(&raw_data, asn1::kOCTETSTRING, &parsed_data) || |
126 raw_data.size() > 0) { // Decoding failure or raw data left | 154 raw_data.size() > 0) { // Decoding failure or raw data left |
127 rv = SECFailure; | 155 rv = SECFailure; |
128 } else { | 156 } else { |
129 parsed_data.CopyToString(extension_data); | 157 parsed_data.CopyToString(extension_data); |
130 } | 158 } |
131 | 159 |
132 SECITEM_FreeItem(&extension, PR_FALSE); | 160 SECITEM_FreeItem(&extension, PR_FALSE); |
133 return rv == SECSuccess; | 161 return rv == SECSuccess; |
134 } | 162 } |
135 | 163 |
| 164 // NSS offers CERT_FindCertExtension for certificates, but that only accepts |
| 165 // CERTCertificate* inputs, so the method below extracts the SCT extension |
| 166 // directly from the CERTCertExtension** of an OCSP response. |
| 167 // |
| 168 // Obtains the data for an OCSP extension identified by kOCSPExtensionOidItem |
| 169 // and encoded as an OCTET STRING. Returns true if the extension was found in |
| 170 // |extensions|, updating |extension_data| to be the extension data after |
| 171 // removing the DER encoding of OCTET STRING. |
| 172 bool GetSCTListFromOCSPExtension(PLArenaPool* arena, |
| 173 const CERTCertExtension* const* extensions, |
| 174 std::string* extension_data) { |
| 175 if (!extensions) |
| 176 return false; |
| 177 |
| 178 const CERTCertExtension* match = NULL; |
| 179 |
| 180 for (const CERTCertExtension* const* exts = extensions; *exts; ++exts) { |
| 181 const CERTCertExtension* ext = *exts; |
| 182 if (SECITEM_ItemsAreEqual(&kOCSPExtensionOidItem, &ext->id)) { |
| 183 match = ext; |
| 184 break; |
| 185 } |
| 186 } |
| 187 |
| 188 if (!match) |
| 189 return false; |
| 190 |
| 191 SECItem contents; |
| 192 // SEC_QuickDERDecodeItem sets |contents| to point to |match|, so it is not |
| 193 // necessary to free the contents of |contents|. |
| 194 SECStatus rv = SEC_QuickDERDecodeItem(arena, &contents, |
| 195 SEC_ASN1_GET(SEC_OctetStringTemplate), |
| 196 &match->value); |
| 197 if (rv != SECSuccess) |
| 198 return false; |
| 199 |
| 200 base::StringPiece parsed_data(reinterpret_cast<char*>(contents.data), |
| 201 contents.len); |
| 202 parsed_data.CopyToString(extension_data); |
| 203 return true; |
| 204 } |
| 205 |
136 // Given a |cert|, extract the TBSCertificate from this certificate, also | 206 // 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, | 207 // removing the X.509 extension with OID 1.3.6.1.4.1.11129.2.4.2 (that is, |
138 // the embedded SCT) | 208 // the embedded SCT) |
139 bool ExtractTBSCertWithoutSCTs(CERTCertificate* cert, | 209 bool ExtractTBSCertWithoutSCTs(CERTCertificate* cert, |
140 std::string* to_be_signed) { | 210 std::string* to_be_signed) { |
141 SECOidData* oid = SECOID_FindOIDByTag(g_ct_singleton.Get().embedded_oid()); | 211 SECOidData* oid = SECOID_FindOIDByTag(g_ct_singleton.Get().embedded_oid()); |
142 if (!oid) | 212 if (!oid) |
143 return false; | 213 return false; |
144 | 214 |
145 // This is a giant hack, due to the fact that NSS does not expose a good API | 215 // This is a giant hack, due to the fact that NSS does not expose a good API |
(...skipping 27 matching lines...) Expand all Loading... |
173 &tbs_data, | 243 &tbs_data, |
174 &temp_cert, | 244 &temp_cert, |
175 SEC_ASN1_GET(CERT_CertificateTemplate)); | 245 SEC_ASN1_GET(CERT_CertificateTemplate)); |
176 if (!result) | 246 if (!result) |
177 return false; | 247 return false; |
178 | 248 |
179 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len); | 249 to_be_signed->assign(reinterpret_cast<char*>(tbs_data.data), tbs_data.len); |
180 return true; | 250 return true; |
181 } | 251 } |
182 | 252 |
| 253 // The following code is adapted from the NSS OCSP module, in order to expose |
| 254 // the internal structure of an OCSP response. |
| 255 |
| 256 // ResponseBytes ::= SEQUENCE { |
| 257 // responseType OBJECT IDENTIFIER, |
| 258 // response OCTET STRING } |
| 259 struct ResponseBytes { |
| 260 SECItem response_type; |
| 261 SECItem der_response; |
| 262 }; |
| 263 |
| 264 const SEC_ASN1Template kResponseBytesTemplate[] = { |
| 265 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseBytes) }, |
| 266 { SEC_ASN1_OBJECT_ID, offsetof(ResponseBytes, response_type) }, |
| 267 { SEC_ASN1_OCTET_STRING, offsetof(ResponseBytes, der_response) }, |
| 268 { 0 } |
| 269 }; |
| 270 |
| 271 // OCSPResponse ::= SEQUENCE { |
| 272 // responseStatus OCSPResponseStatus, |
| 273 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } |
| 274 struct OCSPResponse { |
| 275 SECItem response_status; |
| 276 // This indirection is needed because |response_bytes| is an optional |
| 277 // component and we need a way to determine if it is missing. |
| 278 ResponseBytes* response_bytes; |
| 279 }; |
| 280 |
| 281 const SEC_ASN1Template kPointerToResponseBytesTemplate[] = { |
| 282 { SEC_ASN1_POINTER, 0, kResponseBytesTemplate } |
| 283 }; |
| 284 |
| 285 const SEC_ASN1Template kOCSPResponseTemplate[] = { |
| 286 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OCSPResponse) }, |
| 287 { SEC_ASN1_ENUMERATED, offsetof(OCSPResponse, response_status) }, |
| 288 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | |
| 289 SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(OCSPResponse, response_bytes), |
| 290 kPointerToResponseBytesTemplate }, |
| 291 { 0 } |
| 292 }; |
| 293 |
| 294 // CertID ::= SEQUENCE { |
| 295 // hashAlgorithm AlgorithmIdentifier, |
| 296 // issuerNameHash OCTET STRING, -- Hash of Issuer's DN |
| 297 // issuerKeyHash OCTET STRING, -- Hash of Issuers public key |
| 298 // serialNumber CertificateSerialNumber } |
| 299 struct CertID { |
| 300 SECAlgorithmID hash_algorithm; |
| 301 SECItem issuer_name_hash; |
| 302 SECItem issuer_key_hash; |
| 303 SECItem serial_number; |
| 304 }; |
| 305 |
| 306 const SEC_ASN1Template kCertIDTemplate[] = { |
| 307 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CertID) }, |
| 308 { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CertID, hash_algorithm), |
| 309 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
| 310 { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_name_hash) }, |
| 311 { SEC_ASN1_OCTET_STRING, offsetof(CertID, issuer_key_hash) }, |
| 312 { SEC_ASN1_INTEGER, offsetof(CertID, serial_number) }, |
| 313 { 0 } |
| 314 }; |
| 315 |
| 316 // SingleResponse ::= SEQUENCE { |
| 317 // certID CertID, |
| 318 // certStatus CertStatus, |
| 319 // thisUpdate GeneralizedTime, |
| 320 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, |
| 321 // singleExtensions [1] EXPLICIT Extensions OPTIONAL } |
| 322 struct SingleResponse { |
| 323 CertID cert_id; |
| 324 // The following three fields are not used. |
| 325 SECItem der_cert_status; |
| 326 SECItem this_update; |
| 327 SECItem next_update; |
| 328 CERTCertExtension** single_extensions; |
| 329 }; |
| 330 |
| 331 const SEC_ASN1Template kSingleResponseTemplate[] = { |
| 332 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SingleResponse) }, |
| 333 { SEC_ASN1_INLINE, offsetof(SingleResponse, cert_id), kCertIDTemplate }, |
| 334 // Really a CHOICE but we make it an ANY because we don't care about the |
| 335 // contents of this field. |
| 336 // TODO(ekasper): use SEC_ASN1_CHOICE. |
| 337 { SEC_ASN1_ANY, offsetof(SingleResponse, der_cert_status) }, |
| 338 { SEC_ASN1_GENERALIZED_TIME, offsetof(SingleResponse, this_update) }, |
| 339 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
| 340 SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
| 341 offsetof(SingleResponse, next_update), |
| 342 SEC_ASN1_SUB(SEC_GeneralizedTimeTemplate) }, |
| 343 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | |
| 344 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, |
| 345 offsetof(SingleResponse, single_extensions), |
| 346 SEC_ASN1_SUB(CERT_SequenceOfCertExtensionTemplate) }, |
| 347 { 0 } |
| 348 }; |
| 349 |
| 350 // ResponseData ::= SEQUENCE { |
| 351 // version [0] EXPLICIT Version DEFAULT v1, |
| 352 // responderID ResponderID, |
| 353 // producedAt GeneralizedTime, |
| 354 // responses SEQUENCE OF SingleResponse, |
| 355 // responseExtensions [1] EXPLICIT Extensions OPTIONAL } |
| 356 struct ResponseData { |
| 357 // The first three fields are not used. |
| 358 SECItem version; |
| 359 SECItem der_responder_id; |
| 360 SECItem produced_at; |
| 361 SingleResponse** single_responses; |
| 362 // Skip extensions. |
| 363 }; |
| 364 |
| 365 const SEC_ASN1Template kResponseDataTemplate[] = { |
| 366 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ResponseData) }, |
| 367 { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | |
| 368 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
| 369 offsetof(ResponseData, version), SEC_ASN1_SUB(SEC_IntegerTemplate) }, |
| 370 // Really a CHOICE but we make it an ANY because we don't care about the |
| 371 // contents of this field. |
| 372 // TODO(ekasper): use SEC_ASN1_CHOICE. |
| 373 { SEC_ASN1_ANY, offsetof(ResponseData, der_responder_id) }, |
| 374 { SEC_ASN1_GENERALIZED_TIME, offsetof(ResponseData, produced_at) }, |
| 375 { SEC_ASN1_SEQUENCE_OF, offsetof(ResponseData, single_responses), |
| 376 kSingleResponseTemplate }, |
| 377 { SEC_ASN1_SKIP_REST }, |
| 378 { 0 } |
| 379 }; |
| 380 |
| 381 // BasicOCSPResponse ::= SEQUENCE { |
| 382 // tbsResponseData ResponseData, |
| 383 // signatureAlgorithm AlgorithmIdentifier, |
| 384 // signature BIT STRING, |
| 385 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } |
| 386 struct BasicOCSPResponse { |
| 387 ResponseData tbs_response_data; |
| 388 // We do not care about the rest. |
| 389 }; |
| 390 |
| 391 const SEC_ASN1Template kBasicOCSPResponseTemplate[] = { |
| 392 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(BasicOCSPResponse) }, |
| 393 { SEC_ASN1_INLINE, offsetof(BasicOCSPResponse, tbs_response_data), |
| 394 kResponseDataTemplate }, |
| 395 { SEC_ASN1_SKIP_REST }, |
| 396 { 0 } |
| 397 }; |
| 398 |
| 399 bool StringEqualToSECItem(const std::string& value1, const SECItem& value2) { |
| 400 if (value1.size() != value2.len) |
| 401 return false; |
| 402 return memcmp(value1.data(), value2.data, value2.len) == 0; |
| 403 } |
| 404 |
| 405 // TODO(ekasper): also use the issuer name hash in matching. |
| 406 bool CertIDMatches(const CertID& cert_id, |
| 407 const std::string& serial_number, |
| 408 const std::string& issuer_key_sha1_hash, |
| 409 const std::string& issuer_key_sha256_hash) { |
| 410 if (!StringEqualToSECItem(serial_number, cert_id.serial_number)) |
| 411 return false; |
| 412 |
| 413 SECOidTag hash_alg = SECOID_FindOIDTag(&cert_id.hash_algorithm.algorithm); |
| 414 switch (hash_alg) { |
| 415 case SEC_OID_SHA1: |
| 416 return StringEqualToSECItem(issuer_key_sha1_hash, |
| 417 cert_id.issuer_key_hash); |
| 418 case SEC_OID_SHA256: |
| 419 return StringEqualToSECItem(issuer_key_sha256_hash, |
| 420 cert_id.issuer_key_hash); |
| 421 default: |
| 422 return false; |
| 423 } |
| 424 } |
| 425 |
183 } // namespace | 426 } // namespace |
184 | 427 |
185 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert, | 428 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert, |
186 std::string* sct_list) { | 429 std::string* sct_list) { |
187 DCHECK(cert); | 430 DCHECK(cert); |
188 | 431 |
189 NSSCertWrapper leaf_cert(cert); | 432 NSSCertWrapper leaf_cert(cert); |
190 if (!leaf_cert.cert) | 433 if (!leaf_cert.cert) |
191 return false; | 434 return false; |
192 | 435 |
193 return GetOctetStringExtension( | 436 return GetCertOctetStringExtension(leaf_cert.cert.get(), |
194 leaf_cert.cert.get(), g_ct_singleton.Get().embedded_oid(), sct_list); | 437 g_ct_singleton.Get().embedded_oid(), |
| 438 sct_list); |
195 } | 439 } |
196 | 440 |
197 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, | 441 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, |
198 X509Certificate::OSCertHandle issuer, | 442 X509Certificate::OSCertHandle issuer, |
199 LogEntry* result) { | 443 LogEntry* result) { |
200 DCHECK(leaf); | 444 DCHECK(leaf); |
201 DCHECK(issuer); | 445 DCHECK(issuer); |
202 | 446 |
203 NSSCertWrapper leaf_cert(leaf); | 447 NSSCertWrapper leaf_cert(leaf); |
204 NSSCertWrapper issuer_cert(issuer); | 448 NSSCertWrapper issuer_cert(issuer); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 std::string encoded; | 506 std::string encoded; |
263 if (!X509Certificate::GetDEREncoded(leaf, &encoded)) | 507 if (!X509Certificate::GetDEREncoded(leaf, &encoded)) |
264 return false; | 508 return false; |
265 | 509 |
266 result->Reset(); | 510 result->Reset(); |
267 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; | 511 result->type = ct::LogEntry::LOG_ENTRY_TYPE_X509; |
268 result->leaf_certificate.swap(encoded); | 512 result->leaf_certificate.swap(encoded); |
269 return true; | 513 return true; |
270 } | 514 } |
271 | 515 |
| 516 bool ExtractSCTListFromOCSPResponse(X509Certificate::OSCertHandle issuer, |
| 517 const std::string& cert_serial_number, |
| 518 const std::string& ocsp_response, |
| 519 std::string* sct_list) { |
| 520 DCHECK(issuer); |
| 521 |
| 522 // Any OCSP response is unlikely to be even close to 2^24 bytes; further, CT |
| 523 // only uses stapled OCSP responses which have this limit imposed by the TLS |
| 524 // protocol. |
| 525 if (ocsp_response.size() > 0xffffff) |
| 526 return false; |
| 527 |
| 528 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
| 529 |
| 530 OCSPResponse response; |
| 531 memset(&response, 0, sizeof(response)); |
| 532 |
| 533 SECItem src = { siBuffer, |
| 534 reinterpret_cast<unsigned char*>(const_cast<char*>( |
| 535 ocsp_response.data())), |
| 536 static_cast<unsigned int>(ocsp_response.size()) }; |
| 537 |
| 538 // |response| will point directly into |src|, so it's not necessary to |
| 539 // free the |response| contents, but they may only be used while |src| |
| 540 // is valid (i.e., in this method). |
| 541 SECStatus rv = SEC_QuickDERDecodeItem(arena.get(), &response, |
| 542 kOCSPResponseTemplate, &src); |
| 543 if (rv != SECSuccess) |
| 544 return false; |
| 545 |
| 546 if (!response.response_bytes) |
| 547 return false; |
| 548 |
| 549 if (!SECITEM_ItemsAreEqual(&kBasicOCSPResponseOidItem, |
| 550 &response.response_bytes->response_type)) { |
| 551 return false; |
| 552 } |
| 553 |
| 554 BasicOCSPResponse basic_response; |
| 555 memset(&basic_response, 0, sizeof(basic_response)); |
| 556 |
| 557 rv = SEC_QuickDERDecodeItem(arena.get(), &basic_response, |
| 558 kBasicOCSPResponseTemplate, |
| 559 &response.response_bytes->der_response); |
| 560 if (rv != SECSuccess) |
| 561 return false; |
| 562 |
| 563 SingleResponse** responses = |
| 564 basic_response.tbs_response_data.single_responses; |
| 565 if (!responses) |
| 566 return false; |
| 567 |
| 568 std::string issuer_der; |
| 569 if (!X509Certificate::GetDEREncoded(issuer, &issuer_der)) |
| 570 return false; |
| 571 |
| 572 base::StringPiece issuer_spki; |
| 573 if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_spki)) |
| 574 return false; |
| 575 |
| 576 // In OCSP, only the key itself is under hash. |
| 577 base::StringPiece issuer_spk; |
| 578 if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk)) |
| 579 return false; |
| 580 |
| 581 // ExtractSubjectPublicKey... does not remove the initial octet encoding the |
| 582 // number of unused bits in the ASN.1 BIT STRING so we do it here. For public |
| 583 // keys, the bitstring is in practice always byte-aligned. |
| 584 if (issuer_spk.empty() || issuer_spk[0] != 0) |
| 585 return false; |
| 586 issuer_spk.remove_prefix(1); |
| 587 |
| 588 // NSS OCSP lib recognizes SHA1, MD5 and MD2; MD5 and MD2 are dead but |
| 589 // https://bugzilla.mozilla.org/show_bug.cgi?id=663315 will add SHA-256 |
| 590 // and SHA-384. |
| 591 // TODO(ekasper): add SHA-384 to crypto/sha2.h and here if it proves |
| 592 // necessary. |
| 593 // TODO(ekasper): only compute the hashes on demand. |
| 594 std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk); |
| 595 std::string issuer_key_sha1_hash = base::SHA1HashString( |
| 596 issuer_spk.as_string()); |
| 597 |
| 598 const SingleResponse* match = NULL; |
| 599 for (const SingleResponse* const* resps = responses; *resps; ++resps) { |
| 600 const SingleResponse* resp = *resps; |
| 601 if (CertIDMatches(resp->cert_id, cert_serial_number, |
| 602 issuer_key_sha1_hash, issuer_key_sha256_hash)) { |
| 603 match = resp; |
| 604 break; |
| 605 } |
| 606 } |
| 607 |
| 608 if (!match) |
| 609 return false; |
| 610 |
| 611 return GetSCTListFromOCSPExtension(arena.get(), match->single_extensions, |
| 612 sct_list); |
| 613 } |
| 614 |
272 } // namespace ct | 615 } // namespace ct |
273 | 616 |
274 } // namespace net | 617 } // namespace net |
OLD | NEW |