Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 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/x509_certificate.h" | |
| 6 | |
| 7 #include "base/pickle.h" | |
| 8 #include "crypto/openssl_util.h" | |
| 9 #include "net/base/ip_address.h" | |
| 10 #include "net/cert/internal/cert_errors.h" | |
| 11 #include "net/cert/internal/name_constraints.h" | |
| 12 #include "net/cert/internal/parse_name.h" | |
| 13 #include "net/cert/internal/parsed_certificate.h" | |
| 14 #include "net/cert/internal/signature_policy.h" | |
| 15 #include "net/cert/internal/verify_name_match.h" | |
| 16 #include "net/cert/internal/verify_signed_data.h" | |
| 17 #include "net/cert/x509_util.h" | |
| 18 #include "net/cert/x509_util_openssl.h" | |
| 19 #include "net/der/parser.h" | |
| 20 #include "third_party/boringssl/src/include/openssl/evp.h" | |
| 21 #include "third_party/boringssl/src/include/openssl/pool.h" | |
| 22 #include "third_party/boringssl/src/include/openssl/sha.h" | |
| 23 | |
| 24 namespace net { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 // Converts a GeneralizedTime struct to a base::Time, returning true on success | |
| 29 // or false if |generalized| was invalid or cannot be represented by | |
| 30 // base::Time. | |
| 31 bool GeneralizedTimeToBaseTime(const der::GeneralizedTime& generalized, | |
| 32 base::Time* result) { | |
| 33 base::Time::Exploded exploded = {0}; | |
| 34 exploded.year = generalized.year; | |
| 35 exploded.month = generalized.month; | |
| 36 exploded.day_of_month = generalized.day; | |
| 37 exploded.hour = generalized.hours; | |
| 38 exploded.minute = generalized.minutes; | |
| 39 exploded.second = generalized.seconds; | |
| 40 return base::Time::FromUTCExploded(exploded, result); | |
| 41 } | |
| 42 | |
| 43 // Sets |value| to the Value from a DER Sequence Tag-Length-Value and return | |
| 44 // true, or return false if the if was not a valid DER Sequence. | |
|
eroman
2017/03/29 23:06:55
if the if --> if the TLV
mattm
2017/03/30 04:38:10
Done.
| |
| 45 WARN_UNUSED_RESULT bool GetSequenceValue(const der::Input& tlv, | |
| 46 der::Input* value) { | |
| 47 der::Parser parser(tlv); | |
| 48 return parser.ReadTag(der::kSequence, value) && !parser.HasMore(); | |
| 49 } | |
| 50 | |
| 51 // Returns true if |cert|'s normalized Issuer value matches any of | |
| 52 // |normalized_issuers| (which must be pre-normalized). Returns false if there | |
| 53 // was no match or if there was a parsing error. | |
| 54 bool IsCertIssuerMatch(CRYPTO_BUFFER* cert, | |
| 55 const std::vector<std::string>& normalized_issuers) { | |
| 56 der::Input tbs_certificate_tlv; | |
| 57 der::Input signature_algorithm_tlv; | |
| 58 der::BitString signature_value; | |
| 59 if (!ParseCertificate( | |
| 60 der::Input(CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert)), | |
| 61 &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value, | |
| 62 nullptr)) { | |
| 63 return false; | |
| 64 } | |
| 65 ParsedTbsCertificate tbs; | |
| 66 if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr)) { | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 der::Input issuer_value; | |
| 71 std::string normalized_cert_issuer; | |
| 72 if (!GetSequenceValue(tbs.issuer_tlv, &issuer_value) || | |
| 73 !NormalizeName(issuer_value, &normalized_cert_issuer)) | |
|
eroman
2017/03/29 23:06:54
Curly brace for consistency with above? Or convers
mattm
2017/03/30 04:38:10
oops, I've switched to what is (I think) the commo
| |
| 74 return false; | |
| 75 | |
| 76 return std::find(normalized_issuers.begin(), normalized_issuers.end(), | |
| 77 normalized_cert_issuer) != normalized_issuers.end(); | |
| 78 } | |
| 79 | |
| 80 // Sets |out| to UTF-8 encoding of |name_attribute|. Returns true on success or | |
| 81 // false if |name_attribute| could not be converted to UTF-8. | |
| 82 bool NameAttributeToString(const X509NameAttribute& name_attribute, | |
| 83 std::string* out) { | |
| 84 if (name_attribute.value_tag == der::kTeletexString) { | |
| 85 for (char c : name_attribute.value.AsStringPiece()) { | |
| 86 if (c < 32 || c > 126) | |
| 87 return false; | |
| 88 } | |
| 89 *out = name_attribute.value.AsString(); | |
| 90 return true; | |
| 91 } | |
| 92 return name_attribute.ValueAsStringUnsafe(out); | |
|
eroman
2017/03/29 23:06:54
Should this case explicitly check value_tag as wel
mattm
2017/03/30 04:38:10
I was gonna say ValueAsStringUnsafe checks value_t
| |
| 93 } | |
| 94 | |
| 95 // Fills |principal| from the DER encoded |name_tlv|, returning true on success | |
| 96 // or false if parsing failed or some of the values could not be converted to | |
| 97 // UTF-8. | |
| 98 bool ParsePrincipal(const der::Input& name_tlv, CertPrincipal* principal) { | |
| 99 RDNSequence rdns; | |
| 100 if (!ParseName(name_tlv, &rdns)) | |
| 101 return false; | |
| 102 | |
| 103 for (const RelativeDistinguishedName& rdn : rdns) { | |
| 104 for (const X509NameAttribute& name_attribute : rdn) { | |
| 105 if (name_attribute.type == TypeCommonNameOid()) { | |
| 106 if (principal->common_name.empty() && | |
| 107 !NameAttributeToString(name_attribute, &principal->common_name)) | |
| 108 return false; | |
| 109 } else if (name_attribute.type == TypeLocalityNameOid()) { | |
| 110 if (principal->locality_name.empty() && | |
| 111 !NameAttributeToString(name_attribute, &principal->locality_name)) | |
| 112 return false; | |
| 113 } else if (name_attribute.type == TypeStateOrProvinceNameOid()) { | |
| 114 if (principal->state_or_province_name.empty() && | |
| 115 !NameAttributeToString(name_attribute, | |
| 116 &principal->state_or_province_name)) | |
| 117 return false; | |
| 118 } else if (name_attribute.type == TypeCountryNameOid()) { | |
| 119 if (principal->country_name.empty() && | |
| 120 !NameAttributeToString(name_attribute, &principal->country_name)) | |
| 121 return false; | |
| 122 } else if (name_attribute.type == TypeStreetAddressOid()) { | |
| 123 std::string s; | |
| 124 if (!NameAttributeToString(name_attribute, &s)) | |
| 125 return false; | |
| 126 principal->street_addresses.push_back(s); | |
| 127 } else if (name_attribute.type == TypeOrganizationNameOid()) { | |
| 128 std::string s; | |
| 129 if (!NameAttributeToString(name_attribute, &s)) | |
| 130 return false; | |
| 131 principal->organization_names.push_back(s); | |
| 132 } else if (name_attribute.type == TypeOrganizationUnitNameOid()) { | |
| 133 std::string s; | |
| 134 if (!NameAttributeToString(name_attribute, &s)) | |
| 135 return false; | |
| 136 principal->organization_unit_names.push_back(s); | |
| 137 } else if (name_attribute.type == TypeDomainComponentOid()) { | |
| 138 std::string s; | |
| 139 if (!NameAttributeToString(name_attribute, &s)) | |
| 140 return false; | |
| 141 principal->domain_components.push_back(s); | |
| 142 } | |
| 143 } | |
| 144 } | |
| 145 return true; | |
| 146 } | |
| 147 | |
| 148 // Parses certificates from a PKCS#7 SignedData structure, appending them to | |
| 149 // |handles|. | |
| 150 void CreateOSCertHandlesFromPKCS7Bytes( | |
| 151 const char* data, | |
| 152 size_t length, | |
| 153 X509Certificate::OSCertHandles* handles) { | |
| 154 crypto::EnsureOpenSSLInit(); | |
| 155 crypto::OpenSSLErrStackTracer err_cleaner(FROM_HERE); | |
| 156 | |
| 157 CBS der_data; | |
| 158 CBS_init(&der_data, reinterpret_cast<const uint8_t*>(data), length); | |
| 159 STACK_OF(X509)* certs = sk_X509_new_null(); | |
| 160 | |
| 161 if (PKCS7_get_certificates(certs, &der_data)) { | |
| 162 for (size_t i = 0; i < sk_X509_num(certs); ++i) { | |
| 163 base::StringPiece stringpiece; | |
| 164 x509_util::GetDER(sk_X509_value(certs, i), &stringpiece); | |
| 165 handles->push_back(x509_util::CreateCryptoBuffer(stringpiece).release()); | |
| 166 } | |
| 167 } | |
| 168 sk_X509_pop_free(certs, X509_free); | |
| 169 } | |
| 170 | |
| 171 } // namespace | |
| 172 | |
| 173 bool X509Certificate::Initialize() { | |
| 174 der::Input tbs_certificate_tlv; | |
| 175 der::Input signature_algorithm_tlv; | |
| 176 der::BitString signature_value; | |
| 177 | |
| 178 if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle_), | |
| 179 CRYPTO_BUFFER_len(cert_handle_)), | |
| 180 &tbs_certificate_tlv, &signature_algorithm_tlv, | |
| 181 &signature_value, nullptr)) { | |
| 182 return false; | |
| 183 } | |
| 184 | |
| 185 ParsedTbsCertificate tbs; | |
| 186 if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr)) | |
| 187 return false; | |
| 188 | |
| 189 if (!ParsePrincipal(tbs.subject_tlv, &subject_) || | |
| 190 !ParsePrincipal(tbs.issuer_tlv, &issuer_)) | |
| 191 return false; | |
| 192 | |
| 193 if (!GeneralizedTimeToBaseTime(tbs.validity_not_before, &valid_start_) || | |
| 194 !GeneralizedTimeToBaseTime(tbs.validity_not_after, &valid_expiry_)) { | |
| 195 return false; | |
| 196 } | |
| 197 serial_number_ = tbs.serial_number.AsString(); | |
| 198 return true; | |
| 199 } | |
| 200 | |
| 201 bool X509Certificate::GetSubjectAltName( | |
| 202 std::vector<std::string>* dns_names, | |
| 203 std::vector<std::string>* ip_addrs) const { | |
| 204 if (dns_names) | |
| 205 dns_names->clear(); | |
| 206 if (ip_addrs) | |
| 207 ip_addrs->clear(); | |
| 208 | |
| 209 der::Input tbs_certificate_tlv; | |
| 210 der::Input signature_algorithm_tlv; | |
| 211 der::BitString signature_value; | |
| 212 if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle_), | |
| 213 CRYPTO_BUFFER_len(cert_handle_)), | |
| 214 &tbs_certificate_tlv, &signature_algorithm_tlv, | |
| 215 &signature_value, nullptr)) { | |
| 216 return false; | |
| 217 } | |
| 218 | |
| 219 ParsedTbsCertificate tbs; | |
| 220 if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr)) { | |
|
eroman
2017/03/29 23:06:55
same comment for single-line ifs
mattm
2017/03/30 04:38:09
Done.
| |
| 221 return false; | |
| 222 } | |
| 223 if (!tbs.has_extensions) | |
| 224 return false; | |
| 225 | |
| 226 std::map<der::Input, ParsedExtension> extensions; | |
| 227 if (!ParseExtensions(tbs.extensions_tlv, &extensions)) | |
| 228 return false; | |
| 229 | |
| 230 ParsedExtension subject_alt_names_extension; | |
| 231 if (!ConsumeExtension(SubjectAltNameOid(), &extensions, | |
| 232 &subject_alt_names_extension)) { | |
| 233 return false; | |
| 234 } | |
| 235 | |
| 236 std::unique_ptr<GeneralNames> subject_alt_names = | |
| 237 GeneralNames::Create(subject_alt_names_extension.value); | |
| 238 if (!subject_alt_names) | |
| 239 return false; | |
| 240 | |
| 241 if (dns_names) | |
| 242 *dns_names = subject_alt_names->dns_names; | |
| 243 if (ip_addrs) { | |
| 244 for (const IPAddress& addr : subject_alt_names->ip_addresses) { | |
| 245 ip_addrs->push_back( | |
| 246 std::string(reinterpret_cast<const char*>(addr.bytes().data()), | |
| 247 addr.bytes().size())); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 return !subject_alt_names->dns_names.empty() || | |
| 252 !subject_alt_names->ip_addresses.empty(); | |
| 253 } | |
| 254 | |
| 255 bool X509Certificate::IsIssuedByEncoded( | |
| 256 const std::vector<std::string>& valid_issuers) { | |
| 257 std::vector<std::string> normalized_issuers; | |
| 258 for (const auto& raw_issuer : valid_issuers) { | |
| 259 der::Input issuer_value; | |
| 260 std::string normalized_issuer; | |
| 261 if (!GetSequenceValue(der::Input(&raw_issuer), &issuer_value) || | |
| 262 !NormalizeName(issuer_value, &normalized_issuer)) | |
| 263 continue; | |
| 264 normalized_issuers.push_back(std::move(normalized_issuer)); | |
| 265 } | |
| 266 | |
| 267 if (IsCertIssuerMatch(cert_handle_, normalized_issuers)) | |
|
eroman
2017/03/29 23:06:55
[optional] I am not a fan of this function, but I
mattm
2017/03/30 04:38:10
Hmm, okay. done.
| |
| 268 return true; | |
| 269 for (CRYPTO_BUFFER* intermediate : intermediate_ca_certs_) { | |
| 270 if (IsCertIssuerMatch(intermediate, normalized_issuers)) | |
| 271 return true; | |
| 272 } | |
| 273 return false; | |
| 274 } | |
| 275 | |
| 276 // static | |
| 277 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle, | |
| 278 std::string* encoded) { | |
| 279 if (!cert_handle) | |
| 280 return false; | |
| 281 encoded->assign( | |
| 282 reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert_handle)), | |
| 283 CRYPTO_BUFFER_len(cert_handle)); | |
| 284 return true; | |
| 285 } | |
| 286 | |
| 287 // static | |
| 288 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle, | |
| 289 size_t* size_bits, | |
| 290 PublicKeyType* type) { | |
| 291 *type = kPublicKeyTypeUnknown; | |
| 292 *size_bits = 0; | |
| 293 | |
| 294 der::Input tbs_certificate_tlv; | |
| 295 der::Input signature_algorithm_tlv; | |
| 296 der::BitString signature_value; | |
| 297 if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle), | |
|
eroman
2017/03/29 23:06:54
You could replace this with asn1::ExtractSPKIFromD
mattm
2017/03/30 04:38:10
Done.
| |
| 298 CRYPTO_BUFFER_len(cert_handle)), | |
| 299 &tbs_certificate_tlv, &signature_algorithm_tlv, | |
| 300 &signature_value, nullptr)) { | |
| 301 return; | |
| 302 } | |
| 303 | |
| 304 ParsedTbsCertificate tbs; | |
| 305 if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr)) | |
| 306 return; | |
| 307 | |
| 308 bssl::UniquePtr<EVP_PKEY> pkey; | |
| 309 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 310 CBS cbs; | |
| 311 CBS_init(&cbs, tbs.spki_tlv.UnsafeData(), tbs.spki_tlv.Length()); | |
| 312 pkey.reset(EVP_parse_public_key(&cbs)); | |
| 313 if (!pkey) | |
| 314 return; | |
| 315 | |
| 316 switch (pkey->type) { | |
| 317 case EVP_PKEY_RSA: | |
| 318 *type = kPublicKeyTypeRSA; | |
| 319 break; | |
| 320 case EVP_PKEY_DSA: | |
| 321 *type = kPublicKeyTypeDSA; | |
| 322 break; | |
| 323 case EVP_PKEY_EC: | |
| 324 *type = kPublicKeyTypeECDSA; | |
| 325 break; | |
| 326 case EVP_PKEY_DH: | |
| 327 *type = kPublicKeyTypeDH; | |
| 328 break; | |
| 329 } | |
| 330 *size_bits = EVP_PKEY_bits(pkey.get()); | |
|
eroman
2017/03/29 23:06:54
EVP_PKEY_bits() returns an int -- do you think it
mattm
2017/03/30 04:38:09
From some code spelunking it seem the impls return
| |
| 331 } | |
| 332 | |
| 333 // static | |
| 334 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, | |
| 335 X509Certificate::OSCertHandle b) { | |
| 336 DCHECK(a && b); | |
| 337 if (a == b) | |
| 338 return true; | |
| 339 return CRYPTO_BUFFER_len(a) == CRYPTO_BUFFER_len(b) && | |
| 340 memcmp(CRYPTO_BUFFER_data(a), CRYPTO_BUFFER_data(b), | |
| 341 CRYPTO_BUFFER_len(a)) == 0; | |
| 342 } | |
| 343 | |
| 344 // static | |
| 345 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( | |
| 346 const char* data, | |
| 347 size_t length) { | |
| 348 der::Input tbs_certificate_tlv; | |
| 349 der::Input signature_algorithm_tlv; | |
| 350 der::BitString signature_value; | |
| 351 // Do a bare minimum of DER parsing here to make sure the input is not | |
| 352 // completely crazy. (This is required for at least | |
| 353 // CreateCertificateListFromBytes with FORMAT_AUTO, if not more.) | |
| 354 if (!ParseCertificate( | |
| 355 der::Input(reinterpret_cast<const uint8_t*>(data), length), | |
| 356 &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value, | |
| 357 nullptr)) { | |
| 358 return nullptr; | |
| 359 } | |
| 360 | |
| 361 return CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(data), length, | |
| 362 x509_util::GetBufferPool()); | |
| 363 } | |
| 364 | |
| 365 // static | |
| 366 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( | |
| 367 const char* data, | |
| 368 size_t length, | |
| 369 Format format) { | |
| 370 OSCertHandles results; | |
| 371 | |
| 372 switch (format) { | |
| 373 case FORMAT_SINGLE_CERTIFICATE: { | |
| 374 OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); | |
| 375 if (handle) | |
| 376 results.push_back(handle); | |
| 377 break; | |
| 378 } | |
| 379 case FORMAT_PKCS7: { | |
| 380 CreateOSCertHandlesFromPKCS7Bytes(data, length, &results); | |
| 381 break; | |
| 382 } | |
| 383 default: { | |
| 384 NOTREACHED() << "Certificate format " << format << " unimplemented"; | |
| 385 break; | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 return results; | |
| 390 } | |
| 391 | |
| 392 // static | |
| 393 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( | |
| 394 OSCertHandle cert_handle) { | |
| 395 CRYPTO_BUFFER_up_ref(cert_handle); | |
| 396 return cert_handle; | |
| 397 } | |
| 398 | |
| 399 // static | |
| 400 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { | |
| 401 CRYPTO_BUFFER_free(cert_handle); | |
| 402 } | |
| 403 | |
| 404 // static | |
| 405 SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) { | |
| 406 SHA256HashValue sha256; | |
| 407 | |
| 408 SHA256(CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert), sha256.data); | |
| 409 return sha256; | |
| 410 } | |
| 411 | |
| 412 // static | |
| 413 SHA256HashValue X509Certificate::CalculateCAFingerprint256( | |
| 414 const OSCertHandles& intermediates) { | |
| 415 SHA256HashValue sha256; | |
| 416 memset(sha256.data, 0, sizeof(sha256.data)); | |
| 417 | |
| 418 SHA256_CTX sha256_ctx; | |
| 419 SHA256_Init(&sha256_ctx); | |
| 420 for (CRYPTO_BUFFER* cert : intermediates) { | |
| 421 SHA256_Update(&sha256_ctx, CRYPTO_BUFFER_data(cert), | |
| 422 CRYPTO_BUFFER_len(cert)); | |
| 423 } | |
| 424 SHA256_Final(sha256.data, &sha256_ctx); | |
|
eroman
2017/03/29 23:06:54
Should we be checking the return values from these
mattm
2017/03/30 04:38:09
It seems like no existing code checks the results
| |
| 425 | |
| 426 return sha256; | |
| 427 } | |
| 428 | |
| 429 // static | |
| 430 bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle) { | |
| 431 der::Input tbs_certificate_tlv; | |
| 432 der::Input signature_algorithm_tlv; | |
| 433 der::BitString signature_value; | |
| 434 if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_handle), | |
|
eroman
2017/03/29 23:06:55
Having seen this pattern a few times, I wonder if
mattm
2017/03/30 04:38:09
Acknowledged.
| |
| 435 CRYPTO_BUFFER_len(cert_handle)), | |
| 436 &tbs_certificate_tlv, &signature_algorithm_tlv, | |
| 437 &signature_value, nullptr)) { | |
| 438 return false; | |
| 439 } | |
| 440 ParsedTbsCertificate tbs; | |
| 441 if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, nullptr)) { | |
| 442 return false; | |
| 443 } | |
| 444 | |
| 445 der::Input subject_value; | |
| 446 std::string normalized_subject; | |
| 447 if (!GetSequenceValue(tbs.subject_tlv, &subject_value) || | |
| 448 !NormalizeName(subject_value, &normalized_subject)) | |
| 449 return false; | |
| 450 der::Input issuer_value; | |
| 451 std::string normalized_issuer; | |
| 452 if (!GetSequenceValue(tbs.issuer_tlv, &issuer_value) || | |
| 453 !NormalizeName(issuer_value, &normalized_issuer)) | |
| 454 return false; | |
| 455 | |
| 456 if (normalized_subject != normalized_issuer) | |
|
eroman
2017/03/29 23:06:54
Interestingly, I just glanced at the _mac.cc versi
mattm
2017/03/30 04:38:09
Apparently:
CSSMOID_X509V1IssuerName and CSSMOID_X
| |
| 457 return false; | |
| 458 | |
| 459 std::unique_ptr<SignatureAlgorithm> signature_algorithm = | |
| 460 SignatureAlgorithm::Create(signature_algorithm_tlv, nullptr /* errors */); | |
| 461 if (!signature_algorithm) | |
| 462 return false; | |
| 463 | |
| 464 SimpleSignaturePolicy signature_policy(1024); | |
| 465 CertErrors unused_errors; | |
| 466 return VerifySignedData(*signature_algorithm, tbs_certificate_tlv, | |
|
eroman
2017/03/29 23:06:54
I wonder is this even necessary? I see some implem
mattm
2017/03/30 04:38:09
Yeah, based on how it's used (and the unittest as
| |
| 467 signature_value, tbs.spki_tlv, &signature_policy, | |
| 468 &unused_errors); | |
| 469 } | |
| 470 | |
| 471 // static | |
| 472 X509Certificate::OSCertHandle X509Certificate::ReadOSCertHandleFromPickle( | |
| 473 base::PickleIterator* pickle_iter) { | |
| 474 const char* data; | |
| 475 int length; | |
| 476 if (!pickle_iter->ReadData(&data, &length)) | |
| 477 return NULL; | |
| 478 | |
| 479 return CreateOSCertHandleFromBytes(data, length); | |
|
eroman
2017/03/29 23:06:54
paranoid crazy person: is length always non-negati
mattm
2017/03/30 04:38:10
Looks like the pickle ReadData code will return fa
| |
| 480 } | |
| 481 | |
| 482 // static | |
| 483 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle, | |
| 484 base::Pickle* pickle) { | |
| 485 return pickle->WriteData( | |
| 486 reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert_handle)), | |
| 487 CRYPTO_BUFFER_len(cert_handle)); | |
| 488 } | |
| 489 | |
| 490 } // namespace net | |
| OLD | NEW |