| 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 #include "net/ssl/scoped_openssl_types.h" | |
| 21 | |
| 22 namespace net { | |
| 23 | |
| 24 namespace ct { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 void FreeX509_EXTENSIONS(X509_EXTENSIONS* ptr) { | |
| 29 sk_X509_EXTENSION_pop_free(ptr, X509_EXTENSION_free); | |
| 30 } | |
| 31 | |
| 32 using ScopedX509_EXTENSIONS = | |
| 33 crypto::ScopedOpenSSL<X509_EXTENSIONS, FreeX509_EXTENSIONS>; | |
| 34 | |
| 35 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of | |
| 36 // RFC6962. | |
| 37 const uint8_t kEmbeddedSCTOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, | |
| 38 0x02, 0x04, 0x02}; | |
| 39 | |
| 40 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for | |
| 41 // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see | |
| 42 // Section 3.3 of RFC6962. | |
| 43 const uint8_t kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, | |
| 44 0x02, 0x04, 0x05}; | |
| 45 | |
| 46 bool StringEqualToCBS(const std::string& value1, const CBS* value2) { | |
| 47 if (CBS_len(value2) != value1.size()) | |
| 48 return false; | |
| 49 return memcmp(value1.data(), CBS_data(value2), CBS_len(value2)) == 0; | |
| 50 } | |
| 51 | |
| 52 ScopedX509 OSCertHandleToOpenSSL(X509Certificate::OSCertHandle os_handle) { | |
| 53 #if defined(USE_OPENSSL_CERTS) | |
| 54 return ScopedX509(X509Certificate::DupOSCertHandle(os_handle)); | |
| 55 #else | |
| 56 std::string der_encoded; | |
| 57 if (!X509Certificate::GetDEREncoded(os_handle, &der_encoded)) | |
| 58 return ScopedX509(); | |
| 59 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(der_encoded.data()); | |
| 60 return ScopedX509(d2i_X509(NULL, &bytes, der_encoded.size())); | |
| 61 #endif | |
| 62 } | |
| 63 | |
| 64 // Finds the SignedCertificateTimestampList in an extension with OID |oid| in | |
| 65 // |x509_exts|. If found, returns true and sets |*out_sct_list| to the encoded | |
| 66 // SCT list. |out_sct_list| may be NULL. | |
| 67 bool GetSCTListFromX509_EXTENSIONS(const X509_EXTENSIONS* x509_exts, | |
| 68 const uint8_t* oid, | |
| 69 size_t oid_len, | |
| 70 std::string* out_sct_list) { | |
| 71 for (size_t i = 0; i < sk_X509_EXTENSION_num(x509_exts); i++) { | |
| 72 X509_EXTENSION* x509_ext = sk_X509_EXTENSION_value(x509_exts, i); | |
| 73 if (static_cast<size_t>(x509_ext->object->length) == oid_len && | |
| 74 memcmp(x509_ext->object->data, oid, oid_len) == 0) { | |
| 75 // The SCT list is an OCTET STRING inside the extension. | |
| 76 CBS ext_value, sct_list; | |
| 77 CBS_init(&ext_value, x509_ext->value->data, x509_ext->value->length); | |
| 78 if (!CBS_get_asn1(&ext_value, &sct_list, CBS_ASN1_OCTETSTRING) || | |
| 79 CBS_len(&ext_value) != 0) { | |
| 80 return false; | |
| 81 } | |
| 82 if (out_sct_list) { | |
| 83 *out_sct_list = std::string( | |
| 84 reinterpret_cast<const char*>(CBS_data(&sct_list)), | |
| 85 CBS_len(&sct_list)); | |
| 86 } | |
| 87 return true; | |
| 88 } | |
| 89 } | |
| 90 return false; | |
| 91 } | |
| 92 | |
| 93 // Finds the SingleResponse in |responses| which matches |issuer| and | |
| 94 // |cert_serial_number|. On success, returns true and sets | |
| 95 // |*out_single_response| to the body of the SingleResponse starting at the | |
| 96 // |certStatus| field. | |
| 97 bool FindMatchingSingleResponse(CBS* responses, | |
| 98 X509Certificate::OSCertHandle issuer, | |
| 99 const std::string& cert_serial_number, | |
| 100 CBS* out_single_response) { | |
| 101 std::string issuer_der; | |
| 102 if (!X509Certificate::GetDEREncoded(issuer, &issuer_der)) | |
| 103 return false; | |
| 104 | |
| 105 base::StringPiece issuer_spki; | |
| 106 if (!asn1::ExtractSPKIFromDERCert(issuer_der, &issuer_spki)) | |
| 107 return false; | |
| 108 | |
| 109 // In OCSP, only the key itself is under hash. | |
| 110 base::StringPiece issuer_spk; | |
| 111 if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk)) | |
| 112 return false; | |
| 113 | |
| 114 // ExtractSubjectPublicKey... does not remove the initial octet encoding the | |
| 115 // number of unused bits in the ASN.1 BIT STRING so we do it here. For public | |
| 116 // keys, the bitstring is in practice always byte-aligned. | |
| 117 if (issuer_spk.empty() || issuer_spk[0] != 0) | |
| 118 return false; | |
| 119 issuer_spk.remove_prefix(1); | |
| 120 | |
| 121 // TODO(ekasper): add SHA-384 to crypto/sha2.h and here if it proves | |
| 122 // necessary. | |
| 123 // TODO(ekasper): only compute the hashes on demand. | |
| 124 std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk); | |
| 125 std::string issuer_key_sha1_hash = base::SHA1HashString( | |
| 126 issuer_spk.as_string()); | |
| 127 | |
| 128 while (CBS_len(responses) > 0) { | |
| 129 CBS single_response, cert_id; | |
| 130 if (!CBS_get_asn1(responses, &single_response, CBS_ASN1_SEQUENCE) || | |
| 131 !CBS_get_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE)) { | |
| 132 return false; | |
| 133 } | |
| 134 | |
| 135 CBS hash_algorithm, hash, serial_number, issuer_name_hash, issuer_key_hash; | |
| 136 if (!CBS_get_asn1(&cert_id, &hash_algorithm, CBS_ASN1_SEQUENCE) || | |
| 137 !CBS_get_asn1(&hash_algorithm, &hash, CBS_ASN1_OBJECT) || | |
| 138 !CBS_get_asn1(&cert_id, &issuer_name_hash, CBS_ASN1_OCTETSTRING) || | |
| 139 !CBS_get_asn1(&cert_id, &issuer_key_hash, CBS_ASN1_OCTETSTRING) || | |
| 140 !CBS_get_asn1(&cert_id, &serial_number, CBS_ASN1_INTEGER) || | |
| 141 CBS_len(&cert_id) != 0) { | |
| 142 return false; | |
| 143 } | |
| 144 | |
| 145 // Check the serial number matches. | |
| 146 if (!StringEqualToCBS(cert_serial_number, &serial_number)) | |
| 147 continue; | |
| 148 | |
| 149 // Check if the issuer_key_hash matches. | |
| 150 // TODO(ekasper): also use the issuer name hash in matching. | |
| 151 switch (OBJ_cbs2nid(&hash)) { | |
| 152 case NID_sha1: | |
| 153 if (StringEqualToCBS(issuer_key_sha1_hash, &issuer_key_hash)) { | |
| 154 *out_single_response = single_response; | |
| 155 return true; | |
| 156 } | |
| 157 break; | |
| 158 case NID_sha256: | |
| 159 if (StringEqualToCBS(issuer_key_sha256_hash, &issuer_key_hash)) { | |
| 160 *out_single_response = single_response; | |
| 161 return true; | |
| 162 } | |
| 163 break; | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 return false; | |
| 168 } | |
| 169 | |
| 170 } // namespace | |
| 171 | |
| 172 bool ExtractEmbeddedSCTList(X509Certificate::OSCertHandle cert, | |
| 173 std::string* sct_list) { | |
| 174 ScopedX509 x509(OSCertHandleToOpenSSL(cert)); | |
| 175 if (!x509) | |
| 176 return false; | |
| 177 X509_EXTENSIONS* x509_exts = x509->cert_info->extensions; | |
| 178 if (!x509_exts) | |
| 179 return false; | |
| 180 return GetSCTListFromX509_EXTENSIONS( | |
| 181 x509->cert_info->extensions, kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), | |
| 182 sct_list); | |
| 183 } | |
| 184 | |
| 185 bool GetPrecertLogEntry(X509Certificate::OSCertHandle leaf, | |
| 186 X509Certificate::OSCertHandle issuer, | |
| 187 LogEntry* result) { | |
| 188 result->Reset(); | |
| 189 | |
| 190 ScopedX509 leaf_x509(OSCertHandleToOpenSSL(leaf)); | |
| 191 if (!leaf_x509) | |
| 192 return false; | |
| 193 | |
| 194 // XXX(rsleevi): This check may be overkill, since we should be able to | |
| 195 // generate precerts for certs without the extension. For now, just a sanity | |
| 196 // check to match the reference implementation. | |
| 197 if (!leaf_x509->cert_info->extensions || | |
| 198 !GetSCTListFromX509_EXTENSIONS(leaf_x509->cert_info->extensions, | |
| 199 kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid), | |
| 200 NULL)) { | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 // The Precertificate log entry is the final certificate's TBSCertificate | |
| 205 // without the SCT extension (RFC6962, section 3.2). | |
| 206 ScopedX509 leaf_copy(X509_dup(leaf_x509.get())); | |
| 207 if (!leaf_copy || !leaf_copy->cert_info->extensions) { | |
| 208 NOTREACHED(); | |
| 209 return false; | |
| 210 } | |
| 211 X509_EXTENSIONS* leaf_copy_exts = leaf_copy->cert_info->extensions; | |
| 212 for (size_t i = 0; i < sk_X509_EXTENSION_num(leaf_copy_exts); i++) { | |
| 213 X509_EXTENSION* ext = sk_X509_EXTENSION_value(leaf_copy_exts, i); | |
| 214 if (static_cast<size_t>(ext->object->length) == sizeof(kEmbeddedSCTOid) && | |
| 215 memcmp(ext->object->data, | |
| 216 kEmbeddedSCTOid, sizeof(kEmbeddedSCTOid)) == 0) { | |
| 217 X509_EXTENSION_free(sk_X509_EXTENSION_delete(leaf_copy_exts, i)); | |
| 218 X509_CINF_set_modified(leaf_copy->cert_info); | |
| 219 break; | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 std::string to_be_signed; | |
| 224 int len = i2d_X509_CINF(leaf_copy->cert_info, NULL); | |
| 225 if (len < 0) | |
| 226 return false; | |
| 227 uint8_t* ptr = | |
| 228 reinterpret_cast<uint8_t*>(base::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 false; | |
| 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 |