summaryrefslogtreecommitdiff
path: root/chromium/net/cert
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/cert')
-rw-r--r--chromium/net/cert/asn1_util.cc331
-rw-r--r--chromium/net/cert/asn1_util.h93
-rw-r--r--chromium/net/cert/cert_database.cc39
-rw-r--r--chromium/net/cert/cert_database.h105
-rw-r--r--chromium/net/cert/cert_database_android.cc39
-rw-r--r--chromium/net/cert/cert_database_ios.cc30
-rw-r--r--chromium/net/cert/cert_database_mac.cc172
-rw-r--r--chromium/net/cert/cert_database_nss.cc112
-rw-r--r--chromium/net/cert/cert_database_openssl.cc58
-rw-r--r--chromium/net/cert/cert_database_win.cc61
-rw-r--r--chromium/net/cert/cert_status_flags.cc83
-rw-r--r--chromium/net/cert/cert_status_flags.h61
-rw-r--r--chromium/net/cert/cert_trust_anchor_provider.h33
-rw-r--r--chromium/net/cert/cert_type.h28
-rw-r--r--chromium/net/cert/cert_verifier.cc16
-rw-r--r--chromium/net/cert/cert_verifier.h124
-rw-r--r--chromium/net/cert/cert_verify_proc.cc392
-rw-r--r--chromium/net/cert/cert_verify_proc.h98
-rw-r--r--chromium/net/cert/cert_verify_proc_android.cc120
-rw-r--r--chromium/net/cert/cert_verify_proc_android.h34
-rw-r--r--chromium/net/cert/cert_verify_proc_mac.cc714
-rw-r--r--chromium/net/cert/cert_verify_proc_mac.h34
-rw-r--r--chromium/net/cert/cert_verify_proc_nss.cc892
-rw-r--r--chromium/net/cert/cert_verify_proc_nss.h34
-rw-r--r--chromium/net/cert/cert_verify_proc_openssl.cc226
-rw-r--r--chromium/net/cert/cert_verify_proc_openssl.h33
-rw-r--r--chromium/net/cert/cert_verify_proc_unittest.cc1359
-rw-r--r--chromium/net/cert/cert_verify_proc_win.cc827
-rw-r--r--chromium/net/cert/cert_verify_proc_win.h34
-rw-r--r--chromium/net/cert/cert_verify_result.cc30
-rw-r--r--chromium/net/cert/cert_verify_result.h69
-rw-r--r--chromium/net/cert/crl_set.cc611
-rw-r--r--chromium/net/cert/crl_set.h128
-rw-r--r--chromium/net/cert/crl_set_unittest.cc326
-rw-r--r--chromium/net/cert/ev_root_ca_metadata.cc571
-rw-r--r--chromium/net/cert/ev_root_ca_metadata.h89
-rw-r--r--chromium/net/cert/ev_root_ca_metadata_unittest.cc144
-rw-r--r--chromium/net/cert/jwk_serializer.h30
-rw-r--r--chromium/net/cert/jwk_serializer_nss.cc118
-rw-r--r--chromium/net/cert/jwk_serializer_openssl.cc23
-rw-r--r--chromium/net/cert/jwk_serializer_unittest.cc148
-rw-r--r--chromium/net/cert/mock_cert_verifier.cc64
-rw-r--r--chromium/net/cert/mock_cert_verifier.h87
-rw-r--r--chromium/net/cert/multi_threaded_cert_verifier.cc566
-rw-r--r--chromium/net/cert/multi_threaded_cert_verifier.h169
-rw-r--r--chromium/net/cert/multi_threaded_cert_verifier_unittest.cc479
-rw-r--r--chromium/net/cert/nss_cert_database.cc345
-rw-r--r--chromium/net/cert/nss_cert_database.h208
-rw-r--r--chromium/net/cert/nss_cert_database_unittest.cc1042
-rw-r--r--chromium/net/cert/pem_tokenizer.cc105
-rw-r--r--chromium/net/cert/pem_tokenizer.h77
-rw-r--r--chromium/net/cert/pem_tokenizer_unittest.cc169
-rw-r--r--chromium/net/cert/single_request_cert_verifier.cc70
-rw-r--r--chromium/net/cert/single_request_cert_verifier.h52
-rw-r--r--chromium/net/cert/test_root_certs.cc76
-rw-r--r--chromium/net/cert/test_root_certs.h134
-rw-r--r--chromium/net/cert/test_root_certs_android.cc43
-rw-r--r--chromium/net/cert/test_root_certs_mac.cc117
-rw-r--r--chromium/net/cert/test_root_certs_nss.cc125
-rw-r--r--chromium/net/cert/test_root_certs_openssl.cc51
-rw-r--r--chromium/net/cert/test_root_certs_unittest.cc142
-rw-r--r--chromium/net/cert/test_root_certs_win.cc213
-rw-r--r--chromium/net/cert/x509_cert_types.cc142
-rw-r--r--chromium/net/cert/x509_cert_types.h144
-rw-r--r--chromium/net/cert/x509_cert_types_mac.cc291
-rw-r--r--chromium/net/cert/x509_cert_types_unittest.cc243
-rw-r--r--chromium/net/cert/x509_cert_types_win.cc139
-rw-r--r--chromium/net/cert/x509_certificate.cc734
-rw-r--r--chromium/net/cert/x509_certificate.h487
-rw-r--r--chromium/net/cert/x509_certificate_ios.cc234
-rw-r--r--chromium/net/cert/x509_certificate_known_roots_mac.h433
-rw-r--r--chromium/net/cert/x509_certificate_known_roots_win.h726
-rw-r--r--chromium/net/cert/x509_certificate_mac.cc611
-rw-r--r--chromium/net/cert/x509_certificate_net_log_param.cc27
-rw-r--r--chromium/net/cert/x509_certificate_net_log_param.h21
-rw-r--r--chromium/net/cert/x509_certificate_nss.cc269
-rw-r--r--chromium/net/cert/x509_certificate_openssl.cc520
-rw-r--r--chromium/net/cert/x509_certificate_unittest.cc1147
-rw-r--r--chromium/net/cert/x509_certificate_win.cc453
-rw-r--r--chromium/net/cert/x509_util.cc49
-rw-r--r--chromium/net/cert/x509_util.h99
-rw-r--r--chromium/net/cert/x509_util_ios.cc141
-rw-r--r--chromium/net/cert/x509_util_ios.h72
-rw-r--r--chromium/net/cert/x509_util_mac.cc231
-rw-r--r--chromium/net/cert/x509_util_mac.h139
-rw-r--r--chromium/net/cert/x509_util_nss.cc624
-rw-r--r--chromium/net/cert/x509_util_nss.h99
-rw-r--r--chromium/net/cert/x509_util_nss_unittest.cc171
-rw-r--r--chromium/net/cert/x509_util_openssl.cc129
-rw-r--r--chromium/net/cert/x509_util_openssl.h45
-rw-r--r--chromium/net/cert/x509_util_openssl_unittest.cc57
-rw-r--r--chromium/net/cert/x509_util_unittest.cc190
92 files changed, 21470 insertions, 0 deletions
diff --git a/chromium/net/cert/asn1_util.cc b/chromium/net/cert/asn1_util.cc
new file mode 100644
index 00000000000..6dcff52ee65
--- /dev/null
+++ b/chromium/net/cert/asn1_util.cc
@@ -0,0 +1,331 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/asn1_util.h"
+
+namespace net {
+
+namespace asn1 {
+
+bool ParseElement(base::StringPiece* in,
+ unsigned tag_value,
+ base::StringPiece* out,
+ unsigned *out_header_len) {
+ const uint8* data = reinterpret_cast<const uint8*>(in->data());
+
+ // We don't support kAny and kOptional at the same time.
+ if ((tag_value & kAny) && (tag_value & kOptional))
+ return false;
+
+ if (in->empty() && (tag_value & kOptional)) {
+ if (out_header_len)
+ *out_header_len = 0;
+ if (out)
+ *out = base::StringPiece();
+ return true;
+ }
+
+ if (in->size() < 2)
+ return false;
+
+ if (tag_value != kAny &&
+ static_cast<unsigned char>(data[0]) != (tag_value & 0xff)) {
+ if (tag_value & kOptional) {
+ if (out_header_len)
+ *out_header_len = 0;
+ if (out)
+ *out = base::StringPiece();
+ return true;
+ }
+ return false;
+ }
+
+ size_t len = 0;
+ if ((data[1] & 0x80) == 0) {
+ // short form length
+ if (out_header_len)
+ *out_header_len = 2;
+ len = static_cast<size_t>(data[1]) + 2;
+ } else {
+ // long form length
+ const unsigned num_bytes = data[1] & 0x7f;
+ if (num_bytes == 0 || num_bytes > 2)
+ return false;
+ if (in->size() < 2 + num_bytes)
+ return false;
+ len = data[2];
+ if (num_bytes == 2) {
+ if (len == 0) {
+ // the length encoding must be minimal.
+ return false;
+ }
+ len <<= 8;
+ len += data[3];
+ }
+ if (len < 128) {
+ // the length should have been encoded in short form. This distinguishes
+ // DER from BER encoding.
+ return false;
+ }
+ if (out_header_len)
+ *out_header_len = 2 + num_bytes;
+ len += 2 + num_bytes;
+ }
+
+ if (in->size() < len)
+ return false;
+ if (out)
+ *out = base::StringPiece(in->data(), len);
+ in->remove_prefix(len);
+ return true;
+}
+
+bool GetElement(base::StringPiece* in,
+ unsigned tag_value,
+ base::StringPiece* out) {
+ unsigned header_len;
+ if (!ParseElement(in, tag_value, out, &header_len))
+ return false;
+ if (out)
+ out->remove_prefix(header_len);
+ return true;
+}
+
+// SeekToSPKI changes |cert| so that it points to a suffix of the
+// TBSCertificate where the suffix begins at the start of the ASN.1
+// SubjectPublicKeyInfo value.
+static bool SeekToSPKI(base::StringPiece* cert) {
+ // From RFC 5280, section 4.1
+ // Certificate ::= SEQUENCE {
+ // tbsCertificate TBSCertificate,
+ // signatureAlgorithm AlgorithmIdentifier,
+ // signatureValue BIT STRING }
+
+ // TBSCertificate ::= SEQUENCE {
+ // version [0] EXPLICIT Version DEFAULT v1,
+ // serialNumber CertificateSerialNumber,
+ // signature AlgorithmIdentifier,
+ // issuer Name,
+ // validity Validity,
+ // subject Name,
+ // subjectPublicKeyInfo SubjectPublicKeyInfo,
+
+ base::StringPiece certificate;
+ if (!GetElement(cert, kSEQUENCE, &certificate))
+ return false;
+
+ // We don't allow junk after the certificate.
+ if (!cert->empty())
+ return false;
+
+ base::StringPiece tbs_certificate;
+ if (!GetElement(&certificate, kSEQUENCE, &tbs_certificate))
+ return false;
+
+ if (!GetElement(&tbs_certificate,
+ kOptional | kConstructed | kContextSpecific | 0,
+ NULL)) {
+ return false;
+ }
+
+ // serialNumber
+ if (!GetElement(&tbs_certificate, kINTEGER, NULL))
+ return false;
+ // signature
+ if (!GetElement(&tbs_certificate, kSEQUENCE, NULL))
+ return false;
+ // issuer
+ if (!GetElement(&tbs_certificate, kSEQUENCE, NULL))
+ return false;
+ // validity
+ if (!GetElement(&tbs_certificate, kSEQUENCE, NULL))
+ return false;
+ // subject
+ if (!GetElement(&tbs_certificate, kSEQUENCE, NULL))
+ return false;
+ *cert = tbs_certificate;
+ return true;
+}
+
+bool ExtractSPKIFromDERCert(base::StringPiece cert,
+ base::StringPiece* spki_out) {
+ if (!SeekToSPKI(&cert))
+ return false;
+ if (!ParseElement(&cert, kSEQUENCE, spki_out, NULL))
+ return false;
+ return true;
+}
+
+bool ExtractSubjectPublicKeyFromSPKI(base::StringPiece spki,
+ base::StringPiece* spk_out) {
+ // From RFC 5280, Section 4.1
+ // SubjectPublicKeyInfo ::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ //
+ // AlgorithmIdentifier ::= SEQUENCE {
+ // algorithm OBJECT IDENTIFIER,
+ // parameters ANY DEFINED BY algorithm OPTIONAL }
+
+ // Step into SubjectPublicKeyInfo sequence.
+ base::StringPiece spki_contents;
+ if (!asn1::GetElement(&spki, asn1::kSEQUENCE, &spki_contents))
+ return false;
+
+ // Step over algorithm field (a SEQUENCE).
+ base::StringPiece algorithm;
+ if (!asn1::GetElement(&spki_contents, asn1::kSEQUENCE, &algorithm))
+ return false;
+
+ // Extract the subjectPublicKey field.
+ if (!asn1::GetElement(&spki_contents, asn1::kBITSTRING, spk_out))
+ return false;
+ return true;
+}
+
+
+bool ExtractCRLURLsFromDERCert(base::StringPiece cert,
+ std::vector<base::StringPiece>* urls_out) {
+ urls_out->clear();
+ std::vector<base::StringPiece> tmp_urls_out;
+
+ if (!SeekToSPKI(&cert))
+ return false;
+
+ // From RFC 5280, section 4.1
+ // TBSCertificate ::= SEQUENCE {
+ // ...
+ // subjectPublicKeyInfo SubjectPublicKeyInfo,
+ // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ // extensions [3] EXPLICIT Extensions OPTIONAL
+
+ // subjectPublicKeyInfo
+ if (!GetElement(&cert, kSEQUENCE, NULL))
+ return false;
+ // issuerUniqueID
+ if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 1, NULL))
+ return false;
+ // subjectUniqueID
+ if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 2, NULL))
+ return false;
+
+ base::StringPiece extensions_seq;
+ if (!GetElement(&cert, kOptional | kConstructed | kContextSpecific | 3,
+ &extensions_seq)) {
+ return false;
+ }
+
+ if (extensions_seq.empty())
+ return true;
+
+ // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ // Extension ::= SEQUENCE {
+ // extnID OBJECT IDENTIFIER,
+ // critical BOOLEAN DEFAULT FALSE,
+ // extnValue OCTET STRING
+
+ // |extensions_seq| was EXPLICITly tagged, so we still need to remove the
+ // ASN.1 SEQUENCE header.
+ base::StringPiece extensions;
+ if (!GetElement(&extensions_seq, kSEQUENCE, &extensions))
+ return false;
+
+ while (extensions.size() > 0) {
+ base::StringPiece extension;
+ if (!GetElement(&extensions, kSEQUENCE, &extension))
+ return false;
+
+ base::StringPiece oid;
+ if (!GetElement(&extension, kOID, &oid))
+ return false;
+
+ // kCRLDistributionPointsOID is the DER encoding of the OID for the X.509
+ // CRL Distribution Points extension.
+ static const uint8 kCRLDistributionPointsOID[] = {0x55, 0x1d, 0x1f};
+
+ if (oid.size() != sizeof(kCRLDistributionPointsOID) ||
+ memcmp(oid.data(), kCRLDistributionPointsOID, oid.size()) != 0) {
+ continue;
+ }
+
+ // critical
+ GetElement(&extension, kBOOLEAN, NULL);
+
+ // extnValue
+ base::StringPiece extension_value;
+ if (!GetElement(&extension, kOCTETSTRING, &extension_value))
+ return false;
+
+ // RFC 5280, section 4.2.1.13.
+ //
+ // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+ //
+ // DistributionPoint ::= SEQUENCE {
+ // distributionPoint [0] DistributionPointName OPTIONAL,
+ // reasons [1] ReasonFlags OPTIONAL,
+ // cRLIssuer [2] GeneralNames OPTIONAL }
+
+ base::StringPiece distribution_points;
+ if (!GetElement(&extension_value, kSEQUENCE, &distribution_points))
+ return false;
+
+ while (distribution_points.size() > 0) {
+ base::StringPiece distrib_point;
+ if (!GetElement(&distribution_points, kSEQUENCE, &distrib_point))
+ return false;
+
+ base::StringPiece name;
+ if (!GetElement(&distrib_point, kContextSpecific | kConstructed | 0,
+ &name)) {
+ // If it doesn't contain a name then we skip it.
+ continue;
+ }
+
+ if (GetElement(&distrib_point, kContextSpecific | 1, NULL)) {
+ // If it contains a subset of reasons then we skip it. We aren't
+ // interested in subsets of CRLs and the RFC states that there MUST be
+ // a CRL that covers all reasons.
+ continue;
+ }
+
+ if (GetElement(&distrib_point,
+ kContextSpecific | kConstructed | 2, NULL)) {
+ // If it contains a alternative issuer, then we skip it.
+ continue;
+ }
+
+ // DistributionPointName ::= CHOICE {
+ // fullName [0] GeneralNames,
+ // nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
+ base::StringPiece general_names;
+ if (!GetElement(&name,
+ kContextSpecific | kConstructed | 0, &general_names)) {
+ continue;
+ }
+
+ // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ // GeneralName ::= CHOICE {
+ // ...
+ // uniformResourceIdentifier [6] IA5String,
+ // ...
+ while (general_names.size() > 0) {
+ base::StringPiece url;
+ if (GetElement(&general_names, kContextSpecific | 6, &url)) {
+ tmp_urls_out.push_back(url);
+ } else {
+ if (!GetElement(&general_names, kAny, NULL))
+ return false;
+ }
+ }
+ }
+ }
+
+ urls_out->swap(tmp_urls_out);
+ return true;
+}
+
+} // namespace asn1
+
+} // namespace net
diff --git a/chromium/net/cert/asn1_util.h b/chromium/net/cert/asn1_util.h
new file mode 100644
index 00000000000..ed379b3f122
--- /dev/null
+++ b/chromium/net/cert/asn1_util.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_ASN1_UTIL_H_
+#define NET_CERT_ASN1_UTIL_H_
+
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace asn1 {
+
+// These are the DER encodings of the tag byte for ASN.1 objects.
+static const unsigned kBOOLEAN = 0x01;
+static const unsigned kINTEGER = 0x02;
+static const unsigned kBITSTRING = 0x03;
+static const unsigned kOCTETSTRING = 0x04;
+static const unsigned kOID = 0x06;
+static const unsigned kSEQUENCE = 0x30;
+
+// These are flags that can be ORed with the above tag numbers.
+static const unsigned kContextSpecific = 0x80;
+static const unsigned kConstructed = 0x20;
+
+// kAny matches any tag value;
+static const unsigned kAny = 0x10000;
+// kOptional denotes an optional element.
+static const unsigned kOptional = 0x20000;
+
+// ParseElement parses a DER encoded ASN1 element from |in|, requiring that
+// it have the given |tag_value|. It returns true on success. The following
+// limitations are imposed:
+// 1) tag numbers > 31 are not permitted.
+// 2) lengths > 65535 are not permitted.
+// On successful return:
+// |in| is advanced over the element
+// |out| contains the element, including the tag and length bytes.
+// |out_header_len| contains the length of the tag and length bytes in |out|.
+//
+// If |tag_value & kOptional| is true then *out_header_len can be zero after a
+// true return value if the element was not found.
+bool ParseElement(base::StringPiece* in,
+ unsigned tag_value,
+ base::StringPiece* out,
+ unsigned *out_header_len);
+
+// GetElement performs the same actions as ParseElement, except that the header
+// bytes are not included in the output.
+//
+// If |tag_value & kOptional| is true then this function cannot distinguish
+// between a missing optional element and an empty one.
+bool GetElement(base::StringPiece* in,
+ unsigned tag_value,
+ base::StringPiece* out);
+
+// ExtractSPKIFromDERCert parses the DER encoded certificate in |cert| and
+// extracts the bytes of the SubjectPublicKeyInfo. On successful return,
+// |spki_out| is set to contain the SPKI, pointing into |cert|.
+NET_EXPORT_PRIVATE bool ExtractSPKIFromDERCert(base::StringPiece cert,
+ base::StringPiece* spki_out);
+
+// ExtractSubjectPublicKeyFromSPKI parses the DER encoded SubjectPublicKeyInfo
+// in |spki| and extracts the bytes of the SubjectPublicKey. On successful
+// return, |spk_out| is set to contain the public key, pointing into |spki|.
+NET_EXPORT_PRIVATE bool ExtractSubjectPublicKeyFromSPKI(
+ base::StringPiece spki,
+ base::StringPiece* spk_out);
+
+// ExtractCRLURLsFromDERCert parses the DER encoded certificate in |cert| and
+// extracts the URL of each CRL. On successful return, the elements of
+// |urls_out| point into |cert|.
+//
+// CRLs that only cover a subset of the reasons are omitted as the spec
+// requires that at least one CRL be included that covers all reasons.
+//
+// CRLs that use an alternative issuer are also omitted.
+//
+// The nested set of GeneralNames is flattened into a single list because
+// having several CRLs with one location is equivalent to having one CRL with
+// several locations as far as a CRL filter is concerned.
+NET_EXPORT_PRIVATE bool ExtractCRLURLsFromDERCert(
+ base::StringPiece cert,
+ std::vector<base::StringPiece>* urls_out);
+
+} // namespace asn1
+
+} // namespace net
+
+#endif // NET_CERT_ASN1_UTIL_H_
diff --git a/chromium/net/cert/cert_database.cc b/chromium/net/cert/cert_database.cc
new file mode 100644
index 00000000000..db54172d070
--- /dev/null
+++ b/chromium/net/cert/cert_database.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include "base/memory/singleton.h"
+#include "base/observer_list_threadsafe.h"
+
+namespace net {
+
+// static
+CertDatabase* CertDatabase::GetInstance() {
+ return Singleton<CertDatabase>::get();
+}
+
+void CertDatabase::AddObserver(Observer* observer) {
+ observer_list_->AddObserver(observer);
+}
+
+void CertDatabase::RemoveObserver(Observer* observer) {
+ observer_list_->RemoveObserver(observer);
+}
+
+void CertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
+ observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
+}
+
+void CertDatabase::NotifyObserversOfCertRemoved(const X509Certificate* cert) {
+ observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
+}
+
+void CertDatabase::NotifyObserversOfCertTrustChanged(
+ const X509Certificate* cert) {
+ observer_list_->Notify(
+ &Observer::OnCertTrustChanged, make_scoped_refptr(cert));
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_database.h b/chromium/net/cert/cert_database.h
new file mode 100644
index 00000000000..c4ead812f2f
--- /dev/null
+++ b/chromium/net/cert/cert_database.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_DATABASE_H_
+#define NET_CERT_CERT_DATABASE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/cert/x509_certificate.h"
+
+template <typename T> struct DefaultSingletonTraits;
+template <class ObserverType> class ObserverListThreadSafe;
+
+namespace net {
+
+// This class provides cross-platform functions to verify and add user
+// certificates, and to observe changes to the underlying certificate stores.
+
+// TODO(gauravsh): This class could be augmented with methods
+// for all operations that manipulate the underlying system
+// certificate store.
+
+class NET_EXPORT CertDatabase {
+ public:
+ // A CertDatabase::Observer will be notified on certificate database changes.
+ // The change could be either a new user certificate is added or trust on
+ // a certificate is changed. Observers can register themselves
+ // via CertDatabase::AddObserver, and can un-register with
+ // CertDatabase::RemoveObserver.
+ class NET_EXPORT Observer {
+ public:
+ virtual ~Observer() {}
+
+ // Will be called when a new certificate is added.
+ virtual void OnCertAdded(const X509Certificate* cert) {}
+
+ // Will be called when a certificate is removed.
+ virtual void OnCertRemoved(const X509Certificate* cert) {}
+
+ // Will be called when a certificate's trust is changed.
+ virtual void OnCertTrustChanged(const X509Certificate* cert) {}
+
+ protected:
+ Observer() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Observer);
+ };
+
+ // Returns the CertDatabase singleton.
+ static CertDatabase* GetInstance();
+
+ // Check whether this is a valid user cert that we have the private key for.
+ // Returns OK or a network error code such as ERR_CERT_CONTAINS_ERRORS.
+ int CheckUserCert(X509Certificate* cert);
+
+ // Store user (client) certificate. Assumes CheckUserCert has already passed.
+ // Returns OK, or ERR_ADD_USER_CERT_FAILED if there was a problem saving to
+ // the platform cert database, or possibly other network error codes.
+ int AddUserCert(X509Certificate* cert);
+
+ // Registers |observer| to receive notifications of certificate changes. The
+ // thread on which this is called is the thread on which |observer| will be
+ // called back with notifications.
+ void AddObserver(Observer* observer);
+
+ // Unregisters |observer| from receiving notifications. This must be called
+ // on the same thread on which AddObserver() was called.
+ void RemoveObserver(Observer* observer);
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // Configures the current message loop to observe and forward events from
+ // Keychain services. The MessageLoop must have an associated CFRunLoop,
+ // which means that this must be called from a MessageLoop of TYPE_UI.
+ void SetMessageLoopForKeychainEvents();
+#endif
+
+ private:
+ friend struct DefaultSingletonTraits<CertDatabase>;
+
+ CertDatabase();
+ ~CertDatabase();
+
+ // Broadcasts notifications to all registered observers.
+ void NotifyObserversOfCertAdded(const X509Certificate* cert);
+ void NotifyObserversOfCertRemoved(const X509Certificate* cert);
+ void NotifyObserversOfCertTrustChanged(const X509Certificate* cert);
+
+ const scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
+
+#if defined(USE_NSS) || (defined(OS_MACOSX) && !defined(OS_IOS))
+ class Notifier;
+ friend class Notifier;
+ scoped_ptr<Notifier> notifier_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CertDatabase);
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_DATABASE_H_
diff --git a/chromium/net/cert/cert_database_android.cc b/chromium/net/cert/cert_database_android.cc
new file mode 100644
index 00000000000..9755805026b
--- /dev/null
+++ b/chromium/net/cert/cert_database_android.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include "base/logging.h"
+#include "base/observer_list_threadsafe.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+CertDatabase::CertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {
+}
+
+CertDatabase::~CertDatabase() {}
+
+int CertDatabase::CheckUserCert(X509Certificate* cert) {
+ // NOTE: This method shall never be called on Android.
+ //
+ // On other platforms, it is only used by the SSLAddCertHandler class
+ // to handle veritication and installation of downloaded certificates.
+ //
+ // On Android, the certificate data is passed directly to the system's
+ // CertInstaller activity, which handles verification, naming,
+ // installation and UI (for success/failure).
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert) {
+ // This method is only used by the content SSLAddCertHandler which is
+ // never used on Android.
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_database_ios.cc b/chromium/net/cert/cert_database_ios.cc
new file mode 100644
index 00000000000..f96f22ae2be
--- /dev/null
+++ b/chromium/net/cert/cert_database_ios.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include "base/logging.h"
+#include "base/observer_list_threadsafe.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+CertDatabase::CertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {}
+
+CertDatabase::~CertDatabase() {}
+
+int CertDatabase::CheckUserCert(X509Certificate* cert_obj) {
+ // iOS doesn't handle user certificates.
+ NOTREACHED();
+ return OK;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
+ // iOS doesn't handle user certificates.
+ NOTREACHED();
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_database_mac.cc b/chromium/net/cert/cert_database_mac.cc
new file mode 100644
index 00000000000..76701963fd5
--- /dev/null
+++ b/chromium/net/cert/cert_database_mac.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include <Security/Security.h>
+
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/observer_list_threadsafe.h"
+#include "base/process/process_handle.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "crypto/mac_security_services_lock.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+// Helper that observes events from the Keychain and forwards them to the
+// given CertDatabase.
+class CertDatabase::Notifier {
+ public:
+ // Creates a new Notifier that will forward Keychain events to |cert_db|.
+ // |message_loop| must refer to a thread with an associated CFRunLoop - a
+ // TYPE_UI thread. Events will be dispatched from this message loop.
+ Notifier(CertDatabase* cert_db, base::MessageLoop* message_loop)
+ : cert_db_(cert_db),
+ registered_(false),
+ called_shutdown_(false) {
+ // Ensure an associated CFRunLoop.
+ DCHECK(message_loop->IsType(base::MessageLoop::TYPE_UI));
+ task_runner_ = message_loop->message_loop_proxy();
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Notifier::Init,
+ base::Unretained(this)));
+ }
+
+ // Should be called from the |task_runner_|'s thread. Use Shutdown()
+ // to shutdown on arbitrary threads.
+ ~Notifier() {
+ DCHECK(called_shutdown_);
+ // Only unregister from the same thread where registration was performed.
+ if (registered_ && task_runner_->RunsTasksOnCurrentThread())
+ SecKeychainRemoveCallback(&Notifier::KeychainCallback);
+ }
+
+ void Shutdown() {
+ called_shutdown_ = true;
+ if (!task_runner_->DeleteSoon(FROM_HERE, this)) {
+ // If the task runner is no longer running, it's safe to just delete
+ // the object, since no further events will or can be delivered by
+ // Keychain Services.
+ delete this;
+ }
+ }
+
+ private:
+ void Init() {
+ SecKeychainEventMask event_mask =
+ kSecKeychainListChangedMask | kSecTrustSettingsChangedEventMask;
+ OSStatus status = SecKeychainAddCallback(&Notifier::KeychainCallback,
+ event_mask, this);
+ if (status == noErr)
+ registered_ = true;
+ }
+
+ // SecKeychainCallback function that receives notifications from securityd
+ // and forwards them to the |cert_db_|.
+ static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
+ SecKeychainCallbackInfo* info,
+ void* context);
+
+ CertDatabase* const cert_db_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ bool registered_;
+ bool called_shutdown_;
+};
+
+// static
+OSStatus CertDatabase::Notifier::KeychainCallback(
+ SecKeychainEvent keychain_event,
+ SecKeychainCallbackInfo* info,
+ void* context) {
+ Notifier* that = reinterpret_cast<Notifier*>(context);
+
+ if (info->version > SEC_KEYCHAIN_SETTINGS_VERS1) {
+ NOTREACHED();
+ return errSecWrongSecVersion;
+ }
+
+ if (info->pid == base::GetCurrentProcId()) {
+ // Ignore events generated by the current process, as the assumption is
+ // that they have already been handled. This may miss events that
+ // originated as a result of spawning native dialogs that allow the user
+ // to modify Keychain settings. However, err on the side of missing
+ // events rather than sending too many events.
+ return errSecSuccess;
+ }
+
+ switch (keychain_event) {
+ case kSecKeychainListChangedEvent:
+ case kSecTrustSettingsChangedEvent:
+ that->cert_db_->NotifyObserversOfCertTrustChanged(NULL);
+ break;
+ }
+
+ return errSecSuccess;
+}
+
+void CertDatabase::SetMessageLoopForKeychainEvents() {
+ // Shutdown will take care to delete the notifier on the right thread.
+ if (notifier_.get())
+ notifier_.release()->Shutdown();
+
+ notifier_.reset(new Notifier(this, base::MessageLoopForUI::current()));
+}
+
+CertDatabase::CertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {
+}
+
+CertDatabase::~CertDatabase() {
+ // Shutdown will take care to delete the notifier on the right thread.
+ if (notifier_.get())
+ notifier_.release()->Shutdown();
+}
+
+int CertDatabase::CheckUserCert(X509Certificate* cert) {
+ if (!cert)
+ return ERR_CERT_INVALID;
+ if (cert->HasExpired())
+ return ERR_CERT_DATE_INVALID;
+
+ // Verify the Keychain already has the corresponding private key:
+ SecIdentityRef identity = NULL;
+ OSStatus err = SecIdentityCreateWithCertificate(NULL, cert->os_cert_handle(),
+ &identity);
+ if (err == errSecItemNotFound)
+ return ERR_NO_PRIVATE_KEY_FOR_CERT;
+
+ if (err != noErr || !identity) {
+ // TODO(snej): Map the error code more intelligently.
+ return ERR_CERT_INVALID;
+ }
+
+ CFRelease(identity);
+ return OK;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert) {
+ OSStatus err;
+ {
+ base::AutoLock locked(crypto::GetMacSecurityServicesLock());
+ err = SecCertificateAddToKeychain(cert->os_cert_handle(), NULL);
+ }
+ switch (err) {
+ case noErr:
+ CertDatabase::NotifyObserversOfCertAdded(cert);
+ // Fall through.
+ case errSecDuplicateItem:
+ return OK;
+ default:
+ OSSTATUS_LOG(ERROR, err) << "CertDatabase failed to add cert to keychain";
+ // TODO(snej): Map the error code more intelligently.
+ return ERR_ADD_USER_CERT_FAILED;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_database_nss.cc b/chromium/net/cert/cert_database_nss.cc
new file mode 100644
index 00000000000..5fa272134a2
--- /dev/null
+++ b/chromium/net/cert/cert_database_nss.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include <cert.h>
+#include <pk11pub.h>
+#include <secmod.h>
+
+#include "base/logging.h"
+#include "base/observer_list_threadsafe.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/net_errors.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_nss.h"
+
+namespace net {
+
+// Helper that observes events from the NSSCertDatabase and forwards them to
+// the given CertDatabase.
+class CertDatabase::Notifier : public NSSCertDatabase::Observer {
+ public:
+ explicit Notifier(CertDatabase* cert_db) : cert_db_(cert_db) {
+ NSSCertDatabase::GetInstance()->AddObserver(this);
+ }
+
+ virtual ~Notifier() {
+ NSSCertDatabase::GetInstance()->RemoveObserver(this);
+ }
+
+ // NSSCertDatabase::Observer implementation:
+ virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
+ cert_db_->NotifyObserversOfCertAdded(cert);
+ }
+
+ virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {
+ cert_db_->NotifyObserversOfCertRemoved(cert);
+ }
+
+ virtual void OnCertTrustChanged(const X509Certificate* cert) OVERRIDE {
+ cert_db_->NotifyObserversOfCertTrustChanged(cert);
+ }
+
+ private:
+ CertDatabase* cert_db_;
+
+ DISALLOW_COPY_AND_ASSIGN(Notifier);
+};
+
+CertDatabase::CertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {
+ // Observe NSSCertDatabase events and forward them to observers of
+ // CertDatabase. This also makes sure that NSS has been initialized.
+ notifier_.reset(new Notifier(this));
+}
+
+CertDatabase::~CertDatabase() {}
+
+int CertDatabase::CheckUserCert(X509Certificate* cert_obj) {
+ if (!cert_obj)
+ return ERR_CERT_INVALID;
+ if (cert_obj->HasExpired())
+ return ERR_CERT_DATE_INVALID;
+
+ // Check if the private key corresponding to the certificate exist
+ // We shouldn't accept any random client certificate sent by a CA.
+
+ // Note: The NSS source documentation wrongly suggests that this
+ // also imports the certificate if the private key exists. This
+ // doesn't seem to be the case.
+
+ CERTCertificate* cert = cert_obj->os_cert_handle();
+ PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL);
+ if (!slot)
+ return ERR_NO_PRIVATE_KEY_FOR_CERT;
+
+ PK11_FreeSlot(slot);
+
+ return OK;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
+ CERTCertificate* cert = cert_obj->os_cert_handle();
+ CK_OBJECT_HANDLE key;
+ crypto::ScopedPK11Slot slot(PK11_KeyForCertExists(cert, &key, NULL));
+ if (!slot.get())
+ return ERR_NO_PRIVATE_KEY_FOR_CERT;
+
+ std::string nickname = x509_util::GetUniqueNicknameForSlot(
+ cert_obj->GetDefaultNickname(USER_CERT),
+ &cert->derSubject,
+ slot.get());
+
+ SECStatus rv;
+ {
+ crypto::AutoNSSWriteLock lock;
+ rv = PK11_ImportCert(slot.get(), cert, key, nickname.c_str(), PR_FALSE);
+ }
+
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Couldn't import user certificate. " << PORT_GetError();
+ return ERR_ADD_USER_CERT_FAILED;
+ }
+
+ NotifyObserversOfCertAdded(cert_obj);
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_database_openssl.cc b/chromium/net/cert/cert_database_openssl.cc
new file mode 100644
index 00000000000..23b64cc256f
--- /dev/null
+++ b/chromium/net/cert/cert_database_openssl.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include <openssl/x509.h>
+
+#include "base/logging.h"
+#include "base/observer_list_threadsafe.h"
+#include "crypto/openssl_util.h"
+#include "net/base/crypto_module.h"
+#include "net/base/net_errors.h"
+#include "net/base/openssl_private_key_store.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+CertDatabase::CertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {
+}
+
+CertDatabase::~CertDatabase() {}
+
+// This method is used to check a client certificate before trying to
+// install it on the system, which will happen later by calling
+// AddUserCert() below.
+//
+// On the Linux/OpenSSL build, there is simply no system keystore, but
+// OpenSSLPrivateKeyStore() implements a small in-memory store for
+// (public/private) key pairs generated through keygen.
+//
+// Try to check for a private key in the in-memory store to check
+// for the case when the browser is trying to install a server-generated
+// certificate from a <keygen> exchange.
+int CertDatabase::CheckUserCert(X509Certificate* cert) {
+ if (!cert)
+ return ERR_CERT_INVALID;
+ if (cert->HasExpired())
+ return ERR_CERT_DATE_INVALID;
+
+ // X509_PUBKEY_get() transfers ownership, not X509_get_X509_PUBKEY()
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> public_key(
+ X509_PUBKEY_get(X509_get_X509_PUBKEY(cert->os_cert_handle())));
+
+ if (!OpenSSLPrivateKeyStore::HasPrivateKey(public_key.get()))
+ return ERR_NO_PRIVATE_KEY_FOR_CERT;
+
+ return OK;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert) {
+ // There is no certificate store on the Linux/OpenSSL build.
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_database_win.cc b/chromium/net/cert/cert_database_win.cc
new file mode 100644
index 00000000000..9bf378cc350
--- /dev/null
+++ b/chromium/net/cert/cert_database_win.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_database.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+#pragma comment(lib, "crypt32.lib")
+
+#include "base/observer_list_threadsafe.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+CertDatabase::CertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {
+}
+
+CertDatabase::~CertDatabase() {}
+
+int CertDatabase::CheckUserCert(X509Certificate* cert) {
+ if (!cert)
+ return ERR_CERT_INVALID;
+ if (cert->HasExpired())
+ return ERR_CERT_DATE_INVALID;
+
+ // TODO(rsleevi): Should CRYPT_FIND_SILENT_KEYSET_FLAG be specified? A UI
+ // may be shown here / this call may block.
+ if (!CryptFindCertificateKeyProvInfo(cert->os_cert_handle(), 0, NULL))
+ return ERR_NO_PRIVATE_KEY_FOR_CERT;
+
+ return OK;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert) {
+ // TODO(rsleevi): Would it be more appropriate to have the CertDatabase take
+ // construction parameters (Keychain filepath on Mac OS X, PKCS #11 slot on
+ // NSS, and Store Type / Path) here? For now, certs will be stashed into the
+ // user's personal store, which will not automatically mark them as trusted,
+ // but will allow them to be used for client auth.
+ HCERTSTORE cert_db = CertOpenSystemStore(NULL, L"MY");
+ if (!cert_db)
+ return ERR_ADD_USER_CERT_FAILED;
+
+ BOOL added = CertAddCertificateContextToStore(cert_db,
+ cert->os_cert_handle(),
+ CERT_STORE_ADD_USE_EXISTING,
+ NULL);
+
+ CertCloseStore(cert_db, 0);
+
+ if (!added)
+ return ERR_ADD_USER_CERT_FAILED;
+
+ NotifyObserversOfCertAdded(cert);
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_status_flags.cc b/chromium/net/cert/cert_status_flags.cc
new file mode 100644
index 00000000000..8cb736c5485
--- /dev/null
+++ b/chromium/net/cert/cert_status_flags.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_status_flags.h"
+
+#include "base/logging.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+bool IsCertStatusMinorError(CertStatus cert_status) {
+ static const CertStatus kMinorErrors =
+ CERT_STATUS_UNABLE_TO_CHECK_REVOCATION |
+ CERT_STATUS_NO_REVOCATION_MECHANISM;
+ cert_status &= CERT_STATUS_ALL_ERRORS;
+ return cert_status != 0 && (cert_status & ~kMinorErrors) == 0;
+}
+
+CertStatus MapNetErrorToCertStatus(int error) {
+ switch (error) {
+ case ERR_CERT_COMMON_NAME_INVALID:
+ return CERT_STATUS_COMMON_NAME_INVALID;
+ case ERR_CERT_DATE_INVALID:
+ return CERT_STATUS_DATE_INVALID;
+ case ERR_CERT_AUTHORITY_INVALID:
+ return CERT_STATUS_AUTHORITY_INVALID;
+ case ERR_CERT_NO_REVOCATION_MECHANISM:
+ return CERT_STATUS_NO_REVOCATION_MECHANISM;
+ case ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
+ return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+ case ERR_CERT_REVOKED:
+ return CERT_STATUS_REVOKED;
+ // We added the ERR_CERT_CONTAINS_ERRORS error code when we were using
+ // WinInet, but we never figured out how it differs from ERR_CERT_INVALID.
+ // We should not use ERR_CERT_CONTAINS_ERRORS in new code.
+ case ERR_CERT_CONTAINS_ERRORS:
+ NOTREACHED();
+ // Falls through.
+ case ERR_CERT_INVALID:
+ return CERT_STATUS_INVALID;
+ case ERR_CERT_WEAK_SIGNATURE_ALGORITHM:
+ return CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
+ case ERR_CERT_WEAK_KEY:
+ return CERT_STATUS_WEAK_KEY;
+ default:
+ return 0;
+ }
+}
+
+int MapCertStatusToNetError(CertStatus cert_status) {
+ // A certificate may have multiple errors. We report the most
+ // serious error.
+
+ // Unrecoverable errors
+ if (cert_status & CERT_STATUS_REVOKED)
+ return ERR_CERT_REVOKED;
+ if (cert_status & CERT_STATUS_INVALID)
+ return ERR_CERT_INVALID;
+
+ // Recoverable errors
+ if (cert_status & CERT_STATUS_AUTHORITY_INVALID)
+ return ERR_CERT_AUTHORITY_INVALID;
+ if (cert_status & CERT_STATUS_COMMON_NAME_INVALID)
+ return ERR_CERT_COMMON_NAME_INVALID;
+ if (cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM)
+ return ERR_CERT_WEAK_SIGNATURE_ALGORITHM;
+ if (cert_status & CERT_STATUS_WEAK_KEY)
+ return ERR_CERT_WEAK_KEY;
+ if (cert_status & CERT_STATUS_DATE_INVALID)
+ return ERR_CERT_DATE_INVALID;
+
+ // Unknown status. Give it the benefit of the doubt.
+ if (cert_status & CERT_STATUS_UNABLE_TO_CHECK_REVOCATION)
+ return ERR_CERT_UNABLE_TO_CHECK_REVOCATION;
+ if (cert_status & CERT_STATUS_NO_REVOCATION_MECHANISM)
+ return ERR_CERT_NO_REVOCATION_MECHANISM;
+
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_status_flags.h b/chromium/net/cert/cert_status_flags.h
new file mode 100644
index 00000000000..8431032f679
--- /dev/null
+++ b/chromium/net/cert/cert_status_flags.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_STATUS_FLAGS_H_
+#define NET_CERT_CERT_STATUS_FLAGS_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Bitmask of status flags of a certificate, representing any errors, as well as
+// other non-error status information such as whether the certificate is EV.
+typedef uint32 CertStatus;
+
+// The possible status bits for CertStatus.
+// NOTE: Because these names have appeared in bug reports, we preserve them as
+// MACRO_STYLE for continuity, instead of renaming them to kConstantStyle as
+// befits most static consts.
+// Bits 0 to 15 are for errors.
+static const CertStatus CERT_STATUS_ALL_ERRORS = 0xFFFF;
+static const CertStatus CERT_STATUS_COMMON_NAME_INVALID = 1 << 0;
+static const CertStatus CERT_STATUS_DATE_INVALID = 1 << 1;
+static const CertStatus CERT_STATUS_AUTHORITY_INVALID = 1 << 2;
+// 1 << 3 is reserved for ERR_CERT_CONTAINS_ERRORS (not useful with WinHTTP).
+static const CertStatus CERT_STATUS_NO_REVOCATION_MECHANISM = 1 << 4;
+static const CertStatus CERT_STATUS_UNABLE_TO_CHECK_REVOCATION = 1 << 5;
+static const CertStatus CERT_STATUS_REVOKED = 1 << 6;
+static const CertStatus CERT_STATUS_INVALID = 1 << 7;
+static const CertStatus CERT_STATUS_WEAK_SIGNATURE_ALGORITHM = 1 << 8;
+// 1 << 9 was used for CERT_STATUS_NOT_IN_DNS
+static const CertStatus CERT_STATUS_NON_UNIQUE_NAME = 1 << 10;
+static const CertStatus CERT_STATUS_WEAK_KEY = 1 << 11;
+
+// Bits 16 to 31 are for non-error statuses.
+static const CertStatus CERT_STATUS_IS_EV = 1 << 16;
+static const CertStatus CERT_STATUS_REV_CHECKING_ENABLED = 1 << 17;
+// bit 18 was CERT_STATUS_IS_DNSSEC.
+
+// Returns true if the specified cert status has an error set.
+static inline bool IsCertStatusError(CertStatus status) {
+ return (CERT_STATUS_ALL_ERRORS & status) != 0;
+}
+
+// IsCertStatusMinorError returns true iff |cert_status| indicates a condition
+// that should typically be ignored by automated requests. (i.e. a revocation
+// check failure.)
+NET_EXPORT bool IsCertStatusMinorError(CertStatus cert_status);
+
+// Maps a network error code to the equivalent certificate status flag. If
+// the error code is not a certificate error, it is mapped to 0.
+NET_EXPORT CertStatus MapNetErrorToCertStatus(int error);
+
+// Maps the most serious certificate error in the certificate status flags
+// to the equivalent network error code.
+NET_EXPORT int MapCertStatusToNetError(CertStatus cert_status);
+
+} // namespace net
+
+#endif // NET_CERT_CERT_STATUS_FLAGS_H_
diff --git a/chromium/net/cert/cert_trust_anchor_provider.h b/chromium/net/cert/cert_trust_anchor_provider.h
new file mode 100644
index 00000000000..1712b2d0211
--- /dev/null
+++ b/chromium/net/cert/cert_trust_anchor_provider.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_TRUST_ANCHOR_PROVIDER_H_
+#define NET_CERT_CERT_TRUST_ANCHOR_PROVIDER_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
+
+// Interface to retrieve the current list of additional trust anchors.
+// This is used by CertVerifier to get a list of anchors to trust in addition to
+// the anchors known to the CertVerifier.
+class NET_EXPORT CertTrustAnchorProvider {
+ public:
+ virtual ~CertTrustAnchorProvider() {}
+
+ // Returns a list of certificates to be used as trust anchors during
+ // certificate validation, in addition to (eg: the union of) any pre-existing
+ // or pre-configured trust anchors.
+ virtual const CertificateList& GetAdditionalTrustAnchors() = 0;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_TRUST_ANCHOR_PROVIDER_H_
diff --git a/chromium/net/cert/cert_type.h b/chromium/net/cert/cert_type.h
new file mode 100644
index 00000000000..cb212274b12
--- /dev/null
+++ b/chromium/net/cert/cert_type.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_TYPE_H_
+#define NET_CERT_CERT_TYPE_H_
+
+namespace net {
+
+// Constants to classify the type of a certificate.
+// This is only used in the context of CertDatabase, but is defined outside to
+// avoid an awkwardly long type name.
+// The type is a combination of intrinsic properties, such as the presense of an
+// Certificate Authority Basic Constraint, and assigned trust values. For
+// example, a cert with no basic constraints or trust would be classified as
+// UNKNOWN_CERT. If that cert is then trusted with SetCertTrust(cert,
+// SERVER_CERT, TRUSTED_SSL), it would become a SERVER_CERT.
+enum CertType {
+ UNKNOWN_CERT,
+ CA_CERT,
+ USER_CERT,
+ SERVER_CERT,
+ NUM_CERT_TYPES
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_TYPE_H_
diff --git a/chromium/net/cert/cert_verifier.cc b/chromium/net/cert/cert_verifier.cc
new file mode 100644
index 00000000000..e4acec4f288
--- /dev/null
+++ b/chromium/net/cert/cert_verifier.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verifier.h"
+
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/multi_threaded_cert_verifier.h"
+
+namespace net {
+
+CertVerifier* CertVerifier::CreateDefault() {
+ return new MultiThreadedCertVerifier(CertVerifyProc::CreateDefault());
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verifier.h b/chromium/net/cert/cert_verifier.h
new file mode 100644
index 00000000000..743c8350b27
--- /dev/null
+++ b/chromium/net/cert/cert_verifier.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFIER_H_
+#define NET_CERT_CERT_VERIFIER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class BoundNetLog;
+class CertVerifyResult;
+class CRLSet;
+class X509Certificate;
+
+// CertVerifier represents a service for verifying certificates.
+//
+// CertVerifiers can handle multiple requests at a time. A simpler alternative
+// for consumers that only have 1 outstanding request at a time is to create a
+// SingleRequestCertVerifier wrapper around CertVerifier (which will
+// automatically cancel the single request when it goes out of scope).
+class NET_EXPORT CertVerifier {
+ public:
+ // Opaque pointer type used to cancel outstanding requests.
+ typedef void* RequestHandle;
+
+ enum VerifyFlags {
+ // If set, enables online revocation checking via CRLs and OCSP for the
+ // certificate chain.
+ VERIFY_REV_CHECKING_ENABLED = 1 << 0,
+
+ // If set, and the certificate being verified may be an EV certificate,
+ // attempt to verify the certificate according to the EV processing
+ // guidelines. In order to successfully verify a certificate as EV,
+ // either an online or offline revocation check must be successfully
+ // completed. To ensure it's possible to complete a revocation check,
+ // callers should also specify either VERIFY_REV_CHECKING_ENABLED or
+ // VERIFY_REV_CHECKING_ENABLED_EV_ONLY (to enable online checks), and
+ // VERIFY_CERT_IO_ENABLED (to enable network fetches for online checks).
+ VERIFY_EV_CERT = 1 << 1,
+
+ // If set, permits NSS to use the network when verifying certificates,
+ // such as to fetch missing intermediates or to check OCSP or CRLs.
+ // TODO(rsleevi): http://crbug.com/143300 - Define this flag for all
+ // verification engines with well-defined semantics, rather than being
+ // NSS only.
+ VERIFY_CERT_IO_ENABLED = 1 << 2,
+
+ // If set, enables online revocation checking via CRLs or OCSP when the
+ // chain is not covered by a fresh CRLSet, but only for certificates which
+ // may be EV, and only when VERIFY_EV_CERT is also set.
+ VERIFY_REV_CHECKING_ENABLED_EV_ONLY = 1 << 3,
+
+ // If set, this is equivalent to VERIFY_REV_CHECKING_ENABLED, in that it
+ // enables online revocation checking via CRLs or OCSP, but only
+ // for certificates issued by non-public trust anchors. Failure to check
+ // revocation is treated as a hard failure.
+ // Note: If VERIFY_CERT_IO_ENABLE is not also supplied, certificates
+ // that chain to local trust anchors will likely fail - for example, due to
+ // lacking fresh cached revocation issue (Windows) or because OCSP stapling
+ // can only provide information for the leaf, and not for any
+ // intermediates.
+ VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS = 1 << 4,
+ };
+
+ // When the verifier is destroyed, all certificate verification requests are
+ // canceled, and their completion callbacks will not be called.
+ virtual ~CertVerifier() {}
+
+ // Verifies the given certificate against the given hostname as an SSL server.
+ // Returns OK if successful or an error code upon failure.
+ //
+ // The |*verify_result| structure, including the |verify_result->cert_status|
+ // bitmask, is always filled out regardless of the return value. If the
+ // certificate has multiple errors, the corresponding status flags are set in
+ // |verify_result->cert_status|, and the error code for the most serious
+ // error is returned.
+ //
+ // |flags| is bitwise OR'd of VerifyFlags.
+ // If VERIFY_REV_CHECKING_ENABLED is set in |flags|, certificate revocation
+ // checking is performed.
+ //
+ // If VERIFY_EV_CERT is set in |flags| too, EV certificate verification is
+ // performed. If |flags| is VERIFY_EV_CERT (that is,
+ // VERIFY_REV_CHECKING_ENABLED is not set), EV certificate verification will
+ // not be performed.
+ //
+ // |crl_set| points to an optional CRLSet structure which can be used to
+ // avoid revocation checks over the network.
+ //
+ // |callback| must not be null. ERR_IO_PENDING is returned if the operation
+ // could not be completed synchronously, in which case the result code will
+ // be passed to the callback when available.
+ //
+ // |*out_req| will be filled with a handle to the async request.
+ // This handle is not valid after the request has completed.
+ //
+ // TODO(rsleevi): Move CRLSet* out of the CertVerifier signature.
+ virtual int Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) = 0;
+
+ // Cancels the specified request. |req| is the handle returned by Verify().
+ // After a request is canceled, its completion callback will not be called.
+ virtual void CancelRequest(RequestHandle req) = 0;
+
+ // Creates a CertVerifier implementation that verifies certificates using
+ // the preferred underlying cryptographic libraries.
+ static CertVerifier* CreateDefault();
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFIER_H_
diff --git a/chromium/net/cert/cert_verify_proc.cc b/chromium/net/cert/cert_verify_proc.cc
new file mode 100644
index 00000000000..ec1ef682b47
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc.cc
@@ -0,0 +1,392 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc.h"
+
+#include "base/metrics/histogram.h"
+#include "base/sha1.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/x509_certificate.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include "net/cert/cert_verify_proc_nss.h"
+#elif defined(USE_OPENSSL) && !defined(OS_ANDROID)
+#include "net/cert/cert_verify_proc_openssl.h"
+#elif defined(OS_ANDROID)
+#include "net/cert/cert_verify_proc_android.h"
+#elif defined(OS_MACOSX)
+#include "net/cert/cert_verify_proc_mac.h"
+#elif defined(OS_WIN)
+#include "net/cert/cert_verify_proc_win.h"
+#else
+#error Implement certificate verification.
+#endif
+
+
+namespace net {
+
+namespace {
+
+// Constants used to build histogram names
+const char kLeafCert[] = "Leaf";
+const char kIntermediateCert[] = "Intermediate";
+const char kRootCert[] = "Root";
+// Matches the order of X509Certificate::PublicKeyType
+const char* const kCertTypeStrings[] = {
+ "Unknown",
+ "RSA",
+ "DSA",
+ "ECDSA",
+ "DH",
+ "ECDH"
+};
+// Histogram buckets for RSA/DSA/DH key sizes.
+const int kRsaDsaKeySizes[] = {512, 768, 1024, 1536, 2048, 3072, 4096, 8192,
+ 16384};
+// Histogram buckets for ECDSA/ECDH key sizes. The list is based upon the FIPS
+// 186-4 approved curves.
+const int kEccKeySizes[] = {163, 192, 224, 233, 256, 283, 384, 409, 521, 571};
+
+const char* CertTypeToString(int cert_type) {
+ if (cert_type < 0 ||
+ static_cast<size_t>(cert_type) >= arraysize(kCertTypeStrings)) {
+ return "Unsupported";
+ }
+ return kCertTypeStrings[cert_type];
+}
+
+void RecordPublicKeyHistogram(const char* chain_position,
+ bool baseline_keysize_applies,
+ size_t size_bits,
+ X509Certificate::PublicKeyType cert_type) {
+ std::string histogram_name =
+ base::StringPrintf("CertificateType2.%s.%s.%s",
+ baseline_keysize_applies ? "BR" : "NonBR",
+ chain_position,
+ CertTypeToString(cert_type));
+ // Do not use UMA_HISTOGRAM_... macros here, as it caches the Histogram
+ // instance and thus only works if |histogram_name| is constant.
+ base::HistogramBase* counter = NULL;
+
+ // Histogram buckets are contingent upon the underlying algorithm being used.
+ if (cert_type == X509Certificate::kPublicKeyTypeECDH ||
+ cert_type == X509Certificate::kPublicKeyTypeECDSA) {
+ // Typical key sizes match SECP/FIPS 186-3 recommendations for prime and
+ // binary curves - which range from 163 bits to 571 bits.
+ counter = base::CustomHistogram::FactoryGet(
+ histogram_name,
+ base::CustomHistogram::ArrayToCustomRanges(kEccKeySizes,
+ arraysize(kEccKeySizes)),
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ } else {
+ // Key sizes < 1024 bits should cause errors, while key sizes > 16K are not
+ // uniformly supported by the underlying cryptographic libraries.
+ counter = base::CustomHistogram::FactoryGet(
+ histogram_name,
+ base::CustomHistogram::ArrayToCustomRanges(kRsaDsaKeySizes,
+ arraysize(kRsaDsaKeySizes)),
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ }
+ counter->Add(size_bits);
+}
+
+// Returns true if |type| is |kPublicKeyTypeRSA| or |kPublicKeyTypeDSA|, and
+// if |size_bits| is < 1024. Note that this means there may be false
+// negatives: keys for other algorithms and which are weak will pass this
+// test.
+bool IsWeakKey(X509Certificate::PublicKeyType type, size_t size_bits) {
+ switch (type) {
+ case X509Certificate::kPublicKeyTypeRSA:
+ case X509Certificate::kPublicKeyTypeDSA:
+ return size_bits < 1024;
+ default:
+ return false;
+ }
+}
+
+// Returns true if |cert| contains a known-weak key. Additionally, histograms
+// the observed keys for future tightening of the definition of what
+// constitutes a weak key.
+bool ExaminePublicKeys(const scoped_refptr<X509Certificate>& cert,
+ bool should_histogram) {
+ // The effective date of the CA/Browser Forum's Baseline Requirements -
+ // 2012-07-01 00:00:00 UTC.
+ const base::Time kBaselineEffectiveDate =
+ base::Time::FromInternalValue(GG_INT64_C(12985574400000000));
+ // The effective date of the key size requirements from Appendix A, v1.1.5
+ // 2014-01-01 00:00:00 UTC.
+ const base::Time kBaselineKeysizeEffectiveDate =
+ base::Time::FromInternalValue(GG_INT64_C(13033008000000000));
+
+ size_t size_bits = 0;
+ X509Certificate::PublicKeyType type = X509Certificate::kPublicKeyTypeUnknown;
+ bool weak_key = false;
+ bool baseline_keysize_applies =
+ cert->valid_start() >= kBaselineEffectiveDate &&
+ cert->valid_expiry() >= kBaselineKeysizeEffectiveDate;
+
+ X509Certificate::GetPublicKeyInfo(cert->os_cert_handle(), &size_bits, &type);
+ if (should_histogram) {
+ RecordPublicKeyHistogram(kLeafCert, baseline_keysize_applies, size_bits,
+ type);
+ }
+ if (IsWeakKey(type, size_bits))
+ weak_key = true;
+
+ const X509Certificate::OSCertHandles& intermediates =
+ cert->GetIntermediateCertificates();
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ X509Certificate::GetPublicKeyInfo(intermediates[i], &size_bits, &type);
+ if (should_histogram) {
+ RecordPublicKeyHistogram(
+ (i < intermediates.size() - 1) ? kIntermediateCert : kRootCert,
+ baseline_keysize_applies,
+ size_bits,
+ type);
+ }
+ if (!weak_key && IsWeakKey(type, size_bits))
+ weak_key = true;
+ }
+
+ return weak_key;
+}
+
+} // namespace
+
+// static
+CertVerifyProc* CertVerifyProc::CreateDefault() {
+#if defined(USE_NSS) || defined(OS_IOS)
+ return new CertVerifyProcNSS();
+#elif defined(USE_OPENSSL) && !defined(OS_ANDROID)
+ return new CertVerifyProcOpenSSL();
+#elif defined(OS_ANDROID)
+ return new CertVerifyProcAndroid();
+#elif defined(OS_MACOSX)
+ return new CertVerifyProcMac();
+#elif defined(OS_WIN)
+ return new CertVerifyProcWin();
+#else
+ return NULL;
+#endif
+}
+
+CertVerifyProc::CertVerifyProc() {}
+
+CertVerifyProc::~CertVerifyProc() {}
+
+int CertVerifyProc::Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ verify_result->Reset();
+ verify_result->verified_cert = cert;
+
+ if (IsBlacklisted(cert)) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ return ERR_CERT_REVOKED;
+ }
+
+ // We do online revocation checking for EV certificates that aren't covered
+ // by a fresh CRLSet.
+ // TODO(rsleevi): http://crbug.com/142974 - Allow preferences to fully
+ // disable revocation checking.
+ if (flags & CertVerifier::VERIFY_EV_CERT)
+ flags |= CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY;
+
+ int rv = VerifyInternal(cert, hostname, flags, crl_set,
+ additional_trust_anchors, verify_result);
+
+ // This check is done after VerifyInternal so that VerifyInternal can fill
+ // in the list of public key hashes.
+ if (IsPublicKeyBlacklisted(verify_result->public_key_hashes)) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ rv = MapCertStatusToNetError(verify_result->cert_status);
+ }
+
+ // Check for weak keys in the entire verified chain.
+ bool weak_key = ExaminePublicKeys(verify_result->verified_cert,
+ verify_result->is_issued_by_known_root);
+
+ if (weak_key) {
+ verify_result->cert_status |= CERT_STATUS_WEAK_KEY;
+ // Avoid replacing a more serious error, such as an OS/library failure,
+ // by ensuring that if verification failed, it failed with a certificate
+ // error.
+ if (rv == OK || IsCertificateError(rv))
+ rv = MapCertStatusToNetError(verify_result->cert_status);
+ }
+
+ // Treat certificates signed using broken signature algorithms as invalid.
+ if (verify_result->has_md2 || verify_result->has_md4) {
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ rv = MapCertStatusToNetError(verify_result->cert_status);
+ }
+
+ // Flag certificates using weak signature algorithms.
+ if (verify_result->has_md5) {
+ verify_result->cert_status |= CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
+ // Avoid replacing a more serious error, such as an OS/library failure,
+ // by ensuring that if verification failed, it failed with a certificate
+ // error.
+ if (rv == OK || IsCertificateError(rv))
+ rv = MapCertStatusToNetError(verify_result->cert_status);
+ }
+
+ // Flag certificates from publicly-trusted CAs that are issued to intranet
+ // hosts. While the CA/Browser Forum Baseline Requirements (v1.1) permit
+ // these to be issued until 1 November 2015, they represent a real risk for
+ // the deployment of gTLDs and are being phased out ahead of the hard
+ // deadline.
+ // TODO(rsleevi): http://crbug.com/119212 - Also match internal IP address
+ // ranges.
+ if (verify_result->is_issued_by_known_root && IsHostnameNonUnique(hostname)) {
+ verify_result->cert_status |= CERT_STATUS_NON_UNIQUE_NAME;
+ }
+
+ return rv;
+}
+
+// static
+bool CertVerifyProc::IsBlacklisted(X509Certificate* cert) {
+ static const unsigned kComodoSerialBytes = 16;
+ static const uint8 kComodoSerials[][kComodoSerialBytes] = {
+ // Not a real certificate. For testing only.
+ {0x07,0x7a,0x59,0xbc,0xd5,0x34,0x59,0x60,0x1c,0xa6,0x90,0x72,0x67,0xa6,0xdd,0x1c},
+
+ // The next nine certificates all expire on Fri Mar 14 23:59:59 2014.
+ // Some serial numbers actually have a leading 0x00 byte required to
+ // encode a positive integer in DER if the most significant bit is 0.
+ // We omit the leading 0x00 bytes to make all serial numbers 16 bytes.
+
+ // Subject: CN=mail.google.com
+ // subjectAltName dNSName: mail.google.com, www.mail.google.com
+ {0x04,0x7e,0xcb,0xe9,0xfc,0xa5,0x5f,0x7b,0xd0,0x9e,0xae,0x36,0xe1,0x0c,0xae,0x1e},
+ // Subject: CN=global trustee
+ // subjectAltName dNSName: global trustee
+ // Note: not a CA certificate.
+ {0xd8,0xf3,0x5f,0x4e,0xb7,0x87,0x2b,0x2d,0xab,0x06,0x92,0xe3,0x15,0x38,0x2f,0xb0},
+ // Subject: CN=login.live.com
+ // subjectAltName dNSName: login.live.com, www.login.live.com
+ {0xb0,0xb7,0x13,0x3e,0xd0,0x96,0xf9,0xb5,0x6f,0xae,0x91,0xc8,0x74,0xbd,0x3a,0xc0},
+ // Subject: CN=addons.mozilla.org
+ // subjectAltName dNSName: addons.mozilla.org, www.addons.mozilla.org
+ {0x92,0x39,0xd5,0x34,0x8f,0x40,0xd1,0x69,0x5a,0x74,0x54,0x70,0xe1,0xf2,0x3f,0x43},
+ // Subject: CN=login.skype.com
+ // subjectAltName dNSName: login.skype.com, www.login.skype.com
+ {0xe9,0x02,0x8b,0x95,0x78,0xe4,0x15,0xdc,0x1a,0x71,0x0a,0x2b,0x88,0x15,0x44,0x47},
+ // Subject: CN=login.yahoo.com
+ // subjectAltName dNSName: login.yahoo.com, www.login.yahoo.com
+ {0xd7,0x55,0x8f,0xda,0xf5,0xf1,0x10,0x5b,0xb2,0x13,0x28,0x2b,0x70,0x77,0x29,0xa3},
+ // Subject: CN=www.google.com
+ // subjectAltName dNSName: www.google.com, google.com
+ {0xf5,0xc8,0x6a,0xf3,0x61,0x62,0xf1,0x3a,0x64,0xf5,0x4f,0x6d,0xc9,0x58,0x7c,0x06},
+ // Subject: CN=login.yahoo.com
+ // subjectAltName dNSName: login.yahoo.com
+ {0x39,0x2a,0x43,0x4f,0x0e,0x07,0xdf,0x1f,0x8a,0xa3,0x05,0xde,0x34,0xe0,0xc2,0x29},
+ // Subject: CN=login.yahoo.com
+ // subjectAltName dNSName: login.yahoo.com
+ {0x3e,0x75,0xce,0xd4,0x6b,0x69,0x30,0x21,0x21,0x88,0x30,0xae,0x86,0xa8,0x2a,0x71},
+ };
+
+ const std::string& serial_number = cert->serial_number();
+ if (!serial_number.empty() && (serial_number[0] & 0x80) != 0) {
+ // This is a negative serial number, which isn't technically allowed but
+ // which probably happens. In order to avoid confusing a negative serial
+ // number with a positive one once the leading zeros have been removed, we
+ // disregard it.
+ return false;
+ }
+
+ base::StringPiece serial(serial_number);
+ // Remove leading zeros.
+ while (serial.size() > 1 && serial[0] == 0)
+ serial.remove_prefix(1);
+
+ if (serial.size() == kComodoSerialBytes) {
+ for (unsigned i = 0; i < arraysize(kComodoSerials); i++) {
+ if (memcmp(kComodoSerials[i], serial.data(), kComodoSerialBytes) == 0) {
+ UMA_HISTOGRAM_ENUMERATION("Net.SSLCertBlacklisted", i,
+ arraysize(kComodoSerials) + 1);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
+// NOTE: This implementation assumes and enforces that the hashes are SHA1.
+bool CertVerifyProc::IsPublicKeyBlacklisted(
+ const HashValueVector& public_key_hashes) {
+ static const unsigned kNumHashes = 10;
+ static const uint8 kHashes[kNumHashes][base::kSHA1Length] = {
+ // Subject: CN=DigiNotar Root CA
+ // Issuer: CN=Entrust.net x2 and self-signed
+ {0x41, 0x0f, 0x36, 0x36, 0x32, 0x58, 0xf3, 0x0b, 0x34, 0x7d,
+ 0x12, 0xce, 0x48, 0x63, 0xe4, 0x33, 0x43, 0x78, 0x06, 0xa8},
+ // Subject: CN=DigiNotar Cyber CA
+ // Issuer: CN=GTE CyberTrust Global Root
+ {0xc4, 0xf9, 0x66, 0x37, 0x16, 0xcd, 0x5e, 0x71, 0xd6, 0x95,
+ 0x0b, 0x5f, 0x33, 0xce, 0x04, 0x1c, 0x95, 0xb4, 0x35, 0xd1},
+ // Subject: CN=DigiNotar Services 1024 CA
+ // Issuer: CN=Entrust.net
+ {0xe2, 0x3b, 0x8d, 0x10, 0x5f, 0x87, 0x71, 0x0a, 0x68, 0xd9,
+ 0x24, 0x80, 0x50, 0xeb, 0xef, 0xc6, 0x27, 0xbe, 0x4c, 0xa6},
+ // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2
+ // Issuer: CN=Staat der Nederlanden Organisatie CA - G2
+ {0x7b, 0x2e, 0x16, 0xbc, 0x39, 0xbc, 0xd7, 0x2b, 0x45, 0x6e,
+ 0x9f, 0x05, 0x5d, 0x1d, 0xe6, 0x15, 0xb7, 0x49, 0x45, 0xdb},
+ // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven
+ // Issuer: CN=Staat der Nederlanden Overheid CA
+ {0xe8, 0xf9, 0x12, 0x00, 0xc6, 0x5c, 0xee, 0x16, 0xe0, 0x39,
+ 0xb9, 0xf8, 0x83, 0x84, 0x16, 0x61, 0x63, 0x5f, 0x81, 0xc5},
+ // Subject: O=Digicert Sdn. Bhd.
+ // Issuer: CN=GTE CyberTrust Global Root
+ // Expires: Jul 17 15:16:54 2012 GMT
+ {0x01, 0x29, 0xbc, 0xd5, 0xb4, 0x48, 0xae, 0x8d, 0x24, 0x96,
+ 0xd1, 0xc3, 0xe1, 0x97, 0x23, 0x91, 0x90, 0x88, 0xe1, 0x52},
+ // Subject: O=Digicert Sdn. Bhd.
+ // Issuer: CN=Entrust.net Certification Authority (2048)
+ // Expires: Jul 16 17:53:37 2015 GMT
+ {0xd3, 0x3c, 0x5b, 0x41, 0xe4, 0x5c, 0xc4, 0xb3, 0xbe, 0x9a,
+ 0xd6, 0x95, 0x2c, 0x4e, 0xcc, 0x25, 0x28, 0x03, 0x29, 0x81},
+ // Issuer: CN=Trustwave Organization Issuing CA, Level 2
+ // Covers two certificates, the latter of which expires Apr 15 21:09:30
+ // 2021 GMT.
+ {0xe1, 0x2d, 0x89, 0xf5, 0x6d, 0x22, 0x76, 0xf8, 0x30, 0xe6,
+ 0xce, 0xaf, 0xa6, 0x6c, 0x72, 0x5c, 0x0b, 0x41, 0xa9, 0x32},
+ // Cyberoam CA certificate. Private key leaked, but this certificate would
+ // only have been installed by Cyberoam customers. The certificate expires
+ // in 2036, but we can probably remove in a couple of years (2014).
+ {0xd9, 0xf5, 0xc6, 0xce, 0x57, 0xff, 0xaa, 0x39, 0xcc, 0x7e,
+ 0xd1, 0x72, 0xbd, 0x53, 0xe0, 0xd3, 0x07, 0x83, 0x4b, 0xd1},
+ // Win32/Sirefef.gen!C generates fake certifciates with this public key.
+ {0xa4, 0xf5, 0x6e, 0x9e, 0x1d, 0x9a, 0x3b, 0x7b, 0x1a, 0xc3,
+ 0x31, 0xcf, 0x64, 0xfc, 0x76, 0x2c, 0xd0, 0x51, 0xfb, 0xa4},
+ };
+
+ for (unsigned i = 0; i < kNumHashes; i++) {
+ for (HashValueVector::const_iterator j = public_key_hashes.begin();
+ j != public_key_hashes.end(); ++j) {
+ if (j->tag == HASH_VALUE_SHA1 &&
+ memcmp(j->data(), kHashes[i], base::kSHA1Length) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc.h b/chromium/net/cert/cert_verify_proc.h
new file mode 100644
index 00000000000..518adbc122a
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_H_
+#define NET_CERT_CERT_VERIFY_PROC_H_
+
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/x509_cert_types.h"
+
+namespace net {
+
+class CertVerifyResult;
+class CRLSet;
+class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
+
+// Class to perform certificate path building and verification for various
+// certificate uses. All methods of this class must be thread-safe, as they
+// may be called from various non-joinable worker threads.
+class NET_EXPORT CertVerifyProc
+ : public base::RefCountedThreadSafe<CertVerifyProc> {
+ public:
+ // Creates and returns the default CertVerifyProc.
+ static CertVerifyProc* CreateDefault();
+
+ // Verifies the certificate against the given hostname as an SSL server
+ // certificate. Returns OK if successful or an error code upon failure.
+ //
+ // The |*verify_result| structure, including the |verify_result->cert_status|
+ // bitmask, is always filled out regardless of the return value. If the
+ // certificate has multiple errors, the corresponding status flags are set in
+ // |verify_result->cert_status|, and the error code for the most serious
+ // error is returned.
+ //
+ // |flags| is bitwise OR'd of VerifyFlags:
+ //
+ // If VERIFY_REV_CHECKING_ENABLED is set in |flags|, online certificate
+ // revocation checking is performed (i.e. OCSP and downloading CRLs). CRLSet
+ // based revocation checking is always enabled, regardless of this flag, if
+ // |crl_set| is given.
+ //
+ // If VERIFY_EV_CERT is set in |flags| too, EV certificate verification is
+ // performed.
+ //
+ // |crl_set| points to an optional CRLSet structure which can be used to
+ // avoid revocation checks over the network.
+ //
+ // |additional_trust_anchors| lists certificates that can be trusted when
+ // building a certificate chain, in addition to the anchors known to the
+ // implementation.
+ int Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result);
+
+ // Returns true if the implementation supports passing additional trust
+ // anchors to the Verify() call. The |additional_trust_anchors| parameter
+ // passed to Verify() is ignored when this returns false.
+ virtual bool SupportsAdditionalTrustAnchors() const = 0;
+
+ protected:
+ CertVerifyProc();
+ virtual ~CertVerifyProc();
+
+ private:
+ friend class base::RefCountedThreadSafe<CertVerifyProc>;
+ FRIEND_TEST_ALL_PREFIXES(CertVerifyProcTest, DigiNotarCerts);
+
+ // Performs the actual verification using the desired underlying
+ // cryptographic library.
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) = 0;
+
+ // Returns true if |cert| is explicitly blacklisted.
+ static bool IsBlacklisted(X509Certificate* cert);
+
+ // IsPublicKeyBlacklisted returns true iff one of |public_key_hashes| (which
+ // are hashes of SubjectPublicKeyInfo structures) is explicitly blocked.
+ static bool IsPublicKeyBlacklisted(const HashValueVector& public_key_hashes);
+
+ DISALLOW_COPY_AND_ASSIGN(CertVerifyProc);
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_H_
diff --git a/chromium/net/cert/cert_verify_proc_android.cc b/chromium/net/cert/cert_verify_proc_android.cc
new file mode 100644
index 00000000000..9a8acf76fa9
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_android.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc_android.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "net/android/cert_verify_result_android.h"
+#include "net/android/network_library.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+// Returns true if the certificate verification call was successful (regardless
+// of its result), i.e. if |verify_result| was set. Otherwise returns false.
+bool VerifyFromAndroidTrustManager(const std::vector<std::string>& cert_bytes,
+ CertVerifyResult* verify_result) {
+ // TODO(joth): Fetch the authentication type from SSL rather than hardcode.
+ android::CertVerifyResultAndroid android_result =
+ android::VerifyX509CertChain(cert_bytes, "RSA");
+ switch (android_result) {
+ case android::VERIFY_FAILED:
+ return false;
+ case android::VERIFY_OK:
+ break;
+ case android::VERIFY_NO_TRUSTED_ROOT:
+ verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+ break;
+ case android::VERIFY_EXPIRED:
+ case android::VERIFY_NOT_YET_VALID:
+ verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
+ break;
+ case android::VERIFY_UNABLE_TO_PARSE:
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ break;
+ case android::VERIFY_INCORRECT_KEY_USAGE:
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ break;
+ default:
+ NOTREACHED();
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ break;
+ }
+ return true;
+}
+
+bool GetChainDEREncodedBytes(X509Certificate* cert,
+ std::vector<std::string>* chain_bytes) {
+ X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle();
+ X509Certificate::OSCertHandles cert_handles =
+ cert->GetIntermediateCertificates();
+
+ // Make sure the peer's own cert is the first in the chain, if it's not
+ // already there.
+ if (cert_handles.empty() || cert_handles[0] != cert_handle)
+ cert_handles.insert(cert_handles.begin(), cert_handle);
+
+ chain_bytes->reserve(cert_handles.size());
+ for (X509Certificate::OSCertHandles::const_iterator it =
+ cert_handles.begin(); it != cert_handles.end(); ++it) {
+ std::string cert_bytes;
+ if(!X509Certificate::GetDEREncoded(*it, &cert_bytes))
+ return false;
+ chain_bytes->push_back(cert_bytes);
+ }
+ return true;
+}
+
+} // namespace
+
+CertVerifyProcAndroid::CertVerifyProcAndroid() {}
+
+CertVerifyProcAndroid::~CertVerifyProcAndroid() {}
+
+bool CertVerifyProcAndroid::SupportsAdditionalTrustAnchors() const {
+ return false;
+}
+
+int CertVerifyProcAndroid::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ if (!cert->VerifyNameMatch(hostname))
+ verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
+
+ std::vector<std::string> cert_bytes;
+ if (!GetChainDEREncodedBytes(cert, &cert_bytes))
+ return ERR_CERT_INVALID;
+ if (!VerifyFromAndroidTrustManager(cert_bytes, verify_result)) {
+ NOTREACHED();
+ return ERR_FAILED;
+ }
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ // TODO(ppi): Implement missing functionality: yielding the constructed trust
+ // chain, public key hashes of its certificates and |is_issued_by_known_root|
+ // flag. All of the above require specific support from the platform, missing
+ // in the Java APIs. See also: http://crbug.com/116838
+
+ // Until the required support is available in the platform, we don't know if
+ // the trust root at the end of the chain was standard or user-added, so we
+ // mark all correctly verified certificates as issued by a known root.
+ verify_result->is_issued_by_known_root = true;
+
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_android.h b/chromium/net/cert/cert_verify_proc_android.h
new file mode 100644
index 00000000000..ca8746b4a96
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_android.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_ANDROID_H_
+#define NET_CERT_CERT_VERIFY_PROC_ANDROID_H_
+
+#include "net/cert/cert_verify_proc.h"
+
+namespace net {
+
+// Performs certificate verification on Android by calling the platform
+// TrustManager through JNI.
+class CertVerifyProcAndroid : public CertVerifyProc {
+ public:
+ CertVerifyProcAndroid();
+
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE;
+
+ protected:
+ virtual ~CertVerifyProcAndroid();
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_ANDROID_H_
diff --git a/chromium/net/cert/cert_verify_proc_mac.cc b/chromium/net/cert/cert_verify_proc_mac.cc
new file mode 100644
index 00000000000..4efdaacffed
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_mac.cc
@@ -0,0 +1,714 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc_mac.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#include <CoreServices/CoreServices.h>
+#include <Security/Security.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/sha1.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "crypto/mac_security_services_lock.h"
+#include "crypto/nss_util.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_certificate_known_roots_mac.h"
+#include "net/cert/x509_util_mac.h"
+
+// From 10.7.2 libsecurity_keychain-55035/lib/SecTrustPriv.h, for use with
+// SecTrustCopyExtendedResult.
+#ifndef kSecEVOrganizationName
+#define kSecEVOrganizationName CFSTR("Organization")
+#endif
+
+using base::ScopedCFTypeRef;
+
+namespace net {
+
+namespace {
+
+typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
+ CFDictionaryRef*);
+
+int NetErrorFromOSStatus(OSStatus status) {
+ switch (status) {
+ case noErr:
+ return OK;
+ case errSecNotAvailable:
+ case errSecNoCertificateModule:
+ case errSecNoPolicyModule:
+ return ERR_NOT_IMPLEMENTED;
+ case errSecAuthFailed:
+ return ERR_ACCESS_DENIED;
+ default: {
+ OSSTATUS_LOG(ERROR, status) << "Unknown error mapped to ERR_FAILED";
+ return ERR_FAILED;
+ }
+ }
+}
+
+CertStatus CertStatusFromOSStatus(OSStatus status) {
+ switch (status) {
+ case noErr:
+ return 0;
+
+ case CSSMERR_TP_INVALID_ANCHOR_CERT:
+ case CSSMERR_TP_NOT_TRUSTED:
+ case CSSMERR_TP_INVALID_CERT_AUTHORITY:
+ return CERT_STATUS_AUTHORITY_INVALID;
+
+ case CSSMERR_TP_CERT_EXPIRED:
+ case CSSMERR_TP_CERT_NOT_VALID_YET:
+ // "Expired" and "not yet valid" collapse into a single status.
+ return CERT_STATUS_DATE_INVALID;
+
+ case CSSMERR_TP_CERT_REVOKED:
+ case CSSMERR_TP_CERT_SUSPENDED:
+ return CERT_STATUS_REVOKED;
+
+ case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
+ return CERT_STATUS_COMMON_NAME_INVALID;
+
+ case CSSMERR_APPLETP_CRL_NOT_FOUND:
+ case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
+ case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
+ return CERT_STATUS_NO_REVOCATION_MECHANISM;
+
+ case CSSMERR_APPLETP_CRL_EXPIRED:
+ case CSSMERR_APPLETP_CRL_NOT_VALID_YET:
+ case CSSMERR_APPLETP_CRL_SERVER_DOWN:
+ case CSSMERR_APPLETP_CRL_NOT_TRUSTED:
+ case CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT:
+ case CSSMERR_APPLETP_CRL_POLICY_FAIL:
+ case CSSMERR_APPLETP_OCSP_BAD_RESPONSE:
+ case CSSMERR_APPLETP_OCSP_BAD_REQUEST:
+ case CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED:
+ case CSSMERR_APPLETP_NETWORK_FAILURE:
+ case CSSMERR_APPLETP_OCSP_NOT_TRUSTED:
+ case CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT:
+ case CSSMERR_APPLETP_OCSP_SIG_ERROR:
+ case CSSMERR_APPLETP_OCSP_NO_SIGNER:
+ case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ:
+ case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR:
+ case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER:
+ case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED:
+ case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED:
+ case CSSMERR_APPLETP_OCSP_NONCE_MISMATCH:
+ // We asked for a revocation check, but didn't get it.
+ return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+
+ case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE:
+ // TODO(wtc): Should we add CERT_STATUS_WRONG_USAGE?
+ return CERT_STATUS_INVALID;
+
+ case CSSMERR_APPLETP_CRL_BAD_URI:
+ case CSSMERR_APPLETP_IDP_FAIL:
+ return CERT_STATUS_INVALID;
+
+ case CSSMERR_CSP_UNSUPPORTED_KEY_SIZE:
+ // Mapping UNSUPPORTED_KEY_SIZE to CERT_STATUS_WEAK_KEY is not strictly
+ // accurate, as the error may have been returned due to a key size
+ // that exceeded the maximum supported. However, within
+ // CertVerifyProcMac::VerifyInternal(), this code should only be
+ // encountered as a certificate status code, and only when the key size
+ // is smaller than the minimum required (1024 bits).
+ return CERT_STATUS_WEAK_KEY;
+
+ default: {
+ // Failure was due to something Chromium doesn't define a
+ // specific status for (such as basic constraints violation, or
+ // unknown critical extension)
+ OSSTATUS_LOG(WARNING, status)
+ << "Unknown error mapped to CERT_STATUS_INVALID";
+ return CERT_STATUS_INVALID;
+ }
+ }
+}
+
+// Creates a series of SecPolicyRefs to be added to a SecTrustRef used to
+// validate a certificate for an SSL server. |hostname| contains the name of
+// the SSL server that the certificate should be verified against. |flags| is
+// a bitwise-OR of VerifyFlags that can further alter how trust is validated,
+// such as how revocation is checked. If successful, returns noErr, and
+// stores the resultant array of SecPolicyRefs in |policies|.
+OSStatus CreateTrustPolicies(const std::string& hostname,
+ int flags,
+ ScopedCFTypeRef<CFArrayRef>* policies) {
+ ScopedCFTypeRef<CFMutableArrayRef> local_policies(
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+ if (!local_policies)
+ return memFullErr;
+
+ SecPolicyRef ssl_policy;
+ OSStatus status = x509_util::CreateSSLServerPolicy(hostname, &ssl_policy);
+ if (status)
+ return status;
+ CFArrayAppendValue(local_policies, ssl_policy);
+ CFRelease(ssl_policy);
+
+ // Explicitly add revocation policies, in order to override system
+ // revocation checking policies and instead respect the application-level
+ // revocation preference.
+ status = x509_util::CreateRevocationPolicies(
+ (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED),
+ (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY),
+ local_policies);
+ if (status)
+ return status;
+
+ policies->reset(local_policies.release());
+ return noErr;
+}
+
+// Saves some information about the certificate chain |cert_chain| in
+// |*verify_result|. The caller MUST initialize |*verify_result| before
+// calling this function.
+void GetCertChainInfo(CFArrayRef cert_chain,
+ CSSM_TP_APPLE_EVIDENCE_INFO* chain_info,
+ CertVerifyResult* verify_result) {
+ SecCertificateRef verified_cert = NULL;
+ std::vector<SecCertificateRef> verified_chain;
+ for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) {
+ SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
+ if (i == 0) {
+ verified_cert = chain_cert;
+ } else {
+ verified_chain.push_back(chain_cert);
+ }
+
+ if ((chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS) ||
+ (chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
+ // The current certificate is either in the user's trusted store or is
+ // a root (self-signed) certificate. Ignore the signature algorithm for
+ // these certificates, as it is meaningless for security. We allow
+ // self-signed certificates (i == 0 & IS_ROOT), since we accept that
+ // any security assertions by such a cert are inherently meaningless.
+ continue;
+ }
+
+ x509_util::CSSMCachedCertificate cached_cert;
+ OSStatus status = cached_cert.Init(chain_cert);
+ if (status)
+ continue;
+ x509_util::CSSMFieldValue signature_field;
+ status = cached_cert.GetField(&CSSMOID_X509V1SignatureAlgorithm,
+ &signature_field);
+ if (status || !signature_field.field())
+ continue;
+ // Match the behaviour of OS X system tools and defensively check that
+ // sizes are appropriate. This would indicate a critical failure of the
+ // OS X certificate library, but based on history, it is best to play it
+ // safe.
+ const CSSM_X509_ALGORITHM_IDENTIFIER* sig_algorithm =
+ signature_field.GetAs<CSSM_X509_ALGORITHM_IDENTIFIER>();
+ if (!sig_algorithm)
+ continue;
+
+ const CSSM_OID* alg_oid = &sig_algorithm->algorithm;
+ if (CSSMOIDEqual(alg_oid, &CSSMOID_MD2WithRSA)) {
+ verify_result->has_md2 = true;
+ } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD4WithRSA)) {
+ verify_result->has_md4 = true;
+ } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD5WithRSA)) {
+ verify_result->has_md5 = true;
+ }
+ }
+ if (!verified_cert)
+ return;
+
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+}
+
+void AppendPublicKeyHashes(CFArrayRef chain,
+ HashValueVector* hashes) {
+ const CFIndex n = CFArrayGetCount(chain);
+ for (CFIndex i = 0; i < n; i++) {
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
+
+ CSSM_DATA cert_data;
+ OSStatus err = SecCertificateGetData(cert, &cert_data);
+ DCHECK_EQ(err, noErr);
+ base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
+ cert_data.Length);
+ base::StringPiece spki_bytes;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
+ continue;
+
+ HashValue sha1(HASH_VALUE_SHA1);
+ CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data());
+ hashes->push_back(sha1);
+
+ HashValue sha256(HASH_VALUE_SHA256);
+ CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data());
+ hashes->push_back(sha256);
+ }
+}
+
+bool CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) {
+ if (CFArrayGetCount(chain) == 0)
+ return true;
+
+ // We iterate from the root certificate down to the leaf, keeping track of
+ // the issuer's SPKI at each step.
+ std::string issuer_spki_hash;
+ for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) {
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
+
+ CSSM_DATA cert_data;
+ OSStatus err = SecCertificateGetData(cert, &cert_data);
+ if (err != noErr) {
+ NOTREACHED();
+ continue;
+ }
+ base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
+ cert_data.Length);
+ base::StringPiece spki;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
+ NOTREACHED();
+ continue;
+ }
+
+ const std::string spki_hash = crypto::SHA256HashString(spki);
+ x509_util::CSSMCachedCertificate cached_cert;
+ if (cached_cert.Init(cert) != CSSM_OK) {
+ NOTREACHED();
+ continue;
+ }
+ x509_util::CSSMFieldValue serial_number;
+ err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number);
+ if (err || !serial_number.field()) {
+ NOTREACHED();
+ continue;
+ }
+
+ base::StringPiece serial(
+ reinterpret_cast<const char*>(serial_number.field()->Data),
+ serial_number.field()->Length);
+
+ CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
+
+ if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
+ result = crl_set->CheckSerial(serial, issuer_spki_hash);
+
+ issuer_spki_hash = spki_hash;
+
+ switch (result) {
+ case CRLSet::REVOKED:
+ return false;
+ case CRLSet::UNKNOWN:
+ case CRLSet::GOOD:
+ continue;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
+// that we recognise as a standard root.
+// static
+bool IsIssuedByKnownRoot(CFArrayRef chain) {
+ int n = CFArrayGetCount(chain);
+ if (n < 1)
+ return false;
+ SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1)));
+ SHA1HashValue hash = X509Certificate::CalculateFingerprint(root_ref);
+ return IsSHA1HashInSortedArray(
+ hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
+}
+
+// Builds and evaluates a SecTrustRef for the certificate chain contained
+// in |cert_array|, using the verification policies in |trust_policies|. On
+// success, returns OK, and updates |trust_ref|, |trust_result|,
+// |verified_chain|, and |chain_info| with the verification results. On
+// failure, no output parameters are modified.
+//
+// Note: An OK return does not mean that |cert_array| is trusted, merely that
+// verification was performed successfully.
+//
+// This function should only be called while the Mac Security Services lock is
+// held.
+int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
+ CFArrayRef trust_policies,
+ int flags,
+ ScopedCFTypeRef<SecTrustRef>* trust_ref,
+ SecTrustResultType* trust_result,
+ ScopedCFTypeRef<CFArrayRef>* verified_chain,
+ CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) {
+ SecTrustRef tmp_trust = NULL;
+ OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies,
+ &tmp_trust);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust);
+
+ if (TestRootCerts::HasInstance()) {
+ status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ }
+
+ CSSM_APPLE_TP_ACTION_DATA tp_action_data;
+ memset(&tp_action_data, 0, sizeof(tp_action_data));
+ tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION;
+ // Allow CSSM to download any missing intermediate certificates if an
+ // authorityInfoAccess extension or issuerAltName extension is present.
+ tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET |
+ CSSM_TP_ACTION_TRUST_SETTINGS;
+
+ // Note: For EV certificates, the Apple TP will handle setting these flags
+ // as part of EV evaluation.
+ if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) {
+ // Require a positive result from an OCSP responder or a CRL (or both)
+ // for every certificate in the chain. The Apple TP automatically
+ // excludes the self-signed root from this requirement. If a certificate
+ // is missing both a crlDistributionPoints extension and an
+ // authorityInfoAccess extension with an OCSP responder URL, then we
+ // will get a kSecTrustResultRecoverableTrustFailure back from
+ // SecTrustEvaluate(), with a
+ // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case,
+ // we'll set our own result to include
+ // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are
+ // present, and a check fails (server unavailable, OCSP retry later,
+ // signature mismatch), then we'll set our own result to include
+ // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
+ tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
+
+ // Note, even if revocation checking is disabled, SecTrustEvaluate() will
+ // modify the OCSP options so as to attempt OCSP checking if it believes a
+ // certificate may chain to an EV root. However, because network fetches
+ // are disabled in CreateTrustPolicies() when revocation checking is
+ // disabled, these will only go against the local cache.
+ }
+
+ CFDataRef action_data_ref =
+ CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+ reinterpret_cast<UInt8*>(&tp_action_data),
+ sizeof(tp_action_data), kCFAllocatorNull);
+ if (!action_data_ref)
+ return ERR_OUT_OF_MEMORY;
+ ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref);
+ status = SecTrustSetParameters(tmp_trust, CSSM_TP_ACTION_DEFAULT,
+ action_data_ref);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ // Verify the certificate. A non-zero result from SecTrustGetResult()
+ // indicates that some fatal error occurred and the chain couldn't be
+ // processed, not that the chain contains no errors. We need to examine the
+ // output of SecTrustGetResult() to determine that.
+ SecTrustResultType tmp_trust_result;
+ status = SecTrustEvaluate(tmp_trust, &tmp_trust_result);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ CFArrayRef tmp_verified_chain = NULL;
+ CSSM_TP_APPLE_EVIDENCE_INFO* tmp_chain_info;
+ status = SecTrustGetResult(tmp_trust, &tmp_trust_result, &tmp_verified_chain,
+ &tmp_chain_info);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ trust_ref->swap(scoped_tmp_trust);
+ *trust_result = tmp_trust_result;
+ verified_chain->reset(tmp_verified_chain);
+ *chain_info = tmp_chain_info;
+
+ return OK;
+}
+
+// OS X ships with both "GTE CyberTrust Global Root" and "Baltimore CyberTrust
+// Root" as part of its trusted root store. However, a cross-certified version
+// of the "Baltimore CyberTrust Root" exists that chains to "GTE CyberTrust
+// Global Root". When OS X/Security.framework attempts to evaluate such a
+// certificate chain, it disregards the "Baltimore CyberTrust Root" that exists
+// within Keychain and instead attempts to terminate the chain in the "GTE
+// CyberTrust Global Root". However, the GTE root is scheduled to be removed in
+// a future OS X update (for sunsetting purposes), and once removed, such
+// chains will fail validation, even though a trust anchor still exists.
+//
+// Rather than over-generalizing a solution that may mask a number of TLS
+// misconfigurations, attempt to specifically match the affected
+// cross-certified certificate and remove it from certificate chain processing.
+bool IsBadBaltimoreGTECertificate(SecCertificateRef cert) {
+ // Matches the GTE-signed Baltimore CyberTrust Root
+ // https://cacert.omniroot.com/Baltimore-to-GTE-04-12.pem
+ static const SHA1HashValue kBadBaltimoreHashNew =
+ { { 0x4D, 0x34, 0xEA, 0x92, 0x76, 0x4B, 0x3A, 0x31, 0x49, 0x11,
+ 0x99, 0x52, 0xF4, 0x19, 0x30, 0xCA, 0x11, 0x34, 0x83, 0x61 } };
+ // Matches the legacy GTE-signed Baltimore CyberTrust Root
+ // https://cacert.omniroot.com/gte-2-2025.pem
+ static const SHA1HashValue kBadBaltimoreHashOld =
+ { { 0x54, 0xD8, 0xCB, 0x49, 0x1F, 0xA1, 0x6D, 0xF8, 0x87, 0xDC,
+ 0x94, 0xA9, 0x34, 0xCC, 0x83, 0x6B, 0xDA, 0xA8, 0xA3, 0x69 } };
+
+ SHA1HashValue fingerprint = X509Certificate::CalculateFingerprint(cert);
+
+ return fingerprint.Equals(kBadBaltimoreHashNew) ||
+ fingerprint.Equals(kBadBaltimoreHashOld);
+}
+
+// Attempts to re-verify |cert_array| after adjusting the inputs to work around
+// known issues in OS X. To be used if BuildAndEvaluateSecTrustRef fails to
+// return a positive result for verification.
+//
+// This function should only be called while the Mac Security Services lock is
+// held.
+void RetrySecTrustEvaluateWithAdjustedChain(
+ CFArrayRef cert_array,
+ CFArrayRef trust_policies,
+ int flags,
+ ScopedCFTypeRef<SecTrustRef>* trust_ref,
+ SecTrustResultType* trust_result,
+ ScopedCFTypeRef<CFArrayRef>* verified_chain,
+ CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) {
+ CFIndex count = CFArrayGetCount(*verified_chain);
+ CFIndex slice_point = 0;
+
+ for (CFIndex i = 1; i < count; ++i) {
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(*verified_chain, i)));
+ if (cert == NULL)
+ return; // Strange times; can't fix things up.
+
+ if (IsBadBaltimoreGTECertificate(cert)) {
+ slice_point = i;
+ break;
+ }
+ }
+ if (slice_point == 0)
+ return; // Nothing to do.
+
+ ScopedCFTypeRef<CFMutableArrayRef> adjusted_cert_array(
+ CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
+ // Note: This excludes the certificate at |slice_point|.
+ CFArrayAppendArray(adjusted_cert_array, cert_array,
+ CFRangeMake(0, slice_point));
+
+ // Ignore the result; failure will preserve the old verification results.
+ BuildAndEvaluateSecTrustRef(
+ adjusted_cert_array, trust_policies, flags, trust_ref, trust_result,
+ verified_chain, chain_info);
+}
+
+} // namespace
+
+CertVerifyProcMac::CertVerifyProcMac() {}
+
+CertVerifyProcMac::~CertVerifyProcMac() {}
+
+bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const {
+ return false;
+}
+
+int CertVerifyProcMac::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ ScopedCFTypeRef<CFArrayRef> trust_policies;
+ OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies);
+ if (status)
+ return NetErrorFromOSStatus(status);
+
+ // Create and configure a SecTrustRef, which takes our certificate(s)
+ // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
+ // array of certificates, the first of which is the certificate we're
+ // verifying, and the subsequent (optional) certificates are used for
+ // chain building.
+ ScopedCFTypeRef<CFArrayRef> cert_array(cert->CreateOSCertChainForCert());
+
+ // Serialize all calls that may use the Keychain, to work around various
+ // issues in OS X 10.6+ with multi-threaded access to Security.framework.
+ base::AutoLock lock(crypto::GetMacSecurityServicesLock());
+
+ ScopedCFTypeRef<SecTrustRef> trust_ref;
+ SecTrustResultType trust_result = kSecTrustResultDeny;
+ ScopedCFTypeRef<CFArrayRef> completed_chain;
+ CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL;
+
+ int rv = BuildAndEvaluateSecTrustRef(
+ cert_array, trust_policies, flags, &trust_ref, &trust_result,
+ &completed_chain, &chain_info);
+ if (rv != OK)
+ return rv;
+ if (trust_result != kSecTrustResultUnspecified &&
+ trust_result != kSecTrustResultProceed) {
+ RetrySecTrustEvaluateWithAdjustedChain(
+ cert_array, trust_policies, flags, &trust_ref, &trust_result,
+ &completed_chain, &chain_info);
+ }
+
+ if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+
+ if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set))
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+
+ GetCertChainInfo(completed_chain, chain_info, verify_result);
+
+ // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits
+ // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds
+ // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping
+ // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only
+ // error was due to an unsupported key size.
+ bool policy_failed = false;
+ bool weak_key = false;
+
+ // Evaluate the results
+ OSStatus cssm_result;
+ switch (trust_result) {
+ case kSecTrustResultUnspecified:
+ case kSecTrustResultProceed:
+ // Certificate chain is valid and trusted ("unspecified" indicates that
+ // the user has not explicitly set a trust setting)
+ break;
+
+ // According to SecTrust.h, kSecTrustResultConfirm isn't returned on 10.5+,
+ // and it is marked deprecated in the 10.9 SDK.
+ case kSecTrustResultDeny:
+ // Certificate chain is explicitly untrusted.
+ verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+ break;
+
+ case kSecTrustResultRecoverableTrustFailure:
+ // Certificate chain has a failure that can be overridden by the user.
+ status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ if (cssm_result == CSSMERR_TP_VERIFY_ACTION_FAILED) {
+ policy_failed = true;
+ } else {
+ verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
+ }
+ // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO
+ // structure which can catch multiple errors from each certificate.
+ for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain);
+ index < chain_count; ++index) {
+ if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED ||
+ chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET)
+ verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
+ if (!IsCertStatusError(verify_result->cert_status) &&
+ chain_info[index].NumStatusCodes == 0) {
+ LOG(WARNING) << "chain_info[" << index << "].NumStatusCodes is 0"
+ ", chain_info[" << index << "].StatusBits is "
+ << chain_info[index].StatusBits;
+ }
+ for (uint32 status_code_index = 0;
+ status_code_index < chain_info[index].NumStatusCodes;
+ ++status_code_index) {
+ CertStatus mapped_status = CertStatusFromOSStatus(
+ chain_info[index].StatusCodes[status_code_index]);
+ if (mapped_status == CERT_STATUS_WEAK_KEY)
+ weak_key = true;
+ verify_result->cert_status |= mapped_status;
+ }
+ }
+ if (policy_failed && !weak_key) {
+ // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak
+ // key, map it back to an appropriate error code.
+ verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
+ }
+ if (!IsCertStatusError(verify_result->cert_status)) {
+ LOG(ERROR) << "cssm_result=" << cssm_result;
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ NOTREACHED();
+ }
+ break;
+
+ default:
+ status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
+ if (status)
+ return NetErrorFromOSStatus(status);
+ verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
+ if (!IsCertStatusError(verify_result->cert_status)) {
+ LOG(WARNING) << "trust_result=" << trust_result;
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ }
+ break;
+ }
+
+ // Perform hostname verification independent of SecTrustEvaluate. In order to
+ // do so, mask off any reported name errors first.
+ verify_result->cert_status &= ~CERT_STATUS_COMMON_NAME_INVALID;
+ if (!cert->VerifyNameMatch(hostname))
+ verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
+
+ // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
+ // compatible with Windows, which in turn implements this behavior to be
+ // compatible with WinHTTP, which doesn't report this error (bug 3004).
+ verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
+
+ AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes);
+ verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(completed_chain);
+
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ if (flags & CertVerifier::VERIFY_EV_CERT) {
+ // Determine the certificate's EV status using SecTrustCopyExtendedResult(),
+ // which is an internal/private API function added in OS X 10.5.7.
+ // Note: "ExtendedResult" means extended validation results.
+ CFBundleRef bundle =
+ CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
+ if (bundle) {
+ SecTrustCopyExtendedResultFuncPtr copy_extended_result =
+ reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>(
+ CFBundleGetFunctionPointerForName(bundle,
+ CFSTR("SecTrustCopyExtendedResult")));
+ if (copy_extended_result) {
+ CFDictionaryRef ev_dict_temp = NULL;
+ status = copy_extended_result(trust_ref, &ev_dict_temp);
+ ScopedCFTypeRef<CFDictionaryRef> ev_dict(ev_dict_temp);
+ ev_dict_temp = NULL;
+ if (status == noErr && ev_dict) {
+ // In 10.7.3, SecTrustCopyExtendedResult returns noErr and populates
+ // ev_dict even for non-EV certificates, but only EV certificates
+ // will cause ev_dict to contain kSecEVOrganizationName. In previous
+ // releases, SecTrustCopyExtendedResult would only return noErr and
+ // populate ev_dict for EV certificates, but would always include
+ // kSecEVOrganizationName in that case, so checking for this key is
+ // appropriate for all known versions of SecTrustCopyExtendedResult.
+ // The actual organization name is unneeded here and can be accessed
+ // through other means. All that matters here is the OS' conception
+ // of whether or not the certificate is EV.
+ if (CFDictionaryContainsKey(ev_dict,
+ kSecEVOrganizationName)) {
+ verify_result->cert_status |= CERT_STATUS_IS_EV;
+ if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY)
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+ }
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_mac.h b/chromium/net/cert/cert_verify_proc_mac.h
new file mode 100644
index 00000000000..cb557671f64
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_mac.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_MAC_H_
+#define NET_CERT_CERT_VERIFY_PROC_MAC_H_
+
+#include "net/cert/cert_verify_proc.h"
+
+namespace net {
+
+// Performs certificate path construction and validation using OS X's
+// Security.framework.
+class CertVerifyProcMac : public CertVerifyProc {
+ public:
+ CertVerifyProcMac();
+
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE;
+
+ protected:
+ virtual ~CertVerifyProcMac();
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_MAC_H_
diff --git a/chromium/net/cert/cert_verify_proc_nss.cc b/chromium/net/cert/cert_verify_proc_nss.cc
new file mode 100644
index 00000000000..f63297e83c5
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_nss.cc
@@ -0,0 +1,892 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc_nss.h"
+
+#include <string>
+#include <vector>
+
+#include <cert.h>
+#include <nss.h>
+#include <prerror.h>
+#include <secerr.h>
+#include <sechash.h>
+#include <sslerr.h>
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/ev_root_ca_metadata.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_nss.h"
+
+#if defined(OS_IOS)
+#include <CommonCrypto/CommonDigest.h>
+#include "net/cert/x509_util_ios.h"
+#endif // defined(OS_IOS)
+
+#define NSS_VERSION_NUM (NSS_VMAJOR * 10000 + NSS_VMINOR * 100 + NSS_VPATCH)
+#if NSS_VERSION_NUM < 31305
+// Added in NSS 3.13.5.
+#define SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED -8016
+#endif
+
+#if NSS_VERSION_NUM < 31402
+// Added in NSS 3.14.2.
+#define cert_pi_useOnlyTrustAnchors static_cast<CERTValParamInType>(14)
+#endif
+
+namespace net {
+
+namespace {
+
+typedef scoped_ptr_malloc<
+ CERTCertificatePolicies,
+ crypto::NSSDestroyer<CERTCertificatePolicies,
+ CERT_DestroyCertificatePoliciesExtension> >
+ ScopedCERTCertificatePolicies;
+
+typedef scoped_ptr_malloc<
+ CERTCertList,
+ crypto::NSSDestroyer<CERTCertList, CERT_DestroyCertList> >
+ ScopedCERTCertList;
+
+// ScopedCERTValOutParam manages destruction of values in the CERTValOutParam
+// array that cvout points to. cvout must be initialized as passed to
+// CERT_PKIXVerifyCert, so that the array must be terminated with
+// cert_po_end type.
+// When it goes out of scope, it destroys values of cert_po_trustAnchor
+// and cert_po_certList types, but doesn't release the array itself.
+class ScopedCERTValOutParam {
+ public:
+ explicit ScopedCERTValOutParam(CERTValOutParam* cvout) : cvout_(cvout) {}
+
+ ~ScopedCERTValOutParam() {
+ Clear();
+ }
+
+ // Free the internal resources, but do not release the array itself.
+ void Clear() {
+ if (cvout_ == NULL)
+ return;
+ for (CERTValOutParam *p = cvout_; p->type != cert_po_end; p++) {
+ switch (p->type) {
+ case cert_po_trustAnchor:
+ if (p->value.pointer.cert) {
+ CERT_DestroyCertificate(p->value.pointer.cert);
+ p->value.pointer.cert = NULL;
+ }
+ break;
+ case cert_po_certList:
+ if (p->value.pointer.chain) {
+ CERT_DestroyCertList(p->value.pointer.chain);
+ p->value.pointer.chain = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ CERTValOutParam* cvout_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam);
+};
+
+// Map PORT_GetError() return values to our network error codes.
+int MapSecurityError(int err) {
+ switch (err) {
+ case PR_DIRECTORY_LOOKUP_ERROR: // DNS lookup error.
+ return ERR_NAME_NOT_RESOLVED;
+ case SEC_ERROR_INVALID_ARGS:
+ return ERR_INVALID_ARGUMENT;
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ return ERR_CERT_COMMON_NAME_INVALID;
+ case SEC_ERROR_INVALID_TIME:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ return ERR_CERT_DATE_INVALID;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ case SEC_ERROR_CA_CERT_INVALID:
+ return ERR_CERT_AUTHORITY_INVALID;
+ // TODO(port): map ERR_CERT_NO_REVOCATION_MECHANISM.
+ case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE:
+ case SEC_ERROR_OCSP_SERVER_ERROR:
+ return ERR_CERT_UNABLE_TO_CHECK_REVOCATION;
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ case SEC_ERROR_UNTRUSTED_CERT: // Treat as revoked.
+ return ERR_CERT_REVOKED;
+ case SEC_ERROR_BAD_DER:
+ case SEC_ERROR_BAD_SIGNATURE:
+ case SEC_ERROR_CERT_NOT_VALID:
+ // TODO(port): add an ERR_CERT_WRONG_USAGE error code.
+ case SEC_ERROR_CERT_USAGES_INVALID:
+ case SEC_ERROR_INADEQUATE_KEY_USAGE: // Key usage.
+ case SEC_ERROR_INADEQUATE_CERT_TYPE: // Extended key usage and whether
+ // the certificate is a CA.
+ case SEC_ERROR_POLICY_VALIDATION_FAILED:
+ case SEC_ERROR_CERT_NOT_IN_NAME_SPACE:
+ case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID:
+ case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
+ case SEC_ERROR_EXTENSION_VALUE_INVALID:
+ return ERR_CERT_INVALID;
+ case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
+ return ERR_CERT_WEAK_SIGNATURE_ALGORITHM;
+ default:
+ LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+}
+
+// Map PORT_GetError() return values to our cert status flags.
+CertStatus MapCertErrorToCertStatus(int err) {
+ int net_error = MapSecurityError(err);
+ return MapNetErrorToCertStatus(net_error);
+}
+
+// Saves some information about the certificate chain cert_list in
+// *verify_result. The caller MUST initialize *verify_result before calling
+// this function.
+// Note that cert_list[0] is the end entity certificate.
+void GetCertChainInfo(CERTCertList* cert_list,
+ CERTCertificate* root_cert,
+ CertVerifyResult* verify_result) {
+ DCHECK(cert_list);
+
+ CERTCertificate* verified_cert = NULL;
+ std::vector<CERTCertificate*> verified_chain;
+ int i = 0;
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node), ++i) {
+ if (i == 0) {
+ verified_cert = node->cert;
+ } else {
+ // Because of an NSS bug, CERT_PKIXVerifyCert may chain a self-signed
+ // certificate of a root CA to another certificate of the same root CA
+ // key. Detect that error and ignore the root CA certificate.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=721288.
+ if (node->cert->isRoot) {
+ // NOTE: isRoot doesn't mean the certificate is a trust anchor. It
+ // means the certificate is self-signed. Here we assume isRoot only
+ // implies the certificate is self-issued.
+ CERTCertListNode* next_node = CERT_LIST_NEXT(node);
+ CERTCertificate* next_cert;
+ if (!CERT_LIST_END(next_node, cert_list)) {
+ next_cert = next_node->cert;
+ } else {
+ next_cert = root_cert;
+ }
+ // Test that |node->cert| is actually a self-signed certificate
+ // whose key is equal to |next_cert|, and not a self-issued
+ // certificate signed by another key of the same CA.
+ if (next_cert && SECITEM_ItemsAreEqual(&node->cert->derPublicKey,
+ &next_cert->derPublicKey)) {
+ continue;
+ }
+ }
+ verified_chain.push_back(node->cert);
+ }
+
+ SECAlgorithmID& signature = node->cert->signature;
+ SECOidTag oid_tag = SECOID_FindOIDTag(&signature.algorithm);
+ switch (oid_tag) {
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ verify_result->has_md5 = true;
+ break;
+ case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+ verify_result->has_md2 = true;
+ break;
+ case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
+ verify_result->has_md4 = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (root_cert)
+ verified_chain.push_back(root_cert);
+#if defined(OS_IOS)
+ verify_result->verified_cert =
+ x509_util_ios::CreateCertFromNSSHandles(verified_cert, verified_chain);
+#else
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+#endif // defined(OS_IOS)
+}
+
+// IsKnownRoot returns true if the given certificate is one that we believe
+// is a standard (as opposed to user-installed) root.
+bool IsKnownRoot(CERTCertificate* root) {
+ if (!root || !root->slot)
+ return false;
+
+ // This magic name is taken from
+ // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
+ return 0 == strcmp(PK11_GetSlotName(root->slot),
+ "NSS Builtin Objects");
+}
+
+// Returns true if the given certificate is one of the additional trust anchors.
+bool IsAdditionalTrustAnchor(CERTCertList* additional_trust_anchors,
+ CERTCertificate* root) {
+ if (!additional_trust_anchors || !root)
+ return false;
+ for (CERTCertListNode* node = CERT_LIST_HEAD(additional_trust_anchors);
+ !CERT_LIST_END(node, additional_trust_anchors);
+ node = CERT_LIST_NEXT(node)) {
+ if (CERT_CompareCerts(node->cert, root))
+ return true;
+ }
+ return false;
+}
+
+enum CRLSetResult {
+ kCRLSetOk,
+ kCRLSetRevoked,
+ kCRLSetUnknown,
+};
+
+// CheckRevocationWithCRLSet attempts to check each element of |cert_list|
+// against |crl_set|. It returns:
+// kCRLSetRevoked: if any element of the chain is known to have been revoked.
+// kCRLSetUnknown: if there is no fresh information about some element in
+// the chain.
+// kCRLSetOk: if every element in the chain is covered by a fresh CRLSet and
+// is unrevoked.
+CRLSetResult CheckRevocationWithCRLSet(CERTCertList* cert_list,
+ CERTCertificate* root,
+ CRLSet* crl_set) {
+ std::vector<CERTCertificate*> certs;
+
+ if (cert_list) {
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ certs.push_back(node->cert);
+ }
+ }
+ if (root)
+ certs.push_back(root);
+
+ bool covered = true;
+
+ // We iterate from the root certificate down to the leaf, keeping track of
+ // the issuer's SPKI at each step.
+ std::string issuer_spki_hash;
+ for (std::vector<CERTCertificate*>::reverse_iterator i = certs.rbegin();
+ i != certs.rend(); ++i) {
+ CERTCertificate* cert = *i;
+
+ base::StringPiece der(reinterpret_cast<char*>(cert->derCert.data),
+ cert->derCert.len);
+
+ base::StringPiece spki;
+ if (!asn1::ExtractSPKIFromDERCert(der, &spki)) {
+ NOTREACHED();
+ covered = false;
+ continue;
+ }
+ const std::string spki_hash = crypto::SHA256HashString(spki);
+
+ base::StringPiece serial_number = base::StringPiece(
+ reinterpret_cast<char*>(cert->serialNumber.data),
+ cert->serialNumber.len);
+
+ CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
+
+ if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
+ result = crl_set->CheckSerial(serial_number, issuer_spki_hash);
+
+ issuer_spki_hash = spki_hash;
+
+ switch (result) {
+ case CRLSet::REVOKED:
+ return kCRLSetRevoked;
+ case CRLSet::UNKNOWN:
+ covered = false;
+ continue;
+ case CRLSet::GOOD:
+ continue;
+ default:
+ NOTREACHED();
+ covered = false;
+ continue;
+ }
+ }
+
+ if (!covered || crl_set->IsExpired())
+ return kCRLSetUnknown;
+ return kCRLSetOk;
+}
+
+// Forward declarations.
+SECStatus RetryPKIXVerifyCertWithWorkarounds(
+ CERTCertificate* cert_handle, int num_policy_oids,
+ bool cert_io_enabled, std::vector<CERTValInParam>* cvin,
+ CERTValOutParam* cvout);
+SECOidTag GetFirstCertPolicy(CERTCertificate* cert_handle);
+
+// Call CERT_PKIXVerifyCert for the cert_handle.
+// Verification results are stored in an array of CERTValOutParam.
+// If |hard_fail| is true, and no policy_oids are supplied (eg: EV is NOT being
+// checked), then the failure to obtain valid CRL/OCSP information for all
+// certificates that contain CRL/OCSP URLs will cause the certificate to be
+// treated as if it was revoked. Since failures may be caused by transient
+// network failures or by malicious attackers, in general, hard_fail should be
+// false.
+// If policy_oids is not NULL and num_policy_oids is positive, policies
+// are also checked.
+// additional_trust_anchors is an optional list of certificates that can be
+// trusted as anchors when building a certificate chain.
+// Caller must initialize cvout before calling this function.
+SECStatus PKIXVerifyCert(CERTCertificate* cert_handle,
+ bool check_revocation,
+ bool hard_fail,
+ bool cert_io_enabled,
+ const SECOidTag* policy_oids,
+ int num_policy_oids,
+ CERTCertList* additional_trust_anchors,
+ CERTValOutParam* cvout) {
+ bool use_crl = check_revocation;
+ bool use_ocsp = check_revocation;
+
+ PRUint64 revocation_method_flags =
+ CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD |
+ CERT_REV_M_ALLOW_NETWORK_FETCHING |
+ CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE |
+ CERT_REV_M_IGNORE_MISSING_FRESH_INFO |
+ CERT_REV_M_STOP_TESTING_ON_FRESH_INFO;
+ PRUint64 revocation_method_independent_flags =
+ CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
+ if (check_revocation && policy_oids && num_policy_oids > 0) {
+ // EV verification requires revocation checking. Consider the certificate
+ // revoked if we don't have revocation info.
+ // TODO(wtc): Add a bool parameter to expressly specify we're doing EV
+ // verification or we want strict revocation flags.
+ revocation_method_flags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE;
+ revocation_method_independent_flags |=
+ CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE;
+ } else if (check_revocation && hard_fail) {
+ revocation_method_flags |= CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO;
+ revocation_method_independent_flags |=
+ CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE;
+ } else {
+ revocation_method_flags |= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE;
+ revocation_method_independent_flags |=
+ CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT;
+ }
+ PRUint64 method_flags[2];
+ method_flags[cert_revocation_method_crl] = revocation_method_flags;
+ method_flags[cert_revocation_method_ocsp] = revocation_method_flags;
+
+ if (use_crl) {
+ method_flags[cert_revocation_method_crl] |=
+ CERT_REV_M_TEST_USING_THIS_METHOD;
+ }
+ if (use_ocsp) {
+ method_flags[cert_revocation_method_ocsp] |=
+ CERT_REV_M_TEST_USING_THIS_METHOD;
+ }
+
+ CERTRevocationMethodIndex preferred_revocation_methods[1];
+ if (use_ocsp) {
+ preferred_revocation_methods[0] = cert_revocation_method_ocsp;
+ } else {
+ preferred_revocation_methods[0] = cert_revocation_method_crl;
+ }
+
+ CERTRevocationFlags revocation_flags;
+ revocation_flags.leafTests.number_of_defined_methods =
+ arraysize(method_flags);
+ revocation_flags.leafTests.cert_rev_flags_per_method = method_flags;
+ revocation_flags.leafTests.number_of_preferred_methods =
+ arraysize(preferred_revocation_methods);
+ revocation_flags.leafTests.preferred_methods = preferred_revocation_methods;
+ revocation_flags.leafTests.cert_rev_method_independent_flags =
+ revocation_method_independent_flags;
+
+ revocation_flags.chainTests.number_of_defined_methods =
+ arraysize(method_flags);
+ revocation_flags.chainTests.cert_rev_flags_per_method = method_flags;
+ revocation_flags.chainTests.number_of_preferred_methods =
+ arraysize(preferred_revocation_methods);
+ revocation_flags.chainTests.preferred_methods = preferred_revocation_methods;
+ revocation_flags.chainTests.cert_rev_method_independent_flags =
+ revocation_method_independent_flags;
+
+
+ std::vector<CERTValInParam> cvin;
+ cvin.reserve(7);
+ CERTValInParam in_param;
+ in_param.type = cert_pi_revocationFlags;
+ in_param.value.pointer.revocation = &revocation_flags;
+ cvin.push_back(in_param);
+ if (policy_oids && num_policy_oids > 0) {
+ in_param.type = cert_pi_policyOID;
+ in_param.value.arraySize = num_policy_oids;
+ in_param.value.array.oids = policy_oids;
+ cvin.push_back(in_param);
+ }
+ if (additional_trust_anchors) {
+ in_param.type = cert_pi_trustAnchors;
+ in_param.value.pointer.chain = additional_trust_anchors;
+ cvin.push_back(in_param);
+ in_param.type = cert_pi_useOnlyTrustAnchors;
+ in_param.value.scalar.b = PR_FALSE;
+ cvin.push_back(in_param);
+ }
+ in_param.type = cert_pi_end;
+ cvin.push_back(in_param);
+
+ SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+ &cvin[0], cvout, NULL);
+ if (rv != SECSuccess) {
+ rv = RetryPKIXVerifyCertWithWorkarounds(cert_handle, num_policy_oids,
+ cert_io_enabled, &cvin, cvout);
+ }
+ return rv;
+}
+
+// PKIXVerifyCert calls this function to work around some bugs in
+// CERT_PKIXVerifyCert. All the arguments of this function are either the
+// arguments or local variables of PKIXVerifyCert.
+SECStatus RetryPKIXVerifyCertWithWorkarounds(
+ CERTCertificate* cert_handle, int num_policy_oids,
+ bool cert_io_enabled, std::vector<CERTValInParam>* cvin,
+ CERTValOutParam* cvout) {
+ // We call this function when the first CERT_PKIXVerifyCert call in
+ // PKIXVerifyCert failed, so we initialize |rv| to SECFailure.
+ SECStatus rv = SECFailure;
+ int nss_error = PORT_GetError();
+ CERTValInParam in_param;
+
+ // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate
+ // CA certificate, so we retry with cert_pi_useAIACertFetch.
+ // cert_pi_useAIACertFetch has several bugs in its error handling and
+ // error reporting (NSS bug 528743), so we don't use it by default.
+ // Note: When building a certificate chain, CERT_PKIXVerifyCert may
+ // incorrectly pick a CA certificate with the same subject name as the
+ // missing intermediate CA certificate, and fail with the
+ // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with
+ // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE.
+ if (cert_io_enabled &&
+ (nss_error == SEC_ERROR_UNKNOWN_ISSUER ||
+ nss_error == SEC_ERROR_BAD_SIGNATURE)) {
+ DCHECK_EQ(cvin->back().type, cert_pi_end);
+ cvin->pop_back();
+ in_param.type = cert_pi_useAIACertFetch;
+ in_param.value.scalar.b = PR_TRUE;
+ cvin->push_back(in_param);
+ in_param.type = cert_pi_end;
+ cvin->push_back(in_param);
+ rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+ &(*cvin)[0], cvout, NULL);
+ if (rv == SECSuccess)
+ return rv;
+ int new_nss_error = PORT_GetError();
+ if (new_nss_error == SEC_ERROR_INVALID_ARGS ||
+ new_nss_error == SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE ||
+ new_nss_error == SEC_ERROR_BAD_INFO_ACCESS_LOCATION ||
+ new_nss_error == SEC_ERROR_BAD_HTTP_RESPONSE ||
+ new_nss_error == SEC_ERROR_BAD_LDAP_RESPONSE ||
+ !IS_SEC_ERROR(new_nss_error)) {
+ // Use the original error code because of cert_pi_useAIACertFetch's
+ // bad error reporting.
+ PORT_SetError(nss_error);
+ return rv;
+ }
+ nss_error = new_nss_error;
+ }
+
+ // If an intermediate CA certificate has requireExplicitPolicy in its
+ // policyConstraints extension, CERT_PKIXVerifyCert fails with
+ // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any
+ // certificate policy (NSS bug 552775). So we retry with the certificate
+ // policy found in the server certificate.
+ if (nss_error == SEC_ERROR_POLICY_VALIDATION_FAILED &&
+ num_policy_oids == 0) {
+ SECOidTag policy = GetFirstCertPolicy(cert_handle);
+ if (policy != SEC_OID_UNKNOWN) {
+ DCHECK_EQ(cvin->back().type, cert_pi_end);
+ cvin->pop_back();
+ in_param.type = cert_pi_policyOID;
+ in_param.value.arraySize = 1;
+ in_param.value.array.oids = &policy;
+ cvin->push_back(in_param);
+ in_param.type = cert_pi_end;
+ cvin->push_back(in_param);
+ rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+ &(*cvin)[0], cvout, NULL);
+ if (rv != SECSuccess) {
+ // Use the original error code.
+ PORT_SetError(nss_error);
+ }
+ }
+ }
+
+ return rv;
+}
+
+// Decodes the certificatePolicies extension of the certificate. Returns
+// NULL if the certificate doesn't have the extension or the extension can't
+// be decoded. The returned value must be freed with a
+// CERT_DestroyCertificatePoliciesExtension call.
+CERTCertificatePolicies* DecodeCertPolicies(
+ CERTCertificate* cert_handle) {
+ SECItem policy_ext;
+ SECStatus rv = CERT_FindCertExtension(cert_handle,
+ SEC_OID_X509_CERTIFICATE_POLICIES,
+ &policy_ext);
+ if (rv != SECSuccess)
+ return NULL;
+ CERTCertificatePolicies* policies =
+ CERT_DecodeCertificatePoliciesExtension(&policy_ext);
+ SECITEM_FreeItem(&policy_ext, PR_FALSE);
+ return policies;
+}
+
+// Returns the OID tag for the first certificate policy in the certificate's
+// certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate
+// has no certificate policy.
+SECOidTag GetFirstCertPolicy(CERTCertificate* cert_handle) {
+ ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle));
+ if (!policies.get())
+ return SEC_OID_UNKNOWN;
+
+ CERTPolicyInfo* policy_info = policies->policyInfos[0];
+ if (!policy_info)
+ return SEC_OID_UNKNOWN;
+ if (policy_info->oid != SEC_OID_UNKNOWN)
+ return policy_info->oid;
+
+ // The certificate policy is unknown to NSS. We need to create a dynamic
+ // OID tag for the policy.
+ SECOidData od;
+ od.oid.len = policy_info->policyID.len;
+ od.oid.data = policy_info->policyID.data;
+ od.offset = SEC_OID_UNKNOWN;
+ // NSS doesn't allow us to pass an empty description, so I use a hardcoded,
+ // default description here. The description doesn't need to be unique for
+ // each OID.
+ od.desc = "a certificate policy";
+ od.mechanism = CKM_INVALID_MECHANISM;
+ od.supportedExtension = INVALID_CERT_EXTENSION;
+ return SECOID_AddEntry(&od);
+}
+
+HashValue CertPublicKeyHashSHA1(CERTCertificate* cert) {
+ HashValue hash(HASH_VALUE_SHA1);
+#if defined(OS_IOS)
+ CC_SHA1(cert->derPublicKey.data, cert->derPublicKey.len, hash.data());
+#else
+ SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, hash.data(),
+ cert->derPublicKey.data, cert->derPublicKey.len);
+ DCHECK_EQ(SECSuccess, rv);
+#endif
+ return hash;
+}
+
+HashValue CertPublicKeyHashSHA256(CERTCertificate* cert) {
+ HashValue hash(HASH_VALUE_SHA256);
+#if defined(OS_IOS)
+ CC_SHA256(cert->derPublicKey.data, cert->derPublicKey.len, hash.data());
+#else
+ SECStatus rv = HASH_HashBuf(HASH_AlgSHA256, hash.data(),
+ cert->derPublicKey.data, cert->derPublicKey.len);
+ DCHECK_EQ(rv, SECSuccess);
+#endif
+ return hash;
+}
+
+void AppendPublicKeyHashes(CERTCertList* cert_list,
+ CERTCertificate* root_cert,
+ HashValueVector* hashes) {
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ hashes->push_back(CertPublicKeyHashSHA1(node->cert));
+ hashes->push_back(CertPublicKeyHashSHA256(node->cert));
+ }
+ if (root_cert) {
+ hashes->push_back(CertPublicKeyHashSHA1(root_cert));
+ hashes->push_back(CertPublicKeyHashSHA256(root_cert));
+ }
+}
+
+// Returns true if |cert_handle| contains a policy OID that is an EV policy
+// OID according to |metadata|, storing the resulting policy OID in
+// |*ev_policy_oid|. A true return is not sufficient to establish that a
+// certificate is EV, but a false return is sufficient to establish the
+// certificate cannot be EV.
+bool IsEVCandidate(EVRootCAMetadata* metadata,
+ CERTCertificate* cert_handle,
+ SECOidTag* ev_policy_oid) {
+ DCHECK(cert_handle);
+ ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle));
+ if (!policies.get())
+ return false;
+
+ CERTPolicyInfo** policy_infos = policies->policyInfos;
+ while (*policy_infos != NULL) {
+ CERTPolicyInfo* policy_info = *policy_infos++;
+ // If the Policy OID is unknown, that implicitly means it has not been
+ // registered as an EV policy.
+ if (policy_info->oid == SEC_OID_UNKNOWN)
+ continue;
+ if (metadata->IsEVPolicyOID(policy_info->oid)) {
+ *ev_policy_oid = policy_info->oid;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp
+// and nsNSSCertHelper.cpp) to learn how to verify EV certificate.
+// TODO(wtc): A possible optimization is that we get the trust anchor from
+// the first PKIXVerifyCert call. We look up the EV policy for the trust
+// anchor. If the trust anchor has no EV policy, we know the cert isn't EV.
+// Otherwise, we pass just that EV policy (as opposed to all the EV policies)
+// to the second PKIXVerifyCert call.
+bool VerifyEV(CERTCertificate* cert_handle,
+ int flags,
+ CRLSet* crl_set,
+ bool rev_checking_enabled,
+ EVRootCAMetadata* metadata,
+ SECOidTag ev_policy_oid,
+ CERTCertList* additional_trust_anchors) {
+ CERTValOutParam cvout[3];
+ int cvout_index = 0;
+ cvout[cvout_index].type = cert_po_certList;
+ cvout[cvout_index].value.pointer.chain = NULL;
+ int cvout_cert_list_index = cvout_index;
+ cvout_index++;
+ cvout[cvout_index].type = cert_po_trustAnchor;
+ cvout[cvout_index].value.pointer.cert = NULL;
+ int cvout_trust_anchor_index = cvout_index;
+ cvout_index++;
+ cvout[cvout_index].type = cert_po_end;
+ ScopedCERTValOutParam scoped_cvout(cvout);
+
+ SECStatus status = PKIXVerifyCert(
+ cert_handle,
+ rev_checking_enabled,
+ true, /* hard fail is implied in EV. */
+ flags & CertVerifier::VERIFY_CERT_IO_ENABLED,
+ &ev_policy_oid,
+ 1,
+ additional_trust_anchors,
+ cvout);
+ if (status != SECSuccess)
+ return false;
+
+ CERTCertificate* root_ca =
+ cvout[cvout_trust_anchor_index].value.pointer.cert;
+ if (root_ca == NULL)
+ return false;
+
+ // This second PKIXVerifyCert call could have found a different certification
+ // path and one or more of the certificates on this new path, that weren't on
+ // the old path, might have been revoked.
+ if (crl_set) {
+ CRLSetResult crl_set_result = CheckRevocationWithCRLSet(
+ cvout[cvout_cert_list_index].value.pointer.chain,
+ cvout[cvout_trust_anchor_index].value.pointer.cert,
+ crl_set);
+ if (crl_set_result == kCRLSetRevoked)
+ return false;
+ }
+
+#if defined(OS_IOS)
+ SHA1HashValue fingerprint = x509_util_ios::CalculateFingerprintNSS(root_ca);
+#else
+ SHA1HashValue fingerprint =
+ X509Certificate::CalculateFingerprint(root_ca);
+#endif
+ return metadata->HasEVPolicyOID(fingerprint, ev_policy_oid);
+}
+
+CERTCertList* CertificateListToCERTCertList(const CertificateList& list) {
+ CERTCertList* result = CERT_NewCertList();
+ for (size_t i = 0; i < list.size(); ++i) {
+#if defined(OS_IOS)
+ // X509Certificate::os_cert_handle() on iOS is a SecCertificateRef; convert
+ // it to an NSS CERTCertificate.
+ CERTCertificate* cert = x509_util_ios::CreateNSSCertHandleFromOSHandle(
+ list[i]->os_cert_handle());
+#else
+ CERTCertificate* cert = list[i]->os_cert_handle();
+#endif
+ CERT_AddCertToListTail(result, CERT_DupCertificate(cert));
+ }
+ return result;
+}
+
+} // namespace
+
+CertVerifyProcNSS::CertVerifyProcNSS() {}
+
+CertVerifyProcNSS::~CertVerifyProcNSS() {}
+
+bool CertVerifyProcNSS::SupportsAdditionalTrustAnchors() const {
+ // This requires APIs introduced in 3.14.2.
+ return NSS_VersionCheck("3.14.2");
+}
+
+int CertVerifyProcNSS::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+#if defined(OS_IOS)
+ // For iOS, the entire chain must be loaded into NSS's in-memory certificate
+ // store.
+ x509_util_ios::NSSCertChain scoped_chain(cert);
+ CERTCertificate* cert_handle = scoped_chain.cert_handle();
+#else
+ CERTCertificate* cert_handle = cert->os_cert_handle();
+#endif // defined(OS_IOS)
+
+ // Make sure that the hostname matches with the common name of the cert.
+ SECStatus status = CERT_VerifyCertName(cert_handle, hostname.c_str());
+ if (status != SECSuccess)
+ verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
+
+ // Make sure that the cert is valid now.
+ SECCertTimeValidity validity = CERT_CheckCertValidTimes(
+ cert_handle, PR_Now(), PR_TRUE);
+ if (validity != secCertTimeValid)
+ verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
+
+ CERTValOutParam cvout[3];
+ int cvout_index = 0;
+ cvout[cvout_index].type = cert_po_certList;
+ cvout[cvout_index].value.pointer.chain = NULL;
+ int cvout_cert_list_index = cvout_index;
+ cvout_index++;
+ cvout[cvout_index].type = cert_po_trustAnchor;
+ cvout[cvout_index].value.pointer.cert = NULL;
+ int cvout_trust_anchor_index = cvout_index;
+ cvout_index++;
+ cvout[cvout_index].type = cert_po_end;
+ ScopedCERTValOutParam scoped_cvout(cvout);
+
+ EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
+ SECOidTag ev_policy_oid = SEC_OID_UNKNOWN;
+ bool is_ev_candidate =
+ (flags & CertVerifier::VERIFY_EV_CERT) &&
+ IsEVCandidate(metadata, cert_handle, &ev_policy_oid);
+ bool cert_io_enabled = flags & CertVerifier::VERIFY_CERT_IO_ENABLED;
+ bool check_revocation =
+ cert_io_enabled &&
+ (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED);
+ if (check_revocation)
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+
+ ScopedCERTCertList trust_anchors;
+ if (SupportsAdditionalTrustAnchors() && !additional_trust_anchors.empty()) {
+ trust_anchors.reset(
+ CertificateListToCERTCertList(additional_trust_anchors));
+ }
+
+ status = PKIXVerifyCert(cert_handle, check_revocation, false,
+ cert_io_enabled, NULL, 0, trust_anchors.get(),
+ cvout);
+
+ if (status == SECSuccess &&
+ (flags & CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS) &&
+ !IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert)) {
+ // TODO(rsleevi): Optimize this by supplying the constructed chain to
+ // libpkix via cvin. Omitting for now, due to lack of coverage in upstream
+ // NSS tests for that feature.
+ scoped_cvout.Clear();
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+ status = PKIXVerifyCert(cert_handle, true, true,
+ cert_io_enabled, NULL, 0, trust_anchors.get(),
+ cvout);
+ }
+
+ if (status == SECSuccess) {
+ AppendPublicKeyHashes(cvout[cvout_cert_list_index].value.pointer.chain,
+ cvout[cvout_trust_anchor_index].value.pointer.cert,
+ &verify_result->public_key_hashes);
+
+ verify_result->is_issued_by_known_root =
+ IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert);
+ verify_result->is_issued_by_additional_trust_anchor =
+ IsAdditionalTrustAnchor(
+ trust_anchors.get(),
+ cvout[cvout_trust_anchor_index].value.pointer.cert);
+
+ GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain,
+ cvout[cvout_trust_anchor_index].value.pointer.cert,
+ verify_result);
+ }
+
+ CRLSetResult crl_set_result = kCRLSetUnknown;
+ if (crl_set) {
+ crl_set_result = CheckRevocationWithCRLSet(
+ cvout[cvout_cert_list_index].value.pointer.chain,
+ cvout[cvout_trust_anchor_index].value.pointer.cert,
+ crl_set);
+ if (crl_set_result == kCRLSetRevoked) {
+ PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+ status = SECFailure;
+ }
+ }
+
+ if (status != SECSuccess) {
+ int err = PORT_GetError();
+ LOG(ERROR) << "CERT_PKIXVerifyCert for " << hostname
+ << " failed err=" << err;
+ // CERT_PKIXVerifyCert rerports the wrong error code for
+ // expired certificates (NSS bug 491174)
+ if (err == SEC_ERROR_CERT_NOT_VALID &&
+ (verify_result->cert_status & CERT_STATUS_DATE_INVALID))
+ err = SEC_ERROR_EXPIRED_CERTIFICATE;
+ CertStatus cert_status = MapCertErrorToCertStatus(err);
+ if (cert_status) {
+ verify_result->cert_status |= cert_status;
+ return MapCertStatusToNetError(verify_result->cert_status);
+ }
+ // |err| is not a certificate error.
+ return MapSecurityError(err);
+ }
+
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ if ((flags & CertVerifier::VERIFY_EV_CERT) && is_ev_candidate) {
+ check_revocation |=
+ crl_set_result != kCRLSetOk &&
+ cert_io_enabled &&
+ (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY);
+ if (check_revocation)
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+
+ if (VerifyEV(cert_handle, flags, crl_set, check_revocation, metadata,
+ ev_policy_oid, trust_anchors.get())) {
+ verify_result->cert_status |= CERT_STATUS_IS_EV;
+ }
+ }
+
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_nss.h b/chromium/net/cert/cert_verify_proc_nss.h
new file mode 100644
index 00000000000..f8bb853544e
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_nss.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_NSS_H_
+#define NET_CERT_CERT_VERIFY_PROC_NSS_H_
+
+#include "net/base/net_export.h"
+#include "net/cert/cert_verify_proc.h"
+
+namespace net {
+
+// Performs certificate path construction and validation using NSS's libpkix.
+class NET_EXPORT_PRIVATE CertVerifyProcNSS : public CertVerifyProc {
+ public:
+ CertVerifyProcNSS();
+
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE;
+
+ protected:
+ virtual ~CertVerifyProcNSS();
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_NSS_H_
diff --git a/chromium/net/cert/cert_verify_proc_openssl.cc b/chromium/net/cert/cert_verify_proc_openssl.cc
new file mode 100644
index 00000000000..a67f194662d
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_openssl.cc
@@ -0,0 +1,226 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc_openssl.h"
+
+#include <openssl/x509v3.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "crypto/openssl_util.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+// Maps X509_STORE_CTX_get_error() return values to our cert status flags.
+CertStatus MapCertErrorToCertStatus(int err) {
+ switch (err) {
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ return CERT_STATUS_COMMON_NAME_INVALID;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ return CERT_STATUS_DATE_INVALID;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_INVALID_CA:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ case X509_V_ERR_INVALID_NON_CA:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ return CERT_STATUS_AUTHORITY_INVALID;
+#if 0
+// TODO(bulach): what should we map to these status?
+ return CERT_STATUS_NO_REVOCATION_MECHANISM;
+ return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+#endif
+ case X509_V_ERR_CERT_REVOKED:
+ return CERT_STATUS_REVOKED;
+ // All these status are mapped to CERT_STATUS_INVALID.
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ case X509_V_ERR_OUT_OF_MEM:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_PURPOSE:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
+ case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
+ case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
+ case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED:
+ case X509_V_ERR_INVALID_EXTENSION:
+ case X509_V_ERR_INVALID_POLICY_EXTENSION:
+ case X509_V_ERR_NO_EXPLICIT_POLICY:
+ case X509_V_ERR_UNNESTED_RESOURCE:
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ return CERT_STATUS_INVALID;
+ default:
+ NOTREACHED() << "Invalid X509 err " << err;
+ return CERT_STATUS_INVALID;
+ }
+}
+
+// sk_X509_free is a function-style macro, so can't be used as a template
+// param directly.
+void sk_X509_free_fn(STACK_OF(X509)* st) {
+ sk_X509_free(st);
+}
+
+void GetCertChainInfo(X509_STORE_CTX* store_ctx,
+ CertVerifyResult* verify_result) {
+ STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(store_ctx);
+ X509* verified_cert = NULL;
+ std::vector<X509*> verified_chain;
+ for (int i = 0; i < sk_X509_num(chain); ++i) {
+ X509* cert = sk_X509_value(chain, i);
+ if (i == 0) {
+ verified_cert = cert;
+ } else {
+ verified_chain.push_back(cert);
+ }
+
+ // Only check the algorithm status for certificates that are not in the
+ // trust store.
+ if (i < store_ctx->last_untrusted) {
+ int sig_alg = OBJ_obj2nid(cert->sig_alg->algorithm);
+ if (sig_alg == NID_md2WithRSAEncryption) {
+ verify_result->has_md2 = true;
+ } else if (sig_alg == NID_md4WithRSAEncryption) {
+ verify_result->has_md4 = true;
+ } else if (sig_alg == NID_md5WithRSAEncryption) {
+ verify_result->has_md5 = true;
+ }
+ }
+ }
+
+ if (verified_cert) {
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+ }
+}
+
+void AppendPublicKeyHashes(X509_STORE_CTX* store_ctx,
+ HashValueVector* hashes) {
+ STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(store_ctx);
+ for (int i = 0; i < sk_X509_num(chain); ++i) {
+ X509* cert = sk_X509_value(chain, i);
+
+ std::string der_data;
+ if (!X509Certificate::GetDEREncoded(cert, &der_data))
+ continue;
+
+ base::StringPiece der_bytes(der_data);
+ base::StringPiece spki_bytes;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
+ continue;
+
+ HashValue sha1(HASH_VALUE_SHA1);
+ base::SHA1HashBytes(reinterpret_cast<const uint8*>(spki_bytes.data()),
+ spki_bytes.size(), sha1.data());
+ hashes->push_back(sha1);
+
+ HashValue sha256(HASH_VALUE_SHA256);
+ crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length);
+ hashes->push_back(sha256);
+ }
+}
+
+} // namespace
+
+CertVerifyProcOpenSSL::CertVerifyProcOpenSSL() {}
+
+CertVerifyProcOpenSSL::~CertVerifyProcOpenSSL() {}
+
+bool CertVerifyProcOpenSSL::SupportsAdditionalTrustAnchors() const {
+ return false;
+}
+
+int CertVerifyProcOpenSSL::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ crypto::EnsureOpenSSLInit();
+
+ if (!cert->VerifyNameMatch(hostname))
+ verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
+
+ crypto::ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx(
+ X509_STORE_CTX_new());
+
+ crypto::ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn> intermediates(
+ sk_X509_new_null());
+ if (!intermediates.get())
+ return ERR_OUT_OF_MEMORY;
+
+ const X509Certificate::OSCertHandles& os_intermediates =
+ cert->GetIntermediateCertificates();
+ for (X509Certificate::OSCertHandles::const_iterator it =
+ os_intermediates.begin(); it != os_intermediates.end(); ++it) {
+ if (!sk_X509_push(intermediates.get(), *it))
+ return ERR_OUT_OF_MEMORY;
+ }
+ if (X509_STORE_CTX_init(ctx.get(), X509Certificate::cert_store(),
+ cert->os_cert_handle(), intermediates.get()) != 1) {
+ NOTREACHED();
+ return ERR_FAILED;
+ }
+
+ if (X509_verify_cert(ctx.get()) != 1) {
+ int x509_error = X509_STORE_CTX_get_error(ctx.get());
+ CertStatus cert_status = MapCertErrorToCertStatus(x509_error);
+ LOG(ERROR) << "X509 Verification error "
+ << X509_verify_cert_error_string(x509_error)
+ << " : " << x509_error
+ << " : " << X509_STORE_CTX_get_error_depth(ctx.get())
+ << " : " << cert_status;
+ verify_result->cert_status |= cert_status;
+ }
+
+ GetCertChainInfo(ctx.get(), verify_result);
+ AppendPublicKeyHashes(ctx.get(), &verify_result->public_key_hashes);
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ // Currently we only ues OpenSSL's default root CA paths, so treat all
+ // correctly verified certs as being from a known root.
+ // TODO(joth): if the motivations described in
+ // http://src.chromium.org/viewvc/chrome?view=rev&revision=80778 become an
+ // issue on OpenSSL builds, we will need to embed a hardcoded list of well
+ // known root CAs, as per the _mac and _win versions.
+ verify_result->is_issued_by_known_root = true;
+
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_openssl.h b/chromium/net/cert/cert_verify_proc_openssl.h
new file mode 100644
index 00000000000..d0d25746bf9
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_openssl.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_OPENSSL_H_
+#define NET_CERT_CERT_VERIFY_PROC_OPENSSL_H_
+
+#include "net/cert/cert_verify_proc.h"
+
+namespace net {
+
+// Performs certificate path construction and validation using OpenSSL.
+class CertVerifyProcOpenSSL : public CertVerifyProc {
+ public:
+ CertVerifyProcOpenSSL();
+
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE;
+
+ protected:
+ virtual ~CertVerifyProcOpenSSL();
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_OPENSSL_H_
diff --git a/chromium/net/cert/cert_verify_proc_unittest.cc b/chromium/net/cert/cert_verify_proc_unittest.cc
new file mode 100644
index 00000000000..a53d10a0845
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_unittest.cc
@@ -0,0 +1,1359 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc.h"
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_certificate_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mac_util.h"
+#endif
+
+using base::HexEncode;
+
+namespace net {
+
+namespace {
+
+// A certificate for www.paypal.com with a NULL byte in the common name.
+// From http://www.gossamer-threads.com/lists/fulldisc/full-disclosure/70363
+unsigned char paypal_null_fingerprint[] = {
+ 0x4c, 0x88, 0x9e, 0x28, 0xd7, 0x7a, 0x44, 0x1e, 0x13, 0xf2, 0x6a, 0xba,
+ 0x1f, 0xe8, 0x1b, 0xd6, 0xab, 0x7b, 0xe8, 0xd7
+};
+
+// Mock CertVerifyProc that will set |verify_result->is_issued_by_known_root|
+// for all certificates that are Verified.
+class WellKnownCaCertVerifyProc : public CertVerifyProc {
+ public:
+ // Initialize a CertVerifyProc that will set
+ // |verify_result->is_issued_by_known_root| to |is_well_known|.
+ explicit WellKnownCaCertVerifyProc(bool is_well_known)
+ : is_well_known_(is_well_known) {}
+
+ // CertVerifyProc implementation:
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE { return false; }
+
+ protected:
+ virtual ~WellKnownCaCertVerifyProc() {}
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+
+ const bool is_well_known_;
+
+ DISALLOW_COPY_AND_ASSIGN(WellKnownCaCertVerifyProc);
+};
+
+int WellKnownCaCertVerifyProc::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ verify_result->is_issued_by_known_root = is_well_known_;
+ return OK;
+}
+
+} // namespace
+
+class CertVerifyProcTest : public testing::Test {
+ public:
+ CertVerifyProcTest()
+ : verify_proc_(CertVerifyProc::CreateDefault()) {
+ }
+ virtual ~CertVerifyProcTest() {}
+
+ protected:
+ bool SupportsAdditionalTrustAnchors() {
+ return verify_proc_->SupportsAdditionalTrustAnchors();
+ }
+
+ int Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ return verify_proc_->Verify(cert, hostname, flags, crl_set,
+ additional_trust_anchors, verify_result);
+ }
+
+ const CertificateList empty_cert_list_;
+ scoped_refptr<CertVerifyProc> verify_proc_;
+};
+
+TEST_F(CertVerifyProcTest, WithoutRevocationChecking) {
+ // Check that verification without revocation checking works.
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(),
+ "googlenew.chain.pem",
+ X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(OK,
+ Verify(google_full_chain.get(),
+ "www.google.com",
+ 0 /* flags */,
+ NULL,
+ empty_cert_list_,
+ &verify_result));
+}
+
+#if defined(OS_ANDROID) || defined(USE_OPENSSL)
+// TODO(jnd): http://crbug.com/117478 - EV verification is not yet supported.
+#define MAYBE_EVVerification DISABLED_EVVerification
+#else
+#define MAYBE_EVVerification EVVerification
+#endif
+TEST_F(CertVerifyProcTest, MAYBE_EVVerification) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(),
+ "comodo.chain.pem",
+ X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+ ASSERT_EQ(3U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+ intermediates.push_back(certs[2]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> comodo_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+
+ scoped_refptr<CRLSet> crl_set(CRLSet::ForTesting(false, NULL, ""));
+ CertVerifyResult verify_result;
+ int flags = CertVerifier::VERIFY_EV_CERT;
+ int error = Verify(comodo_chain.get(),
+ "comodo.com",
+ flags,
+ crl_set.get(),
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV);
+}
+
+TEST_F(CertVerifyProcTest, PaypalNullCertParsing) {
+ scoped_refptr<X509Certificate> paypal_null_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(paypal_null_der),
+ sizeof(paypal_null_der)));
+
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), paypal_null_cert);
+
+ const SHA1HashValue& fingerprint =
+ paypal_null_cert->fingerprint();
+ for (size_t i = 0; i < 20; ++i)
+ EXPECT_EQ(paypal_null_fingerprint[i], fingerprint.data[i]);
+
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(paypal_null_cert.get(),
+ "www.paypal.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+#if defined(USE_NSS) || defined(OS_IOS) || defined(OS_ANDROID)
+ EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error);
+#else
+ // TOOD(bulach): investigate why macosx and win aren't returning
+ // ERR_CERT_INVALID or ERR_CERT_COMMON_NAME_INVALID.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+#endif
+ // Either the system crypto library should correctly report a certificate
+ // name mismatch, or our certificate blacklist should cause us to report an
+ // invalid certificate.
+#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_IOS)
+ EXPECT_TRUE(verify_result.cert_status &
+ (CERT_STATUS_COMMON_NAME_INVALID | CERT_STATUS_INVALID));
+#endif
+}
+
+// A regression test for http://crbug.com/31497.
+#if defined(OS_ANDROID)
+// Disabled on Android, as the Android verification libraries require an
+// explicit policy to be specified, even when anyPolicy is permitted.
+#define MAYBE_IntermediateCARequireExplicitPolicy \
+ DISABLED_IntermediateCARequireExplicitPolicy
+#else
+#define MAYBE_IntermediateCARequireExplicitPolicy \
+ IntermediateCARequireExplicitPolicy
+#endif
+TEST_F(CertVerifyProcTest, MAYBE_IntermediateCARequireExplicitPolicy) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "explicit-policy-chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> cert =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_TRUE(cert.get());
+
+ ScopedTestRoot scoped_root(certs[2].get());
+
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(cert.get(),
+ "policy_test.example",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0u, verify_result.cert_status);
+}
+
+
+// Test for bug 58437.
+// This certificate will expire on 2011-12-21. The test will still
+// pass if error == ERR_CERT_DATE_INVALID.
+// This test is DISABLED because it appears that we cannot do
+// certificate revocation checking when running all of the net unit tests.
+// This test passes when run individually, but when run with all of the net
+// unit tests, the call to PKIXVerifyCert returns the NSS error -8180, which is
+// SEC_ERROR_REVOKED_CERTIFICATE. This indicates a lack of revocation
+// status, i.e. that the revocation check is failing for some reason.
+TEST_F(CertVerifyProcTest, DISABLED_GlobalSignR3EVTest) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "2029_globalsign_com_cert.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
+
+ scoped_refptr<X509Certificate> intermediate_cert =
+ ImportCertFromFile(certs_dir, "globalsign_ev_sha256_ca_cert.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate_cert->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ CertVerifyResult verify_result;
+ int flags = CertVerifier::VERIFY_REV_CHECKING_ENABLED |
+ CertVerifier::VERIFY_EV_CERT;
+ int error = Verify(cert_chain.get(),
+ "2029.globalsign.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ if (error == OK)
+ EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV);
+ else
+ EXPECT_EQ(ERR_CERT_DATE_INVALID, error);
+}
+
+// Test that verifying an ECDSA certificate doesn't crash on XP. (See
+// crbug.com/144466).
+TEST_F(CertVerifyProcTest, ECDSA_RSA) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> cert =
+ ImportCertFromFile(certs_dir,
+ "prime256v1-ecdsa-ee-by-1024-rsa-intermediate.pem");
+
+ CertVerifyResult verify_result;
+ Verify(cert.get(), "127.0.0.1", 0, NULL, empty_cert_list_, &verify_result);
+
+ // We don't check verify_result because the certificate is signed by an
+ // unknown CA and will be considered invalid on XP because of the ECDSA
+ // public key.
+}
+
+// Currently, only RSA and DSA keys are checked for weakness, and our example
+// weak size is 768. These could change in the future.
+//
+// Note that this means there may be false negatives: keys for other
+// algorithms and which are weak will pass this test.
+static bool IsWeakKeyType(const std::string& key_type) {
+ size_t pos = key_type.find("-");
+ std::string size = key_type.substr(0, pos);
+ std::string type = key_type.substr(pos + 1);
+
+ if (type == "rsa" || type == "dsa")
+ return size == "768";
+
+ return false;
+}
+
+TEST_F(CertVerifyProcTest, RejectWeakKeys) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ typedef std::vector<std::string> Strings;
+ Strings key_types;
+
+ // generate-weak-test-chains.sh currently has:
+ // key_types="768-rsa 1024-rsa 2048-rsa prime256v1-ecdsa"
+ // We must use the same key types here. The filenames generated look like:
+ // 2048-rsa-ee-by-768-rsa-intermediate.pem
+ key_types.push_back("768-rsa");
+ key_types.push_back("1024-rsa");
+ key_types.push_back("2048-rsa");
+
+ bool use_ecdsa = true;
+#if defined(OS_WIN)
+ use_ecdsa = base::win::GetVersion() > base::win::VERSION_XP;
+#endif
+
+ if (use_ecdsa)
+ key_types.push_back("prime256v1-ecdsa");
+
+ // Add the root that signed the intermediates for this test.
+ scoped_refptr<X509Certificate> root_cert =
+ ImportCertFromFile(certs_dir, "2048-rsa-root.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert);
+ ScopedTestRoot scoped_root(root_cert.get());
+
+ // Now test each chain.
+ for (Strings::const_iterator ee_type = key_types.begin();
+ ee_type != key_types.end(); ++ee_type) {
+ for (Strings::const_iterator signer_type = key_types.begin();
+ signer_type != key_types.end(); ++signer_type) {
+ std::string basename = *ee_type + "-ee-by-" + *signer_type +
+ "-intermediate.pem";
+ SCOPED_TRACE(basename);
+ scoped_refptr<X509Certificate> ee_cert =
+ ImportCertFromFile(certs_dir, basename);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), ee_cert);
+
+ basename = *signer_type + "-intermediate.pem";
+ scoped_refptr<X509Certificate> intermediate =
+ ImportCertFromFile(certs_dir, basename);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(ee_cert->os_cert_handle(),
+ intermediates);
+
+ CertVerifyResult verify_result;
+ int error = Verify(cert_chain.get(),
+ "127.0.0.1",
+ 0,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+
+ if (IsWeakKeyType(*ee_type) || IsWeakKeyType(*signer_type)) {
+ EXPECT_NE(OK, error);
+ EXPECT_EQ(CERT_STATUS_WEAK_KEY,
+ verify_result.cert_status & CERT_STATUS_WEAK_KEY);
+ EXPECT_NE(CERT_STATUS_INVALID,
+ verify_result.cert_status & CERT_STATUS_INVALID);
+ } else {
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status & CERT_STATUS_WEAK_KEY);
+ }
+ }
+ }
+}
+
+// Regression test for http://crbug.com/108514.
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Disabled on OS X - Security.framework doesn't ignore superflous certificates
+// provided by servers. See CertVerifyProcTest.CybertrustGTERoot for further
+// details.
+#define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert
+#elif defined(USE_OPENSSL) || defined(OS_ANDROID)
+// Disabled for OpenSSL / Android - Android and OpenSSL do not attempt to find
+// a minimal certificate chain, thus prefer the MD5 root over the SHA-1 root.
+#define MAYBE_ExtraneousMD5RootCert DISABLED_ExtraneousMD5RootCert
+#else
+#define MAYBE_ExtraneousMD5RootCert ExtraneousMD5RootCert
+#endif
+TEST_F(CertVerifyProcTest, MAYBE_ExtraneousMD5RootCert) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "cross-signed-leaf.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert.get());
+
+ scoped_refptr<X509Certificate> extra_cert =
+ ImportCertFromFile(certs_dir, "cross-signed-root-md5.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), extra_cert.get());
+
+ scoped_refptr<X509Certificate> root_cert =
+ ImportCertFromFile(certs_dir, "cross-signed-root-sha1.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert.get());
+
+ ScopedTestRoot scoped_root(root_cert.get());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(extra_cert->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ CertVerifyResult verify_result;
+ int flags = 0;
+ int error = Verify(cert_chain.get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+
+ // The extra MD5 root should be discarded
+ ASSERT_TRUE(verify_result.verified_cert.get());
+ ASSERT_EQ(1u,
+ verify_result.verified_cert->GetIntermediateCertificates().size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ verify_result.verified_cert->GetIntermediateCertificates().front(),
+ root_cert->os_cert_handle()));
+
+ EXPECT_FALSE(verify_result.has_md5);
+}
+
+// Test for bug 94673.
+TEST_F(CertVerifyProcTest, GoogleDigiNotarTest) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "google_diginotar.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
+
+ scoped_refptr<X509Certificate> intermediate_cert =
+ ImportCertFromFile(certs_dir, "diginotar_public_ca_2025.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate_cert->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ CertVerifyResult verify_result;
+ int flags = CertVerifier::VERIFY_REV_CHECKING_ENABLED;
+ int error = Verify(cert_chain.get(),
+ "mail.google.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_NE(OK, error);
+
+ // Now turn off revocation checking. Certificate verification should still
+ // fail.
+ flags = 0;
+ error = Verify(cert_chain.get(),
+ "mail.google.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_NE(OK, error);
+}
+
+TEST_F(CertVerifyProcTest, DigiNotarCerts) {
+ static const char* const kDigiNotarFilenames[] = {
+ "diginotar_root_ca.pem",
+ "diginotar_cyber_ca.pem",
+ "diginotar_services_1024_ca.pem",
+ "diginotar_pkioverheid.pem",
+ "diginotar_pkioverheid_g2.pem",
+ NULL,
+ };
+
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ for (size_t i = 0; kDigiNotarFilenames[i]; i++) {
+ scoped_refptr<X509Certificate> diginotar_cert =
+ ImportCertFromFile(certs_dir, kDigiNotarFilenames[i]);
+ std::string der_bytes;
+ ASSERT_TRUE(X509Certificate::GetDEREncoded(
+ diginotar_cert->os_cert_handle(), &der_bytes));
+
+ base::StringPiece spki;
+ ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(der_bytes, &spki));
+
+ std::string spki_sha1 = base::SHA1HashString(spki.as_string());
+
+ HashValueVector public_keys;
+ HashValue hash(HASH_VALUE_SHA1);
+ ASSERT_EQ(hash.size(), spki_sha1.size());
+ memcpy(hash.data(), spki_sha1.data(), spki_sha1.size());
+ public_keys.push_back(hash);
+
+ EXPECT_TRUE(CertVerifyProc::IsPublicKeyBlacklisted(public_keys)) <<
+ "Public key not blocked for " << kDigiNotarFilenames[i];
+ }
+}
+
+// The certse.pem certificate has been revoked. crbug.com/259723.
+TEST_F(CertVerifyProcTest, TestKnownRoot) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(2U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+
+ int flags = 0;
+ CertVerifyResult verify_result;
+ // This will blow up, May 24th, 2019. Sorry! Please disable and file a bug
+ // against agl. See also PublicKeyHashes.
+ int error = Verify(cert_chain.get(),
+ "satveda.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+ EXPECT_TRUE(verify_result.is_issued_by_known_root);
+}
+
+// The certse.pem certificate has been revoked. crbug.com/259723.
+TEST_F(CertVerifyProcTest, PublicKeyHashes) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "satveda.pem", X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(2U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ int flags = 0;
+ CertVerifyResult verify_result;
+
+ // This will blow up, May 24th, 2019. Sorry! Please disable and file a bug
+ // against agl. See also TestKnownRoot.
+ int error = Verify(cert_chain.get(),
+ "satveda.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+ ASSERT_LE(2U, verify_result.public_key_hashes.size());
+
+ HashValueVector sha1_hashes;
+ for (size_t i = 0; i < verify_result.public_key_hashes.size(); ++i) {
+ if (verify_result.public_key_hashes[i].tag != HASH_VALUE_SHA1)
+ continue;
+ sha1_hashes.push_back(verify_result.public_key_hashes[i]);
+ }
+ ASSERT_LE(2u, sha1_hashes.size());
+
+ for (size_t i = 0; i < 2; ++i) {
+ EXPECT_EQ(HexEncode(kSatvedaSPKIs[i], base::kSHA1Length),
+ HexEncode(sha1_hashes[i].data(), base::kSHA1Length));
+ }
+
+ HashValueVector sha256_hashes;
+ for (size_t i = 0; i < verify_result.public_key_hashes.size(); ++i) {
+ if (verify_result.public_key_hashes[i].tag != HASH_VALUE_SHA256)
+ continue;
+ sha256_hashes.push_back(verify_result.public_key_hashes[i]);
+ }
+ ASSERT_LE(2u, sha256_hashes.size());
+
+ for (size_t i = 0; i < 2; ++i) {
+ EXPECT_EQ(HexEncode(kSatvedaSPKIsSHA256[i], crypto::kSHA256Length),
+ HexEncode(sha256_hashes[i].data(), crypto::kSHA256Length));
+ }
+}
+
+// A regression test for http://crbug.com/70293.
+// The Key Usage extension in this RSA SSL server certificate does not have
+// the keyEncipherment bit.
+TEST_F(CertVerifyProcTest, InvalidKeyUsage) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "invalid_key_usage_cert.der");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
+
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(server_cert.get(),
+ "jira.aquameta.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+#if defined(USE_OPENSSL) && !defined(OS_ANDROID)
+ // This certificate has two errors: "invalid key usage" and "untrusted CA".
+ // However, OpenSSL returns only one (the latter), and we can't detect
+ // the other errors.
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+#else
+ EXPECT_EQ(ERR_CERT_INVALID, error);
+ EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
+#endif
+ // TODO(wtc): fix http://crbug.com/75520 to get all the certificate errors
+ // from NSS.
+#if !defined(USE_NSS) && !defined(OS_IOS) && !defined(OS_ANDROID)
+ // The certificate is issued by an unknown CA.
+ EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
+#endif
+}
+
+// Basic test for returning the chain in CertVerifyResult. Note that the
+// returned chain may just be a reflection of the originally supplied chain;
+// that is, if any errors occur, the default chain returned is an exact copy
+// of the certificate to be verified. The remaining VerifyReturn* tests are
+// used to ensure that the actual, verified chain is being returned by
+// Verify().
+TEST_F(CertVerifyProcTest, VerifyReturnChainBasic) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "x509_verify_results.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+ intermediates.push_back(certs[2]->os_cert_handle());
+
+ ScopedTestRoot scoped_root(certs[2].get());
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_full_chain);
+ ASSERT_EQ(2U, google_full_chain->GetIntermediateCertificates().size());
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+ int error = Verify(google_full_chain.get(),
+ "127.0.0.1",
+ 0,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+
+ EXPECT_NE(google_full_chain, verify_result.verified_cert);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ google_full_chain->os_cert_handle(),
+ verify_result.verified_cert->os_cert_handle()));
+ const X509Certificate::OSCertHandles& return_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(2U, return_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[0],
+ certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[1],
+ certs[2]->os_cert_handle()));
+}
+
+// Test that certificates issued for 'intranet' names (that is, containing no
+// known public registry controlled domain information) issued by well-known
+// CAs are flagged appropriately, while certificates that are issued by
+// internal CAs are not flagged.
+TEST_F(CertVerifyProcTest, IntranetHostsRejected) {
+ CertificateList cert_list = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ CertVerifyResult verify_result;
+ int error = 0;
+
+ // Intranet names for public CAs should be flagged:
+ verify_proc_ = new WellKnownCaCertVerifyProc(true);
+ error =
+ Verify(cert.get(), "intranet", 0, NULL, empty_cert_list_, &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
+
+ // However, if the CA is not well known, these should not be flagged:
+ verify_proc_ = new WellKnownCaCertVerifyProc(false);
+ error =
+ Verify(cert.get(), "intranet", 0, NULL, empty_cert_list_, &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_NON_UNIQUE_NAME);
+}
+
+// Test that the certificate returned in CertVerifyResult is able to reorder
+// certificates that are not ordered from end-entity to root. While this is
+// a protocol violation if sent during a TLS handshake, if multiple sources
+// of intermediate certificates are combined, it's possible that order may
+// not be maintained.
+TEST_F(CertVerifyProcTest, VerifyReturnChainProperlyOrdered) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "x509_verify_results.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+
+ // Construct the chain out of order.
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[2]->os_cert_handle());
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ ScopedTestRoot scoped_root(certs[2].get());
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_full_chain);
+ ASSERT_EQ(2U, google_full_chain->GetIntermediateCertificates().size());
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+ int error = Verify(google_full_chain.get(),
+ "127.0.0.1",
+ 0,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+
+ EXPECT_NE(google_full_chain, verify_result.verified_cert);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ google_full_chain->os_cert_handle(),
+ verify_result.verified_cert->os_cert_handle()));
+ const X509Certificate::OSCertHandles& return_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(2U, return_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[0],
+ certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[1],
+ certs[2]->os_cert_handle()));
+}
+
+// Test that Verify() filters out certificates which are not related to
+// or part of the certificate chain being verified.
+TEST_F(CertVerifyProcTest, VerifyReturnChainFiltersUnrelatedCerts) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, "x509_verify_results.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3U, certs.size());
+ ScopedTestRoot scoped_root(certs[2].get());
+
+ scoped_refptr<X509Certificate> unrelated_certificate =
+ ImportCertFromFile(certs_dir, "duplicate_cn_1.pem");
+ scoped_refptr<X509Certificate> unrelated_certificate2 =
+ ImportCertFromFile(certs_dir, "aia-cert.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), unrelated_certificate);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), unrelated_certificate2);
+
+ // Interject unrelated certificates into the list of intermediates.
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(unrelated_certificate->os_cert_handle());
+ intermediates.push_back(certs[1]->os_cert_handle());
+ intermediates.push_back(unrelated_certificate2->os_cert_handle());
+ intermediates.push_back(certs[2]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_full_chain);
+ ASSERT_EQ(4U, google_full_chain->GetIntermediateCertificates().size());
+
+ CertVerifyResult verify_result;
+ EXPECT_EQ(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+ int error = Verify(google_full_chain.get(),
+ "127.0.0.1",
+ 0,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), verify_result.verified_cert);
+
+ EXPECT_NE(google_full_chain, verify_result.verified_cert);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ google_full_chain->os_cert_handle(),
+ verify_result.verified_cert->os_cert_handle()));
+ const X509Certificate::OSCertHandles& return_intermediates =
+ verify_result.verified_cert->GetIntermediateCertificates();
+ ASSERT_EQ(2U, return_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[0],
+ certs[1]->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(return_intermediates[1],
+ certs[2]->os_cert_handle()));
+}
+
+TEST_F(CertVerifyProcTest, AdditionalTrustAnchors) {
+ if (!SupportsAdditionalTrustAnchors()) {
+ LOG(INFO) << "Skipping this test in this platform.";
+ return;
+ }
+
+ // |ca_cert| is the issuer of |cert|.
+ CertificateList ca_cert_list = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, ca_cert_list.size());
+ scoped_refptr<X509Certificate> ca_cert(ca_cert_list[0]);
+
+ CertificateList cert_list = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ // Verification of |cert| fails when |ca_cert| is not in the trust anchors
+ // list.
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(
+ cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_, &verify_result);
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
+ EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
+
+ // Now add the |ca_cert| to the |trust_anchors|, and verification should pass.
+ CertificateList trust_anchors;
+ trust_anchors.push_back(ca_cert);
+ error = Verify(
+ cert.get(), "127.0.0.1", flags, NULL, trust_anchors, &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+ EXPECT_TRUE(verify_result.is_issued_by_additional_trust_anchor);
+
+ // Clearing the |trust_anchors| makes verification fail again (the cache
+ // should be skipped).
+ error = Verify(
+ cert.get(), "127.0.0.1", flags, NULL, empty_cert_list_, &verify_result);
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
+ EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Tests that, on OS X, issues with a cross-certified Baltimore CyberTrust
+// Root can be successfully worked around once Apple completes removing the
+// older GTE CyberTrust Root from its trusted root store.
+//
+// The issue is caused by servers supplying the cross-certified intermediate
+// (necessary for certain mobile platforms), which OS X does not recognize
+// as already existing within its trust store.
+TEST_F(CertVerifyProcTest, CybertrustGTERoot) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(),
+ "cybertrust_omniroot_chain.pem",
+ X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+ ASSERT_EQ(2U, certs.size());
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> cybertrust_basic =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+ ASSERT_TRUE(cybertrust_basic.get());
+
+ scoped_refptr<X509Certificate> baltimore_root =
+ ImportCertFromFile(GetTestCertsDirectory(),
+ "cybertrust_baltimore_root.pem");
+ ASSERT_TRUE(baltimore_root.get());
+
+ ScopedTestRoot scoped_root(baltimore_root.get());
+
+ // Ensure that ONLY the Baltimore CyberTrust Root is trusted. This
+ // simulates Keychain removing support for the GTE CyberTrust Root.
+ TestRootCerts::GetInstance()->SetAllowSystemTrust(false);
+ base::ScopedClosureRunner reset_system_trust(
+ base::Bind(&TestRootCerts::SetAllowSystemTrust,
+ base::Unretained(TestRootCerts::GetInstance()),
+ true));
+
+ // First, make sure a simple certificate chain from
+ // EE -> Public SureServer SV -> Baltimore CyberTrust
+ // works. Only the first two certificates are included in the chain.
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = Verify(cybertrust_basic.get(),
+ "cacert.omniroot.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+ // Attempt to verify with the first known cross-certified intermediate
+ // provided.
+ scoped_refptr<X509Certificate> baltimore_intermediate_1 =
+ ImportCertFromFile(GetTestCertsDirectory(),
+ "cybertrust_baltimore_cross_certified_1.pem");
+ ASSERT_TRUE(baltimore_intermediate_1.get());
+
+ X509Certificate::OSCertHandles intermediate_chain_1 =
+ cybertrust_basic->GetIntermediateCertificates();
+ intermediate_chain_1.push_back(baltimore_intermediate_1->os_cert_handle());
+
+ scoped_refptr<X509Certificate> baltimore_chain_1 =
+ X509Certificate::CreateFromHandle(cybertrust_basic->os_cert_handle(),
+ intermediate_chain_1);
+ error = Verify(baltimore_chain_1.get(),
+ "cacert.omniroot.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+ // Attempt to verify with the second known cross-certified intermediate
+ // provided.
+ scoped_refptr<X509Certificate> baltimore_intermediate_2 =
+ ImportCertFromFile(GetTestCertsDirectory(),
+ "cybertrust_baltimore_cross_certified_2.pem");
+ ASSERT_TRUE(baltimore_intermediate_2.get());
+
+ X509Certificate::OSCertHandles intermediate_chain_2 =
+ cybertrust_basic->GetIntermediateCertificates();
+ intermediate_chain_2.push_back(baltimore_intermediate_2->os_cert_handle());
+
+ scoped_refptr<X509Certificate> baltimore_chain_2 =
+ X509Certificate::CreateFromHandle(cybertrust_basic->os_cert_handle(),
+ intermediate_chain_2);
+ error = Verify(baltimore_chain_2.get(),
+ "cacert.omniroot.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+ // Attempt to verify when both a cross-certified intermediate AND
+ // the legacy GTE root are provided.
+ scoped_refptr<X509Certificate> cybertrust_root =
+ ImportCertFromFile(GetTestCertsDirectory(),
+ "cybertrust_gte_root.pem");
+ ASSERT_TRUE(cybertrust_root.get());
+
+ intermediate_chain_2.push_back(cybertrust_root->os_cert_handle());
+ scoped_refptr<X509Certificate> baltimore_chain_with_root =
+ X509Certificate::CreateFromHandle(cybertrust_basic->os_cert_handle(),
+ intermediate_chain_2);
+ error = Verify(baltimore_chain_with_root.get(),
+ "cacert.omniroot.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+}
+#endif
+
+#if defined(USE_NSS) || defined(OS_IOS) || defined(OS_WIN) || defined(OS_MACOSX)
+static const uint8 kCRLSetThawteSPKIBlocked[] = {
+ 0x8e, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x22, 0x2c, 0x22,
+ 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22,
+ 0x44, 0x65, 0x6c, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c,
+ 0x22, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x50, 0x4b,
+ 0x49, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x36, 0x58, 0x36, 0x4d, 0x78, 0x52, 0x37,
+ 0x58, 0x70, 0x4d, 0x51, 0x4b, 0x78, 0x49, 0x41, 0x39, 0x50, 0x6a, 0x36, 0x37,
+ 0x36, 0x38, 0x76, 0x74, 0x55, 0x6b, 0x6b, 0x7a, 0x48, 0x79, 0x7a, 0x41, 0x6f,
+ 0x6d, 0x6f, 0x4f, 0x68, 0x4b, 0x55, 0x6e, 0x7a, 0x73, 0x55, 0x3d, 0x22, 0x5d,
+ 0x7d,
+};
+
+static const uint8 kCRLSetThawteSerialBlocked[] = {
+ 0x60, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x22, 0x2c, 0x22,
+ 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22,
+ 0x44, 0x65, 0x6c, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c,
+ 0x22, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a,
+ 0x31, 0x2c, 0x22, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x50, 0x4b,
+ 0x49, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x7d, 0xb1, 0x12, 0x41, 0x42, 0xa5, 0xa1,
+ 0xa5, 0xa2, 0x88, 0x19, 0xc7, 0x35, 0x34, 0x0e, 0xff, 0x8c, 0x9e, 0x2f, 0x81,
+ 0x68, 0xfe, 0xe3, 0xba, 0x18, 0x7f, 0x25, 0x3b, 0xc1, 0xa3, 0x92, 0xd7, 0xe2,
+ // Note that this is actually blocking two serial numbers because on XP and
+ // Vista, CryptoAPI finds a different Thawte certificate.
+ 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x30, 0x00, 0x00, 0x02,
+ 0x04, 0x30, 0x00, 0x00, 0x06,
+};
+
+static const uint8 kCRLSetGoogleSerialBlocked[] = {
+ 0x60, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x22, 0x2c, 0x22,
+ 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22,
+ 0x44, 0x65, 0x6c, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c,
+ 0x22, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a,
+ 0x31, 0x2c, 0x22, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x50, 0x4b,
+ 0x49, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x7d, 0xe9, 0x7e, 0x8c, 0xc5, 0x1e, 0xd7,
+ 0xa4, 0xc4, 0x0a, 0xc4, 0x80, 0x3d, 0x3e, 0x3e, 0xbb, 0xeb, 0xcb, 0xed, 0x52,
+ 0x49, 0x33, 0x1f, 0x2c, 0xc0, 0xa2, 0x6a, 0x0e, 0x84, 0xa5, 0x27, 0xce, 0xc5,
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x4f, 0x9d, 0x96, 0xd9, 0x66, 0xb0, 0x99, 0x2b,
+ 0x54, 0xc2, 0x95, 0x7c, 0xb4, 0x15, 0x7d, 0x4d,
+};
+
+// Test that CRLSets are effective in making a certificate appear to be
+// revoked.
+TEST_F(CertVerifyProcTest, CRLSet) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(),
+ "googlenew.chain.pem",
+ X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(certs[1]->os_cert_handle());
+
+ scoped_refptr<X509Certificate> google_full_chain =
+ X509Certificate::CreateFromHandle(certs[0]->os_cert_handle(),
+ intermediates);
+
+ CertVerifyResult verify_result;
+ int error = Verify(google_full_chain.get(),
+ "www.google.com",
+ 0,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+
+ // First test blocking by SPKI.
+ base::StringPiece crl_set_bytes(
+ reinterpret_cast<const char*>(kCRLSetThawteSPKIBlocked),
+ sizeof(kCRLSetThawteSPKIBlocked));
+ scoped_refptr<CRLSet> crl_set;
+ ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set));
+
+ error = Verify(google_full_chain.get(),
+ "www.google.com",
+ 0,
+ crl_set.get(),
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(ERR_CERT_REVOKED, error);
+
+ // Second, test revocation by serial number of a cert directly under the
+ // root.
+ crl_set_bytes = base::StringPiece(
+ reinterpret_cast<const char*>(kCRLSetThawteSerialBlocked),
+ sizeof(kCRLSetThawteSerialBlocked));
+ ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set));
+
+ error = Verify(google_full_chain.get(),
+ "www.google.com",
+ 0,
+ crl_set.get(),
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(ERR_CERT_REVOKED, error);
+
+ // Lastly, test revocation by serial number of a certificate not under the
+ // root.
+ crl_set_bytes = base::StringPiece(
+ reinterpret_cast<const char*>(kCRLSetGoogleSerialBlocked),
+ sizeof(kCRLSetGoogleSerialBlocked));
+ ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set));
+
+ error = Verify(google_full_chain.get(),
+ "www.google.com",
+ 0,
+ crl_set.get(),
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(ERR_CERT_REVOKED, error);
+}
+#endif
+
+struct WeakDigestTestData {
+ const char* root_cert_filename;
+ const char* intermediate_cert_filename;
+ const char* ee_cert_filename;
+ bool expected_has_md5;
+ bool expected_has_md4;
+ bool expected_has_md2;
+};
+
+// GTest 'magic' pretty-printer, so that if/when a test fails, it knows how
+// to output the parameter that was passed. Without this, it will simply
+// attempt to print out the first twenty bytes of the object, which depending
+// on platform and alignment, may result in an invalid read.
+void PrintTo(const WeakDigestTestData& data, std::ostream* os) {
+ *os << "root: "
+ << (data.root_cert_filename ? data.root_cert_filename : "none")
+ << "; intermediate: " << data.intermediate_cert_filename
+ << "; end-entity: " << data.ee_cert_filename;
+}
+
+class CertVerifyProcWeakDigestTest
+ : public CertVerifyProcTest,
+ public testing::WithParamInterface<WeakDigestTestData> {
+ public:
+ CertVerifyProcWeakDigestTest() {}
+ virtual ~CertVerifyProcWeakDigestTest() {}
+};
+
+TEST_P(CertVerifyProcWeakDigestTest, Verify) {
+ WeakDigestTestData data = GetParam();
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ ScopedTestRoot test_root;
+ if (data.root_cert_filename) {
+ scoped_refptr<X509Certificate> root_cert =
+ ImportCertFromFile(certs_dir, data.root_cert_filename);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert);
+ test_root.Reset(root_cert.get());
+ }
+
+ scoped_refptr<X509Certificate> intermediate_cert =
+ ImportCertFromFile(certs_dir, data.intermediate_cert_filename);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert);
+ scoped_refptr<X509Certificate> ee_cert =
+ ImportCertFromFile(certs_dir, data.ee_cert_filename);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), ee_cert);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate_cert->os_cert_handle());
+
+ scoped_refptr<X509Certificate> ee_chain =
+ X509Certificate::CreateFromHandle(ee_cert->os_cert_handle(),
+ intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), ee_chain);
+
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int rv = Verify(ee_chain.get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(data.expected_has_md5, verify_result.has_md5);
+ EXPECT_EQ(data.expected_has_md4, verify_result.has_md4);
+ EXPECT_EQ(data.expected_has_md2, verify_result.has_md2);
+ EXPECT_FALSE(verify_result.is_issued_by_additional_trust_anchor);
+
+ // Ensure that MD4 and MD2 are tagged as invalid.
+ if (data.expected_has_md4 || data.expected_has_md2) {
+ EXPECT_EQ(CERT_STATUS_INVALID,
+ verify_result.cert_status & CERT_STATUS_INVALID);
+ }
+
+ // Ensure that MD5 is flagged as weak.
+ if (data.expected_has_md5) {
+ EXPECT_EQ(
+ CERT_STATUS_WEAK_SIGNATURE_ALGORITHM,
+ verify_result.cert_status & CERT_STATUS_WEAK_SIGNATURE_ALGORITHM);
+ }
+
+ // If a root cert is present, then check that the chain was rejected if any
+ // weak algorithms are present. This is only checked when a root cert is
+ // present because the error reported for incomplete chains with weak
+ // algorithms depends on which implementation was used to validate (NSS,
+ // OpenSSL, CryptoAPI, Security.framework) and upon which weak algorithm
+ // present (MD2, MD4, MD5).
+ if (data.root_cert_filename) {
+ if (data.expected_has_md4 || data.expected_has_md2) {
+ EXPECT_EQ(ERR_CERT_INVALID, rv);
+ } else if (data.expected_has_md5) {
+ EXPECT_EQ(ERR_CERT_WEAK_SIGNATURE_ALGORITHM, rv);
+ } else {
+ EXPECT_EQ(OK, rv);
+ }
+ }
+}
+
+// Unlike TEST/TEST_F, which are macros that expand to further macros,
+// INSTANTIATE_TEST_CASE_P is a macro that expands directly to code that
+// stringizes the arguments. As a result, macros passed as parameters (such as
+// prefix or test_case_name) will not be expanded by the preprocessor. To work
+// around this, indirect the macro for INSTANTIATE_TEST_CASE_P, so that the
+// pre-processor will expand macros such as MAYBE_test_name before
+// instantiating the test.
+#define WRAPPED_INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
+ INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator)
+
+// The signature algorithm of the root CA should not matter.
+const WeakDigestTestData kVerifyRootCATestData[] = {
+ { "weak_digest_md5_root.pem", "weak_digest_sha1_intermediate.pem",
+ "weak_digest_sha1_ee.pem", false, false, false },
+#if defined(USE_OPENSSL) || defined(OS_WIN)
+ // MD4 is not supported by OS X / NSS
+ { "weak_digest_md4_root.pem", "weak_digest_sha1_intermediate.pem",
+ "weak_digest_sha1_ee.pem", false, false, false },
+#endif
+ { "weak_digest_md2_root.pem", "weak_digest_sha1_intermediate.pem",
+ "weak_digest_sha1_ee.pem", false, false, false },
+};
+INSTANTIATE_TEST_CASE_P(VerifyRoot, CertVerifyProcWeakDigestTest,
+ testing::ValuesIn(kVerifyRootCATestData));
+
+// The signature algorithm of intermediates should be properly detected.
+const WeakDigestTestData kVerifyIntermediateCATestData[] = {
+ { "weak_digest_sha1_root.pem", "weak_digest_md5_intermediate.pem",
+ "weak_digest_sha1_ee.pem", true, false, false },
+#if defined(USE_OPENSSL) || defined(OS_WIN)
+ // MD4 is not supported by OS X / NSS
+ { "weak_digest_sha1_root.pem", "weak_digest_md4_intermediate.pem",
+ "weak_digest_sha1_ee.pem", false, true, false },
+#endif
+ { "weak_digest_sha1_root.pem", "weak_digest_md2_intermediate.pem",
+ "weak_digest_sha1_ee.pem", false, false, true },
+};
+// Disabled on NSS - MD4 is not supported, and MD2 and MD5 are disabled.
+#if defined(USE_NSS) || defined(OS_IOS)
+#define MAYBE_VerifyIntermediate DISABLED_VerifyIntermediate
+#else
+#define MAYBE_VerifyIntermediate VerifyIntermediate
+#endif
+WRAPPED_INSTANTIATE_TEST_CASE_P(
+ MAYBE_VerifyIntermediate,
+ CertVerifyProcWeakDigestTest,
+ testing::ValuesIn(kVerifyIntermediateCATestData));
+
+// The signature algorithm of end-entity should be properly detected.
+const WeakDigestTestData kVerifyEndEntityTestData[] = {
+ { "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
+ "weak_digest_md5_ee.pem", true, false, false },
+#if defined(USE_OPENSSL) || defined(OS_WIN)
+ // MD4 is not supported by OS X / NSS
+ { "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
+ "weak_digest_md4_ee.pem", false, true, false },
+#endif
+ { "weak_digest_sha1_root.pem", "weak_digest_sha1_intermediate.pem",
+ "weak_digest_md2_ee.pem", false, false, true },
+};
+// Disabled on NSS - NSS caches chains/signatures in such a way that cannot
+// be cleared until NSS is cleanly shutdown, which is not presently supported
+// in Chromium.
+#if defined(USE_NSS) || defined(OS_IOS)
+#define MAYBE_VerifyEndEntity DISABLED_VerifyEndEntity
+#else
+#define MAYBE_VerifyEndEntity VerifyEndEntity
+#endif
+WRAPPED_INSTANTIATE_TEST_CASE_P(MAYBE_VerifyEndEntity,
+ CertVerifyProcWeakDigestTest,
+ testing::ValuesIn(kVerifyEndEntityTestData));
+
+// Incomplete chains should still report the status of the intermediate.
+const WeakDigestTestData kVerifyIncompleteIntermediateTestData[] = {
+ { NULL, "weak_digest_md5_intermediate.pem", "weak_digest_sha1_ee.pem",
+ true, false, false },
+#if defined(USE_OPENSSL) || defined(OS_WIN)
+ // MD4 is not supported by OS X / NSS
+ { NULL, "weak_digest_md4_intermediate.pem", "weak_digest_sha1_ee.pem",
+ false, true, false },
+#endif
+ { NULL, "weak_digest_md2_intermediate.pem", "weak_digest_sha1_ee.pem",
+ false, false, true },
+};
+// Disabled on NSS - libpkix does not return constructed chains on error,
+// preventing us from detecting/inspecting the verified chain.
+#if defined(USE_NSS) || defined(OS_IOS)
+#define MAYBE_VerifyIncompleteIntermediate \
+ DISABLED_VerifyIncompleteIntermediate
+#else
+#define MAYBE_VerifyIncompleteIntermediate VerifyIncompleteIntermediate
+#endif
+WRAPPED_INSTANTIATE_TEST_CASE_P(
+ MAYBE_VerifyIncompleteIntermediate,
+ CertVerifyProcWeakDigestTest,
+ testing::ValuesIn(kVerifyIncompleteIntermediateTestData));
+
+// Incomplete chains should still report the status of the end-entity.
+const WeakDigestTestData kVerifyIncompleteEETestData[] = {
+ { NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md5_ee.pem",
+ true, false, false },
+#if defined(USE_OPENSSL) || defined(OS_WIN)
+ // MD4 is not supported by OS X / NSS
+ { NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md4_ee.pem",
+ false, true, false },
+#endif
+ { NULL, "weak_digest_sha1_intermediate.pem", "weak_digest_md2_ee.pem",
+ false, false, true },
+};
+// Disabled on NSS - libpkix does not return constructed chains on error,
+// preventing us from detecting/inspecting the verified chain.
+#if defined(USE_NSS) || defined(OS_IOS)
+#define MAYBE_VerifyIncompleteEndEntity DISABLED_VerifyIncompleteEndEntity
+#else
+#define MAYBE_VerifyIncompleteEndEntity VerifyIncompleteEndEntity
+#endif
+WRAPPED_INSTANTIATE_TEST_CASE_P(
+ MAYBE_VerifyIncompleteEndEntity,
+ CertVerifyProcWeakDigestTest,
+ testing::ValuesIn(kVerifyIncompleteEETestData));
+
+// Differing algorithms between the intermediate and the EE should still be
+// reported.
+const WeakDigestTestData kVerifyMixedTestData[] = {
+ { "weak_digest_sha1_root.pem", "weak_digest_md5_intermediate.pem",
+ "weak_digest_md2_ee.pem", true, false, true },
+ { "weak_digest_sha1_root.pem", "weak_digest_md2_intermediate.pem",
+ "weak_digest_md5_ee.pem", true, false, true },
+#if defined(USE_OPENSSL) || defined(OS_WIN)
+ // MD4 is not supported by OS X / NSS
+ { "weak_digest_sha1_root.pem", "weak_digest_md4_intermediate.pem",
+ "weak_digest_md2_ee.pem", false, true, true },
+#endif
+};
+// NSS does not support MD4 and does not enable MD2 by default, making all
+// permutations invalid.
+#if defined(USE_NSS) || defined(OS_IOS)
+#define MAYBE_VerifyMixed DISABLED_VerifyMixed
+#else
+#define MAYBE_VerifyMixed VerifyMixed
+#endif
+WRAPPED_INSTANTIATE_TEST_CASE_P(
+ MAYBE_VerifyMixed,
+ CertVerifyProcWeakDigestTest,
+ testing::ValuesIn(kVerifyMixedTestData));
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_win.cc b/chromium/net/cert/cert_verify_proc_win.cc
new file mode 100644
index 00000000000..7e94246af96
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_win.cc
@@ -0,0 +1,827 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_proc_win.h"
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/sha1.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/capi_util.h"
+#include "crypto/scoped_capi_types.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/ev_root_ca_metadata.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_certificate_known_roots_win.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+#if !defined(CERT_TRUST_HAS_WEAK_SIGNATURE)
+// This was introduced in Windows 8 / Windows Server 2012, but retroactively
+// ported as far back as Windows XP via system update.
+#define CERT_TRUST_HAS_WEAK_SIGNATURE 0x00100000
+#endif
+
+namespace net {
+
+namespace {
+
+struct FreeChainEngineFunctor {
+ void operator()(HCERTCHAINENGINE engine) const {
+ if (engine)
+ CertFreeCertificateChainEngine(engine);
+ }
+};
+
+struct FreeCertChainContextFunctor {
+ void operator()(PCCERT_CHAIN_CONTEXT chain_context) const {
+ if (chain_context)
+ CertFreeCertificateChain(chain_context);
+ }
+};
+
+struct FreeCertContextFunctor {
+ void operator()(PCCERT_CONTEXT context) const {
+ if (context)
+ CertFreeCertificateContext(context);
+ }
+};
+
+typedef crypto::ScopedCAPIHandle<HCERTCHAINENGINE, FreeChainEngineFunctor>
+ ScopedHCERTCHAINENGINE;
+
+typedef scoped_ptr_malloc<const CERT_CHAIN_CONTEXT,
+ FreeCertChainContextFunctor>
+ ScopedPCCERT_CHAIN_CONTEXT;
+
+typedef scoped_ptr_malloc<const CERT_CONTEXT,
+ FreeCertContextFunctor> ScopedPCCERT_CONTEXT;
+
+//-----------------------------------------------------------------------------
+
+int MapSecurityError(SECURITY_STATUS err) {
+ // There are numerous security error codes, but these are the ones we thus
+ // far find interesting.
+ switch (err) {
+ case SEC_E_WRONG_PRINCIPAL: // Schannel
+ case CERT_E_CN_NO_MATCH: // CryptoAPI
+ return ERR_CERT_COMMON_NAME_INVALID;
+ case SEC_E_UNTRUSTED_ROOT: // Schannel
+ case CERT_E_UNTRUSTEDROOT: // CryptoAPI
+ return ERR_CERT_AUTHORITY_INVALID;
+ case SEC_E_CERT_EXPIRED: // Schannel
+ case CERT_E_EXPIRED: // CryptoAPI
+ return ERR_CERT_DATE_INVALID;
+ case CRYPT_E_NO_REVOCATION_CHECK:
+ return ERR_CERT_NO_REVOCATION_MECHANISM;
+ case CRYPT_E_REVOCATION_OFFLINE:
+ return ERR_CERT_UNABLE_TO_CHECK_REVOCATION;
+ case CRYPT_E_REVOKED: // Schannel and CryptoAPI
+ return ERR_CERT_REVOKED;
+ case SEC_E_CERT_UNKNOWN:
+ case CERT_E_ROLE:
+ return ERR_CERT_INVALID;
+ case CERT_E_WRONG_USAGE:
+ // TODO(wtc): Should we add ERR_CERT_WRONG_USAGE?
+ return ERR_CERT_INVALID;
+ // We received an unexpected_message or illegal_parameter alert message
+ // from the server.
+ case SEC_E_ILLEGAL_MESSAGE:
+ return ERR_SSL_PROTOCOL_ERROR;
+ case SEC_E_ALGORITHM_MISMATCH:
+ return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
+ case SEC_E_INVALID_HANDLE:
+ return ERR_UNEXPECTED;
+ case SEC_E_OK:
+ return OK;
+ default:
+ LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+}
+
+// Map the errors in the chain_context->TrustStatus.dwErrorStatus returned by
+// CertGetCertificateChain to our certificate status flags.
+int MapCertChainErrorStatusToCertStatus(DWORD error_status) {
+ CertStatus cert_status = 0;
+
+ // We don't include CERT_TRUST_IS_NOT_TIME_NESTED because it's obsolete and
+ // we wouldn't consider it an error anyway
+ const DWORD kDateInvalidErrors = CERT_TRUST_IS_NOT_TIME_VALID |
+ CERT_TRUST_CTL_IS_NOT_TIME_VALID;
+ if (error_status & kDateInvalidErrors)
+ cert_status |= CERT_STATUS_DATE_INVALID;
+
+ const DWORD kAuthorityInvalidErrors = CERT_TRUST_IS_UNTRUSTED_ROOT |
+ CERT_TRUST_IS_EXPLICIT_DISTRUST |
+ CERT_TRUST_IS_PARTIAL_CHAIN;
+ if (error_status & kAuthorityInvalidErrors)
+ cert_status |= CERT_STATUS_AUTHORITY_INVALID;
+
+ if ((error_status & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) &&
+ !(error_status & CERT_TRUST_IS_OFFLINE_REVOCATION))
+ cert_status |= CERT_STATUS_NO_REVOCATION_MECHANISM;
+
+ if (error_status & CERT_TRUST_IS_OFFLINE_REVOCATION)
+ cert_status |= CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+
+ if (error_status & CERT_TRUST_IS_REVOKED)
+ cert_status |= CERT_STATUS_REVOKED;
+
+ const DWORD kWrongUsageErrors = CERT_TRUST_IS_NOT_VALID_FOR_USAGE |
+ CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
+ if (error_status & kWrongUsageErrors) {
+ // TODO(wtc): Should we add CERT_STATUS_WRONG_USAGE?
+ cert_status |= CERT_STATUS_INVALID;
+ }
+
+ if (error_status & CERT_TRUST_IS_NOT_SIGNATURE_VALID) {
+ // Check for a signature that does not meet the OS criteria for strong
+ // signatures.
+ // Note: These checks may be more restrictive than the current weak key
+ // criteria implemented within CertVerifier, such as excluding SHA-1 or
+ // excluding RSA keys < 2048 bits. However, if the user has configured
+ // these more stringent checks, respect that configuration and err on the
+ // more restrictive criteria.
+ if (error_status & CERT_TRUST_HAS_WEAK_SIGNATURE) {
+ cert_status |= CERT_STATUS_WEAK_KEY;
+ } else {
+ cert_status |= CERT_STATUS_INVALID;
+ }
+ }
+
+ // The rest of the errors.
+ const DWORD kCertInvalidErrors =
+ CERT_TRUST_IS_CYCLIC |
+ CERT_TRUST_INVALID_EXTENSION |
+ CERT_TRUST_INVALID_POLICY_CONSTRAINTS |
+ CERT_TRUST_INVALID_BASIC_CONSTRAINTS |
+ CERT_TRUST_INVALID_NAME_CONSTRAINTS |
+ CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID |
+ CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT |
+ CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT |
+ CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT |
+ CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT |
+ CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY |
+ CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT;
+ if (error_status & kCertInvalidErrors)
+ cert_status |= CERT_STATUS_INVALID;
+
+ return cert_status;
+}
+
+// Returns true if any common name in the certificate's Subject field contains
+// a NULL character.
+bool CertSubjectCommonNameHasNull(PCCERT_CONTEXT cert) {
+ CRYPT_DECODE_PARA decode_para;
+ decode_para.cbSize = sizeof(decode_para);
+ decode_para.pfnAlloc = crypto::CryptAlloc;
+ decode_para.pfnFree = crypto::CryptFree;
+ CERT_NAME_INFO* name_info = NULL;
+ DWORD name_info_size = 0;
+ BOOL rv;
+ rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ X509_NAME,
+ cert->pCertInfo->Subject.pbData,
+ cert->pCertInfo->Subject.cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decode_para,
+ &name_info,
+ &name_info_size);
+ if (rv) {
+ scoped_ptr_malloc<CERT_NAME_INFO> scoped_name_info(name_info);
+
+ // The Subject field may have multiple common names. According to the
+ // "PKI Layer Cake" paper, CryptoAPI uses every common name in the
+ // Subject field, so we inspect every common name.
+ //
+ // From RFC 5280:
+ // X520CommonName ::= CHOICE {
+ // teletexString TeletexString (SIZE (1..ub-common-name)),
+ // printableString PrintableString (SIZE (1..ub-common-name)),
+ // universalString UniversalString (SIZE (1..ub-common-name)),
+ // utf8String UTF8String (SIZE (1..ub-common-name)),
+ // bmpString BMPString (SIZE (1..ub-common-name)) }
+ //
+ // We also check IA5String and VisibleString.
+ for (DWORD i = 0; i < name_info->cRDN; ++i) {
+ PCERT_RDN rdn = &name_info->rgRDN[i];
+ for (DWORD j = 0; j < rdn->cRDNAttr; ++j) {
+ PCERT_RDN_ATTR rdn_attr = &rdn->rgRDNAttr[j];
+ if (strcmp(rdn_attr->pszObjId, szOID_COMMON_NAME) == 0) {
+ switch (rdn_attr->dwValueType) {
+ // After the CryptoAPI ASN.1 security vulnerabilities described in
+ // http://www.microsoft.com/technet/security/Bulletin/MS09-056.mspx
+ // were patched, we get CERT_RDN_ENCODED_BLOB for a common name
+ // that contains a NULL character.
+ case CERT_RDN_ENCODED_BLOB:
+ break;
+ // Array of 8-bit characters.
+ case CERT_RDN_PRINTABLE_STRING:
+ case CERT_RDN_TELETEX_STRING:
+ case CERT_RDN_IA5_STRING:
+ case CERT_RDN_VISIBLE_STRING:
+ for (DWORD k = 0; k < rdn_attr->Value.cbData; ++k) {
+ if (rdn_attr->Value.pbData[k] == '\0')
+ return true;
+ }
+ break;
+ // Array of 16-bit characters.
+ case CERT_RDN_BMP_STRING:
+ case CERT_RDN_UTF8_STRING: {
+ DWORD num_wchars = rdn_attr->Value.cbData / 2;
+ wchar_t* common_name =
+ reinterpret_cast<wchar_t*>(rdn_attr->Value.pbData);
+ for (DWORD k = 0; k < num_wchars; ++k) {
+ if (common_name[k] == L'\0')
+ return true;
+ }
+ break;
+ }
+ // Array of ints (32-bit).
+ case CERT_RDN_UNIVERSAL_STRING: {
+ DWORD num_ints = rdn_attr->Value.cbData / 4;
+ int* common_name =
+ reinterpret_cast<int*>(rdn_attr->Value.pbData);
+ for (DWORD k = 0; k < num_ints; ++k) {
+ if (common_name[k] == 0)
+ return true;
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
+// which we recognise as a standard root.
+// static
+bool IsIssuedByKnownRoot(PCCERT_CHAIN_CONTEXT chain_context) {
+ PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0];
+ int num_elements = first_chain->cElement;
+ if (num_elements < 1)
+ return false;
+ PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
+ PCCERT_CONTEXT cert = element[num_elements - 1]->pCertContext;
+
+ SHA1HashValue hash = X509Certificate::CalculateFingerprint(cert);
+ return IsSHA1HashInSortedArray(
+ hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes));
+}
+
+// Saves some information about the certificate chain |chain_context| in
+// |*verify_result|. The caller MUST initialize |*verify_result| before
+// calling this function.
+void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context,
+ CertVerifyResult* verify_result) {
+ if (chain_context->cChain == 0)
+ return;
+
+ PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0];
+ int num_elements = first_chain->cElement;
+ PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
+
+ PCCERT_CONTEXT verified_cert = NULL;
+ std::vector<PCCERT_CONTEXT> verified_chain;
+
+ bool has_root_ca = num_elements > 1 &&
+ !(chain_context->TrustStatus.dwErrorStatus &
+ CERT_TRUST_IS_PARTIAL_CHAIN);
+
+ // Each chain starts with the end entity certificate (i = 0) and ends with
+ // either the root CA certificate or the last available intermediate. If a
+ // root CA certificate is present, do not inspect the signature algorithm of
+ // the root CA certificate because the signature on the trust anchor is not
+ // important.
+ if (has_root_ca) {
+ // If a full chain was constructed, regardless of whether it was trusted,
+ // don't inspect the root's signature algorithm.
+ num_elements -= 1;
+ }
+
+ for (int i = 0; i < num_elements; ++i) {
+ PCCERT_CONTEXT cert = element[i]->pCertContext;
+ if (i == 0) {
+ verified_cert = cert;
+ } else {
+ verified_chain.push_back(cert);
+ }
+
+ const char* algorithm = cert->pCertInfo->SignatureAlgorithm.pszObjId;
+ if (strcmp(algorithm, szOID_RSA_MD5RSA) == 0) {
+ // md5WithRSAEncryption: 1.2.840.113549.1.1.4
+ verify_result->has_md5 = true;
+ } else if (strcmp(algorithm, szOID_RSA_MD2RSA) == 0) {
+ // md2WithRSAEncryption: 1.2.840.113549.1.1.2
+ verify_result->has_md2 = true;
+ } else if (strcmp(algorithm, szOID_RSA_MD4RSA) == 0) {
+ // md4WithRSAEncryption: 1.2.840.113549.1.1.3
+ verify_result->has_md4 = true;
+ }
+ }
+
+ if (verified_cert) {
+ // Add the root certificate, if present, as it was not added above.
+ if (has_root_ca)
+ verified_chain.push_back(element[num_elements]->pCertContext);
+ verify_result->verified_cert =
+ X509Certificate::CreateFromHandle(verified_cert, verified_chain);
+ }
+}
+
+// Decodes the cert's certificatePolicies extension into a CERT_POLICIES_INFO
+// structure and stores it in *output.
+void GetCertPoliciesInfo(PCCERT_CONTEXT cert,
+ scoped_ptr_malloc<CERT_POLICIES_INFO>* output) {
+ PCERT_EXTENSION extension = CertFindExtension(szOID_CERT_POLICIES,
+ cert->pCertInfo->cExtension,
+ cert->pCertInfo->rgExtension);
+ if (!extension)
+ return;
+
+ CRYPT_DECODE_PARA decode_para;
+ decode_para.cbSize = sizeof(decode_para);
+ decode_para.pfnAlloc = crypto::CryptAlloc;
+ decode_para.pfnFree = crypto::CryptFree;
+ CERT_POLICIES_INFO* policies_info = NULL;
+ DWORD policies_info_size = 0;
+ BOOL rv;
+ rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ szOID_CERT_POLICIES,
+ extension->Value.pbData,
+ extension->Value.cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decode_para,
+ &policies_info,
+ &policies_info_size);
+ if (rv)
+ output->reset(policies_info);
+}
+
+enum CRLSetResult {
+ kCRLSetOk,
+ kCRLSetUnknown,
+ kCRLSetRevoked,
+};
+
+// CheckRevocationWithCRLSet attempts to check each element of |chain|
+// against |crl_set|. It returns:
+// kCRLSetRevoked: if any element of the chain is known to have been revoked.
+// kCRLSetUnknown: if there is no fresh information about some element in
+// the chain.
+// kCRLSetOk: if every element in the chain is covered by a fresh CRLSet and
+// is unrevoked.
+CRLSetResult CheckRevocationWithCRLSet(PCCERT_CHAIN_CONTEXT chain,
+ CRLSet* crl_set) {
+ if (chain->cChain == 0)
+ return kCRLSetOk;
+
+ const PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0];
+ const PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement;
+
+ const int num_elements = first_chain->cElement;
+ if (num_elements == 0)
+ return kCRLSetOk;
+
+ bool covered = true;
+
+ // We iterate from the root certificate down to the leaf, keeping track of
+ // the issuer's SPKI at each step.
+ std::string issuer_spki_hash;
+ for (int i = num_elements - 1; i >= 0; i--) {
+ PCCERT_CONTEXT cert = element[i]->pCertContext;
+
+ base::StringPiece der_bytes(
+ reinterpret_cast<const char*>(cert->pbCertEncoded),
+ cert->cbCertEncoded);
+
+ base::StringPiece spki;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
+ NOTREACHED();
+ covered = false;
+ continue;
+ }
+
+ const std::string spki_hash = crypto::SHA256HashString(spki);
+
+ const CRYPT_INTEGER_BLOB* serial_blob = &cert->pCertInfo->SerialNumber;
+ scoped_ptr<uint8[]> serial_bytes(new uint8[serial_blob->cbData]);
+ // The bytes of the serial number are stored little-endian.
+ for (unsigned j = 0; j < serial_blob->cbData; j++)
+ serial_bytes[j] = serial_blob->pbData[serial_blob->cbData - j - 1];
+ base::StringPiece serial(reinterpret_cast<const char*>(serial_bytes.get()),
+ serial_blob->cbData);
+
+ CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
+
+ if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
+ result = crl_set->CheckSerial(serial, issuer_spki_hash);
+
+ issuer_spki_hash = spki_hash;
+
+ switch (result) {
+ case CRLSet::REVOKED:
+ return kCRLSetRevoked;
+ case CRLSet::UNKNOWN:
+ covered = false;
+ continue;
+ case CRLSet::GOOD:
+ continue;
+ default:
+ NOTREACHED();
+ covered = false;
+ continue;
+ }
+ }
+
+ if (!covered || crl_set->IsExpired())
+ return kCRLSetUnknown;
+ return kCRLSetOk;
+}
+
+void AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain,
+ HashValueVector* hashes) {
+ if (chain->cChain == 0)
+ return;
+
+ PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0];
+ PCERT_CHAIN_ELEMENT* const element = first_chain->rgpElement;
+
+ const DWORD num_elements = first_chain->cElement;
+ for (DWORD i = 0; i < num_elements; i++) {
+ PCCERT_CONTEXT cert = element[i]->pCertContext;
+
+ base::StringPiece der_bytes(
+ reinterpret_cast<const char*>(cert->pbCertEncoded),
+ cert->cbCertEncoded);
+ base::StringPiece spki_bytes;
+ if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
+ continue;
+
+ HashValue sha1(HASH_VALUE_SHA1);
+ base::SHA1HashBytes(reinterpret_cast<const uint8*>(spki_bytes.data()),
+ spki_bytes.size(), sha1.data());
+ hashes->push_back(sha1);
+
+ HashValue sha256(HASH_VALUE_SHA256);
+ crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length);
+ hashes->push_back(sha256);
+ }
+}
+
+// Returns true if the certificate is an extended-validation certificate.
+//
+// This function checks the certificatePolicies extensions of the
+// certificates in the certificate chain according to Section 7 (pp. 11-12)
+// of the EV Certificate Guidelines Version 1.0 at
+// http://cabforum.org/EV_Certificate_Guidelines.pdf.
+bool CheckEV(PCCERT_CHAIN_CONTEXT chain_context,
+ bool rev_checking_enabled,
+ const char* policy_oid) {
+ DCHECK_NE(static_cast<DWORD>(0), chain_context->cChain);
+ // If the cert doesn't match any of the policies, the
+ // CERT_TRUST_IS_NOT_VALID_FOR_USAGE bit (0x10) in
+ // chain_context->TrustStatus.dwErrorStatus is set.
+ DWORD error_status = chain_context->TrustStatus.dwErrorStatus;
+
+ if (!rev_checking_enabled) {
+ // If online revocation checking is disabled then we will have still
+ // requested that the revocation cache be checked. However, that will often
+ // cause the following two error bits to be set. These error bits mean that
+ // the local OCSP/CRL is stale or missing entries for these certificates.
+ // Since they are expected, we mask them away.
+ error_status &= ~(CERT_TRUST_IS_OFFLINE_REVOCATION |
+ CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
+ }
+ if (!chain_context->cChain || error_status != CERT_TRUST_NO_ERROR)
+ return false;
+
+ // Check the end certificate simple chain (chain_context->rgpChain[0]).
+ // If the end certificate's certificatePolicies extension contains the
+ // EV policy OID of the root CA, return true.
+ PCERT_CHAIN_ELEMENT* element = chain_context->rgpChain[0]->rgpElement;
+ int num_elements = chain_context->rgpChain[0]->cElement;
+ if (num_elements < 2)
+ return false;
+
+ // Look up the EV policy OID of the root CA.
+ PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext;
+ SHA1HashValue fingerprint =
+ X509Certificate::CalculateFingerprint(root_cert);
+ EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
+ return metadata->HasEVPolicyOID(fingerprint, policy_oid);
+}
+
+} // namespace
+
+CertVerifyProcWin::CertVerifyProcWin() {}
+
+CertVerifyProcWin::~CertVerifyProcWin() {}
+
+bool CertVerifyProcWin::SupportsAdditionalTrustAnchors() const {
+ return false;
+}
+
+int CertVerifyProcWin::VerifyInternal(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) {
+ PCCERT_CONTEXT cert_handle = cert->os_cert_handle();
+ if (!cert_handle)
+ return ERR_UNEXPECTED;
+
+ // Build and validate certificate chain.
+ CERT_CHAIN_PARA chain_para;
+ memset(&chain_para, 0, sizeof(chain_para));
+ chain_para.cbSize = sizeof(chain_para);
+ // ExtendedKeyUsage.
+ // We still need to request szOID_SERVER_GATED_CRYPTO and szOID_SGC_NETSCAPE
+ // today because some certificate chains need them. IE also requests these
+ // two usages.
+ static const LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ chain_para.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chain_para.RequestedUsage.Usage.cUsageIdentifier = arraysize(usage);
+ chain_para.RequestedUsage.Usage.rgpszUsageIdentifier =
+ const_cast<LPSTR*>(usage);
+
+ // Get the certificatePolicies extension of the certificate.
+ scoped_ptr_malloc<CERT_POLICIES_INFO> policies_info;
+ LPSTR ev_policy_oid = NULL;
+ if (flags & CertVerifier::VERIFY_EV_CERT) {
+ GetCertPoliciesInfo(cert_handle, &policies_info);
+ if (policies_info.get()) {
+ EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
+ for (DWORD i = 0; i < policies_info->cPolicyInfo; ++i) {
+ LPSTR policy_oid = policies_info->rgPolicyInfo[i].pszPolicyIdentifier;
+ if (metadata->IsEVPolicyOID(policy_oid)) {
+ ev_policy_oid = policy_oid;
+ chain_para.RequestedIssuancePolicy.dwType = USAGE_MATCH_TYPE_AND;
+ chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 1;
+ chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier =
+ &ev_policy_oid;
+ break;
+ }
+ }
+ }
+ }
+
+ // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains.
+ DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT |
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
+ bool rev_checking_enabled =
+ (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED);
+
+ if (rev_checking_enabled) {
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+ } else {
+ chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
+ }
+
+ // For non-test scenarios, use the default HCERTCHAINENGINE, NULL, which
+ // corresponds to HCCE_CURRENT_USER and is is initialized as needed by
+ // crypt32. However, when testing, it is necessary to create a new
+ // HCERTCHAINENGINE and use that instead. This is because each
+ // HCERTCHAINENGINE maintains a cache of information about certificates
+ // encountered, and each test run may modify the trust status of a
+ // certificate.
+ ScopedHCERTCHAINENGINE chain_engine(NULL);
+ if (TestRootCerts::HasInstance())
+ chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine());
+
+ ScopedPCCERT_CONTEXT cert_list(cert->CreateOSCertChainForCert());
+ PCCERT_CHAIN_CONTEXT chain_context;
+ // IE passes a non-NULL pTime argument that specifies the current system
+ // time. IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the
+ // chain_flags argument.
+ if (!CertGetCertificateChain(
+ chain_engine,
+ cert_list.get(),
+ NULL, // current system time
+ cert_list->hCertStore,
+ &chain_para,
+ chain_flags,
+ NULL, // reserved
+ &chain_context)) {
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ return MapSecurityError(GetLastError());
+ }
+
+ CRLSetResult crl_set_result = kCRLSetUnknown;
+ if (crl_set)
+ crl_set_result = CheckRevocationWithCRLSet(chain_context, crl_set);
+
+ if (crl_set_result == kCRLSetRevoked) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ } else if (crl_set_result == kCRLSetUnknown &&
+ (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) &&
+ !rev_checking_enabled &&
+ ev_policy_oid != NULL) {
+ // We don't have fresh information about this chain from the CRLSet and
+ // it's probably an EV certificate. Retry with online revocation checking.
+ rev_checking_enabled = true;
+ chain_flags &= ~CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+
+ if (!CertGetCertificateChain(
+ chain_engine,
+ cert_list.get(),
+ NULL, // current system time
+ cert_list->hCertStore,
+ &chain_para,
+ chain_flags,
+ NULL, // reserved
+ &chain_context)) {
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ return MapSecurityError(GetLastError());
+ }
+ }
+
+ if (chain_context->TrustStatus.dwErrorStatus &
+ CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
+ ev_policy_oid = NULL;
+ chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 0;
+ chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = NULL;
+ CertFreeCertificateChain(chain_context);
+ if (!CertGetCertificateChain(
+ chain_engine,
+ cert_list.get(),
+ NULL, // current system time
+ cert_list->hCertStore,
+ &chain_para,
+ chain_flags,
+ NULL, // reserved
+ &chain_context)) {
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ return MapSecurityError(GetLastError());
+ }
+ }
+
+ CertVerifyResult temp_verify_result = *verify_result;
+ GetCertChainInfo(chain_context, verify_result);
+ if (!verify_result->is_issued_by_known_root &&
+ (flags & CertVerifier::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS)) {
+ *verify_result = temp_verify_result;
+
+ rev_checking_enabled = true;
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+ chain_flags &= ~CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
+
+ CertFreeCertificateChain(chain_context);
+ if (!CertGetCertificateChain(
+ chain_engine,
+ cert_list.get(),
+ NULL, // current system time
+ cert_list->hCertStore,
+ &chain_para,
+ chain_flags,
+ NULL, // reserved
+ &chain_context)) {
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+ return MapSecurityError(GetLastError());
+ }
+ GetCertChainInfo(chain_context, verify_result);
+
+ if (chain_context->TrustStatus.dwErrorStatus &
+ CERT_TRUST_IS_OFFLINE_REVOCATION) {
+ verify_result->cert_status |= CERT_STATUS_REVOKED;
+ }
+ }
+
+ ScopedPCCERT_CHAIN_CONTEXT scoped_chain_context(chain_context);
+
+ verify_result->cert_status |= MapCertChainErrorStatusToCertStatus(
+ chain_context->TrustStatus.dwErrorStatus);
+
+ // Flag certificates that have a Subject common name with a NULL character.
+ if (CertSubjectCommonNameHasNull(cert_handle))
+ verify_result->cert_status |= CERT_STATUS_INVALID;
+
+ std::wstring wstr_hostname = ASCIIToWide(hostname);
+
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para;
+ memset(&extra_policy_para, 0, sizeof(extra_policy_para));
+ extra_policy_para.cbSize = sizeof(extra_policy_para);
+ extra_policy_para.dwAuthType = AUTHTYPE_SERVER;
+ extra_policy_para.fdwChecks = 0;
+ extra_policy_para.pwszServerName =
+ const_cast<wchar_t*>(wstr_hostname.c_str());
+
+ CERT_CHAIN_POLICY_PARA policy_para;
+ memset(&policy_para, 0, sizeof(policy_para));
+ policy_para.cbSize = sizeof(policy_para);
+ policy_para.dwFlags = 0;
+ policy_para.pvExtraPolicyPara = &extra_policy_para;
+
+ CERT_CHAIN_POLICY_STATUS policy_status;
+ memset(&policy_status, 0, sizeof(policy_status));
+ policy_status.cbSize = sizeof(policy_status);
+
+ if (!CertVerifyCertificateChainPolicy(
+ CERT_CHAIN_POLICY_SSL,
+ chain_context,
+ &policy_para,
+ &policy_status)) {
+ return MapSecurityError(GetLastError());
+ }
+
+ if (policy_status.dwError) {
+ verify_result->cert_status |= MapNetErrorToCertStatus(
+ MapSecurityError(policy_status.dwError));
+
+ // CertVerifyCertificateChainPolicy reports only one error (in
+ // policy_status.dwError) if the certificate has multiple errors.
+ // CertGetCertificateChain doesn't report certificate name mismatch, so
+ // CertVerifyCertificateChainPolicy is the only function that can report
+ // certificate name mismatch.
+ //
+ // To prevent a potential certificate name mismatch from being hidden by
+ // some other certificate error, if we get any other certificate error,
+ // we call CertVerifyCertificateChainPolicy again, ignoring all other
+ // certificate errors. Both extra_policy_para.fdwChecks and
+ // policy_para.dwFlags allow us to ignore certificate errors, so we set
+ // them both.
+ if (policy_status.dwError != CERT_E_CN_NO_MATCH) {
+ const DWORD extra_ignore_flags =
+ 0x00000080 | // SECURITY_FLAG_IGNORE_REVOCATION
+ 0x00000100 | // SECURITY_FLAG_IGNORE_UNKNOWN_CA
+ 0x00002000 | // SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
+ 0x00000200; // SECURITY_FLAG_IGNORE_WRONG_USAGE
+ extra_policy_para.fdwChecks = extra_ignore_flags;
+ const DWORD ignore_flags =
+ CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS |
+ CERT_CHAIN_POLICY_IGNORE_INVALID_BASIC_CONSTRAINTS_FLAG |
+ CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG |
+ CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG |
+ CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG |
+ CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG |
+ CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS |
+ CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG |
+ CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG |
+ CERT_CHAIN_POLICY_IGNORE_NOT_SUPPORTED_CRITICAL_EXT_FLAG |
+ CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG;
+ policy_para.dwFlags = ignore_flags;
+ if (!CertVerifyCertificateChainPolicy(
+ CERT_CHAIN_POLICY_SSL,
+ chain_context,
+ &policy_para,
+ &policy_status)) {
+ return MapSecurityError(GetLastError());
+ }
+ if (policy_status.dwError) {
+ verify_result->cert_status |= MapNetErrorToCertStatus(
+ MapSecurityError(policy_status.dwError));
+ }
+ }
+ }
+
+ // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
+ // compatible with WinHTTP, which doesn't report this error (bug 3004).
+ verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
+
+ if (!rev_checking_enabled) {
+ // If we didn't do online revocation checking then Windows will report
+ // CERT_UNABLE_TO_CHECK_REVOCATION unless it had cached OCSP or CRL
+ // information for every certificate. We only want to put up revoked
+ // statuses from the offline checks so we squash this error.
+ verify_result->cert_status &= ~CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+ }
+
+ AppendPublicKeyHashes(chain_context, &verify_result->public_key_hashes);
+ verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(chain_context);
+
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ if (ev_policy_oid &&
+ CheckEV(chain_context, rev_checking_enabled, ev_policy_oid)) {
+ verify_result->cert_status |= CERT_STATUS_IS_EV;
+ }
+ return OK;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_proc_win.h b/chromium/net/cert/cert_verify_proc_win.h
new file mode 100644
index 00000000000..147f47ab5a5
--- /dev/null
+++ b/chromium/net/cert/cert_verify_proc_win.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_PROC_WIN_H_
+#define NET_CERT_CERT_VERIFY_PROC_WIN_H_
+
+#include "net/cert/cert_verify_proc.h"
+
+namespace net {
+
+// Performs certificate path construction and validation using Windows'
+// CryptoAPI.
+class CertVerifyProcWin : public CertVerifyProc {
+ public:
+ CertVerifyProcWin();
+
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE;
+
+ protected:
+ virtual ~CertVerifyProcWin();
+
+ private:
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_PROC_WIN_H_
diff --git a/chromium/net/cert/cert_verify_result.cc b/chromium/net/cert/cert_verify_result.cc
new file mode 100644
index 00000000000..68e76a77f7a
--- /dev/null
+++ b/chromium/net/cert/cert_verify_result.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/cert_verify_result.h"
+
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+CertVerifyResult::CertVerifyResult() {
+ Reset();
+}
+
+CertVerifyResult::~CertVerifyResult() {
+}
+
+void CertVerifyResult::Reset() {
+ verified_cert = NULL;
+ cert_status = 0;
+ has_md5 = false;
+ has_md2 = false;
+ has_md4 = false;
+ is_issued_by_known_root = false;
+ is_issued_by_additional_trust_anchor = false;
+
+ public_key_hashes.clear();
+}
+
+} // namespace net
diff --git a/chromium/net/cert/cert_verify_result.h b/chromium/net/cert/cert_verify_result.h
new file mode 100644
index 00000000000..a00c03e4a24
--- /dev/null
+++ b/chromium/net/cert/cert_verify_result.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CERT_VERIFY_RESULT_H_
+#define NET_CERT_CERT_VERIFY_RESULT_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/x509_cert_types.h"
+
+namespace net {
+
+class X509Certificate;
+
+// The result of certificate verification.
+class NET_EXPORT CertVerifyResult {
+ public:
+ CertVerifyResult();
+ ~CertVerifyResult();
+
+ void Reset();
+
+ // Copies from |other| to |this|.
+ void CopyFrom(const CertVerifyResult& other) {
+ *this = other;
+ }
+
+ // The certificate and chain that was constructed during verification.
+ // Note that the though the verified certificate will match the originally
+ // supplied certificate, the intermediate certificates stored within may
+ // be substantially different. In the event of a verification failure, this
+ // will contain the chain as supplied by the server. This may be NULL if
+ // running within the sandbox.
+ scoped_refptr<X509Certificate> verified_cert;
+
+ // Bitmask of CERT_STATUS_* from net/base/cert_status_flags.h. Note that
+ // these status flags apply to the certificate chain returned in
+ // |verified_cert|, rather than the originally supplied certificate
+ // chain.
+ CertStatus cert_status;
+
+ // Properties of the certificate chain.
+ bool has_md5;
+ bool has_md2;
+ bool has_md4;
+
+ // If the certificate was successfully verified then this contains the
+ // hashes, in several hash algorithms, of the SubjectPublicKeyInfos of the
+ // chain.
+ HashValueVector public_key_hashes;
+
+ // is_issued_by_known_root is true if we recognise the root CA as a standard
+ // root. If it isn't then it's probably the case that this certificate was
+ // generated by a MITM proxy whose root has been installed locally. This is
+ // meaningless if the certificate was not trusted.
+ bool is_issued_by_known_root;
+
+ // is_issued_by_additional_trust_anchor is true if the root CA used for this
+ // verification came from the list of additional trust anchors.
+ bool is_issued_by_additional_trust_anchor;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CERT_VERIFY_RESULT_H_
diff --git a/chromium/net/cert/crl_set.cc b/chromium/net/cert/crl_set.cc
new file mode 100644
index 00000000000..de0651666d8
--- /dev/null
+++ b/chromium/net/cert/crl_set.cc
@@ -0,0 +1,611 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/base64.h"
+#include "base/format_macros.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "crypto/sha2.h"
+#include "net/cert/crl_set.h"
+#include "third_party/zlib/zlib.h"
+
+namespace net {
+
+// Decompress zlib decompressed |in| into |out|. |out_len| is the number of
+// bytes at |out| and must be exactly equal to the size of the decompressed
+// data.
+static bool DecompressZlib(uint8* out, int out_len, base::StringPiece in) {
+ z_stream z;
+ memset(&z, 0, sizeof(z));
+
+ z.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(in.data()));
+ z.avail_in = in.size();
+ z.next_out = reinterpret_cast<Bytef*>(out);
+ z.avail_out = out_len;
+
+ if (inflateInit(&z) != Z_OK)
+ return false;
+ bool ret = false;
+ int r = inflate(&z, Z_FINISH);
+ if (r != Z_STREAM_END)
+ goto err;
+ if (z.avail_in || z.avail_out)
+ goto err;
+ ret = true;
+
+ err:
+ inflateEnd(&z);
+ return ret;
+}
+
+CRLSet::CRLSet()
+ : sequence_(0),
+ not_after_(0) {
+}
+
+CRLSet::~CRLSet() {
+}
+
+// CRLSet format:
+//
+// uint16le header_len
+// byte[header_len] header_bytes
+// repeated {
+// byte[32] parent_spki_sha256
+// uint32le num_serials
+// [num_serials] {
+// uint8 serial_length;
+// byte[serial_length] serial;
+// }
+//
+// header_bytes consists of a JSON dictionary with the following keys:
+// Version (int): currently 0
+// ContentType (string): "CRLSet" or "CRLSetDelta" (magic value)
+// DeltaFrom (int32): if this is a delta update (see below), then this
+// contains the sequence number of the base CRLSet.
+// Sequence (int32): the monotonic sequence number of this CRL set.
+//
+// A delta CRLSet is similar to a CRLSet:
+//
+// struct CompressedChanges {
+// uint32le uncompressed_size
+// uint32le compressed_size
+// byte[compressed_size] zlib_data
+// }
+//
+// uint16le header_len
+// byte[header_len] header_bytes
+// CompressedChanges crl_changes
+// [crl_changes.uncompressed_size] {
+// switch (crl_changes[i]) {
+// case 0:
+// // CRL is the same
+// case 1:
+// // New CRL inserted
+// // See CRL structure from the non-delta format
+// case 2:
+// // CRL deleted
+// case 3:
+// // CRL changed
+// CompressedChanges serials_changes
+// [serials_changes.uncompressed_size] {
+// switch (serials_changes[i]) {
+// case 0:
+// // the serial is the same
+// case 1:
+// // serial inserted
+// uint8 serial_length
+// byte[serial_length] serial
+// case 2:
+// // serial deleted
+// }
+// }
+// }
+// }
+//
+// A delta CRLSet applies to a specific CRL set as given in the
+// header's "DeltaFrom" value. The delta describes the changes to each CRL
+// in turn with a zlib compressed array of options: either the CRL is the same,
+// a new CRL is inserted, the CRL is deleted or the CRL is updated. In the case
+// of an update, the serials in the CRL are considered in the same fashion
+// except there is no delta update of a serial number: they are either
+// inserted, deleted or left the same.
+
+// ReadHeader reads the header (including length prefix) from |data| and
+// updates |data| to remove the header on return. Caller takes ownership of the
+// returned pointer.
+static base::DictionaryValue* ReadHeader(base::StringPiece* data) {
+ if (data->size() < 2)
+ return NULL;
+ uint16 header_len;
+ memcpy(&header_len, data->data(), 2); // assumes little-endian.
+ data->remove_prefix(2);
+
+ if (data->size() < header_len)
+ return NULL;
+
+ const base::StringPiece header_bytes(data->data(), header_len);
+ data->remove_prefix(header_len);
+
+ scoped_ptr<base::Value> header(base::JSONReader::Read(
+ header_bytes, base::JSON_ALLOW_TRAILING_COMMAS));
+ if (header.get() == NULL)
+ return NULL;
+
+ if (!header->IsType(base::Value::TYPE_DICTIONARY))
+ return NULL;
+ return reinterpret_cast<base::DictionaryValue*>(header.release());
+}
+
+// kCurrentFileVersion is the version of the CRLSet file format that we
+// currently implement.
+static const int kCurrentFileVersion = 0;
+
+static bool ReadCRL(base::StringPiece* data, std::string* out_parent_spki_hash,
+ std::vector<std::string>* out_serials) {
+ if (data->size() < crypto::kSHA256Length)
+ return false;
+ *out_parent_spki_hash = std::string(data->data(), crypto::kSHA256Length);
+ data->remove_prefix(crypto::kSHA256Length);
+
+ if (data->size() < sizeof(uint32))
+ return false;
+ uint32 num_serials;
+ memcpy(&num_serials, data->data(), sizeof(uint32)); // assumes little endian
+ data->remove_prefix(sizeof(uint32));
+
+ for (uint32 i = 0; i < num_serials; ++i) {
+ uint8 serial_length;
+ if (data->size() < sizeof(uint8))
+ return false;
+ memcpy(&serial_length, data->data(), sizeof(uint8));
+ data->remove_prefix(sizeof(uint8));
+
+ if (data->size() < serial_length)
+ return false;
+ std::string serial(data->data(), serial_length);
+ data->remove_prefix(serial_length);
+ out_serials->push_back(serial);
+ }
+
+ return true;
+}
+
+bool CRLSet::CopyBlockedSPKIsFromHeader(base::DictionaryValue* header_dict) {
+ base::ListValue* blocked_spkis_list = NULL;
+ if (!header_dict->GetList("BlockedSPKIs", &blocked_spkis_list)) {
+ // BlockedSPKIs is optional, so it's fine if we don't find it.
+ return true;
+ }
+
+ blocked_spkis_.clear();
+
+ for (size_t i = 0; i < blocked_spkis_list->GetSize(); ++i) {
+ std::string spki_sha256_base64, spki_sha256;
+ if (!blocked_spkis_list->GetString(i, &spki_sha256_base64))
+ return false;
+ if (!base::Base64Decode(spki_sha256_base64, &spki_sha256))
+ return false;
+ blocked_spkis_.push_back(spki_sha256);
+ }
+
+ return true;
+}
+
+// static
+bool CRLSet::Parse(base::StringPiece data, scoped_refptr<CRLSet>* out_crl_set) {
+ // Other parts of Chrome assume that we're little endian, so we don't lose
+ // anything by doing this.
+#if defined(__BYTE_ORDER)
+ // Linux check
+ COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian);
+#elif defined(__BIG_ENDIAN__)
+ // Mac check
+ #error assumes little endian
+#endif
+
+ scoped_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
+ if (!header_dict.get())
+ return false;
+
+ std::string contents;
+ if (!header_dict->GetString("ContentType", &contents))
+ return false;
+ if (contents != "CRLSet")
+ return false;
+
+ int version;
+ if (!header_dict->GetInteger("Version", &version) ||
+ version != kCurrentFileVersion) {
+ return false;
+ }
+
+ int sequence;
+ if (!header_dict->GetInteger("Sequence", &sequence))
+ return false;
+
+ double not_after;
+ if (!header_dict->GetDouble("NotAfter", &not_after)) {
+ // NotAfter is optional for now.
+ not_after = 0;
+ }
+ if (not_after < 0)
+ return false;
+
+ scoped_refptr<CRLSet> crl_set(new CRLSet);
+ crl_set->sequence_ = static_cast<uint32>(sequence);
+ crl_set->not_after_ = static_cast<uint64>(not_after);
+
+ for (size_t crl_index = 0; !data.empty(); crl_index++) {
+ std::string parent_spki_sha256;
+ std::vector<std::string> serials;
+ if (!ReadCRL(&data, &parent_spki_sha256, &serials))
+ return false;
+
+ crl_set->crls_.push_back(std::make_pair(parent_spki_sha256, serials));
+ crl_set->crls_index_by_issuer_[parent_spki_sha256] = crl_index;
+ }
+
+ if (!crl_set->CopyBlockedSPKIsFromHeader(header_dict.get()))
+ return false;
+
+ *out_crl_set = crl_set;
+ return true;
+}
+
+// kMaxUncompressedChangesLength is the largest changes array that we'll
+// accept. This bounds the number of CRLs in the CRLSet as well as the number
+// of serial numbers in a given CRL.
+static const unsigned kMaxUncompressedChangesLength = 1024 * 1024;
+
+static bool ReadChanges(base::StringPiece* data,
+ std::vector<uint8>* out_changes) {
+ uint32 uncompressed_size, compressed_size;
+ if (data->size() < 2 * sizeof(uint32))
+ return false;
+ // assumes little endian.
+ memcpy(&uncompressed_size, data->data(), sizeof(uint32));
+ data->remove_prefix(4);
+ memcpy(&compressed_size, data->data(), sizeof(uint32));
+ data->remove_prefix(4);
+
+ if (uncompressed_size > kMaxUncompressedChangesLength)
+ return false;
+ if (data->size() < compressed_size)
+ return false;
+
+ out_changes->clear();
+ if (uncompressed_size == 0)
+ return true;
+
+ out_changes->resize(uncompressed_size);
+ base::StringPiece compressed(data->data(), compressed_size);
+ data->remove_prefix(compressed_size);
+ return DecompressZlib(&(*out_changes)[0], uncompressed_size, compressed);
+}
+
+// These are the range coder symbols used in delta updates.
+enum {
+ SYMBOL_SAME = 0,
+ SYMBOL_INSERT = 1,
+ SYMBOL_DELETE = 2,
+ SYMBOL_CHANGED = 3,
+};
+
+bool ReadDeltaCRL(base::StringPiece* data,
+ const std::vector<std::string>& old_serials,
+ std::vector<std::string>* out_serials) {
+ std::vector<uint8> changes;
+ if (!ReadChanges(data, &changes))
+ return false;
+
+ size_t i = 0;
+ for (std::vector<uint8>::const_iterator k = changes.begin();
+ k != changes.end(); ++k) {
+ if (*k == SYMBOL_SAME) {
+ if (i >= old_serials.size())
+ return false;
+ out_serials->push_back(old_serials[i]);
+ i++;
+ } else if (*k == SYMBOL_INSERT) {
+ uint8 serial_length;
+ if (data->size() < sizeof(uint8))
+ return false;
+ memcpy(&serial_length, data->data(), sizeof(uint8));
+ data->remove_prefix(sizeof(uint8));
+
+ if (data->size() < serial_length)
+ return false;
+ const std::string serial(data->data(), serial_length);
+ data->remove_prefix(serial_length);
+
+ out_serials->push_back(serial);
+ } else if (*k == SYMBOL_DELETE) {
+ if (i >= old_serials.size())
+ return false;
+ i++;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if (i != old_serials.size())
+ return false;
+ return true;
+}
+
+bool CRLSet::ApplyDelta(const base::StringPiece& in_data,
+ scoped_refptr<CRLSet>* out_crl_set) {
+ base::StringPiece data(in_data);
+ scoped_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
+ if (!header_dict.get())
+ return false;
+
+ std::string contents;
+ if (!header_dict->GetString("ContentType", &contents))
+ return false;
+ if (contents != "CRLSetDelta")
+ return false;
+
+ int version;
+ if (!header_dict->GetInteger("Version", &version) ||
+ version != kCurrentFileVersion) {
+ return false;
+ }
+
+ int sequence, delta_from;
+ if (!header_dict->GetInteger("Sequence", &sequence) ||
+ !header_dict->GetInteger("DeltaFrom", &delta_from) ||
+ delta_from < 0 ||
+ static_cast<uint32>(delta_from) != sequence_) {
+ return false;
+ }
+
+ double not_after;
+ if (!header_dict->GetDouble("NotAfter", &not_after)) {
+ // NotAfter is optional for now.
+ not_after = 0;
+ }
+ if (not_after < 0)
+ return false;
+
+ scoped_refptr<CRLSet> crl_set(new CRLSet);
+ crl_set->sequence_ = static_cast<uint32>(sequence);
+ crl_set->not_after_ = static_cast<uint64>(not_after);
+
+ if (!crl_set->CopyBlockedSPKIsFromHeader(header_dict.get()))
+ return false;
+
+ std::vector<uint8> crl_changes;
+
+ if (!ReadChanges(&data, &crl_changes))
+ return false;
+
+ size_t i = 0, j = 0;
+ for (std::vector<uint8>::const_iterator k = crl_changes.begin();
+ k != crl_changes.end(); ++k) {
+ if (*k == SYMBOL_SAME) {
+ if (i >= crls_.size())
+ return false;
+ crl_set->crls_.push_back(crls_[i]);
+ crl_set->crls_index_by_issuer_[crls_[i].first] = j;
+ i++;
+ j++;
+ } else if (*k == SYMBOL_INSERT) {
+ std::string parent_spki_hash;
+ std::vector<std::string> serials;
+ if (!ReadCRL(&data, &parent_spki_hash, &serials))
+ return false;
+ crl_set->crls_.push_back(std::make_pair(parent_spki_hash, serials));
+ crl_set->crls_index_by_issuer_[parent_spki_hash] = j;
+ j++;
+ } else if (*k == SYMBOL_DELETE) {
+ if (i >= crls_.size())
+ return false;
+ i++;
+ } else if (*k == SYMBOL_CHANGED) {
+ if (i >= crls_.size())
+ return false;
+ std::vector<std::string> serials;
+ if (!ReadDeltaCRL(&data, crls_[i].second, &serials))
+ return false;
+ crl_set->crls_.push_back(std::make_pair(crls_[i].first, serials));
+ crl_set->crls_index_by_issuer_[crls_[i].first] = j;
+ i++;
+ j++;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if (!data.empty())
+ return false;
+ if (i != crls_.size())
+ return false;
+
+ *out_crl_set = crl_set;
+ return true;
+}
+
+// static
+bool CRLSet::GetIsDeltaUpdate(const base::StringPiece& in_data,
+ bool* is_delta) {
+ base::StringPiece data(in_data);
+ scoped_ptr<base::DictionaryValue> header_dict(ReadHeader(&data));
+ if (!header_dict.get())
+ return false;
+
+ std::string contents;
+ if (!header_dict->GetString("ContentType", &contents))
+ return false;
+
+ if (contents == "CRLSet") {
+ *is_delta = false;
+ } else if (contents == "CRLSetDelta") {
+ *is_delta = true;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+std::string CRLSet::Serialize() const {
+ std::string header = base::StringPrintf(
+ "{"
+ "\"Version\":0,"
+ "\"ContentType\":\"CRLSet\","
+ "\"Sequence\":%u,"
+ "\"DeltaFrom\":0,"
+ "\"NumParents\":%u,"
+ "\"BlockedSPKIs\":[",
+ static_cast<unsigned>(sequence_),
+ static_cast<unsigned>(crls_.size()));
+
+ for (std::vector<std::string>::const_iterator i = blocked_spkis_.begin();
+ i != blocked_spkis_.end(); ++i) {
+ std::string spki_hash_base64;
+ base::Base64Encode(*i, &spki_hash_base64);
+
+ if (i != blocked_spkis_.begin())
+ header += ",";
+ header += "\"" + spki_hash_base64 + "\"";
+ }
+ header += "]";
+ if (not_after_ != 0)
+ header += base::StringPrintf(",\"NotAfter\":%" PRIu64, not_after_);
+ header += "}";
+
+ size_t len = 2 /* header len */ + header.size();
+
+ for (CRLList::const_iterator i = crls_.begin(); i != crls_.end(); ++i) {
+ len += i->first.size() + 4 /* num serials */;
+ for (std::vector<std::string>::const_iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ len += 1 /* serial length */ + j->size();
+ }
+ }
+
+ std::string ret;
+ char* out = WriteInto(&ret, len + 1 /* to include final NUL */);
+ size_t off = 0;
+ out[off++] = header.size();
+ out[off++] = header.size() >> 8;
+ memcpy(out + off, header.data(), header.size());
+ off += header.size();
+
+ for (CRLList::const_iterator i = crls_.begin(); i != crls_.end(); ++i) {
+ memcpy(out + off, i->first.data(), i->first.size());
+ off += i->first.size();
+ const uint32 num_serials = i->second.size();
+ memcpy(out + off, &num_serials, sizeof(num_serials));
+ off += sizeof(num_serials);
+
+ for (std::vector<std::string>::const_iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ out[off++] = j->size();
+ memcpy(out + off, j->data(), j->size());
+ off += j->size();
+ }
+ }
+
+ CHECK_EQ(off, len);
+ return ret;
+}
+
+CRLSet::Result CRLSet::CheckSPKI(const base::StringPiece& spki_hash) const {
+ for (std::vector<std::string>::const_iterator i = blocked_spkis_.begin();
+ i != blocked_spkis_.end(); ++i) {
+ if (spki_hash.size() == i->size() &&
+ memcmp(spki_hash.data(), i->data(), i->size()) == 0) {
+ return REVOKED;
+ }
+ }
+
+ return GOOD;
+}
+
+CRLSet::Result CRLSet::CheckSerial(
+ const base::StringPiece& serial_number,
+ const base::StringPiece& issuer_spki_hash) const {
+ base::StringPiece serial(serial_number);
+
+ if (!serial.empty() && (serial[0] & 0x80) != 0) {
+ // This serial number is negative but the process which generates CRL sets
+ // will reject any certificates with negative serial numbers as invalid.
+ return UNKNOWN;
+ }
+
+ // Remove any leading zero bytes.
+ while (serial.size() > 1 && serial[0] == 0x00)
+ serial.remove_prefix(1);
+
+ std::map<std::string, size_t>::const_iterator i =
+ crls_index_by_issuer_.find(issuer_spki_hash.as_string());
+ if (i == crls_index_by_issuer_.end())
+ return UNKNOWN;
+ const std::vector<std::string>& serials = crls_[i->second].second;
+
+ for (std::vector<std::string>::const_iterator i = serials.begin();
+ i != serials.end(); ++i) {
+ if (base::StringPiece(*i) == serial)
+ return REVOKED;
+ }
+
+ return GOOD;
+}
+
+bool CRLSet::IsExpired() const {
+ if (not_after_ == 0)
+ return false;
+
+ uint64 now = base::Time::Now().ToTimeT();
+ return now > not_after_;
+}
+
+uint32 CRLSet::sequence() const {
+ return sequence_;
+}
+
+const CRLSet::CRLList& CRLSet::crls() const {
+ return crls_;
+}
+
+// static
+CRLSet* CRLSet::EmptyCRLSetForTesting() {
+ return ForTesting(false, NULL, "");
+}
+
+CRLSet* CRLSet::ExpiredCRLSetForTesting() {
+ return ForTesting(true, NULL, "");
+}
+
+// static
+CRLSet* CRLSet::ForTesting(bool is_expired,
+ const SHA256HashValue* issuer_spki,
+ const std::string& serial_number) {
+ CRLSet* crl_set = new CRLSet;
+ if (is_expired)
+ crl_set->not_after_ = 1;
+ if (issuer_spki != NULL) {
+ const std::string spki(reinterpret_cast<const char*>(issuer_spki->data),
+ sizeof(issuer_spki->data));
+ crl_set->crls_.push_back(make_pair(spki, std::vector<std::string>()));
+ crl_set->crls_index_by_issuer_[spki] = 0;
+ }
+
+ if (!serial_number.empty())
+ crl_set->crls_[0].second.push_back(serial_number);
+
+ return crl_set;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/crl_set.h b/chromium/net/cert/crl_set.h
new file mode 100644
index 00000000000..348005f097b
--- /dev/null
+++ b/chromium/net/cert/crl_set.h
@@ -0,0 +1,128 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_CRL_SET_H_
+#define NET_CERT_CRL_SET_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/cert/x509_cert_types.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace net {
+
+// A CRLSet is a structure that lists the serial numbers of revoked
+// certificates from a number of issuers where issuers are identified by the
+// SHA256 of their SubjectPublicKeyInfo.
+class NET_EXPORT CRLSet : public base::RefCountedThreadSafe<CRLSet> {
+ public:
+ enum Result {
+ REVOKED, // the certificate should be rejected.
+ UNKNOWN, // the CRL for the certificate is not included in the set.
+ GOOD, // the certificate is not listed.
+ };
+
+ // Parse parses the bytes in |data| and, on success, puts a new CRLSet in
+ // |out_crl_set| and returns true.
+ static bool Parse(base::StringPiece data,
+ scoped_refptr<CRLSet>* out_crl_set);
+
+ // CheckSPKI checks whether the given SPKI has been listed as blocked.
+ // spki_hash: the SHA256 of the SubjectPublicKeyInfo of the certificate.
+ Result CheckSPKI(const base::StringPiece& spki_hash) const;
+
+ // CheckSerial returns the information contained in the set for a given
+ // certificate:
+ // serial_number: the serial number of the certificate
+ // issuer_spki_hash: the SHA256 of the SubjectPublicKeyInfo of the CRL
+ // signer
+ Result CheckSerial(
+ const base::StringPiece& serial_number,
+ const base::StringPiece& issuer_spki_hash) const;
+
+ // IsExpired returns true iff the current time is past the NotAfter time
+ // specified in the CRLSet.
+ bool IsExpired() const;
+
+ // ApplyDelta returns a new CRLSet in |out_crl_set| that is the result of
+ // updating the current CRL set with the delta information in |delta_bytes|.
+ bool ApplyDelta(const base::StringPiece& delta_bytes,
+ scoped_refptr<CRLSet>* out_crl_set);
+
+ // GetIsDeltaUpdate extracts the header from |bytes|, sets *is_delta to
+ // whether |bytes| is a delta CRL set or not and returns true. In the event
+ // of a parse error, it returns false.
+ static bool GetIsDeltaUpdate(const base::StringPiece& bytes, bool *is_delta);
+
+ // Serialize returns a string of bytes suitable for passing to Parse. Parsing
+ // and serializing a CRLSet is a lossless operation - the resulting bytes
+ // will be equal.
+ std::string Serialize() const;
+
+ // sequence returns the sequence number of this CRL set. CRL sets generated
+ // by the same source are given strictly monotonically increasing sequence
+ // numbers.
+ uint32 sequence() const;
+
+ // CRLList contains a list of (issuer SPKI hash, revoked serial numbers)
+ // pairs.
+ typedef std::vector< std::pair<std::string, std::vector<std::string> > >
+ CRLList;
+
+ // crls returns the internal state of this CRLSet. It should only be used in
+ // testing.
+ const CRLList& crls() const;
+
+ // EmptyCRLSetForTesting returns a valid, but empty, CRLSet for unit tests.
+ static CRLSet* EmptyCRLSetForTesting();
+
+ // ExpiredCRLSetForTesting returns a expired, empty CRLSet for unit tests.
+ static CRLSet* ExpiredCRLSetForTesting();
+
+ // ForTesting returns a CRLSet for testing. If |is_expired| is true, calling
+ // IsExpired on the result will return true. If |issuer_spki| is not NULL,
+ // the CRLSet will cover certificates issued by that SPKI. If |serial_number|
+ // is not emtpy, then that big-endian serial number will be considered to
+ // have been revoked by |issuer_spki|.
+ static CRLSet* ForTesting(bool is_expired,
+ const SHA256HashValue* issuer_spki,
+ const std::string& serial_number);
+
+ private:
+ CRLSet();
+ ~CRLSet();
+
+ friend class base::RefCountedThreadSafe<CRLSet>;
+
+ // CopyBlockedSPKIsFromHeader sets |blocked_spkis_| to the list of values
+ // from "BlockedSPKIs" in |header_dict|.
+ bool CopyBlockedSPKIsFromHeader(base::DictionaryValue* header_dict);
+
+ uint32 sequence_;
+ CRLList crls_;
+ // not_after_ contains the time, in UNIX epoch seconds, after which the
+ // CRLSet should be considered stale, or 0 if no such time was given.
+ uint64 not_after_;
+ // crls_index_by_issuer_ maps from issuer SPKI hashes to the index in |crls_|
+ // where the information for that issuer can be found. We have both |crls_|
+ // and |crls_index_by_issuer_| because, when applying a delta update, we need
+ // to identify a CRL by index.
+ std::map<std::string, size_t> crls_index_by_issuer_;
+ // blocked_spkis_ contains the SHA256 hashes of SPKIs which are to be blocked
+ // no matter where in a certificate chain they might appear.
+ std::vector<std::string> blocked_spkis_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_CRL_SET_H_
diff --git a/chromium/net/cert/crl_set_unittest.cc b/chromium/net/cert/crl_set_unittest.cc
new file mode 100644
index 00000000000..88eb65467c0
--- /dev/null
+++ b/chromium/net/cert/crl_set_unittest.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/crl_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// These data blocks were generated using a lot of code that is still in
+// development. For now, if you need to update them, you have to contact agl.
+static const uint8 kGIACRLSet[] = {
+ 0x60, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x22, 0x2c, 0x22,
+ 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22,
+ 0x44, 0x65, 0x6c, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c,
+ 0x22, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a,
+ 0x31, 0x2c, 0x22, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x50, 0x4b,
+ 0x49, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x7d, 0xb6, 0xb9, 0x54, 0x32, 0xab, 0xae,
+ 0x57, 0xfe, 0x02, 0x0c, 0xb2, 0xb7, 0x4f, 0x4f, 0x9f, 0x91, 0x73, 0xc8, 0xc7,
+ 0x08, 0xaf, 0xc9, 0xe7, 0x32, 0xac, 0xe2, 0x32, 0x79, 0x04, 0x7c, 0x6d, 0x05,
+ 0x0d, 0x00, 0x00, 0x00, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00,
+ 0x23, 0xb0, 0x0a, 0x10, 0x0e, 0x37, 0x06, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb1,
+ 0x0a, 0x16, 0x25, 0x42, 0x54, 0x00, 0x03, 0x00, 0x00, 0x14, 0x51, 0x0a, 0x16,
+ 0x69, 0xd1, 0xd7, 0x00, 0x03, 0x00, 0x00, 0x14, 0x52, 0x0a, 0x16, 0x70, 0x8c,
+ 0x22, 0x00, 0x03, 0x00, 0x00, 0x14, 0x53, 0x0a, 0x16, 0x71, 0x31, 0x2c, 0x00,
+ 0x03, 0x00, 0x00, 0x14, 0x54, 0x0a, 0x16, 0x7d, 0x75, 0x9d, 0x00, 0x03, 0x00,
+ 0x00, 0x14, 0x55, 0x0a, 0x1f, 0xee, 0xf9, 0x49, 0x00, 0x03, 0x00, 0x00, 0x23,
+ 0xae, 0x0a, 0x1f, 0xfc, 0xd1, 0x89, 0x00, 0x03, 0x00, 0x00, 0x23, 0xaf, 0x0a,
+ 0x61, 0xdd, 0xc7, 0x48, 0x00, 0x03, 0x00, 0x00, 0x18, 0x0e, 0x0a, 0x61, 0xe6,
+ 0x12, 0x64, 0x00, 0x03, 0x00, 0x00, 0x18, 0x0f, 0x0a, 0x61, 0xe9, 0x46, 0x56,
+ 0x00, 0x03, 0x00, 0x00, 0x18, 0x10, 0x0a, 0x64, 0x63, 0x49, 0xd2, 0x00, 0x03,
+ 0x00, 0x00, 0x1d, 0x77,
+};
+
+static const uint8 kNoopDeltaCRL[] = {
+ 0xc3, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x44, 0x65, 0x6c,
+ 0x74, 0x61, 0x22, 0x2c, 0x22, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
+ 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x22, 0x3a, 0x31, 0x33, 0x31, 0x31, 0x31, 0x32, 0x33, 0x37, 0x39,
+ 0x33, 0x2c, 0x22, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x73,
+ 0x22, 0x3a, 0x34, 0x33, 0x32, 0x30, 0x30, 0x2c, 0x22, 0x44, 0x65, 0x6c, 0x74,
+ 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x75, 0x6d,
+ 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x53,
+ 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
+ 0x65, 0x79, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
+ 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61,
+ 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3a, 0x22,
+ 0x22, 0x7d, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x62,
+ 0x00, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 0x00, 0x01,
+};
+
+static const uint8 kAddCRLDelta[] = {
+ 0xc3, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x44, 0x65, 0x6c,
+ 0x74, 0x61, 0x22, 0x2c, 0x22, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
+ 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x22, 0x3a, 0x31, 0x33, 0x31, 0x31, 0x31, 0x32, 0x35, 0x39, 0x34,
+ 0x38, 0x2c, 0x22, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x73,
+ 0x22, 0x3a, 0x34, 0x33, 0x32, 0x30, 0x30, 0x2c, 0x22, 0x44, 0x65, 0x6c, 0x74,
+ 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x75, 0x6d,
+ 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x32, 0x2c, 0x22, 0x53,
+ 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
+ 0x65, 0x79, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
+ 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61,
+ 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3a, 0x22,
+ 0x22, 0x7d, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x62,
+ 0x60, 0x04, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0x03, 0x00, 0x02, 0xe4, 0x2f,
+ 0x24, 0xbd, 0x4d, 0x37, 0xf4, 0xaa, 0x2e, 0x56, 0xb9, 0x79, 0xd8, 0x3d, 0x1e,
+ 0x65, 0x21, 0x9f, 0xe0, 0xe9, 0xe3, 0xa3, 0x82, 0xa1, 0xb3, 0xcb, 0x66, 0xc9,
+ 0x39, 0x55, 0xde, 0x75, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x03, 0x01,
+ 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x07, 0x01, 0x08, 0x01, 0x09, 0x01, 0x2f,
+ 0x01, 0x30, 0x01, 0x31, 0x01, 0x32,
+};
+
+static const uint8 kRemoveCRLDelta[] = {
+ 0xc3, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x44, 0x65, 0x6c,
+ 0x74, 0x61, 0x22, 0x2c, 0x22, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
+ 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x22, 0x3a, 0x31, 0x33, 0x31, 0x31, 0x31, 0x32, 0x36, 0x31, 0x31,
+ 0x36, 0x2c, 0x22, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x73,
+ 0x22, 0x3a, 0x34, 0x33, 0x32, 0x30, 0x30, 0x2c, 0x22, 0x44, 0x65, 0x6c, 0x74,
+ 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x75, 0x6d,
+ 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x53,
+ 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
+ 0x65, 0x79, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
+ 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61,
+ 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3a, 0x22,
+ 0x22, 0x7d, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x62,
+ 0x60, 0x02, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0x04, 0x00, 0x03,
+};
+
+static const uint8 kUpdateSerialsDelta[] = {
+ 0xc3, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x44, 0x65, 0x6c,
+ 0x74, 0x61, 0x22, 0x2c, 0x22, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65,
+ 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x22, 0x3a, 0x31, 0x33, 0x31, 0x31, 0x31, 0x32, 0x36, 0x34, 0x36,
+ 0x31, 0x2c, 0x22, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x73,
+ 0x22, 0x3a, 0x34, 0x33, 0x32, 0x30, 0x30, 0x2c, 0x22, 0x44, 0x65, 0x6c, 0x74,
+ 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x4e, 0x75, 0x6d,
+ 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x53,
+ 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
+ 0x65, 0x79, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
+ 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61,
+ 0x64, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3a, 0x22,
+ 0x22, 0x7d, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x62,
+ 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x00, 0x04, 0x00, 0x04, 0x2d, 0x00, 0x00,
+ 0x00, 0x15, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x62, 0x80, 0x00, 0x46, 0x2c, 0x00,
+ 0x45, 0x14, 0xac, 0x08, 0x10, 0x00, 0x00, 0xff, 0xff, 0x02, 0xe1, 0x00, 0x21,
+ 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10,
+ 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f,
+ 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00,
+ 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00,
+ 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23,
+ 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a,
+ 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d,
+ 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30,
+ 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03,
+ 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00,
+ 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0,
+ 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10,
+ 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f,
+ 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00,
+ 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00,
+ 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23,
+ 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a,
+ 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d,
+ 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30,
+ 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03,
+ 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00,
+ 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0,
+ 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10,
+ 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f,
+ 0x30, 0x00, 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00,
+ 0x03, 0x00, 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00,
+ 0x00, 0x23, 0xb0, 0x0a, 0x10, 0x0d, 0x7f, 0x30, 0x00, 0x03, 0x00, 0x00, 0x23,
+ 0xb0,
+};
+
+static const uint8 kBlockedSPKICRLSet[] = {
+ 0x8e, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x22, 0x2c, 0x22,
+ 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x30, 0x2c, 0x22,
+ 0x44, 0x65, 0x6c, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c,
+ 0x22, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x50, 0x4b,
+ 0x49, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x34, 0x37, 0x44, 0x45, 0x51, 0x70, 0x6a,
+ 0x38, 0x48, 0x42, 0x53, 0x61, 0x2b, 0x2f, 0x54, 0x49, 0x6d, 0x57, 0x2b, 0x35,
+ 0x4a, 0x43, 0x65, 0x75, 0x51, 0x65, 0x52, 0x6b, 0x6d, 0x35, 0x4e, 0x4d, 0x70,
+ 0x4a, 0x57, 0x5a, 0x47, 0x33, 0x68, 0x53, 0x75, 0x46, 0x55, 0x3d, 0x22, 0x5d,
+ 0x7d,
+};
+
+static const uint8 kExpiredCRLSet[] = {
+ 0x6d, 0x00, 0x7b, 0x22, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x22, 0x3a, 0x22, 0x43, 0x52, 0x4c, 0x53, 0x65, 0x74, 0x22, 0x2c, 0x22,
+ 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x31, 0x2c, 0x22,
+ 0x44, 0x65, 0x6c, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x22, 0x3a, 0x30, 0x2c,
+ 0x22, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a,
+ 0x30, 0x2c, 0x22, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x53, 0x50, 0x4b,
+ 0x49, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x2c, 0x22, 0x4e, 0x6f, 0x74, 0x41, 0x66,
+ 0x74, 0x65, 0x72, 0x22, 0x3a, 0x31, 0x7d,
+};
+
+// kGIASPKISHA256 is the SHA256 digest the Google Internet Authority's
+// SubjectPublicKeyInfo.
+static const uint8 kGIASPKISHA256[32] = {
+ 0xb6, 0xb9, 0x54, 0x32, 0xab, 0xae, 0x57, 0xfe, 0x02, 0x0c, 0xb2, 0xb7, 0x4f,
+ 0x4f, 0x9f, 0x91, 0x73, 0xc8, 0xc7, 0x08, 0xaf, 0xc9, 0xe7, 0x32, 0xac, 0xe2,
+ 0x32, 0x79, 0x04, 0x7c, 0x6d, 0x05,
+};
+
+TEST(CRLSetTest, Parse) {
+ base::StringPiece s(reinterpret_cast<const char*>(kGIACRLSet),
+ sizeof(kGIACRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ const net::CRLSet::CRLList& crls = set->crls();
+ ASSERT_EQ(1u, crls.size());
+ const std::vector<std::string>& serials = crls[0].second;
+ static const unsigned kExpectedNumSerials = 13;
+ ASSERT_EQ(kExpectedNumSerials, serials.size());
+ EXPECT_EQ(std::string("\x10\x0D\x7F\x30\x00\x03\x00\x00\x23\xB0", 10),
+ serials[0]);
+ EXPECT_EQ(std::string("\x64\x63\x49\xD2\x00\x03\x00\x00\x1D\x77", 10),
+ serials[kExpectedNumSerials - 1]);
+
+ const std::string gia_spki_hash(
+ reinterpret_cast<const char*>(kGIASPKISHA256),
+ sizeof(kGIASPKISHA256));
+ EXPECT_EQ(net::CRLSet::REVOKED, set->CheckSerial(
+ std::string("\x16\x7D\x75\x9D\x00\x03\x00\x00\x14\x55", 10),
+ gia_spki_hash));
+ EXPECT_EQ(net::CRLSet::GOOD, set->CheckSerial(
+ std::string("\x47\x54\x3E\x79\x00\x03\x00\x00\x14\xF5", 10),
+ gia_spki_hash));
+
+ EXPECT_FALSE(set->IsExpired());
+}
+
+TEST(CRLSetTest, NoOpDeltaUpdate) {
+ base::StringPiece s(reinterpret_cast<const char*>(kGIACRLSet),
+ sizeof(kGIACRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ scoped_refptr<net::CRLSet> delta_set;
+ base::StringPiece delta(reinterpret_cast<const char*>(kNoopDeltaCRL),
+ sizeof(kNoopDeltaCRL));
+ EXPECT_TRUE(set->ApplyDelta(delta, &delta_set));
+ ASSERT_TRUE(delta_set.get() != NULL);
+
+ std::string out = delta_set->Serialize();
+ EXPECT_EQ(s.as_string(), out);
+}
+
+TEST(CRLSetTest, AddCRLDelta) {
+ base::StringPiece s(reinterpret_cast<const char*>(kGIACRLSet),
+ sizeof(kGIACRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ scoped_refptr<net::CRLSet> delta_set;
+ base::StringPiece delta(reinterpret_cast<const char*>(kAddCRLDelta),
+ sizeof(kAddCRLDelta));
+ EXPECT_TRUE(set->ApplyDelta(delta, &delta_set));
+ ASSERT_TRUE(delta_set.get() != NULL);
+
+ const net::CRLSet::CRLList& crls = delta_set->crls();
+ ASSERT_EQ(2u, crls.size());
+ const std::vector<std::string>& serials = crls[1].second;
+ ASSERT_EQ(12u, serials.size());
+ EXPECT_EQ(std::string("\x02", 1), serials[0]);
+ EXPECT_EQ(std::string("\x03", 1), serials[1]);
+ EXPECT_EQ(std::string("\x04", 1), serials[2]);
+}
+
+TEST(CRLSetTest, AddRemoveCRLDelta) {
+ base::StringPiece s(reinterpret_cast<const char*>(kGIACRLSet),
+ sizeof(kGIACRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ scoped_refptr<net::CRLSet> delta_set;
+ base::StringPiece delta(reinterpret_cast<const char*>(kAddCRLDelta),
+ sizeof(kAddCRLDelta));
+ EXPECT_TRUE(set->ApplyDelta(delta, &delta_set));
+ ASSERT_TRUE(delta_set.get() != NULL);
+
+ scoped_refptr<net::CRLSet> delta2_set;
+ base::StringPiece delta2(reinterpret_cast<const char*>(kRemoveCRLDelta),
+ sizeof(kRemoveCRLDelta));
+ EXPECT_TRUE(delta_set->ApplyDelta(delta2, &delta2_set));
+ ASSERT_TRUE(delta2_set.get() != NULL);
+
+ const net::CRLSet::CRLList& crls = delta2_set->crls();
+ ASSERT_EQ(1u, crls.size());
+ const std::vector<std::string>& serials = crls[0].second;
+ ASSERT_EQ(13u, serials.size());
+}
+
+TEST(CRLSetTest, UpdateSerialsDelta) {
+ base::StringPiece s(reinterpret_cast<const char*>(kGIACRLSet),
+ sizeof(kGIACRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ scoped_refptr<net::CRLSet> delta_set;
+ base::StringPiece delta(reinterpret_cast<const char*>(kUpdateSerialsDelta),
+ sizeof(kUpdateSerialsDelta));
+ EXPECT_TRUE(set->ApplyDelta(delta, &delta_set));
+ ASSERT_TRUE(delta_set.get() != NULL);
+
+ const net::CRLSet::CRLList& crls = delta_set->crls();
+ ASSERT_EQ(1u, crls.size());
+ const std::vector<std::string>& serials = crls[0].second;
+ EXPECT_EQ(45u, serials.size());
+}
+
+TEST(CRLSetTest, BlockedSPKIs) {
+ base::StringPiece s(reinterpret_cast<const char*>(kBlockedSPKICRLSet),
+ sizeof(kBlockedSPKICRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ const uint8 spki_hash[] = {
+ 227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36,
+ 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85,
+ 0,
+ };
+
+ EXPECT_EQ(net::CRLSet::GOOD, set->CheckSPKI(""));
+ EXPECT_EQ(net::CRLSet::REVOKED, set->CheckSPKI(
+ reinterpret_cast<const char*>(spki_hash)));
+}
+
+TEST(CRLSetTest, Expired) {
+ // This CRLSet has an expiry value set to one second past midnight, 1st Jan,
+ // 1970.
+ base::StringPiece s(reinterpret_cast<const char*>(kExpiredCRLSet),
+ sizeof(kExpiredCRLSet));
+ scoped_refptr<net::CRLSet> set;
+ EXPECT_TRUE(net::CRLSet::Parse(s, &set));
+ ASSERT_TRUE(set.get() != NULL);
+
+ EXPECT_TRUE(set->IsExpired());
+}
diff --git a/chromium/net/cert/ev_root_ca_metadata.cc b/chromium/net/cert/ev_root_ca_metadata.cc
new file mode 100644
index 00000000000..9e611a3093d
--- /dev/null
+++ b/chromium/net/cert/ev_root_ca_metadata.cc
@@ -0,0 +1,571 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/ev_root_ca_metadata.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include <cert.h>
+#include <pkcs11n.h>
+#include <secerr.h>
+#include <secoid.h>
+#elif defined(OS_WIN)
+#include <stdlib.h>
+#endif
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#if defined(USE_NSS) || defined(OS_IOS)
+#include "crypto/nss_util.h"
+#endif
+
+namespace net {
+
+// Raw metadata.
+struct EVMetadata {
+ // kMaxOIDsPerCA is the number of OIDs that we can support per root CA. At
+ // least one CA has different EV policies for businuss vs government
+ // entities and, in the case of cross-signing, we might need to list another
+ // CA's policy OID under the cross-signing root.
+ static const size_t kMaxOIDsPerCA = 2;
+ // This is the maximum length of an OID string (including the trailing NUL).
+ static const size_t kMaxOIDLength = 32;
+
+ // The SHA-1 fingerprint of the root CA certificate, used as a unique
+ // identifier for a root CA certificate.
+ SHA1HashValue fingerprint;
+
+ // The EV policy OIDs of the root CA.
+ const char policy_oids[kMaxOIDsPerCA][kMaxOIDLength];
+};
+
+static const EVMetadata ev_root_ca_metadata[] = {
+ // AC Camerfirma S.A. Chambers of Commerce Root - 2008
+ // https://www.camerfirma.com
+ { { { 0x78, 0x6a, 0x74, 0xac, 0x76, 0xab, 0x14, 0x7f, 0x9c, 0x6a,
+ 0x30, 0x50, 0xba, 0x9e, 0xa8, 0x7e, 0xfe, 0x9a, 0xce, 0x3c } },
+ { // AC Camerfirma uses the last two arcs to track how the private key is
+ // managed - the effective verification policy is the same.
+ "1.3.6.1.4.1.17326.10.14.2.1.2",
+ "1.3.6.1.4.1.17326.10.14.2.2.2", },
+ },
+ // AC Camerfirma S.A. Global Chambersign Root - 2008
+ // https://server2.camerfirma.com:8082
+ { { { 0x4a, 0xbd, 0xee, 0xec, 0x95, 0x0d, 0x35, 0x9c, 0x89, 0xae,
+ 0xc7, 0x52, 0xa1, 0x2c, 0x5b, 0x29, 0xf6, 0xd6, 0xaa, 0x0c } },
+ { // AC Camerfirma uses the last two arcs to track how the private key is
+ // managed - the effective verification policy is the same.
+ "1.3.6.1.4.1.17326.10.8.12.1.2",
+ "1.3.6.1.4.1.17326.10.8.12.2.2", },
+ },
+ // AddTrust External CA Root
+ // https://addtrustexternalcaroot-ev.comodoca.com
+ { { { 0x02, 0xfa, 0xf3, 0xe2, 0x91, 0x43, 0x54, 0x68, 0x60, 0x78,
+ 0x57, 0x69, 0x4d, 0xf5, 0xe4, 0x5b, 0x68, 0x85, 0x18, 0x68 } },
+ {
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ // This is the Network Solutions EV OID. However, this root
+ // cross-certifies NetSol and so we need it here too.
+ "1.3.6.1.4.1.782.1.2.1.8.1",
+ },
+ },
+ // AffirmTrust Commercial
+ // https://commercial.affirmtrust.com/
+ { { { 0xf9, 0xb5, 0xb6, 0x32, 0x45, 0x5f, 0x9c, 0xbe, 0xec, 0x57,
+ 0x5f, 0x80, 0xdc, 0xe9, 0x6e, 0x2c, 0xc7, 0xb2, 0x78, 0xb7 } },
+ {"1.3.6.1.4.1.34697.2.1", ""},
+ },
+ // AffirmTrust Networking
+ // https://networking.affirmtrust.com:4431
+ { { { 0x29, 0x36, 0x21, 0x02, 0x8b, 0x20, 0xed, 0x02, 0xf5, 0x66,
+ 0xc5, 0x32, 0xd1, 0xd6, 0xed, 0x90, 0x9f, 0x45, 0x00, 0x2f } },
+ {"1.3.6.1.4.1.34697.2.2", ""},
+ },
+ // AffirmTrust Premium
+ // https://premium.affirmtrust.com:4432/
+ { { { 0xd8, 0xa6, 0x33, 0x2c, 0xe0, 0x03, 0x6f, 0xb1, 0x85, 0xf6,
+ 0x63, 0x4f, 0x7d, 0x6a, 0x06, 0x65, 0x26, 0x32, 0x28, 0x27 } },
+ {"1.3.6.1.4.1.34697.2.3", ""},
+ },
+ // AffirmTrust Premium ECC
+ // https://premiumecc.affirmtrust.com:4433/
+ { { { 0xb8, 0x23, 0x6b, 0x00, 0x2f, 0x1d, 0x16, 0x86, 0x53, 0x01,
+ 0x55, 0x6c, 0x11, 0xa4, 0x37, 0xca, 0xeb, 0xff, 0xc3, 0xbb } },
+ {"1.3.6.1.4.1.34697.2.4", ""},
+ },
+ // CertPlus Class 2 Primary CA (KEYNECTIS)
+ // https://www.keynectis.com/
+ { { { 0x74, 0x20, 0x74, 0x41, 0x72, 0x9c, 0xdd, 0x92, 0xec, 0x79,
+ 0x31, 0xd8, 0x23, 0x10, 0x8d, 0xc2, 0x81, 0x92, 0xe2, 0xbb } },
+ {"1.3.6.1.4.1.22234.2.5.2.3.1", ""},
+ },
+ // Certum Trusted Network CA
+ // https://juice.certum.pl/
+ { { { 0x07, 0xe0, 0x32, 0xe0, 0x20, 0xb7, 0x2c, 0x3f, 0x19, 0x2f,
+ 0x06, 0x28, 0xa2, 0x59, 0x3a, 0x19, 0xa7, 0x0f, 0x06, 0x9e } },
+ {"1.2.616.1.113527.2.5.1.1", ""},
+ },
+ // COMODO Certification Authority
+ // https://secure.comodo.com/
+ { { { 0x66, 0x31, 0xbf, 0x9e, 0xf7, 0x4f, 0x9e, 0xb6, 0xc9, 0xd5,
+ 0xa6, 0x0c, 0xba, 0x6a, 0xbe, 0xd1, 0xf7, 0xbd, 0xef, 0x7b } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
+ // COMODO Certification Authority (reissued certificate with NotBefore of Jan
+ // 1 00:00:00 2011 GMT)
+ // https://secure.comodo.com/
+ { { { 0xee, 0x86, 0x93, 0x87, 0xff, 0xfd, 0x83, 0x49, 0xab, 0x5a,
+ 0xd1, 0x43, 0x22, 0x58, 0x87, 0x89, 0xa4, 0x57, 0xb0, 0x12 } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
+ // COMODO ECC Certification Authority
+ // https://comodoecccertificationauthority-ev.comodoca.com/
+ { { { 0x9f, 0x74, 0x4e, 0x9f, 0x2b, 0x4d, 0xba, 0xec, 0x0f, 0x31,
+ 0x2c, 0x50, 0xb6, 0x56, 0x3b, 0x8e, 0x2d, 0x93, 0xc3, 0x11 } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
+ // Cybertrust Global Root
+ // https://evup.cybertrust.ne.jp/ctj-ev-upgrader/evseal.gif
+ { { { 0x5f, 0x43, 0xe5, 0xb1, 0xbf, 0xf8, 0x78, 0x8c, 0xac, 0x1c,
+ 0xc7, 0xca, 0x4a, 0x9a, 0xc6, 0x22, 0x2b, 0xcc, 0x34, 0xc6 } },
+ {"1.3.6.1.4.1.6334.1.100.1", ""},
+ },
+ // DigiCert High Assurance EV Root CA
+ // https://www.digicert.com
+ { { { 0x5f, 0xb7, 0xee, 0x06, 0x33, 0xe2, 0x59, 0xdb, 0xad, 0x0c,
+ 0x4c, 0x9a, 0xe6, 0xd3, 0x8f, 0x1a, 0x61, 0xc7, 0xdc, 0x25 } },
+ {"2.16.840.1.114412.2.1", ""},
+ },
+ // D-TRUST Root Class 3 CA 2 EV 2009
+ // https://certdemo-ev-valid.ssl.d-trust.net/
+ { { { 0x96, 0xc9, 0x1b, 0x0b, 0x95, 0xb4, 0x10, 0x98, 0x42, 0xfa,
+ 0xd0, 0xd8, 0x22, 0x79, 0xfe, 0x60, 0xfa, 0xb9, 0x16, 0x83 } },
+ {"1.3.6.1.4.1.4788.2.202.1", ""},
+ },
+ // Entrust.net Secure Server Certification Authority
+ // https://www.entrust.net/
+ { { { 0x99, 0xa6, 0x9b, 0xe6, 0x1a, 0xfe, 0x88, 0x6b, 0x4d, 0x2b,
+ 0x82, 0x00, 0x7c, 0xb8, 0x54, 0xfc, 0x31, 0x7e, 0x15, 0x39 } },
+ {"2.16.840.1.114028.10.1.2", ""},
+ },
+ // Entrust Root Certification Authority
+ // https://www.entrust.net/
+ { { { 0xb3, 0x1e, 0xb1, 0xb7, 0x40, 0xe3, 0x6c, 0x84, 0x02, 0xda,
+ 0xdc, 0x37, 0xd4, 0x4d, 0xf5, 0xd4, 0x67, 0x49, 0x52, 0xf9 } },
+ {"2.16.840.1.114028.10.1.2", ""},
+ },
+ // Equifax Secure Certificate Authority (GeoTrust)
+ // https://www.geotrust.com/
+ { { { 0xd2, 0x32, 0x09, 0xad, 0x23, 0xd3, 0x14, 0x23, 0x21, 0x74,
+ 0xe4, 0x0d, 0x7f, 0x9d, 0x62, 0x13, 0x97, 0x86, 0x63, 0x3a } },
+ {"1.3.6.1.4.1.14370.1.6", ""},
+ },
+ // GeoTrust Primary Certification Authority
+ // https://www.geotrust.com/
+ { { { 0x32, 0x3c, 0x11, 0x8e, 0x1b, 0xf7, 0xb8, 0xb6, 0x52, 0x54,
+ 0xe2, 0xe2, 0x10, 0x0d, 0xd6, 0x02, 0x90, 0x37, 0xf0, 0x96 } },
+ {"1.3.6.1.4.1.14370.1.6", ""},
+ },
+ // GeoTrust Primary Certification Authority - G2
+ { { { 0x8d, 0x17, 0x84, 0xd5, 0x37, 0xf3, 0x03, 0x7d, 0xec, 0x70,
+ 0xfe, 0x57, 0x8b, 0x51, 0x9a, 0x99, 0xe6, 0x10, 0xd7, 0xb0 } },
+ {"1.3.6.1.4.1.14370.1.6", ""},
+ },
+ // GeoTrust Primary Certification Authority - G3
+ { { { 0x03, 0x9e, 0xed, 0xb8, 0x0b, 0xe7, 0xa0, 0x3c, 0x69, 0x53,
+ 0x89, 0x3b, 0x20, 0xd2, 0xd9, 0x32, 0x3a, 0x4c, 0x2a, 0xfd } },
+ {"1.3.6.1.4.1.14370.1.6", ""},
+ },
+ // GlobalSign Root CA - R2
+ // https://www.globalsign.com/
+ { { { 0x75, 0xe0, 0xab, 0xb6, 0x13, 0x85, 0x12, 0x27, 0x1c, 0x04,
+ 0xf8, 0x5f, 0xdd, 0xde, 0x38, 0xe4, 0xb7, 0x24, 0x2e, 0xfe } },
+ {"1.3.6.1.4.1.4146.1.1", ""},
+ },
+ // GlobalSign Root CA
+ { { { 0xb1, 0xbc, 0x96, 0x8b, 0xd4, 0xf4, 0x9d, 0x62, 0x2a, 0xa8,
+ 0x9a, 0x81, 0xf2, 0x15, 0x01, 0x52, 0xa4, 0x1d, 0x82, 0x9c } },
+ {"1.3.6.1.4.1.4146.1.1", ""},
+ },
+ // GlobalSign Root CA - R3
+ // https://2029.globalsign.com/
+ { { { 0xd6, 0x9b, 0x56, 0x11, 0x48, 0xf0, 0x1c, 0x77, 0xc5, 0x45,
+ 0x78, 0xc1, 0x09, 0x26, 0xdf, 0x5b, 0x85, 0x69, 0x76, 0xad } },
+ {"1.3.6.1.4.1.4146.1.1", ""},
+ },
+ // Go Daddy Class 2 Certification Authority
+ // https://www.godaddy.com/
+ { { { 0x27, 0x96, 0xba, 0xe6, 0x3f, 0x18, 0x01, 0xe2, 0x77, 0x26,
+ 0x1b, 0xa0, 0xd7, 0x77, 0x70, 0x02, 0x8f, 0x20, 0xee, 0xe4 } },
+ {"2.16.840.1.114413.1.7.23.3", ""},
+ },
+ // Go Daddy Root Certificate Authority - G2
+ // https://valid.gdig2.catest.godaddy.com/
+ { { { 0x47, 0xbe, 0xab, 0xc9, 0x22, 0xea, 0xe8, 0x0e, 0x78, 0x78,
+ 0x34, 0x62, 0xa7, 0x9f, 0x45, 0xc2, 0x54, 0xfd, 0xe6, 0x8b } },
+ {"2.16.840.1.114413.1.7.23.3", ""},
+ },
+ // GTE CyberTrust Global Root
+ // https://www.cybertrust.ne.jp/
+ { { { 0x97, 0x81, 0x79, 0x50, 0xd8, 0x1c, 0x96, 0x70, 0xcc, 0x34,
+ 0xd8, 0x09, 0xcf, 0x79, 0x44, 0x31, 0x36, 0x7e, 0xf4, 0x74 } },
+ {"1.3.6.1.4.1.6334.1.100.1", ""},
+ },
+ // Izenpe.com - SHA256 root
+ // The first OID is for businesses and the second for government entities.
+ // These are the test sites, respectively:
+ // https://servicios.izenpe.com
+ // https://servicios1.izenpe.com
+ { { { 0x2f, 0x78, 0x3d, 0x25, 0x52, 0x18, 0xa7, 0x4a, 0x65, 0x39,
+ 0x71, 0xb5, 0x2c, 0xa2, 0x9c, 0x45, 0x15, 0x6f, 0xe9, 0x19} },
+ {"1.3.6.1.4.1.14777.6.1.1", "1.3.6.1.4.1.14777.6.1.2"},
+ },
+ // Izenpe.com - SHA1 root
+ // Windows XP finds this, SHA1, root instead. The policy OIDs are the same as
+ // for the SHA256 root, above.
+ { { { 0x30, 0x77, 0x9e, 0x93, 0x15, 0x02, 0x2e, 0x94, 0x85, 0x6a,
+ 0x3f, 0xf8, 0xbc, 0xf8, 0x15, 0xb0, 0x82, 0xf9, 0xae, 0xfd} },
+ {"1.3.6.1.4.1.14777.6.1.1", "1.3.6.1.4.1.14777.6.1.2"},
+ },
+ // Network Solutions Certificate Authority
+ // https://www.networksolutions.com/website-packages/index.jsp
+ { { { 0x74, 0xf8, 0xa3, 0xc3, 0xef, 0xe7, 0xb3, 0x90, 0x06, 0x4b,
+ 0x83, 0x90, 0x3c, 0x21, 0x64, 0x60, 0x20, 0xe5, 0xdf, 0xce } },
+ {"1.3.6.1.4.1.782.1.2.1.8.1", ""},
+ },
+ // Network Solutions Certificate Authority (reissued certificate with
+ // NotBefore of Jan 1 00:00:00 2011 GMT).
+ // https://www.networksolutions.com/website-packages/index.jsp
+ { { { 0x71, 0x89, 0x9a, 0x67, 0xbf, 0x33, 0xaf, 0x31, 0xbe, 0xfd,
+ 0xc0, 0x71, 0xf8, 0xf7, 0x33, 0xb1, 0x83, 0x85, 0x63, 0x32 } },
+ {"1.3.6.1.4.1.782.1.2.1.8.1", ""},
+ },
+ // QuoVadis Root CA 2
+ // https://www.quovadis.bm/
+ { { { 0xca, 0x3a, 0xfb, 0xcf, 0x12, 0x40, 0x36, 0x4b, 0x44, 0xb2,
+ 0x16, 0x20, 0x88, 0x80, 0x48, 0x39, 0x19, 0x93, 0x7c, 0xf7 } },
+ {"1.3.6.1.4.1.8024.0.2.100.1.2", ""},
+ },
+ // SecureTrust CA, SecureTrust Corporation
+ // https://www.securetrust.com
+ // https://www.trustwave.com/
+ { { { 0x87, 0x82, 0xc6, 0xc3, 0x04, 0x35, 0x3b, 0xcf, 0xd2, 0x96,
+ 0x92, 0xd2, 0x59, 0x3e, 0x7d, 0x44, 0xd9, 0x34, 0xff, 0x11 } },
+ {"2.16.840.1.114404.1.1.2.4.1", ""},
+ },
+ // Secure Global CA, SecureTrust Corporation
+ { { { 0x3a, 0x44, 0x73, 0x5a, 0xe5, 0x81, 0x90, 0x1f, 0x24, 0x86,
+ 0x61, 0x46, 0x1e, 0x3b, 0x9c, 0xc4, 0x5f, 0xf5, 0x3a, 0x1b } },
+ {"2.16.840.1.114404.1.1.2.4.1", ""},
+ },
+ // Security Communication RootCA1
+ // https://www.secomtrust.net/contact/form.html
+ { { { 0x36, 0xb1, 0x2b, 0x49, 0xf9, 0x81, 0x9e, 0xd7, 0x4c, 0x9e,
+ 0xbc, 0x38, 0x0f, 0xc6, 0x56, 0x8f, 0x5d, 0xac, 0xb2, 0xf7 } },
+ {"1.2.392.200091.100.721.1", ""},
+ },
+ // Security Communication EV RootCA1
+ // https://www.secomtrust.net/contact/form.html
+ { { { 0xfe, 0xb8, 0xc4, 0x32, 0xdc, 0xf9, 0x76, 0x9a, 0xce, 0xae,
+ 0x3d, 0xd8, 0x90, 0x8f, 0xfd, 0x28, 0x86, 0x65, 0x64, 0x7d } },
+ {"1.2.392.200091.100.721.1", ""},
+ },
+ // StartCom Certification Authority
+ // https://www.startssl.com/
+ { { { 0x3e, 0x2b, 0xf7, 0xf2, 0x03, 0x1b, 0x96, 0xf3, 0x8c, 0xe6,
+ 0xc4, 0xd8, 0xa8, 0x5d, 0x3e, 0x2d, 0x58, 0x47, 0x6a, 0x0f } },
+ {"1.3.6.1.4.1.23223.1.1.1", ""},
+ },
+ // Starfield Class 2 Certification Authority
+ // https://www.starfieldtech.com/
+ { { { 0xad, 0x7e, 0x1c, 0x28, 0xb0, 0x64, 0xef, 0x8f, 0x60, 0x03,
+ 0x40, 0x20, 0x14, 0xc3, 0xd0, 0xe3, 0x37, 0x0e, 0xb5, 0x8a } },
+ {"2.16.840.1.114414.1.7.23.3", ""},
+ },
+ // Starfield Root Certificate Authority - G2
+ // https://valid.sfig2.catest.starfieldtech.com/
+ { { { 0xb5, 0x1c, 0x06, 0x7c, 0xee, 0x2b, 0x0c, 0x3d, 0xf8, 0x55,
+ 0xab, 0x2d, 0x92, 0xf4, 0xfe, 0x39, 0xd4, 0xe7, 0x0f, 0x0e } },
+ {"2.16.840.1.114414.1.7.23.3", ""},
+ },
+ // Starfield Services Root Certificate Authority - G2
+ // https://valid.sfsg2.catest.starfieldtech.com/
+ { { { 0x92, 0x5a, 0x8f, 0x8d, 0x2c, 0x6d, 0x04, 0xe0, 0x66, 0x5f,
+ 0x59, 0x6a, 0xff, 0x22, 0xd8, 0x63, 0xe8, 0x25, 0x6f, 0x3f } },
+ {"2.16.840.1.114414.1.7.24.3", ""},
+ },
+ // SwissSign Gold CA - G2
+ // https://testevg2.swisssign.net/
+ { { { 0xd8, 0xc5, 0x38, 0x8a, 0xb7, 0x30, 0x1b, 0x1b, 0x6e, 0xd4,
+ 0x7a, 0xe6, 0x45, 0x25, 0x3a, 0x6f, 0x9f, 0x1a, 0x27, 0x61 } },
+ {"2.16.756.1.89.1.2.1.1", ""},
+ },
+ // Thawte Premium Server CA
+ // https://www.thawte.com/
+ { { { 0x62, 0x7f, 0x8d, 0x78, 0x27, 0x65, 0x63, 0x99, 0xd2, 0x7d,
+ 0x7f, 0x90, 0x44, 0xc9, 0xfe, 0xb3, 0xf3, 0x3e, 0xfa, 0x9a } },
+ {"2.16.840.1.113733.1.7.48.1", ""},
+ },
+ // thawte Primary Root CA
+ // https://www.thawte.com/
+ { { { 0x91, 0xc6, 0xd6, 0xee, 0x3e, 0x8a, 0xc8, 0x63, 0x84, 0xe5,
+ 0x48, 0xc2, 0x99, 0x29, 0x5c, 0x75, 0x6c, 0x81, 0x7b, 0x81 } },
+ {"2.16.840.1.113733.1.7.48.1", ""},
+ },
+ // thawte Primary Root CA - G2
+ { { { 0xaa, 0xdb, 0xbc, 0x22, 0x23, 0x8f, 0xc4, 0x01, 0xa1, 0x27,
+ 0xbb, 0x38, 0xdd, 0xf4, 0x1d, 0xdb, 0x08, 0x9e, 0xf0, 0x12 } },
+ {"2.16.840.1.113733.1.7.48.1", ""},
+ },
+ // thawte Primary Root CA - G3
+ { { { 0xf1, 0x8b, 0x53, 0x8d, 0x1b, 0xe9, 0x03, 0xb6, 0xa6, 0xf0,
+ 0x56, 0x43, 0x5b, 0x17, 0x15, 0x89, 0xca, 0xf3, 0x6b, 0xf2 } },
+ {"2.16.840.1.113733.1.7.48.1", ""},
+ },
+ // TWCA Root Certification Authority
+ // https://evssldemo.twca.com.tw/index.html
+ { { { 0xcf, 0x9e, 0x87, 0x6d, 0xd3, 0xeb, 0xfc, 0x42, 0x26, 0x97,
+ 0xa3, 0xb5, 0xa3, 0x7a, 0xa0, 0x76, 0xa9, 0x06, 0x23, 0x48 } },
+ {"1.3.6.1.4.1.40869.1.1.22.3", ""},
+ },
+ // T-TeleSec GlobalRoot Class 3
+ // http://www.telesec.de/ / https://root-class3.test.telesec.de/
+ { { { 0x55, 0xa6, 0x72, 0x3e, 0xcb, 0xf2, 0xec, 0xcd, 0xc3, 0x23,
+ 0x74, 0x70, 0x19, 0x9d, 0x2a, 0xbe, 0x11, 0xe3, 0x81, 0xd1 } },
+ {"1.3.6.1.4.1.7879.13.24.1", "" },
+ },
+ // UTN - DATACorp SGC
+ { { { 0x58, 0x11, 0x9f, 0x0e, 0x12, 0x82, 0x87, 0xea, 0x50, 0xfd,
+ 0xd9, 0x87, 0x45, 0x6f, 0x4f, 0x78, 0xdc, 0xfa, 0xd6, 0xd4 } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
+ // UTN-USERFirst-Hardware
+ { { { 0x04, 0x83, 0xed, 0x33, 0x99, 0xac, 0x36, 0x08, 0x05, 0x87,
+ 0x22, 0xed, 0xbc, 0x5e, 0x46, 0x00, 0xe3, 0xbe, 0xf9, 0xd7 } },
+ {
+ "1.3.6.1.4.1.6449.1.2.1.5.1",
+ // This is the Network Solutions EV OID. However, this root
+ // cross-certifies NetSol and so we need it here too.
+ "1.3.6.1.4.1.782.1.2.1.8.1",
+ },
+ },
+ // ValiCert Class 2 Policy Validation Authority
+ { { { 0x31, 0x7a, 0x2a, 0xd0, 0x7f, 0x2b, 0x33, 0x5e, 0xf5, 0xa1,
+ 0xc3, 0x4e, 0x4b, 0x57, 0xe8, 0xb7, 0xd8, 0xf1, 0xfc, 0xa6 } },
+ {"2.16.840.1.114413.1.7.23.3", "2.16.840.1.114414.1.7.23.3"},
+ },
+ // VeriSign Class 3 Public Primary Certification Authority
+ // https://www.verisign.com/
+ { { { 0x74, 0x2c, 0x31, 0x92, 0xe6, 0x07, 0xe4, 0x24, 0xeb, 0x45,
+ 0x49, 0x54, 0x2b, 0xe1, 0xbb, 0xc5, 0x3e, 0x61, 0x74, 0xe2 } },
+ {"2.16.840.1.113733.1.7.23.6", ""},
+ },
+ // VeriSign Class 3 Public Primary Certification Authority - G4
+ { { { 0x22, 0xD5, 0xD8, 0xDF, 0x8F, 0x02, 0x31, 0xD1, 0x8D, 0xF7,
+ 0x9D, 0xB7, 0xCF, 0x8A, 0x2D, 0x64, 0xC9, 0x3F, 0x6C, 0x3A } },
+ {"2.16.840.1.113733.1.7.23.6", ""},
+ },
+ // VeriSign Class 3 Public Primary Certification Authority - G5
+ // https://www.verisign.com/
+ { { { 0x4e, 0xb6, 0xd5, 0x78, 0x49, 0x9b, 0x1c, 0xcf, 0x5f, 0x58,
+ 0x1e, 0xad, 0x56, 0xbe, 0x3d, 0x9b, 0x67, 0x44, 0xa5, 0xe5 } },
+ {"2.16.840.1.113733.1.7.23.6", ""},
+ },
+ // VeriSign Universal Root Certification Authority
+ { { { 0x36, 0x79, 0xca, 0x35, 0x66, 0x87, 0x72, 0x30, 0x4d, 0x30,
+ 0xa5, 0xfb, 0x87, 0x3b, 0x0f, 0xa7, 0x7b, 0xb7, 0x0d, 0x54 } },
+ {"2.16.840.1.113733.1.7.23.6", ""},
+ },
+ // Wells Fargo WellsSecure Public Root Certificate Authority
+ // https://nerys.wellsfargo.com/test.html
+ { { { 0xe7, 0xb4, 0xf6, 0x9d, 0x61, 0xec, 0x90, 0x69, 0xdb, 0x7e,
+ 0x90, 0xa7, 0x40, 0x1a, 0x3c, 0xf4, 0x7d, 0x4f, 0xe8, 0xee } },
+ {"2.16.840.1.114171.500.9", ""},
+ },
+ // XRamp Global Certification Authority
+ { { { 0xb8, 0x01, 0x86, 0xd1, 0xeb, 0x9c, 0x86, 0xa5, 0x41, 0x04,
+ 0xcf, 0x30, 0x54, 0xf3, 0x4c, 0x52, 0xb7, 0xe5, 0x58, 0xc6 } },
+ {"2.16.840.1.114404.1.1.2.4.1", ""},
+ }
+};
+
+static base::LazyInstance<EVRootCAMetadata>::Leaky
+ g_ev_root_ca_metadata = LAZY_INSTANCE_INITIALIZER;
+
+// static
+EVRootCAMetadata* EVRootCAMetadata::GetInstance() {
+ return g_ev_root_ca_metadata.Pointer();
+}
+
+#if defined(USE_NSS) || defined(OS_IOS)
+bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
+ return policy_oids_.find(policy_oid) != policy_oids_.end();
+}
+
+bool EVRootCAMetadata::HasEVPolicyOID(
+ const SHA1HashValue& fingerprint,
+ PolicyOID policy_oid) const {
+ PolicyOIDMap::const_iterator iter = ev_policy_.find(fingerprint);
+ if (iter == ev_policy_.end())
+ return false;
+ for (std::vector<PolicyOID>::const_iterator
+ j = iter->second.begin(); j != iter->second.end(); ++j) {
+ if (*j == policy_oid)
+ return true;
+ }
+ return false;
+}
+
+bool EVRootCAMetadata::AddEVCA(const SHA1HashValue& fingerprint,
+ const char* policy) {
+ if (ev_policy_.find(fingerprint) != ev_policy_.end())
+ return false;
+
+ PolicyOID oid;
+ if (!RegisterOID(policy, &oid))
+ return false;
+
+ ev_policy_[fingerprint].push_back(oid);
+ policy_oids_.insert(oid);
+
+ return true;
+}
+
+bool EVRootCAMetadata::RemoveEVCA(const SHA1HashValue& fingerprint) {
+ PolicyOIDMap::iterator it = ev_policy_.find(fingerprint);
+ if (it == ev_policy_.end())
+ return false;
+ PolicyOID oid = it->second[0];
+ ev_policy_.erase(it);
+ policy_oids_.erase(oid);
+ return true;
+}
+
+// static
+bool EVRootCAMetadata::RegisterOID(const char* policy,
+ PolicyOID* out) {
+ PRUint8 buf[64];
+ SECItem oid_item;
+ oid_item.data = buf;
+ oid_item.len = sizeof(buf);
+ SECStatus status = SEC_StringToOID(NULL, &oid_item, policy, 0);
+ if (status != SECSuccess)
+ return false;
+
+ // Register the OID.
+ SECOidData od;
+ od.oid.len = oid_item.len;
+ od.oid.data = oid_item.data;
+ od.offset = SEC_OID_UNKNOWN;
+ od.desc = policy;
+ od.mechanism = CKM_INVALID_MECHANISM;
+ od.supportedExtension = INVALID_CERT_EXTENSION;
+ *out = SECOID_AddEntry(&od);
+ return *out != SEC_OID_UNKNOWN;
+}
+
+#elif defined(OS_WIN)
+
+bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
+ for (size_t i = 0; i < arraysize(ev_root_ca_metadata); i++) {
+ for (size_t j = 0; j < arraysize(ev_root_ca_metadata[i].policy_oids); j++) {
+ if (ev_root_ca_metadata[i].policy_oids[j][0] == '\0')
+ break;
+ if (strcmp(policy_oid, ev_root_ca_metadata[i].policy_oids[j]) == 0)
+ return true;
+ }
+ }
+
+ for (ExtraEVCAMap::const_iterator i = extra_cas_.begin();
+ i != extra_cas_.end(); i++) {
+ if (i->second == policy_oid)
+ return true;
+ }
+
+ return false;
+}
+
+bool EVRootCAMetadata::HasEVPolicyOID(const SHA1HashValue& fingerprint,
+ PolicyOID policy_oid) const {
+ for (size_t i = 0; i < arraysize(ev_root_ca_metadata); i++) {
+ if (!fingerprint.Equals(ev_root_ca_metadata[i].fingerprint))
+ continue;
+ for (size_t j = 0; j < arraysize(ev_root_ca_metadata[i].policy_oids); j++) {
+ if (ev_root_ca_metadata[i].policy_oids[j][0] == '\0')
+ break;
+ if (strcmp(policy_oid, ev_root_ca_metadata[i].policy_oids[j]) == 0)
+ return true;
+ }
+ return false;
+ }
+
+ ExtraEVCAMap::const_iterator it = extra_cas_.find(fingerprint);
+ return it != extra_cas_.end() && it->second == policy_oid;
+}
+
+bool EVRootCAMetadata::AddEVCA(const SHA1HashValue& fingerprint,
+ const char* policy) {
+ for (size_t i = 0; i < arraysize(ev_root_ca_metadata); i++) {
+ if (fingerprint.Equals(ev_root_ca_metadata[i].fingerprint))
+ return false;
+ }
+
+ if (extra_cas_.find(fingerprint) != extra_cas_.end())
+ return false;
+
+ extra_cas_[fingerprint] = policy;
+ return true;
+}
+
+bool EVRootCAMetadata::RemoveEVCA(const SHA1HashValue& fingerprint) {
+ ExtraEVCAMap::iterator it = extra_cas_.find(fingerprint);
+ if (it == extra_cas_.end())
+ return false;
+ extra_cas_.erase(it);
+ return true;
+}
+
+#else
+
+// These are just stub functions for platforms where we don't use this EV
+// metadata.
+
+bool EVRootCAMetadata::AddEVCA(const SHA1HashValue& fingerprint,
+ const char* policy) {
+ return true;
+}
+
+bool EVRootCAMetadata::RemoveEVCA(const SHA1HashValue& fingerprint) {
+ return true;
+}
+
+#endif
+
+EVRootCAMetadata::EVRootCAMetadata() {
+ // Constructs the object from the raw metadata in ev_root_ca_metadata.
+#if defined(USE_NSS) || defined(OS_IOS)
+ crypto::EnsureNSSInit();
+
+ for (size_t i = 0; i < arraysize(ev_root_ca_metadata); i++) {
+ const EVMetadata& metadata = ev_root_ca_metadata[i];
+ for (size_t j = 0; j < arraysize(metadata.policy_oids); j++) {
+ if (metadata.policy_oids[j][0] == '\0')
+ break;
+ const char* policy_oid = metadata.policy_oids[j];
+
+ PolicyOID policy;
+ if (!RegisterOID(policy_oid, &policy)) {
+ LOG(ERROR) << "Failed to register OID: " << policy_oid;
+ continue;
+ }
+
+ ev_policy_[metadata.fingerprint].push_back(policy);
+ policy_oids_.insert(policy);
+ }
+ }
+#endif
+}
+
+EVRootCAMetadata::~EVRootCAMetadata() { }
+
+} // namespace net
diff --git a/chromium/net/cert/ev_root_ca_metadata.h b/chromium/net/cert/ev_root_ca_metadata.h
new file mode 100644
index 00000000000..aad78484cd1
--- /dev/null
+++ b/chromium/net/cert/ev_root_ca_metadata.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_EV_ROOT_CA_METADATA_H_
+#define NET_CERT_EV_ROOT_CA_METADATA_H_
+
+#include "build/build_config.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include <secoidt.h>
+#endif
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "net/base/net_export.h"
+#include "net/cert/x509_certificate.h"
+
+namespace base {
+template <typename T>
+struct DefaultLazyInstanceTraits;
+} // namespace base
+
+namespace net {
+
+// A singleton. This class stores the meta data of the root CAs that issue
+// extended-validation (EV) certificates.
+class NET_EXPORT_PRIVATE EVRootCAMetadata {
+ public:
+#if defined(USE_NSS) || defined(OS_IOS)
+ typedef SECOidTag PolicyOID;
+#elif defined(OS_WIN)
+ typedef const char* PolicyOID;
+#endif
+
+ static EVRootCAMetadata* GetInstance();
+
+#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_IOS)
+ // Returns true if policy_oid is an EV policy OID of some root CA.
+ bool IsEVPolicyOID(PolicyOID policy_oid) const;
+
+ // Returns true if the root CA with the given certificate fingerprint has
+ // the EV policy OID policy_oid.
+ bool HasEVPolicyOID(const SHA1HashValue& fingerprint,
+ PolicyOID policy_oid) const;
+#endif
+
+ // AddEVCA adds an EV CA to the list of known EV CAs with the given policy.
+ // |policy| is expressed as a string of dotted numbers. It returns true on
+ // success.
+ bool AddEVCA(const SHA1HashValue& fingerprint, const char* policy);
+
+ // RemoveEVCA removes an EV CA that was previously added by AddEVCA. It
+ // returns true on success.
+ bool RemoveEVCA(const SHA1HashValue& fingerprint);
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<EVRootCAMetadata>;
+
+ EVRootCAMetadata();
+ ~EVRootCAMetadata();
+
+#if defined(USE_NSS) || defined(OS_IOS)
+ typedef std::map<SHA1HashValue, std::vector<PolicyOID>,
+ SHA1HashValueLessThan> PolicyOIDMap;
+
+ // RegisterOID registers |policy|, a policy OID in dotted string form, and
+ // writes the memoized form to |*out|. It returns true on success.
+ static bool RegisterOID(const char* policy, PolicyOID* out);
+
+ PolicyOIDMap ev_policy_;
+ std::set<PolicyOID> policy_oids_;
+#elif defined(OS_WIN)
+ typedef std::map<SHA1HashValue, std::string,
+ SHA1HashValueLessThan> ExtraEVCAMap;
+
+ // extra_cas_ contains any EV CA metadata that was added at runtime.
+ ExtraEVCAMap extra_cas_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(EVRootCAMetadata);
+};
+
+} // namespace net
+
+#endif // NET_CERT_EV_ROOT_CA_METADATA_H_
diff --git a/chromium/net/cert/ev_root_ca_metadata_unittest.cc b/chromium/net/cert/ev_root_ca_metadata_unittest.cc
new file mode 100644
index 00000000000..2c845dbfcff
--- /dev/null
+++ b/chromium/net/cert/ev_root_ca_metadata_unittest.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/ev_root_ca_metadata.h"
+
+#include "net/cert/x509_cert_types.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_NSS)
+#include "crypto/scoped_nss_types.h"
+#endif
+
+namespace net {
+
+namespace {
+
+static const char kVerisignPolicy[] = "2.16.840.1.113733.1.7.23.6";
+static const char kThawtePolicy[] = "2.16.840.1.113733.1.7.48.1";
+static const char kFakePolicy[] = "2.16.840.1.42";
+static const SHA1HashValue kVerisignFingerprint =
+ { { 0x74, 0x2c, 0x31, 0x92, 0xe6, 0x07, 0xe4, 0x24, 0xeb, 0x45,
+ 0x49, 0x54, 0x2b, 0xe1, 0xbb, 0xc5, 0x3e, 0x61, 0x74, 0xe2 } };
+static const SHA1HashValue kFakeFingerprint =
+ { { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 } };
+
+#if defined(USE_NSS) || defined(OS_WIN)
+class EVOidData {
+ public:
+ EVOidData();
+ bool Init();
+
+ EVRootCAMetadata::PolicyOID verisign_policy;
+ EVRootCAMetadata::PolicyOID thawte_policy;
+ EVRootCAMetadata::PolicyOID fake_policy;
+};
+
+#endif // defined(USE_NSS) || defined(OS_WIN)
+
+#if defined(USE_NSS)
+
+SECOidTag RegisterOID(PLArenaPool* arena, const char* oid_string) {
+ SECOidData oid_data;
+ memset(&oid_data, 0, sizeof(oid_data));
+ oid_data.offset = SEC_OID_UNKNOWN;
+ oid_data.desc = oid_string;
+ oid_data.mechanism = CKM_INVALID_MECHANISM;
+ oid_data.supportedExtension = INVALID_CERT_EXTENSION;
+
+ SECStatus rv = SEC_StringToOID(arena, &oid_data.oid, oid_string, 0);
+ if (rv != SECSuccess)
+ return SEC_OID_UNKNOWN;
+
+ return SECOID_AddEntry(&oid_data);
+}
+
+EVOidData::EVOidData()
+ : verisign_policy(SEC_OID_UNKNOWN),
+ thawte_policy(SEC_OID_UNKNOWN),
+ fake_policy(SEC_OID_UNKNOWN) {
+}
+
+bool EVOidData::Init() {
+ crypto::ScopedPLArenaPool pool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!pool.get())
+ return false;
+
+ verisign_policy = RegisterOID(pool.get(), kVerisignPolicy);
+ thawte_policy = RegisterOID(pool.get(), kThawtePolicy);
+ fake_policy = RegisterOID(pool.get(), kFakePolicy);
+
+ return verisign_policy != SEC_OID_UNKNOWN &&
+ thawte_policy != SEC_OID_UNKNOWN &&
+ fake_policy != SEC_OID_UNKNOWN;
+}
+
+#elif defined(OS_WIN)
+
+EVOidData::EVOidData()
+ : verisign_policy(kVerisignPolicy),
+ thawte_policy(kThawtePolicy),
+ fake_policy(kFakePolicy) {
+}
+
+bool EVOidData::Init() {
+ return true;
+}
+
+#endif
+
+#if defined(USE_NSS) || defined(OS_WIN)
+
+class EVRootCAMetadataTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(ev_oid_data.Init());
+ }
+
+ EVOidData ev_oid_data;
+};
+
+TEST_F(EVRootCAMetadataTest, Basic) {
+ EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance());
+
+ EXPECT_TRUE(ev_metadata->IsEVPolicyOID(ev_oid_data.verisign_policy));
+ EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
+ EXPECT_TRUE(ev_metadata->HasEVPolicyOID(kVerisignFingerprint,
+ ev_oid_data.verisign_policy));
+ EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint,
+ ev_oid_data.verisign_policy));
+ EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kVerisignFingerprint,
+ ev_oid_data.fake_policy));
+ EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kVerisignFingerprint,
+ ev_oid_data.thawte_policy));
+}
+
+TEST_F(EVRootCAMetadataTest, AddRemove) {
+ EVRootCAMetadata* ev_metadata(EVRootCAMetadata::GetInstance());
+
+ EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
+ EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint,
+ ev_oid_data.fake_policy));
+
+ {
+ ScopedTestEVPolicy test_ev_policy(ev_metadata, kFakeFingerprint,
+ kFakePolicy);
+
+ EXPECT_TRUE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
+ EXPECT_TRUE(ev_metadata->HasEVPolicyOID(kFakeFingerprint,
+ ev_oid_data.fake_policy));
+ }
+
+ EXPECT_FALSE(ev_metadata->IsEVPolicyOID(ev_oid_data.fake_policy));
+ EXPECT_FALSE(ev_metadata->HasEVPolicyOID(kFakeFingerprint,
+ ev_oid_data.fake_policy));
+}
+
+#endif // defined(USE_NSS) || defined(OS_WIN)
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/jwk_serializer.h b/chromium/net/cert/jwk_serializer.h
new file mode 100644
index 00000000000..7a12a366246
--- /dev/null
+++ b/chromium/net/cert/jwk_serializer.h
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_JWK_SERIALIZER_H_
+#define NET_CERT_JWK_SERIALIZER_H_
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace net {
+
+namespace JwkSerializer {
+
+// Converts a subject public key info from DER to JWK.
+// See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-13 for
+// the output format.
+NET_EXPORT_PRIVATE bool ConvertSpkiFromDerToJwk(
+ const base::StringPiece& spki_der,
+ base::DictionaryValue* public_key_jwk);
+
+} // namespace JwkSerializer
+
+} // namespace net
+
+#endif // NET_CERT_JWK_SERIALIZER_H_
diff --git a/chromium/net/cert/jwk_serializer_nss.cc b/chromium/net/cert/jwk_serializer_nss.cc
new file mode 100644
index 00000000000..d8445805f8e
--- /dev/null
+++ b/chromium/net/cert/jwk_serializer_nss.cc
@@ -0,0 +1,118 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/jwk_serializer.h"
+
+#include <cert.h>
+#include <keyhi.h>
+#include <nss.h>
+
+#include "base/base64.h"
+#include "base/values.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace net {
+
+namespace JwkSerializer {
+
+namespace {
+
+bool ConvertEcPrime256v1PublicKeyInfoToJwk(
+ CERTSubjectPublicKeyInfo* spki,
+ base::DictionaryValue* public_key_jwk) {
+ static const int kUncompressedEncodingType = 4;
+ static const int kPrime256v1PublicKeyLength = 64;
+ // The public key value is encoded as 0x04 + 64 bytes of public key.
+ // NSS gives the length as the bit length.
+ if (spki->subjectPublicKey.len != (kPrime256v1PublicKeyLength + 1) * 8 ||
+ spki->subjectPublicKey.data[0] != kUncompressedEncodingType)
+ return false;
+
+ public_key_jwk->SetString("alg", "EC");
+ public_key_jwk->SetString("crv", "P-256");
+
+ base::StringPiece x(
+ reinterpret_cast<char*>(spki->subjectPublicKey.data + 1),
+ kPrime256v1PublicKeyLength / 2);
+ std::string x_b64;
+ base::Base64Encode(x, &x_b64);
+ public_key_jwk->SetString("x", x_b64);
+
+ base::StringPiece y(
+ reinterpret_cast<char*>(spki->subjectPublicKey.data + 1 +
+ kPrime256v1PublicKeyLength / 2),
+ kPrime256v1PublicKeyLength / 2);
+ std::string y_b64;
+ base::Base64Encode(y, &y_b64);
+ public_key_jwk->SetString("y", y_b64);
+ return true;
+}
+
+bool ConvertEcPublicKeyInfoToJwk(
+ CERTSubjectPublicKeyInfo* spki,
+ base::DictionaryValue* public_key_jwk) {
+ // 1.2.840.10045.3.1.7
+ // (iso.member-body.us.ansi-x9-62.ellipticCurve.primeCurve.prime256v1)
+ // (This includes the DER-encoded type (OID) and length: parameters can be
+ // anything, so the DER type isn't implied, and NSS includes it.)
+ static const unsigned char kPrime256v1[] = {
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
+ };
+ if (spki->algorithm.parameters.len == sizeof(kPrime256v1) &&
+ !memcmp(spki->algorithm.parameters.data, kPrime256v1,
+ sizeof(kPrime256v1))) {
+ return ConvertEcPrime256v1PublicKeyInfoToJwk(spki, public_key_jwk);
+ }
+ // TODO(juanlang): other curves
+ return false;
+}
+
+typedef scoped_ptr_malloc<
+ CERTSubjectPublicKeyInfo,
+ crypto::NSSDestroyer<CERTSubjectPublicKeyInfo,
+ SECKEY_DestroySubjectPublicKeyInfo> >
+ ScopedCERTSubjectPublicKeyInfo;
+
+} // namespace
+
+bool ConvertSpkiFromDerToJwk(
+ const base::StringPiece& spki_der,
+ base::DictionaryValue* public_key_jwk) {
+ public_key_jwk->Clear();
+
+ crypto::EnsureNSSInit();
+
+ if (!NSS_IsInitialized())
+ return false;
+
+ SECItem sec_item;
+ sec_item.data = const_cast<unsigned char*>(
+ reinterpret_cast<const unsigned char*>(spki_der.data()));
+ sec_item.len = spki_der.size();
+ ScopedCERTSubjectPublicKeyInfo spki(
+ SECKEY_DecodeDERSubjectPublicKeyInfo(&sec_item));
+ if (!spki)
+ return false;
+
+ // 1.2.840.10045.2
+ // (iso.member-body.us.ansi-x9-62.id-ecPublicKey)
+ // (This omits the ASN.1 encoding of the type (OID) and length: the fact that
+ // this is an OID is already clear, and NSS omits it here.)
+ static const unsigned char kIdEcPublicKey[] = {
+ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01
+ };
+ bool rv = false;
+ if (spki->algorithm.algorithm.len == sizeof(kIdEcPublicKey) &&
+ !memcmp(spki->algorithm.algorithm.data, kIdEcPublicKey,
+ sizeof(kIdEcPublicKey))) {
+ rv = ConvertEcPublicKeyInfoToJwk(spki.get(), public_key_jwk);
+ }
+ // TODO(juanlang): other algorithms
+ return rv;
+}
+
+} // namespace JwkSerializer
+
+} // namespace net
diff --git a/chromium/net/cert/jwk_serializer_openssl.cc b/chromium/net/cert/jwk_serializer_openssl.cc
new file mode 100644
index 00000000000..b11e9696d6f
--- /dev/null
+++ b/chromium/net/cert/jwk_serializer_openssl.cc
@@ -0,0 +1,23 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/jwk_serializer.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+namespace JwkSerializer {
+
+bool ConvertSpkiFromDerToJwk(
+ const base::StringPiece& spki_der,
+ base::DictionaryValue* public_key_jwk) {
+ // TODO(juanlang): implement
+ NOTIMPLEMENTED();
+ return false;
+}
+
+} // namespace JwkSerializer
+
+} // namespace net
diff --git a/chromium/net/cert/jwk_serializer_unittest.cc b/chromium/net/cert/jwk_serializer_unittest.cc
new file mode 100644
index 00000000000..8059e73a3a6
--- /dev/null
+++ b/chromium/net/cert/jwk_serializer_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/jwk_serializer.h"
+
+#include "base/base64.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+// This is the ASN.1 prefix for a P-256 public key. Specifically it's:
+// SEQUENCE
+// SEQUENCE
+// OID id-ecPublicKey
+// OID prime256v1
+// BIT STRING, length 66, 0 trailing bits: 0x04
+//
+// The 0x04 in the BIT STRING is the prefix for an uncompressed, X9.62
+// public key. Following that are the two field elements as 32-byte,
+// big-endian numbers, as required by the Channel ID.
+static const unsigned char kP256SpkiPrefix[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00, 0x04
+};
+static const unsigned int kEcCoordinateSize = 32U;
+
+// This is a valid P-256 public key.
+static const unsigned char kSpkiEc[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00, 0x04,
+ 0x29, 0x5d, 0x6e, 0xfe, 0x33, 0x77, 0x26, 0xea,
+ 0x5b, 0xa4, 0xe6, 0x1b, 0x34, 0x6e, 0x7b, 0xa0,
+ 0xa3, 0x8f, 0x33, 0x49, 0xa0, 0x9c, 0xae, 0x98,
+ 0xbd, 0x46, 0x0d, 0xf6, 0xd4, 0x5a, 0xdc, 0x8a,
+ 0x1f, 0x8a, 0xb2, 0x20, 0x51, 0xb7, 0xd2, 0x87,
+ 0x0d, 0x53, 0x7e, 0x5d, 0x94, 0xa3, 0xe0, 0x34,
+ 0x16, 0xa1, 0xcc, 0x10, 0x48, 0xcd, 0x70, 0x9c,
+ 0x05, 0xd3, 0xd2, 0xca, 0xdf, 0x44, 0x2f, 0xf4
+};
+
+// This is a P-256 public key with 0 X and Y values.
+static const unsigned char kSpkiEcWithZeroXY[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+ 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#if !defined(USE_OPENSSL)
+
+TEST(JwkSerializerNSSTest, ConvertSpkiFromDerToJwkEc) {
+ base::StringPiece spki;
+ base::DictionaryValue public_key_jwk;
+
+ EXPECT_FALSE(JwkSerializer::ConvertSpkiFromDerToJwk(spki, &public_key_jwk));
+ EXPECT_TRUE(public_key_jwk.empty());
+
+ // Test the result of a "normal" point on this curve.
+ spki.set(reinterpret_cast<const char*>(kSpkiEc), sizeof(kSpkiEc));
+ EXPECT_TRUE(JwkSerializer::ConvertSpkiFromDerToJwk(spki, &public_key_jwk));
+
+ std::string string_value;
+ EXPECT_TRUE(public_key_jwk.GetString("alg", &string_value));
+ EXPECT_STREQ("EC", string_value.c_str());
+ EXPECT_TRUE(public_key_jwk.GetString("crv", &string_value));
+ EXPECT_STREQ("P-256", string_value.c_str());
+
+ EXPECT_TRUE(public_key_jwk.GetString("x", &string_value));
+ std::string decoded_coordinate;
+ EXPECT_TRUE(base::Base64Decode(string_value, &decoded_coordinate));
+ EXPECT_EQ(kEcCoordinateSize, decoded_coordinate.size());
+ EXPECT_EQ(0,
+ memcmp(decoded_coordinate.data(),
+ kSpkiEc + sizeof(kP256SpkiPrefix),
+ kEcCoordinateSize));
+
+ EXPECT_TRUE(public_key_jwk.GetString("y", &string_value));
+ EXPECT_TRUE(base::Base64Decode(string_value, &decoded_coordinate));
+ EXPECT_EQ(kEcCoordinateSize, decoded_coordinate.size());
+ EXPECT_EQ(0,
+ memcmp(decoded_coordinate.data(),
+ kSpkiEc + sizeof(kP256SpkiPrefix) + kEcCoordinateSize,
+ kEcCoordinateSize));
+
+ // Test the result of a corner case: leading 0s in the x, y coordinates are
+ // not trimmed, but the point is fixed-length encoded.
+ spki.set(reinterpret_cast<const char*>(kSpkiEcWithZeroXY),
+ sizeof(kSpkiEcWithZeroXY));
+ EXPECT_TRUE(JwkSerializer::ConvertSpkiFromDerToJwk(spki, &public_key_jwk));
+
+ EXPECT_TRUE(public_key_jwk.GetString("alg", &string_value));
+ EXPECT_STREQ("EC", string_value.c_str());
+ EXPECT_TRUE(public_key_jwk.GetString("crv", &string_value));
+ EXPECT_STREQ("P-256", string_value.c_str());
+
+ EXPECT_TRUE(public_key_jwk.GetString("x", &string_value));
+ EXPECT_TRUE(base::Base64Decode(string_value, &decoded_coordinate));
+ EXPECT_EQ(kEcCoordinateSize, decoded_coordinate.size());
+ EXPECT_EQ(0,
+ memcmp(decoded_coordinate.data(),
+ kSpkiEcWithZeroXY + sizeof(kP256SpkiPrefix),
+ kEcCoordinateSize));
+
+ EXPECT_TRUE(public_key_jwk.GetString("y", &string_value));
+ EXPECT_TRUE(base::Base64Decode(string_value, &decoded_coordinate));
+ EXPECT_EQ(kEcCoordinateSize, decoded_coordinate.size());
+ EXPECT_EQ(0,
+ memcmp(decoded_coordinate.data(),
+ kSpkiEcWithZeroXY + sizeof(kP256SpkiPrefix) + kEcCoordinateSize,
+ kEcCoordinateSize));
+}
+
+#else
+
+// For OpenSSL, JwkSerializer::ConvertSpkiFromDerToJwk() is not yet implemented
+// and should return false. This unit test ensures that a stub implementation
+// is present.
+TEST(JwkSerializerOpenSSLTest, ConvertSpkiFromDerToJwkNotImplemented) {
+ base::StringPiece spki;
+ base::DictionaryValue public_key_jwk;
+
+ // The empty SPKI is trivially non-convertible...
+ EXPECT_FALSE(JwkSerializer::ConvertSpkiFromDerToJwk(spki, &public_key_jwk));
+ EXPECT_TRUE(public_key_jwk.empty());
+ // but even a valid SPKI is non-convertible via the stub OpenSSL
+ // implementation.
+ spki.set(reinterpret_cast<const char*>(kSpkiEc), sizeof(kSpkiEc));
+ EXPECT_FALSE(JwkSerializer::ConvertSpkiFromDerToJwk(spki, &public_key_jwk));
+ EXPECT_TRUE(public_key_jwk.empty());
+}
+
+#endif // !defined(USE_OPENSSL)
+
+} // namespace net
diff --git a/chromium/net/cert/mock_cert_verifier.cc b/chromium/net/cert/mock_cert_verifier.cc
new file mode 100644
index 00000000000..a30e3d54c86
--- /dev/null
+++ b/chromium/net/cert/mock_cert_verifier.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/mock_cert_verifier.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+MockCertVerifier::MockCertVerifier() : default_result_(ERR_CERT_INVALID) {}
+
+MockCertVerifier::~MockCertVerifier() {}
+
+int MockCertVerifier::Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) {
+ RuleList::const_iterator it;
+ for (it = rules_.begin(); it != rules_.end(); ++it) {
+ // Check just the server cert. Intermediates will be ignored.
+ if (!it->cert->Equals(cert))
+ continue;
+ if (!MatchPattern(hostname, it->hostname))
+ continue;
+ *verify_result = it->result;
+ return it->rv;
+ }
+
+ // Fall through to the default.
+ verify_result->verified_cert = cert;
+ verify_result->cert_status = MapNetErrorToCertStatus(default_result_);
+ return default_result_;
+}
+
+void MockCertVerifier::CancelRequest(RequestHandle req) {
+ NOTIMPLEMENTED();
+}
+
+void MockCertVerifier::AddResultForCert(X509Certificate* cert,
+ const CertVerifyResult& verify_result,
+ int rv) {
+ AddResultForCertAndHost(cert, "*", verify_result, rv);
+}
+
+void MockCertVerifier::AddResultForCertAndHost(
+ X509Certificate* cert,
+ const std::string& host_pattern,
+ const CertVerifyResult& verify_result,
+ int rv) {
+ Rule rule(cert, host_pattern, verify_result, rv);
+ rules_.push_back(rule);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/mock_cert_verifier.h b/chromium/net/cert/mock_cert_verifier.h
new file mode 100644
index 00000000000..704c66b2b80
--- /dev/null
+++ b/chromium/net/cert/mock_cert_verifier.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_MOCK_CERT_VERIFIER_H_
+#define NET_CERT_MOCK_CERT_VERIFIER_H_
+
+#include <list>
+
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+
+namespace net {
+
+class MockCertVerifier : public CertVerifier {
+ public:
+ // Creates a new MockCertVerifier. By default, any call to Verify() will
+ // result in the cert status being flagged as CERT_STATUS_INVALID and return
+ // an ERR_CERT_INVALID network error code. This behaviour can be overridden
+ // by calling set_default_result() to change the default return value for
+ // Verify() or by calling one of the AddResult*() methods to specifically
+ // handle a certificate or certificate and host.
+ MockCertVerifier();
+
+ virtual ~MockCertVerifier();
+
+ // CertVerifier implementation
+ virtual int Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual void CancelRequest(RequestHandle req) OVERRIDE;
+
+ // Sets the default return value for Verify() for certificates/hosts that do
+ // not have explicit results added via the AddResult*() methods.
+ void set_default_result(int default_result) {
+ default_result_ = default_result;
+ }
+
+ // Adds a rule that will cause any call to Verify() for |cert| to return rv,
+ // copying |verify_result| into the verified result.
+ // Note: Only the primary certificate of |cert| is checked. Any intermediate
+ // certificates will be ignored.
+ void AddResultForCert(X509Certificate* cert,
+ const CertVerifyResult& verify_result,
+ int rv);
+
+ // Same as AddResultForCert(), but further restricts it to only return for
+ // hostnames that match |host_pattern|.
+ void AddResultForCertAndHost(X509Certificate* cert,
+ const std::string& host_pattern,
+ const CertVerifyResult& verify_result,
+ int rv);
+
+ private:
+ struct Rule {
+ Rule(X509Certificate* cert,
+ const std::string& hostname,
+ const CertVerifyResult& result,
+ int rv)
+ : cert(cert),
+ hostname(hostname),
+ result(result),
+ rv(rv) {
+ DCHECK(cert);
+ DCHECK(result.verified_cert.get());
+ }
+
+ scoped_refptr<X509Certificate> cert;
+ std::string hostname;
+ CertVerifyResult result;
+ int rv;
+ };
+
+ typedef std::list<Rule> RuleList;
+
+ int default_result_;
+ RuleList rules_;
+};
+
+} // namespace net
+
+#endif // NET_CERT_MOCK_CERT_VERIFIER_H_
diff --git a/chromium/net/cert/multi_threaded_cert_verifier.cc b/chromium/net/cert/multi_threaded_cert_verifier.cc
new file mode 100644
index 00000000000..821cec1220b
--- /dev/null
+++ b/chromium/net/cert/multi_threaded_cert_verifier.cc
@@ -0,0 +1,566 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/multi_threaded_cert_verifier.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/worker_pool.h"
+#include "base/time/time.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/cert/cert_trust_anchor_provider.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_certificate_net_log_param.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include <private/pprthred.h> // PR_DetachThread
+#endif
+
+namespace net {
+
+////////////////////////////////////////////////////////////////////////////
+
+// Life of a request:
+//
+// MultiThreadedCertVerifier CertVerifierJob CertVerifierWorker Request
+// | (origin loop) (worker loop)
+// |
+// Verify()
+// |---->-------------------------------------<creates>
+// |
+// |---->-------------------<creates>
+// |
+// |---->-------------------------------------------------------<creates>
+// |
+// |---->---------------------------------------Start
+// | |
+// | PostTask
+// |
+// | <starts verifying>
+// |---->-------------------AddRequest |
+// |
+// |
+// |
+// Finish
+// |
+// PostTask
+//
+// |
+// DoReply
+// |----<-----------------------------------------|
+// HandleResult
+// |
+// |---->------------------HandleResult
+// |
+// |------>---------------------------Post
+//
+//
+//
+// On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously
+// without posting a task to a worker thread.
+
+namespace {
+
+// The default value of max_cache_entries_.
+const unsigned kMaxCacheEntries = 256;
+
+// The number of seconds for which we'll cache a cache entry.
+const unsigned kTTLSecs = 1800; // 30 minutes.
+
+} // namespace
+
+MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
+
+MultiThreadedCertVerifier::CachedResult::~CachedResult() {}
+
+MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
+ const base::Time& now)
+ : verification_time(now),
+ expiration_time(now) {
+}
+
+MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
+ const base::Time& now,
+ const base::Time& expiration)
+ : verification_time(now),
+ expiration_time(expiration) {
+}
+
+bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()(
+ const CacheValidityPeriod& now,
+ const CacheValidityPeriod& expiration) const {
+ // Ensure this functor is being used for expiration only, and not strict
+ // weak ordering/sorting. |now| should only ever contain a single
+ // base::Time.
+ // Note: DCHECK_EQ is not used due to operator<< overloading requirements.
+ DCHECK(now.verification_time == now.expiration_time);
+
+ // |now| contains only a single time (verification_time), while |expiration|
+ // contains the validity range - both when the certificate was verified and
+ // when the verification result should expire.
+ //
+ // If the user receives a "not yet valid" message, and adjusts their clock
+ // foward to the correct time, this will (typically) cause
+ // now.verification_time to advance past expiration.expiration_time, thus
+ // treating the cached result as an expired entry and re-verifying.
+ // If the user receives a "expired" message, and adjusts their clock
+ // backwards to the correct time, this will cause now.verification_time to
+ // be less than expiration_verification_time, thus treating the cached
+ // result as an expired entry and re-verifying.
+ // If the user receives either of those messages, and does not adjust their
+ // clock, then the result will be (typically) be cached until the expiration
+ // TTL.
+ //
+ // This algorithm is only problematic if the user consistently keeps
+ // adjusting their clock backwards in increments smaller than the expiration
+ // TTL, in which case, cached elements continue to be added. However,
+ // because the cache has a fixed upper bound, if no entries are expired, a
+ // 'random' entry will be, thus keeping the memory constraints bounded over
+ // time.
+ return now.verification_time >= expiration.verification_time &&
+ now.verification_time < expiration.expiration_time;
+};
+
+
+// Represents the output and result callback of a request.
+class CertVerifierRequest {
+ public:
+ CertVerifierRequest(const CompletionCallback& callback,
+ CertVerifyResult* verify_result,
+ const BoundNetLog& net_log)
+ : callback_(callback),
+ verify_result_(verify_result),
+ net_log_(net_log) {
+ net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
+ }
+
+ ~CertVerifierRequest() {
+ }
+
+ // Ensures that the result callback will never be made.
+ void Cancel() {
+ callback_.Reset();
+ verify_result_ = NULL;
+ net_log_.AddEvent(NetLog::TYPE_CANCELLED);
+ net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
+ }
+
+ // Copies the contents of |verify_result| to the caller's
+ // CertVerifyResult and calls the callback.
+ void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) {
+ if (!callback_.is_null()) {
+ net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
+ *verify_result_ = verify_result.result;
+ callback_.Run(verify_result.error);
+ }
+ delete this;
+ }
+
+ bool canceled() const { return callback_.is_null(); }
+
+ const BoundNetLog& net_log() const { return net_log_; }
+
+ private:
+ CompletionCallback callback_;
+ CertVerifyResult* verify_result_;
+ const BoundNetLog net_log_;
+};
+
+
+// CertVerifierWorker runs on a worker thread and takes care of the blocking
+// process of performing the certificate verification. Deletes itself
+// eventually if Start() succeeds.
+class CertVerifierWorker {
+ public:
+ CertVerifierWorker(CertVerifyProc* verify_proc,
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ MultiThreadedCertVerifier* cert_verifier)
+ : verify_proc_(verify_proc),
+ cert_(cert),
+ hostname_(hostname),
+ flags_(flags),
+ crl_set_(crl_set),
+ additional_trust_anchors_(additional_trust_anchors),
+ origin_loop_(base::MessageLoop::current()),
+ cert_verifier_(cert_verifier),
+ canceled_(false),
+ error_(ERR_FAILED) {
+ }
+
+ // Returns the certificate being verified. May only be called /before/
+ // Start() is called.
+ X509Certificate* certificate() const { return cert_.get(); }
+
+ bool Start() {
+ DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
+
+ return base::WorkerPool::PostTask(
+ FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
+ true /* task is slow */);
+ }
+
+ // Cancel is called from the origin loop when the MultiThreadedCertVerifier is
+ // getting deleted.
+ void Cancel() {
+ DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
+ base::AutoLock locked(lock_);
+ canceled_ = true;
+ }
+
+ private:
+ void Run() {
+ // Runs on a worker thread.
+ error_ = verify_proc_->Verify(cert_.get(),
+ hostname_,
+ flags_,
+ crl_set_.get(),
+ additional_trust_anchors_,
+ &verify_result_);
+#if defined(USE_NSS) || defined(OS_IOS)
+ // Detach the thread from NSPR.
+ // Calling NSS functions attaches the thread to NSPR, which stores
+ // the NSPR thread ID in thread-specific data.
+ // The threads in our thread pool terminate after we have called
+ // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
+ // segfaults on shutdown when the threads' thread-specific data
+ // destructors run.
+ PR_DetachThread();
+#endif
+ Finish();
+ }
+
+ // DoReply runs on the origin thread.
+ void DoReply() {
+ DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
+ {
+ // We lock here because the worker thread could still be in Finished,
+ // after the PostTask, but before unlocking |lock_|. If we do not lock in
+ // this case, we will end up deleting a locked Lock, which can lead to
+ // memory leaks or worse errors.
+ base::AutoLock locked(lock_);
+ if (!canceled_) {
+ cert_verifier_->HandleResult(cert_.get(),
+ hostname_,
+ flags_,
+ additional_trust_anchors_,
+ error_,
+ verify_result_);
+ }
+ }
+ delete this;
+ }
+
+ void Finish() {
+ // Runs on the worker thread.
+ // We assume that the origin loop outlives the MultiThreadedCertVerifier. If
+ // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If
+ // it does so before the Acquire, we'll delete ourselves and return. If it's
+ // trying to do so concurrently, then it'll block on the lock and we'll call
+ // PostTask while the MultiThreadedCertVerifier (and therefore the
+ // MessageLoop) is still alive.
+ // If it does so after this function, we assume that the MessageLoop will
+ // process pending tasks. In which case we'll notice the |canceled_| flag
+ // in DoReply.
+
+ bool canceled;
+ {
+ base::AutoLock locked(lock_);
+ canceled = canceled_;
+ if (!canceled) {
+ origin_loop_->PostTask(
+ FROM_HERE, base::Bind(
+ &CertVerifierWorker::DoReply, base::Unretained(this)));
+ }
+ }
+
+ if (canceled)
+ delete this;
+ }
+
+ scoped_refptr<CertVerifyProc> verify_proc_;
+ scoped_refptr<X509Certificate> cert_;
+ const std::string hostname_;
+ const int flags_;
+ scoped_refptr<CRLSet> crl_set_;
+ const CertificateList additional_trust_anchors_;
+ base::MessageLoop* const origin_loop_;
+ MultiThreadedCertVerifier* const cert_verifier_;
+
+ // lock_ protects canceled_.
+ base::Lock lock_;
+
+ // If canceled_ is true,
+ // * origin_loop_ cannot be accessed by the worker thread,
+ // * cert_verifier_ cannot be accessed by any thread.
+ bool canceled_;
+
+ int error_;
+ CertVerifyResult verify_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
+};
+
+// A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It
+// lives only on the CertVerifier's origin message loop.
+class CertVerifierJob {
+ public:
+ CertVerifierJob(CertVerifierWorker* worker,
+ const BoundNetLog& net_log)
+ : start_time_(base::TimeTicks::Now()),
+ worker_(worker),
+ net_log_(net_log) {
+ net_log_.BeginEvent(
+ NetLog::TYPE_CERT_VERIFIER_JOB,
+ base::Bind(&NetLogX509CertificateCallback,
+ base::Unretained(worker_->certificate())));
+ }
+
+ ~CertVerifierJob() {
+ if (worker_) {
+ net_log_.AddEvent(NetLog::TYPE_CANCELLED);
+ net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
+ worker_->Cancel();
+ DeleteAllCanceled();
+ }
+ }
+
+ void AddRequest(CertVerifierRequest* request) {
+ request->net_log().AddEvent(
+ NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
+ net_log_.source().ToEventParametersCallback());
+
+ requests_.push_back(request);
+ }
+
+ void HandleResult(
+ const MultiThreadedCertVerifier::CachedResult& verify_result) {
+ worker_ = NULL;
+ net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
+ UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
+ base::TimeTicks::Now() - start_time_,
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMinutes(10),
+ 100);
+ PostAll(verify_result);
+ }
+
+ private:
+ void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) {
+ std::vector<CertVerifierRequest*> requests;
+ requests_.swap(requests);
+
+ for (std::vector<CertVerifierRequest*>::iterator
+ i = requests.begin(); i != requests.end(); i++) {
+ (*i)->Post(verify_result);
+ // Post() causes the CertVerifierRequest to delete itself.
+ }
+ }
+
+ void DeleteAllCanceled() {
+ for (std::vector<CertVerifierRequest*>::iterator
+ i = requests_.begin(); i != requests_.end(); i++) {
+ if ((*i)->canceled()) {
+ delete *i;
+ } else {
+ LOG(DFATAL) << "CertVerifierRequest leaked!";
+ }
+ }
+ }
+
+ const base::TimeTicks start_time_;
+ std::vector<CertVerifierRequest*> requests_;
+ CertVerifierWorker* worker_;
+ const BoundNetLog net_log_;
+};
+
+MultiThreadedCertVerifier::MultiThreadedCertVerifier(
+ CertVerifyProc* verify_proc)
+ : cache_(kMaxCacheEntries),
+ requests_(0),
+ cache_hits_(0),
+ inflight_joins_(0),
+ verify_proc_(verify_proc),
+ trust_anchor_provider_(NULL) {
+ CertDatabase::GetInstance()->AddObserver(this);
+}
+
+MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
+ STLDeleteValues(&inflight_);
+ CertDatabase::GetInstance()->RemoveObserver(this);
+}
+
+void MultiThreadedCertVerifier::SetCertTrustAnchorProvider(
+ CertTrustAnchorProvider* trust_anchor_provider) {
+ DCHECK(CalledOnValidThread());
+ trust_anchor_provider_ = trust_anchor_provider;
+}
+
+int MultiThreadedCertVerifier::Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) {
+ DCHECK(CalledOnValidThread());
+
+ if (callback.is_null() || !verify_result || hostname.empty()) {
+ *out_req = NULL;
+ return ERR_INVALID_ARGUMENT;
+ }
+
+ requests_++;
+
+ const CertificateList empty_cert_list;
+ const CertificateList& additional_trust_anchors =
+ trust_anchor_provider_ ?
+ trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list;
+
+ const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
+ hostname, flags, additional_trust_anchors);
+ const CertVerifierCache::value_type* cached_entry =
+ cache_.Get(key, CacheValidityPeriod(base::Time::Now()));
+ if (cached_entry) {
+ ++cache_hits_;
+ *out_req = NULL;
+ *verify_result = cached_entry->result;
+ return cached_entry->error;
+ }
+
+ // No cache hit. See if an identical request is currently in flight.
+ CertVerifierJob* job;
+ std::map<RequestParams, CertVerifierJob*>::const_iterator j;
+ j = inflight_.find(key);
+ if (j != inflight_.end()) {
+ // An identical request is in flight already. We'll just attach our
+ // callback.
+ inflight_joins_++;
+ job = j->second;
+ } else {
+ // Need to make a new request.
+ CertVerifierWorker* worker =
+ new CertVerifierWorker(verify_proc_.get(),
+ cert,
+ hostname,
+ flags,
+ crl_set,
+ additional_trust_anchors,
+ this);
+ job = new CertVerifierJob(
+ worker,
+ BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB));
+ if (!worker->Start()) {
+ delete job;
+ delete worker;
+ *out_req = NULL;
+ // TODO(wtc): log to the NetLog.
+ LOG(ERROR) << "CertVerifierWorker couldn't be started.";
+ return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
+ }
+ inflight_.insert(std::make_pair(key, job));
+ }
+
+ CertVerifierRequest* request =
+ new CertVerifierRequest(callback, verify_result, net_log);
+ job->AddRequest(request);
+ *out_req = request;
+ return ERR_IO_PENDING;
+}
+
+void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) {
+ DCHECK(CalledOnValidThread());
+ CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
+ request->Cancel();
+}
+
+MultiThreadedCertVerifier::RequestParams::RequestParams(
+ const SHA1HashValue& cert_fingerprint_arg,
+ const SHA1HashValue& ca_fingerprint_arg,
+ const std::string& hostname_arg,
+ int flags_arg,
+ const CertificateList& additional_trust_anchors)
+ : hostname(hostname_arg),
+ flags(flags_arg) {
+ hash_values.reserve(2 + additional_trust_anchors.size());
+ hash_values.push_back(cert_fingerprint_arg);
+ hash_values.push_back(ca_fingerprint_arg);
+ for (size_t i = 0; i < additional_trust_anchors.size(); ++i)
+ hash_values.push_back(additional_trust_anchors[i]->fingerprint());
+}
+
+MultiThreadedCertVerifier::RequestParams::~RequestParams() {}
+
+bool MultiThreadedCertVerifier::RequestParams::operator<(
+ const RequestParams& other) const {
+ // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and
+ // |hostname| under assumption that integer comparisons are faster than
+ // memory and string comparisons.
+ if (flags != other.flags)
+ return flags < other.flags;
+ if (hostname != other.hostname)
+ return hostname < other.hostname;
+ return std::lexicographical_compare(
+ hash_values.begin(), hash_values.end(),
+ other.hash_values.begin(), other.hash_values.end(),
+ net::SHA1HashValueLessThan());
+}
+
+// HandleResult is called by CertVerifierWorker on the origin message loop.
+// It deletes CertVerifierJob.
+void MultiThreadedCertVerifier::HandleResult(
+ X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ const CertificateList& additional_trust_anchors,
+ int error,
+ const CertVerifyResult& verify_result) {
+ DCHECK(CalledOnValidThread());
+
+ const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
+ hostname, flags, additional_trust_anchors);
+
+ CachedResult cached_result;
+ cached_result.error = error;
+ cached_result.result = verify_result;
+ base::Time now = base::Time::Now();
+ cache_.Put(
+ key, cached_result, CacheValidityPeriod(now),
+ CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs)));
+
+ std::map<RequestParams, CertVerifierJob*>::iterator j;
+ j = inflight_.find(key);
+ if (j == inflight_.end()) {
+ NOTREACHED();
+ return;
+ }
+ CertVerifierJob* job = j->second;
+ inflight_.erase(j);
+
+ job->HandleResult(cached_result);
+ delete job;
+}
+
+void MultiThreadedCertVerifier::OnCertTrustChanged(
+ const X509Certificate* cert) {
+ DCHECK(CalledOnValidThread());
+
+ ClearCache();
+}
+
+} // namespace net
diff --git a/chromium/net/cert/multi_threaded_cert_verifier.h b/chromium/net/cert/multi_threaded_cert_verifier.h
new file mode 100644
index 00000000000..bc9cd4f6ce6
--- /dev/null
+++ b/chromium/net/cert/multi_threaded_cert_verifier.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_MULTI_THREADED_CERT_VERIFIER_H_
+#define NET_CERT_MULTI_THREADED_CERT_VERIFIER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/non_thread_safe.h"
+#include "net/base/completion_callback.h"
+#include "net/base/expiring_cache.h"
+#include "net/base/hash_value.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_database.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_cert_types.h"
+
+namespace net {
+
+class CertTrustAnchorProvider;
+class CertVerifierJob;
+class CertVerifierRequest;
+class CertVerifierWorker;
+class CertVerifyProc;
+
+// MultiThreadedCertVerifier is a CertVerifier implementation that runs
+// synchronous CertVerifier implementations on worker threads.
+class NET_EXPORT_PRIVATE MultiThreadedCertVerifier
+ : public CertVerifier,
+ NON_EXPORTED_BASE(public base::NonThreadSafe),
+ public CertDatabase::Observer {
+ public:
+ explicit MultiThreadedCertVerifier(CertVerifyProc* verify_proc);
+
+ // When the verifier is destroyed, all certificate verifications requests are
+ // canceled, and their completion callbacks will not be called.
+ virtual ~MultiThreadedCertVerifier();
+
+ // Configures a source of additional certificates that should be treated as
+ // trust anchors during verification, provided that the underlying
+ // CertVerifyProc supports additional trust beyond the default implementation.
+ // The CertTrustAnchorProvider will only be accessed on the same
+ // thread that Verify() is called on; that is, it will not be
+ // accessed from worker threads.
+ // It must outlive the MultiThreadedCertVerifier.
+ void SetCertTrustAnchorProvider(
+ CertTrustAnchorProvider* trust_anchor_provider);
+
+ // CertVerifier implementation
+ virtual int Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ CertVerifier::RequestHandle* out_req,
+ const BoundNetLog& net_log) OVERRIDE;
+
+ virtual void CancelRequest(CertVerifier::RequestHandle req) OVERRIDE;
+
+ private:
+ friend class CertVerifierWorker; // Calls HandleResult.
+ friend class CertVerifierRequest;
+ friend class CertVerifierJob;
+ friend class MultiThreadedCertVerifierTest;
+ FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, CacheHit);
+ FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, DifferentCACerts);
+ FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, InflightJoin);
+ FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest, CancelRequest);
+ FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest,
+ RequestParamsComparators);
+ FRIEND_TEST_ALL_PREFIXES(MultiThreadedCertVerifierTest,
+ CertTrustAnchorProvider);
+
+ // Input parameters of a certificate verification request.
+ struct NET_EXPORT_PRIVATE RequestParams {
+ RequestParams(const SHA1HashValue& cert_fingerprint_arg,
+ const SHA1HashValue& ca_fingerprint_arg,
+ const std::string& hostname_arg,
+ int flags_arg,
+ const CertificateList& additional_trust_anchors);
+ ~RequestParams();
+
+ bool operator<(const RequestParams& other) const;
+
+ std::string hostname;
+ int flags;
+ std::vector<SHA1HashValue> hash_values;
+ };
+
+ // CachedResult contains the result of a certificate verification.
+ struct CachedResult {
+ CachedResult();
+ ~CachedResult();
+
+ int error; // The return value of CertVerifier::Verify.
+ CertVerifyResult result; // The output of CertVerifier::Verify.
+ };
+
+ // Rather than having a single validity point along a monotonically increasing
+ // timeline, certificate verification is based on falling within a range of
+ // the certificate's NotBefore and NotAfter and based on what the current
+ // system clock says (which may advance forwards or backwards as users correct
+ // clock skew). CacheValidityPeriod and CacheExpirationFunctor are helpers to
+ // ensure that expiration is measured both by the 'general' case (now + cache
+ // TTL) and by whether or not significant enough clock skew was introduced
+ // since the last verification.
+ struct CacheValidityPeriod {
+ explicit CacheValidityPeriod(const base::Time& now);
+ CacheValidityPeriod(const base::Time& now, const base::Time& expiration);
+
+ base::Time verification_time;
+ base::Time expiration_time;
+ };
+
+ struct CacheExpirationFunctor {
+ // Returns true iff |now| is within the validity period of |expiration|.
+ bool operator()(const CacheValidityPeriod& now,
+ const CacheValidityPeriod& expiration) const;
+ };
+
+ typedef ExpiringCache<RequestParams, CachedResult, CacheValidityPeriod,
+ CacheExpirationFunctor> CertVerifierCache;
+
+ void HandleResult(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ const CertificateList& additional_trust_anchors,
+ int error,
+ const CertVerifyResult& verify_result);
+
+ // CertDatabase::Observer methods:
+ virtual void OnCertTrustChanged(const X509Certificate* cert) OVERRIDE;
+
+ // For unit testing.
+ void ClearCache() { cache_.Clear(); }
+ size_t GetCacheSize() const { return cache_.size(); }
+ uint64 cache_hits() const { return cache_hits_; }
+ uint64 requests() const { return requests_; }
+ uint64 inflight_joins() const { return inflight_joins_; }
+
+ // cache_ maps from a request to a cached result.
+ CertVerifierCache cache_;
+
+ // inflight_ maps from a request to an active verification which is taking
+ // place.
+ std::map<RequestParams, CertVerifierJob*> inflight_;
+
+ uint64 requests_;
+ uint64 cache_hits_;
+ uint64 inflight_joins_;
+
+ scoped_refptr<CertVerifyProc> verify_proc_;
+
+ CertTrustAnchorProvider* trust_anchor_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiThreadedCertVerifier);
+};
+
+} // namespace net
+
+#endif // NET_CERT_MULTI_THREADED_CERT_VERIFIER_H_
diff --git a/chromium/net/cert/multi_threaded_cert_verifier_unittest.cc b/chromium/net/cert/multi_threaded_cert_verifier_unittest.cc
new file mode 100644
index 00000000000..17d23d34995
--- /dev/null
+++ b/chromium/net/cert/multi_threaded_cert_verifier_unittest.cc
@@ -0,0 +1,479 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/multi_threaded_cert_verifier.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_trust_anchor_provider.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::ReturnRef;
+
+namespace net {
+
+namespace {
+
+void FailTest(int /* result */) {
+ FAIL();
+}
+
+class MockCertVerifyProc : public CertVerifyProc {
+ public:
+ MockCertVerifyProc() {}
+
+ private:
+ virtual ~MockCertVerifyProc() {}
+
+ // CertVerifyProc implementation
+ virtual bool SupportsAdditionalTrustAnchors() const OVERRIDE {
+ return false;
+ }
+
+ virtual int VerifyInternal(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ const CertificateList& additional_trust_anchors,
+ CertVerifyResult* verify_result) OVERRIDE {
+ verify_result->Reset();
+ verify_result->verified_cert = cert;
+ verify_result->cert_status = CERT_STATUS_COMMON_NAME_INVALID;
+ return ERR_CERT_COMMON_NAME_INVALID;
+ }
+};
+
+class MockCertTrustAnchorProvider : public CertTrustAnchorProvider {
+ public:
+ MockCertTrustAnchorProvider() {}
+ virtual ~MockCertTrustAnchorProvider() {}
+
+ MOCK_METHOD0(GetAdditionalTrustAnchors, const CertificateList&());
+};
+
+} // namespace
+
+class MultiThreadedCertVerifierTest : public ::testing::Test {
+ public:
+ MultiThreadedCertVerifierTest() : verifier_(new MockCertVerifyProc()) {}
+ virtual ~MultiThreadedCertVerifierTest() {}
+
+ protected:
+ MultiThreadedCertVerifier verifier_;
+};
+
+TEST_F(MultiThreadedCertVerifierTest, CacheHit) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "ok_cert.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+
+ int error;
+ CertVerifyResult verify_result;
+ TestCompletionCallback callback;
+ CertVerifier::RequestHandle request_handle;
+
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = callback.WaitForResult();
+ ASSERT_TRUE(IsCertificateError(error));
+ ASSERT_EQ(1u, verifier_.requests());
+ ASSERT_EQ(0u, verifier_.cache_hits());
+ ASSERT_EQ(0u, verifier_.inflight_joins());
+ ASSERT_EQ(1u, verifier_.GetCacheSize());
+
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ // Synchronous completion.
+ ASSERT_NE(ERR_IO_PENDING, error);
+ ASSERT_TRUE(IsCertificateError(error));
+ ASSERT_TRUE(request_handle == NULL);
+ ASSERT_EQ(2u, verifier_.requests());
+ ASSERT_EQ(1u, verifier_.cache_hits());
+ ASSERT_EQ(0u, verifier_.inflight_joins());
+ ASSERT_EQ(1u, verifier_.GetCacheSize());
+}
+
+// Tests the same server certificate with different intermediate CA
+// certificates. These should be treated as different certificate chains even
+// though the two X509Certificate objects contain the same server certificate.
+TEST_F(MultiThreadedCertVerifierTest, DifferentCACerts) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "salesforce_com_test.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
+
+ scoped_refptr<X509Certificate> intermediate_cert1 =
+ ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert1);
+
+ scoped_refptr<X509Certificate> intermediate_cert2 =
+ ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert2);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate_cert1->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain1 =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ intermediates.clear();
+ intermediates.push_back(intermediate_cert2->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain2 =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ int error;
+ CertVerifyResult verify_result;
+ TestCompletionCallback callback;
+ CertVerifier::RequestHandle request_handle;
+
+ error = verifier_.Verify(cert_chain1.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = callback.WaitForResult();
+ ASSERT_TRUE(IsCertificateError(error));
+ ASSERT_EQ(1u, verifier_.requests());
+ ASSERT_EQ(0u, verifier_.cache_hits());
+ ASSERT_EQ(0u, verifier_.inflight_joins());
+ ASSERT_EQ(1u, verifier_.GetCacheSize());
+
+ error = verifier_.Verify(cert_chain2.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = callback.WaitForResult();
+ ASSERT_TRUE(IsCertificateError(error));
+ ASSERT_EQ(2u, verifier_.requests());
+ ASSERT_EQ(0u, verifier_.cache_hits());
+ ASSERT_EQ(0u, verifier_.inflight_joins());
+ ASSERT_EQ(2u, verifier_.GetCacheSize());
+}
+
+// Tests an inflight join.
+TEST_F(MultiThreadedCertVerifierTest, InflightJoin) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "ok_cert.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+
+ int error;
+ CertVerifyResult verify_result;
+ TestCompletionCallback callback;
+ CertVerifier::RequestHandle request_handle;
+ CertVerifyResult verify_result2;
+ TestCompletionCallback callback2;
+ CertVerifier::RequestHandle request_handle2;
+
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result2,
+ callback2.callback(),
+ &request_handle2,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle2 != NULL);
+ error = callback.WaitForResult();
+ EXPECT_TRUE(IsCertificateError(error));
+ error = callback2.WaitForResult();
+ ASSERT_TRUE(IsCertificateError(error));
+ ASSERT_EQ(2u, verifier_.requests());
+ ASSERT_EQ(0u, verifier_.cache_hits());
+ ASSERT_EQ(1u, verifier_.inflight_joins());
+}
+
+// Tests that the callback of a canceled request is never made.
+TEST_F(MultiThreadedCertVerifierTest, CancelRequest) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "ok_cert.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+
+ int error;
+ CertVerifyResult verify_result;
+ CertVerifier::RequestHandle request_handle;
+
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ base::Bind(&FailTest),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ ASSERT_TRUE(request_handle != NULL);
+ verifier_.CancelRequest(request_handle);
+
+ // Issue a few more requests to the worker pool and wait for their
+ // completion, so that the task of the canceled request (which runs on a
+ // worker thread) is likely to complete by the end of this test.
+ TestCompletionCallback callback;
+ for (int i = 0; i < 5; ++i) {
+ error = verifier_.Verify(test_cert.get(),
+ "www2.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = callback.WaitForResult();
+ verifier_.ClearCache();
+ }
+}
+
+// Tests that a canceled request is not leaked.
+TEST_F(MultiThreadedCertVerifierTest, CancelRequestThenQuit) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "ok_cert.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+
+ int error;
+ CertVerifyResult verify_result;
+ TestCompletionCallback callback;
+ CertVerifier::RequestHandle request_handle;
+
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ verifier_.CancelRequest(request_handle);
+ // Destroy |verifier| by going out of scope.
+}
+
+TEST_F(MultiThreadedCertVerifierTest, RequestParamsComparators) {
+ SHA1HashValue a_key;
+ memset(a_key.data, 'a', sizeof(a_key.data));
+
+ SHA1HashValue z_key;
+ memset(z_key.data, 'z', sizeof(z_key.data));
+
+ const CertificateList empty_list;
+ CertificateList test_list;
+ test_list.push_back(
+ ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+
+ struct {
+ // Keys to test
+ MultiThreadedCertVerifier::RequestParams key1;
+ MultiThreadedCertVerifier::RequestParams key2;
+
+ // Expectation:
+ // -1 means key1 is less than key2
+ // 0 means key1 equals key2
+ // 1 means key1 is greater than key2
+ int expected_result;
+ } tests[] = {
+ { // Test for basic equivalence.
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, test_list),
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, test_list),
+ 0,
+ },
+ { // Test that different certificates but with the same CA and for
+ // the same host are different validation keys.
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, test_list),
+ MultiThreadedCertVerifier::RequestParams(z_key, a_key, "www.example.test",
+ 0, test_list),
+ -1,
+ },
+ { // Test that the same EE certificate for the same host, but with
+ // different chains are different validation keys.
+ MultiThreadedCertVerifier::RequestParams(a_key, z_key, "www.example.test",
+ 0, test_list),
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, test_list),
+ 1,
+ },
+ { // The same certificate, with the same chain, but for different
+ // hosts are different validation keys.
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key,
+ "www1.example.test", 0,
+ test_list),
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key,
+ "www2.example.test", 0,
+ test_list),
+ -1,
+ },
+ { // The same certificate, chain, and host, but with different flags
+ // are different validation keys.
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ CertVerifier::VERIFY_EV_CERT,
+ test_list),
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, test_list),
+ 1,
+ },
+ { // Different additional_trust_anchors.
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, empty_list),
+ MultiThreadedCertVerifier::RequestParams(a_key, a_key, "www.example.test",
+ 0, test_list),
+ -1,
+ },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
+
+ const MultiThreadedCertVerifier::RequestParams& key1 = tests[i].key1;
+ const MultiThreadedCertVerifier::RequestParams& key2 = tests[i].key2;
+
+ switch (tests[i].expected_result) {
+ case -1:
+ EXPECT_TRUE(key1 < key2);
+ EXPECT_FALSE(key2 < key1);
+ break;
+ case 0:
+ EXPECT_FALSE(key1 < key2);
+ EXPECT_FALSE(key2 < key1);
+ break;
+ case 1:
+ EXPECT_FALSE(key1 < key2);
+ EXPECT_TRUE(key2 < key1);
+ break;
+ default:
+ FAIL() << "Invalid expectation. Can be only -1, 0, 1";
+ }
+ }
+}
+
+TEST_F(MultiThreadedCertVerifierTest, CertTrustAnchorProvider) {
+ MockCertTrustAnchorProvider trust_provider;
+ verifier_.SetCertTrustAnchorProvider(&trust_provider);
+
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
+ ASSERT_TRUE(test_cert.get());
+
+ const CertificateList empty_cert_list;
+ CertificateList cert_list;
+ cert_list.push_back(test_cert);
+
+ // Check that Verify() asks the |trust_provider| for the current list of
+ // additional trust anchors.
+ int error;
+ CertVerifyResult verify_result;
+ TestCompletionCallback callback;
+ CertVerifier::RequestHandle request_handle;
+ EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors())
+ .WillOnce(ReturnRef(empty_cert_list));
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ Mock::VerifyAndClearExpectations(&trust_provider);
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = callback.WaitForResult();
+ EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error);
+ ASSERT_EQ(1u, verifier_.requests());
+ ASSERT_EQ(0u, verifier_.cache_hits());
+
+ // The next Verify() uses the cached result.
+ EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors())
+ .WillOnce(ReturnRef(empty_cert_list));
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ Mock::VerifyAndClearExpectations(&trust_provider);
+ EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error);
+ EXPECT_FALSE(request_handle);
+ ASSERT_EQ(2u, verifier_.requests());
+ ASSERT_EQ(1u, verifier_.cache_hits());
+
+ // Another Verify() for the same certificate but with a different list of
+ // trust anchors will not reuse the cache.
+ EXPECT_CALL(trust_provider, GetAdditionalTrustAnchors())
+ .WillOnce(ReturnRef(cert_list));
+ error = verifier_.Verify(test_cert.get(),
+ "www.example.com",
+ 0,
+ NULL,
+ &verify_result,
+ callback.callback(),
+ &request_handle,
+ BoundNetLog());
+ Mock::VerifyAndClearExpectations(&trust_provider);
+ ASSERT_EQ(ERR_IO_PENDING, error);
+ EXPECT_TRUE(request_handle);
+ error = callback.WaitForResult();
+ EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, error);
+ ASSERT_EQ(3u, verifier_.requests());
+ ASSERT_EQ(1u, verifier_.cache_hits());
+}
+
+} // namespace net
diff --git a/chromium/net/cert/nss_cert_database.cc b/chromium/net/cert/nss_cert_database.cc
new file mode 100644
index 00000000000..8e9ef4e6f01
--- /dev/null
+++ b/chromium/net/cert/nss_cert_database.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/nss_cert_database.h"
+
+#include <cert.h>
+#include <certdb.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secmod.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list_threadsafe.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "net/base/crypto_module.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_database.h"
+#include "net/cert/x509_certificate.h"
+#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
+#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
+
+// In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
+// the new name of the macro.
+#if !defined(CERTDB_TERMINAL_RECORD)
+#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
+#endif
+
+// PSM = Mozilla's Personal Security Manager.
+namespace psm = mozilla_security_manager;
+
+namespace net {
+
+NSSCertDatabase::ImportCertFailure::ImportCertFailure(
+ const scoped_refptr<X509Certificate>& cert,
+ int err)
+ : certificate(cert), net_error(err) {}
+
+NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
+
+// static
+NSSCertDatabase* NSSCertDatabase::GetInstance() {
+ return Singleton<NSSCertDatabase,
+ LeakySingletonTraits<NSSCertDatabase> >::get();
+}
+
+NSSCertDatabase::NSSCertDatabase()
+ : observer_list_(new ObserverListThreadSafe<Observer>) {
+ crypto::EnsureNSSInit();
+ psm::EnsurePKCS12Init();
+}
+
+NSSCertDatabase::~NSSCertDatabase() {}
+
+void NSSCertDatabase::ListCerts(CertificateList* certs) {
+ certs->clear();
+
+ CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
+ CERTCertListNode* node;
+ for (node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ certs->push_back(X509Certificate::CreateFromHandle(
+ node->cert, X509Certificate::OSCertHandles()));
+ }
+ CERT_DestroyCertList(cert_list);
+}
+
+CryptoModule* NSSCertDatabase::GetPublicModule() const {
+ CryptoModule* module =
+ CryptoModule::CreateFromHandle(crypto::GetPublicNSSKeySlot());
+ // The module is already referenced when returned from
+ // GetPublicNSSKeySlot, so we need to deref it once.
+ PK11_FreeSlot(module->os_module_handle());
+
+ return module;
+}
+
+CryptoModule* NSSCertDatabase::GetPrivateModule() const {
+ CryptoModule* module =
+ CryptoModule::CreateFromHandle(crypto::GetPrivateNSSKeySlot());
+ // The module is already referenced when returned from
+ // GetPrivateNSSKeySlot, so we need to deref it once.
+ PK11_FreeSlot(module->os_module_handle());
+
+ return module;
+}
+
+void NSSCertDatabase::ListModules(CryptoModuleList* modules,
+ bool need_rw) const {
+ modules->clear();
+
+ PK11SlotList* slot_list = NULL;
+ // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
+ slot_list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,
+ need_rw ? PR_TRUE : PR_FALSE, // needRW
+ PR_TRUE, // loadCerts (unused)
+ NULL); // wincx
+ if (!slot_list) {
+ LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
+ return;
+ }
+
+ PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list);
+ while (slot_element) {
+ modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
+ slot_element = PK11_GetNextSafe(slot_list, slot_element,
+ PR_FALSE); // restart
+ }
+
+ PK11_FreeSlotList(slot_list);
+}
+
+int NSSCertDatabase::ImportFromPKCS12(
+ CryptoModule* module,
+ const std::string& data,
+ const base::string16& password,
+ bool is_extractable,
+ net::CertificateList* imported_certs) {
+ int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
+ data.data(), data.size(),
+ password,
+ is_extractable,
+ imported_certs);
+ if (result == net::OK)
+ NotifyObserversOfCertAdded(NULL);
+
+ return result;
+}
+
+int NSSCertDatabase::ExportToPKCS12(
+ const CertificateList& certs,
+ const base::string16& password,
+ std::string* output) const {
+ return psm::nsPKCS12Blob_Export(output, certs, password);
+}
+
+X509Certificate* NSSCertDatabase::FindRootInList(
+ const CertificateList& certificates) const {
+ DCHECK_GT(certificates.size(), 0U);
+
+ if (certificates.size() == 1)
+ return certificates[0].get();
+
+ X509Certificate* cert0 = certificates[0].get();
+ X509Certificate* cert1 = certificates[1].get();
+ X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
+ X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
+
+ if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
+ &cert0->os_cert_handle()->subject) == SECEqual)
+ return cert0;
+ if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
+ &certn_1->os_cert_handle()->subject) == SECEqual)
+ return certn_1;
+
+ VLOG(1) << "certificate list is not a hierarchy";
+ return cert0;
+}
+
+bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
+ TrustBits trust_bits,
+ ImportCertFailureList* not_imported) {
+ X509Certificate* root = FindRootInList(certificates);
+ bool success = psm::ImportCACerts(certificates, root, trust_bits,
+ not_imported);
+ if (success)
+ NotifyObserversOfCertTrustChanged(NULL);
+
+ return success;
+}
+
+bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
+ TrustBits trust_bits,
+ ImportCertFailureList* not_imported) {
+ return psm::ImportServerCert(certificates, trust_bits, not_imported);
+}
+
+NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
+ const X509Certificate* cert,
+ CertType type) const {
+ CERTCertTrust trust;
+ SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
+ if (srv != SECSuccess) {
+ LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
+ return TRUST_DEFAULT;
+ }
+ // We define our own more "friendly" TrustBits, which means we aren't able to
+ // round-trip all possible NSS trust flag combinations. We try to map them in
+ // a sensible way.
+ switch (type) {
+ case CA_CERT: {
+ const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
+ const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
+
+ TrustBits trust_bits = TRUST_DEFAULT;
+ if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
+ trust_bits |= DISTRUSTED_SSL;
+ else if (trust.sslFlags & kTrustedCA)
+ trust_bits |= TRUSTED_SSL;
+
+ if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
+ trust_bits |= DISTRUSTED_EMAIL;
+ else if (trust.emailFlags & kTrustedCA)
+ trust_bits |= TRUSTED_EMAIL;
+
+ if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
+ trust_bits |= DISTRUSTED_OBJ_SIGN;
+ else if (trust.objectSigningFlags & kTrustedCA)
+ trust_bits |= TRUSTED_OBJ_SIGN;
+
+ return trust_bits;
+ }
+ case SERVER_CERT:
+ if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
+ if (trust.sslFlags & CERTDB_TRUSTED)
+ return TRUSTED_SSL;
+ return DISTRUSTED_SSL;
+ }
+ return TRUST_DEFAULT;
+ default:
+ return TRUST_DEFAULT;
+ }
+}
+
+bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
+ CERTCertTrust nsstrust;
+ SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
+ return false;
+ }
+
+ // The CERTCertTrust structure contains three trust records:
+ // sslFlags, emailFlags, and objectSigningFlags. The three
+ // trust records are independent of each other.
+ //
+ // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
+ // then that trust record is a terminal record. A terminal
+ // record is used for explicit trust and distrust of an
+ // end-entity or intermediate CA cert.
+ //
+ // In a terminal record, if neither CERTDB_TRUSTED_CA nor
+ // CERTDB_TRUSTED is set, then the terminal record means
+ // explicit distrust. On the other hand, if the terminal
+ // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
+ // set, then the terminal record means explicit trust.
+ //
+ // For a root CA, the trust record does not have
+ // the CERTDB_TERMINAL_RECORD bit set.
+
+ static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
+ if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
+ (nsstrust.sslFlags & kTrusted) == 0) {
+ return true;
+ }
+ if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
+ (nsstrust.emailFlags & kTrusted) == 0) {
+ return true;
+ }
+ if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
+ (nsstrust.objectSigningFlags & kTrusted) == 0) {
+ return true;
+ }
+
+ // Self-signed certificates that don't have any trust bits set are untrusted.
+ // Other certificates that don't have any trust bits set may still be trusted
+ // if they chain up to a trust anchor.
+ if (CERT_CompareName(&cert->os_cert_handle()->issuer,
+ &cert->os_cert_handle()->subject) == SECEqual) {
+ return (nsstrust.sslFlags & kTrusted) == 0 &&
+ (nsstrust.emailFlags & kTrusted) == 0 &&
+ (nsstrust.objectSigningFlags & kTrusted) == 0;
+ }
+
+ return false;
+}
+
+bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
+ CertType type,
+ TrustBits trust_bits) {
+ bool success = psm::SetCertTrust(cert, type, trust_bits);
+ if (success)
+ NotifyObserversOfCertTrustChanged(cert);
+
+ return success;
+}
+
+bool NSSCertDatabase::DeleteCertAndKey(const X509Certificate* cert) {
+ // For some reason, PK11_DeleteTokenCertAndKey only calls
+ // SEC_DeletePermCertificate if the private key is found. So, we check
+ // whether a private key exists before deciding which function to call to
+ // delete the cert.
+ SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(),
+ NULL);
+ if (privKey) {
+ SECKEY_DestroyPrivateKey(privKey);
+ if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
+ LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
+ return false;
+ }
+ } else {
+ if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
+ LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
+ return false;
+ }
+ }
+
+ NotifyObserversOfCertRemoved(cert);
+
+ return true;
+}
+
+bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
+ PK11SlotInfo* slot = cert->os_cert_handle()->slot;
+ return slot && PK11_IsReadOnly(slot);
+}
+
+void NSSCertDatabase::AddObserver(Observer* observer) {
+ observer_list_->AddObserver(observer);
+}
+
+void NSSCertDatabase::RemoveObserver(Observer* observer) {
+ observer_list_->RemoveObserver(observer);
+}
+
+void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
+ observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
+}
+
+void NSSCertDatabase::NotifyObserversOfCertRemoved(
+ const X509Certificate* cert) {
+ observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
+}
+
+void NSSCertDatabase::NotifyObserversOfCertTrustChanged(
+ const X509Certificate* cert) {
+ observer_list_->Notify(
+ &Observer::OnCertTrustChanged, make_scoped_refptr(cert));
+}
+
+} // namespace net
diff --git a/chromium/net/cert/nss_cert_database.h b/chromium/net/cert/nss_cert_database.h
new file mode 100644
index 00000000000..9db1b75d973
--- /dev/null
+++ b/chromium/net/cert/nss_cert_database.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_NSS_CERT_DATABASE_H_
+#define NET_CERT_NSS_CERT_DATABASE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_type.h"
+#include "net/cert/x509_certificate.h"
+
+template <typename T> struct DefaultSingletonTraits;
+template <class ObserverType> class ObserverListThreadSafe;
+
+namespace net {
+
+class CryptoModule;
+typedef std::vector<scoped_refptr<CryptoModule> > CryptoModuleList;
+
+// Provides functions to manipulate the NSS certificate stores.
+class NET_EXPORT NSSCertDatabase {
+ public:
+
+ class NET_EXPORT Observer {
+ public:
+ virtual ~Observer() {}
+
+ // Will be called when a new certificate is added.
+ // Called with |cert| == NULL after importing a list of certificates
+ // in ImportFromPKCS12().
+ virtual void OnCertAdded(const X509Certificate* cert) {}
+
+ // Will be called when a certificate is removed.
+ virtual void OnCertRemoved(const X509Certificate* cert) {}
+
+ // Will be called when a certificate's trust is changed.
+ // Called with |cert| == NULL after importing a list of certificates
+ // in ImportCACerts().
+ virtual void OnCertTrustChanged(const X509Certificate* cert) {}
+
+ protected:
+ Observer() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Observer);
+ };
+
+ // Stores per-certificate error codes for import failures.
+ struct NET_EXPORT ImportCertFailure {
+ public:
+ ImportCertFailure(const scoped_refptr<X509Certificate>& cert, int err);
+ ~ImportCertFailure();
+
+ scoped_refptr<X509Certificate> certificate;
+ int net_error;
+ };
+ typedef std::vector<ImportCertFailure> ImportCertFailureList;
+
+ // Constants that define which usages a certificate is trusted for.
+ // They are used in combination with CertType to specify trust for each type
+ // of certificate.
+ // For a CA_CERT, they specify that the CA is trusted for issuing server and
+ // client certs of each type.
+ // For SERVER_CERT, only TRUSTED_SSL makes sense, and specifies the cert is
+ // trusted as a server.
+ // For EMAIL_CERT, only TRUSTED_EMAIL makes sense, and specifies the cert is
+ // trusted for email.
+ // DISTRUSTED_* specifies that the cert should not be trusted for the given
+ // usage, regardless of whether it would otherwise inherit trust from the
+ // issuer chain.
+ // Use TRUST_DEFAULT to inherit trust as normal.
+ // NOTE: The actual constants are defined using an enum instead of static
+ // consts due to compilation/linkage constraints with template functions.
+ typedef uint32 TrustBits;
+ enum {
+ TRUST_DEFAULT = 0,
+ TRUSTED_SSL = 1 << 0,
+ TRUSTED_EMAIL = 1 << 1,
+ TRUSTED_OBJ_SIGN = 1 << 2,
+ DISTRUSTED_SSL = 1 << 3,
+ DISTRUSTED_EMAIL = 1 << 4,
+ DISTRUSTED_OBJ_SIGN = 1 << 5,
+ };
+
+ static NSSCertDatabase* GetInstance();
+
+ // Get a list of unique certificates in the certificate database (one
+ // instance of all certificates).
+ void ListCerts(CertificateList* certs);
+
+ // Get the default module for public key data.
+ // The returned pointer must be stored in a scoped_refptr<CryptoModule>.
+ CryptoModule* GetPublicModule() const;
+
+ // Get the default module for private key or mixed private/public key data.
+ // The returned pointer must be stored in a scoped_refptr<CryptoModule>.
+ CryptoModule* GetPrivateModule() const;
+
+ // Get all modules.
+ // If |need_rw| is true, only writable modules will be returned.
+ void ListModules(CryptoModuleList* modules, bool need_rw) const;
+
+ // Import certificates and private keys from PKCS #12 blob into the module.
+ // If |is_extractable| is false, mark the private key as being unextractable
+ // from the module.
+ // Returns OK or a network error code such as ERR_PKCS12_IMPORT_BAD_PASSWORD
+ // or ERR_PKCS12_IMPORT_ERROR. |imported_certs|, if non-NULL, returns a list
+ // of certs that were imported.
+ int ImportFromPKCS12(CryptoModule* module,
+ const std::string& data,
+ const base::string16& password,
+ bool is_extractable,
+ CertificateList* imported_certs);
+
+ // Export the given certificates and private keys into a PKCS #12 blob,
+ // storing into |output|.
+ // Returns the number of certificates successfully exported.
+ int ExportToPKCS12(const CertificateList& certs,
+ const base::string16& password,
+ std::string* output) const;
+
+ // Uses similar logic to nsNSSCertificateDB::handleCACertDownload to find the
+ // root. Assumes the list is an ordered hierarchy with the root being either
+ // the first or last element.
+ // TODO(mattm): improve this to handle any order.
+ X509Certificate* FindRootInList(const CertificateList& certificates) const;
+
+ // Import CA certificates.
+ // Tries to import all the certificates given. The root will be trusted
+ // according to |trust_bits|. Any certificates that could not be imported
+ // will be listed in |not_imported|.
+ // Returns false if there is an internal error, otherwise true is returned and
+ // |not_imported| should be checked for any certificates that were not
+ // imported.
+ bool ImportCACerts(const CertificateList& certificates,
+ TrustBits trust_bits,
+ ImportCertFailureList* not_imported);
+
+ // Import server certificate. The first cert should be the server cert. Any
+ // additional certs should be intermediate/CA certs and will be imported but
+ // not given any trust.
+ // Any certificates that could not be imported will be listed in
+ // |not_imported|.
+ // |trust_bits| can be set to explicitly trust or distrust the certificate, or
+ // use TRUST_DEFAULT to inherit trust as normal.
+ // Returns false if there is an internal error, otherwise true is returned and
+ // |not_imported| should be checked for any certificates that were not
+ // imported.
+ bool ImportServerCert(const CertificateList& certificates,
+ TrustBits trust_bits,
+ ImportCertFailureList* not_imported);
+
+ // Get trust bits for certificate.
+ TrustBits GetCertTrust(const X509Certificate* cert, CertType type) const;
+
+ // IsUntrusted returns true if |cert| is specifically untrusted. These
+ // certificates are stored in the database for the specific purpose of
+ // rejecting them.
+ bool IsUntrusted(const X509Certificate* cert) const;
+
+ // Set trust values for certificate.
+ // Returns true on success or false on failure.
+ bool SetCertTrust(const X509Certificate* cert,
+ CertType type,
+ TrustBits trust_bits);
+
+ // Delete certificate and associated private key (if one exists).
+ // |cert| is still valid when this function returns. Returns true on
+ // success.
+ bool DeleteCertAndKey(const X509Certificate* cert);
+
+ // Check whether cert is stored in a readonly slot.
+ bool IsReadOnly(const X509Certificate* cert) const;
+
+ // Registers |observer| to receive notifications of certificate changes. The
+ // thread on which this is called is the thread on which |observer| will be
+ // called back with notifications.
+ void AddObserver(Observer* observer);
+
+ // Unregisters |observer| from receiving notifications. This must be called
+ // on the same thread on which AddObserver() was called.
+ void RemoveObserver(Observer* observer);
+
+ private:
+ friend struct DefaultSingletonTraits<NSSCertDatabase>;
+
+ NSSCertDatabase();
+ ~NSSCertDatabase();
+
+ // Broadcasts notifications to all registered observers.
+ void NotifyObserversOfCertAdded(const X509Certificate* cert);
+ void NotifyObserversOfCertRemoved(const X509Certificate* cert);
+ void NotifyObserversOfCertTrustChanged(const X509Certificate* cert);
+
+ const scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(NSSCertDatabase);
+};
+
+} // namespace net
+
+#endif // NET_CERT_NSS_CERT_DATABASE_H_
diff --git a/chromium/net/cert/nss_cert_database_unittest.cc b/chromium/net/cert/nss_cert_database_unittest.cc
new file mode 100644
index 00000000000..2e712bb54ad
--- /dev/null
+++ b/chromium/net/cert/nss_cert_database_unittest.cc
@@ -0,0 +1,1042 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cert.h>
+#include <certdb.h>
+#include <pk11pub.h>
+
+#include <algorithm>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/crypto_module.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_proc_nss.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
+// the new name of the macro.
+#if !defined(CERTDB_TERMINAL_RECORD)
+#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
+#endif
+
+namespace net {
+
+class CertDatabaseNSSTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ ASSERT_TRUE(test_nssdb_.is_open());
+ cert_db_ = NSSCertDatabase::GetInstance();
+ slot_ = cert_db_->GetPublicModule();
+
+ // Test db should be empty at start of test.
+ EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
+ }
+
+ virtual void TearDown() {
+ // Don't try to cleanup if the setup failed.
+ ASSERT_TRUE(slot_->os_module_handle());
+
+ EXPECT_TRUE(CleanupSlotContents());
+
+ // Run the message loop to process any observer callbacks (e.g. for the
+ // ClientSocketFactory singleton) so that the scoped ref ptrs created in
+ // NSSCertDatabase::NotifyObservers* get released.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
+ }
+
+ protected:
+ static std::string ReadTestFile(const std::string& name) {
+ std::string result;
+ base::FilePath cert_path = GetTestCertsDirectory().AppendASCII(name);
+ EXPECT_TRUE(file_util::ReadFileToString(cert_path, &result));
+ return result;
+ }
+
+ static bool ReadCertIntoList(const std::string& name,
+ CertificateList* certs) {
+ scoped_refptr<X509Certificate> cert(
+ ImportCertFromFile(GetTestCertsDirectory(), name));
+ if (!cert.get())
+ return false;
+
+ certs->push_back(cert);
+ return true;
+ }
+
+ static CertificateList ListCertsInSlot(PK11SlotInfo* slot) {
+ CertificateList result;
+ CERTCertList* cert_list = PK11_ListCertsInSlot(slot);
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ result.push_back(X509Certificate::CreateFromHandle(
+ node->cert, X509Certificate::OSCertHandles()));
+ }
+ CERT_DestroyCertList(cert_list);
+
+ // Sort the result so that test comparisons can be deterministic.
+ std::sort(result.begin(), result.end(), X509Certificate::LessThan());
+ return result;
+ }
+
+ scoped_refptr<CryptoModule> slot_;
+ NSSCertDatabase* cert_db_;
+ const CertificateList empty_cert_list_;
+
+ private:
+ bool CleanupSlotContents() {
+ bool ok = true;
+ CertificateList certs = ListCertsInSlot(slot_->os_module_handle());
+ CERTCertTrust default_trust = {0};
+ for (size_t i = 0; i < certs.size(); ++i) {
+ // Reset cert trust values to defaults before deleting. Otherwise NSS
+ // somehow seems to remember the trust which can break following tests.
+ SECStatus srv = CERT_ChangeCertTrust(
+ CERT_GetDefaultCertDB(), certs[i]->os_cert_handle(), &default_trust);
+ if (srv != SECSuccess)
+ ok = false;
+
+ if (!cert_db_->DeleteCertAndKey(certs[i].get()))
+ ok = false;
+ }
+ return ok;
+ }
+
+ crypto::ScopedTestNSSDB test_nssdb_;
+};
+
+TEST_F(CertDatabaseNSSTest, ListCerts) {
+ // This test isn't terribly useful, though it will at least let valgrind test
+ // for leaks.
+ CertificateList certs;
+ cert_db_->ListCerts(&certs);
+ // The test DB is empty, but let's assume there will always be something in
+ // the other slots.
+ EXPECT_LT(0U, certs.size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12WrongPassword) {
+ std::string pkcs12_data = ReadTestFile("client.p12");
+
+ EXPECT_EQ(ERR_PKCS12_IMPORT_BAD_PASSWORD,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ base::string16(),
+ true, // is_extractable
+ NULL));
+
+ // Test db should still be empty.
+ EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsExtractableAndExportAgain) {
+ std::string pkcs12_data = ReadTestFile("client.p12");
+
+ EXPECT_EQ(OK,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ true, // is_extractable
+ NULL));
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ EXPECT_EQ("testusercert",
+ cert->subject().common_name);
+
+ // TODO(mattm): move export test to separate test case?
+ std::string exported_data;
+ EXPECT_EQ(1, cert_db_->ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
+ &exported_data));
+ ASSERT_LT(0U, exported_data.size());
+ // TODO(mattm): further verification of exported data?
+}
+
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12Twice) {
+ std::string pkcs12_data = ReadTestFile("client.p12");
+
+ EXPECT_EQ(OK,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ true, // is_extractable
+ NULL));
+ EXPECT_EQ(1U, ListCertsInSlot(slot_->os_module_handle()).size());
+
+ // NSS has a SEC_ERROR_PKCS12_DUPLICATE_DATA error, but it doesn't look like
+ // it's ever used. This test verifies that.
+ EXPECT_EQ(OK,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ true, // is_extractable
+ NULL));
+ EXPECT_EQ(1U, ListCertsInSlot(slot_->os_module_handle()).size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsUnextractableAndExportAgain) {
+ std::string pkcs12_data = ReadTestFile("client.p12");
+
+ EXPECT_EQ(OK,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ false, // is_extractable
+ NULL));
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ EXPECT_EQ("testusercert",
+ cert->subject().common_name);
+
+ std::string exported_data;
+ EXPECT_EQ(0, cert_db_->ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
+ &exported_data));
+}
+
+// Importing a PKCS#12 file with a certificate but no corresponding
+// private key should not mark an existing private key as unextractable.
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12OnlyMarkIncludedKey) {
+ std::string pkcs12_data = ReadTestFile("client.p12");
+ EXPECT_EQ(OK,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ true, // is_extractable
+ NULL));
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+
+ // Now import a PKCS#12 file with just a certificate but no private key.
+ pkcs12_data = ReadTestFile("client-nokey.p12");
+ EXPECT_EQ(OK,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ false, // is_extractable
+ NULL));
+
+ cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+
+ // Make sure the imported private key is still extractable.
+ std::string exported_data;
+ EXPECT_EQ(1, cert_db_->ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
+ &exported_data));
+ ASSERT_LT(0U, exported_data.size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12InvalidFile) {
+ std::string pkcs12_data = "Foobarbaz";
+
+ EXPECT_EQ(ERR_PKCS12_IMPORT_INVALID_FILE,
+ cert_db_->ImportFromPKCS12(slot_.get(),
+ pkcs12_data,
+ base::string16(),
+ true, // is_extractable
+ NULL));
+
+ // Test db should still be empty.
+ EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACert_SSLTrust) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+ EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_SSL,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+ EXPECT_EQ("Test Root CA", cert->subject().common_name);
+
+ EXPECT_EQ(NSSCertDatabase::TRUSTED_SSL,
+ cert_db_->GetCertTrust(cert.get(), CA_CERT));
+
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
+ CERTDB_TRUSTED_CLIENT_CA),
+ cert->os_cert_handle()->trust->sslFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ cert->os_cert_handle()->trust->emailFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ cert->os_cert_handle()->trust->objectSigningFlags);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACert_EmailTrust) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+ EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_EMAIL,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+ EXPECT_EQ("Test Root CA", cert->subject().common_name);
+
+ EXPECT_EQ(NSSCertDatabase::TRUSTED_EMAIL,
+ cert_db_->GetCertTrust(cert.get(), CA_CERT));
+
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ cert->os_cert_handle()->trust->sslFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
+ CERTDB_TRUSTED_CLIENT_CA),
+ cert->os_cert_handle()->trust->emailFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ cert->os_cert_handle()->trust->objectSigningFlags);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACert_ObjSignTrust) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+ EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_OBJ_SIGN,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+ EXPECT_EQ("Test Root CA", cert->subject().common_name);
+
+ EXPECT_EQ(NSSCertDatabase::TRUSTED_OBJ_SIGN,
+ cert_db_->GetCertTrust(cert.get(), CA_CERT));
+
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ cert->os_cert_handle()->trust->sslFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ cert->os_cert_handle()->trust->emailFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA |
+ CERTDB_TRUSTED_CLIENT_CA),
+ cert->os_cert_handle()->trust->objectSigningFlags);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCA_NotCACert) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+ EXPECT_FALSE(certs[0]->os_cert_handle()->isperm);
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUSTED_SSL,
+ &failed));
+ ASSERT_EQ(1U, failed.size());
+ // Note: this compares pointers directly. It's okay in this case because
+ // ImportCACerts returns the same pointers that were passed in. In the
+ // general case IsSameOSCert should be used.
+ EXPECT_EQ(certs[0], failed[0].certificate);
+ EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[0].net_error);
+
+ EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACertHierarchy) {
+ CertificateList certs;
+ ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("www_us_army_mil_cert.der", &certs));
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ // Have to specify email trust for the cert verification of the child cert to
+ // work (see
+ // http://mxr.mozilla.org/mozilla/source/security/nss/lib/certhigh/certvfy.c#752
+ // "XXX This choice of trustType seems arbitrary.")
+ EXPECT_TRUE(cert_db_->ImportCACerts(
+ certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
+ &failed));
+
+ ASSERT_EQ(2U, failed.size());
+ EXPECT_EQ("DOD CA-17", failed[0].certificate->subject().common_name);
+ EXPECT_EQ(ERR_FAILED, failed[0].net_error); // The certificate expired.
+ EXPECT_EQ("www.us.army.mil", failed[1].certificate->subject().common_name);
+ EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[1].net_error);
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyDupeRoot) {
+ CertificateList certs;
+ ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
+
+ // First import just the root.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(
+ certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
+
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("www_us_army_mil_cert.der", &certs));
+
+ // Now import with the other certs in the list too. Even though the root is
+ // already present, we should still import the rest.
+ failed.clear();
+ EXPECT_TRUE(cert_db_->ImportCACerts(
+ certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
+ &failed));
+
+ ASSERT_EQ(3U, failed.size());
+ EXPECT_EQ("DoD Root CA 2", failed[0].certificate->subject().common_name);
+ EXPECT_EQ(ERR_IMPORT_CERT_ALREADY_EXISTS, failed[0].net_error);
+ EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name);
+ EXPECT_EQ(ERR_FAILED, failed[1].net_error); // The certificate expired.
+ EXPECT_EQ("www.us.army.mil", failed[2].certificate->subject().common_name);
+ EXPECT_EQ(ERR_IMPORT_CA_CERT_NOT_CA, failed[2].net_error);
+
+ cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyUntrusted) {
+ CertificateList certs;
+ ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(certs, NSSCertDatabase::TRUST_DEFAULT,
+ &failed));
+
+ ASSERT_EQ(1U, failed.size());
+ EXPECT_EQ("DOD CA-17", failed[0].certificate->subject().common_name);
+ // TODO(mattm): should check for net error equivalent of
+ // SEC_ERROR_UNTRUSTED_ISSUER
+ EXPECT_EQ(ERR_FAILED, failed[0].net_error);
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACertHierarchyTree) {
+ CertificateList certs;
+ ASSERT_TRUE(ReadCertIntoList("dod_root_ca_2_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_13_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(
+ certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL,
+ &failed));
+
+ EXPECT_EQ(2U, failed.size());
+ EXPECT_EQ("DOD CA-13", failed[0].certificate->subject().common_name);
+ EXPECT_EQ(ERR_FAILED, failed[0].net_error); // The certificate expired.
+ EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name);
+ EXPECT_EQ(ERR_FAILED, failed[1].net_error); // The certificate expired.
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ EXPECT_EQ("DoD Root CA 2", cert_list[0]->subject().common_name);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCACertNotHierarchy) {
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_13_cert.der", &certs));
+ ASSERT_TRUE(ReadCertIntoList("dod_ca_17_cert.der", &certs));
+
+ // Import it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(
+ certs, NSSCertDatabase::TRUSTED_SSL | NSSCertDatabase::TRUSTED_EMAIL |
+ NSSCertDatabase::TRUSTED_OBJ_SIGN, &failed));
+
+ ASSERT_EQ(2U, failed.size());
+ // TODO(mattm): should check for net error equivalent of
+ // SEC_ERROR_UNKNOWN_ISSUER
+ EXPECT_EQ("DOD CA-13", failed[0].certificate->subject().common_name);
+ EXPECT_EQ(ERR_FAILED, failed[0].net_error);
+ EXPECT_EQ("DOD CA-17", failed[1].certificate->subject().common_name);
+ EXPECT_EQ(ERR_FAILED, failed[1].net_error);
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ EXPECT_EQ("Test Root CA", cert_list[0]->subject().common_name);
+}
+
+// http://crbug.com/108009 - Disabled, as google.chain.pem is an expired
+// certificate.
+TEST_F(CertDatabaseNSSTest, DISABLED_ImportServerCert) {
+ // Need to import intermediate cert for the verify of google cert, otherwise
+ // it will try to fetch it automatically with cert_pi_useAIACertFetch, which
+ // will cause OCSPCreateSession on the main thread, which is not allowed.
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "google.chain.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(2U, certs.size());
+
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUST_DEFAULT,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(2U, cert_list.size());
+ scoped_refptr<X509Certificate> goog_cert(cert_list[0]);
+ scoped_refptr<X509Certificate> thawte_cert(cert_list[1]);
+ EXPECT_EQ("www.google.com", goog_cert->subject().common_name);
+ EXPECT_EQ("Thawte SGC CA", thawte_cert->subject().common_name);
+
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(goog_cert.get(), SERVER_CERT));
+
+ EXPECT_EQ(0U, goog_cert->os_cert_handle()->trust->sslFlags);
+
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(goog_cert.get(),
+ "www.google.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned) {
+ CertificateList certs;
+ ASSERT_TRUE(ReadCertIntoList("punycodetest.der", &certs));
+
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUST_DEFAULT,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> puny_cert(cert_list[0]);
+
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(puny_cert.get(), SERVER_CERT));
+ EXPECT_EQ(0U, puny_cert->os_cert_handle()->trust->sslFlags);
+
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(puny_cert.get(),
+ "xn--wgv71a119e.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportServerCert_SelfSigned_Trusted) {
+ // When using CERT_PKIXVerifyCert (which we do), server trust only works from
+ // 3.13.4 onwards. See https://bugzilla.mozilla.org/show_bug.cgi?id=647364.
+ if (!NSS_VersionCheck("3.13.4")) {
+ LOG(INFO) << "test skipped on NSS < 3.13.4";
+ return;
+ }
+
+ CertificateList certs;
+ ASSERT_TRUE(ReadCertIntoList("punycodetest.der", &certs));
+
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUSTED_SSL,
+ &failed));
+
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> puny_cert(cert_list[0]);
+
+ EXPECT_EQ(NSSCertDatabase::TRUSTED_SSL,
+ cert_db_->GetCertTrust(puny_cert.get(), SERVER_CERT));
+ EXPECT_EQ(unsigned(CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD),
+ puny_cert->os_cert_handle()->trust->sslFlags);
+
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(puny_cert.get(),
+ "xn--wgv71a119e.com",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert) {
+ CertificateList ca_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, ca_certs.size());
+
+ // Import CA cert and trust it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUSTED_SSL,
+ &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ // Import server cert with default trust.
+ EXPECT_TRUE(cert_db_->ImportServerCert(certs, NSSCertDatabase::TRUST_DEFAULT,
+ &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ // Server cert should verify.
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, ImportCaAndServerCert_DistrustServer) {
+ // Explicit distrust only works starting in NSS 3.13.
+ if (!NSS_VersionCheck("3.13")) {
+ LOG(INFO) << "test skipped on NSS < 3.13";
+ return;
+ }
+
+ CertificateList ca_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "root_ca_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, ca_certs.size());
+
+ // Import CA cert and trust it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUSTED_SSL,
+ &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "ok_cert.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ // Import server cert without inheriting trust from issuer (explicit
+ // distrust).
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs, NSSCertDatabase::DISTRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::DISTRUSTED_SSL,
+ cert_db_->GetCertTrust(certs[0].get(), SERVER_CERT));
+
+ EXPECT_EQ(unsigned(CERTDB_TERMINAL_RECORD),
+ certs[0]->os_cert_handle()->trust->sslFlags);
+
+ // Server cert should fail to verify.
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(ERR_CERT_REVOKED, error);
+ EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, TrustIntermediateCa) {
+ CertificateList ca_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-root.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, ca_certs.size());
+
+ // Import Root CA cert and distrust it.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::DISTRUSTED_SSL,
+ &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList intermediate_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, intermediate_certs.size());
+
+ // Import Intermediate CA cert and trust it.
+ EXPECT_TRUE(cert_db_->ImportCACerts(intermediate_certs,
+ NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ // Import server cert with default trust.
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(certs[0].get(), SERVER_CERT));
+
+ // Server cert should verify.
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+ // Explicit distrust only works starting in NSS 3.13.
+ if (!NSS_VersionCheck("3.13")) {
+ LOG(INFO) << "test partially skipped on NSS < 3.13";
+ return;
+ }
+
+ // Trust the root cert and distrust the intermediate.
+ EXPECT_TRUE(cert_db_->SetCertTrust(
+ ca_certs[0].get(), CA_CERT, NSSCertDatabase::TRUSTED_SSL));
+ EXPECT_TRUE(cert_db_->SetCertTrust(
+ intermediate_certs[0].get(), CA_CERT, NSSCertDatabase::DISTRUSTED_SSL));
+ EXPECT_EQ(
+ unsigned(CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA),
+ ca_certs[0]->os_cert_handle()->trust->sslFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ ca_certs[0]->os_cert_handle()->trust->emailFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ ca_certs[0]->os_cert_handle()->trust->objectSigningFlags);
+ EXPECT_EQ(unsigned(CERTDB_TERMINAL_RECORD),
+ intermediate_certs[0]->os_cert_handle()->trust->sslFlags);
+ EXPECT_EQ(unsigned(CERTDB_VALID_CA),
+ intermediate_certs[0]->os_cert_handle()->trust->emailFlags);
+ EXPECT_EQ(
+ unsigned(CERTDB_VALID_CA),
+ intermediate_certs[0]->os_cert_handle()->trust->objectSigningFlags);
+
+ // Server cert should fail to verify.
+ CertVerifyResult verify_result2;
+ error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result2);
+ EXPECT_EQ(ERR_CERT_REVOKED, error);
+ EXPECT_EQ(CERT_STATUS_REVOKED, verify_result2.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, TrustIntermediateCa2) {
+ if (NSS_VersionCheck("3.14.2") && !NSS_VersionCheck("3.15")) {
+ // See http://bugzil.la/863947 for details.
+ LOG(INFO) << "Skipping test for NSS 3.14.2 - NSS 3.15";
+ return;
+ }
+
+ NSSCertDatabase::ImportCertFailureList failed;
+
+ CertificateList intermediate_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, intermediate_certs.size());
+
+ // Import Intermediate CA cert and trust it.
+ EXPECT_TRUE(cert_db_->ImportCACerts(intermediate_certs,
+ NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ // Import server cert with default trust.
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(certs[0].get(), SERVER_CERT));
+
+ // Server cert should verify.
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+ // Without explicit trust of the intermediate, verification should fail.
+ EXPECT_TRUE(cert_db_->SetCertTrust(
+ intermediate_certs[0].get(), CA_CERT, NSSCertDatabase::TRUST_DEFAULT));
+
+ // Server cert should fail to verify.
+ CertVerifyResult verify_result2;
+ error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result2);
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, TrustIntermediateCa3) {
+ if (NSS_VersionCheck("3.14.2") && !NSS_VersionCheck("3.15")) {
+ // See http://bugzil.la/863947 for details.
+ LOG(INFO) << "Skipping test for NSS 3.14.2 - NSS 3.15";
+ return;
+ }
+
+ NSSCertDatabase::ImportCertFailureList failed;
+
+ CertificateList ca_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-root.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, ca_certs.size());
+
+ // Import Root CA cert and default trust it.
+ EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUST_DEFAULT,
+ &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList intermediate_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, intermediate_certs.size());
+
+ // Import Intermediate CA cert and trust it.
+ EXPECT_TRUE(cert_db_->ImportCACerts(intermediate_certs,
+ NSSCertDatabase::TRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ // Import server cert with default trust.
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(certs[0].get(), SERVER_CERT));
+
+ // Server cert should verify.
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result.cert_status);
+
+ // Without explicit trust of the intermediate, verification should fail.
+ EXPECT_TRUE(cert_db_->SetCertTrust(
+ intermediate_certs[0].get(), CA_CERT, NSSCertDatabase::TRUST_DEFAULT));
+
+ // Server cert should fail to verify.
+ CertVerifyResult verify_result2;
+ error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result2);
+ EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, error);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, verify_result2.cert_status);
+}
+
+TEST_F(CertDatabaseNSSTest, TrustIntermediateCa4) {
+ // Explicit distrust only works starting in NSS 3.13.
+ if (!NSS_VersionCheck("3.13")) {
+ LOG(INFO) << "test skipped on NSS < 3.13";
+ return;
+ }
+
+ NSSCertDatabase::ImportCertFailureList failed;
+
+ CertificateList ca_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-root.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, ca_certs.size());
+
+ // Import Root CA cert and trust it.
+ EXPECT_TRUE(cert_db_->ImportCACerts(ca_certs, NSSCertDatabase::TRUSTED_SSL,
+ &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList intermediate_certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, intermediate_certs.size());
+
+ // Import Intermediate CA cert and distrust it.
+ EXPECT_TRUE(cert_db_->ImportCACerts(
+ intermediate_certs, NSSCertDatabase::DISTRUSTED_SSL, &failed));
+ EXPECT_EQ(0U, failed.size());
+
+ CertificateList certs = CreateCertificateListFromFile(
+ GetTestCertsDirectory(), "2048-rsa-ee-by-2048-rsa-intermediate.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ // Import server cert with default trust.
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(certs[0].get(), SERVER_CERT));
+
+ // Server cert should not verify.
+ scoped_refptr<CertVerifyProc> verify_proc(new CertVerifyProcNSS());
+ int flags = 0;
+ CertVerifyResult verify_result;
+ int error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result);
+ EXPECT_EQ(ERR_CERT_REVOKED, error);
+ EXPECT_EQ(CERT_STATUS_REVOKED, verify_result.cert_status);
+
+ // Without explicit distrust of the intermediate, verification should succeed.
+ EXPECT_TRUE(cert_db_->SetCertTrust(
+ intermediate_certs[0].get(), CA_CERT, NSSCertDatabase::TRUST_DEFAULT));
+
+ // Server cert should verify.
+ CertVerifyResult verify_result2;
+ error = verify_proc->Verify(certs[0].get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ empty_cert_list_,
+ &verify_result2);
+ EXPECT_EQ(OK, error);
+ EXPECT_EQ(0U, verify_result2.cert_status);
+}
+
+// Importing two certificates with the same issuer and subject common name,
+// but overall distinct subject names, should succeed and generate a unique
+// nickname for the second certificate.
+TEST_F(CertDatabaseNSSTest, ImportDuplicateCommonName) {
+ CertificateList certs =
+ CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "duplicate_cn_1.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs.size());
+
+ EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
+
+ // Import server cert with default trust.
+ NSSCertDatabase::ImportCertFailureList failed;
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs, NSSCertDatabase::TRUST_DEFAULT, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(certs[0].get(), SERVER_CERT));
+
+ CertificateList new_certs = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, new_certs.size());
+
+ // Now attempt to import a different certificate with the same common name.
+ CertificateList certs2 =
+ CreateCertificateListFromFile(GetTestCertsDirectory(),
+ "duplicate_cn_2.pem",
+ X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(1U, certs2.size());
+
+ // Import server cert with default trust.
+ EXPECT_TRUE(cert_db_->ImportServerCert(
+ certs2, NSSCertDatabase::TRUST_DEFAULT, &failed));
+ EXPECT_EQ(0U, failed.size());
+ EXPECT_EQ(NSSCertDatabase::TRUST_DEFAULT,
+ cert_db_->GetCertTrust(certs2[0].get(), SERVER_CERT));
+
+ new_certs = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(2U, new_certs.size());
+ EXPECT_STRNE(new_certs[0]->os_cert_handle()->nickname,
+ new_certs[1]->os_cert_handle()->nickname);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pem_tokenizer.cc b/chromium/net/cert/pem_tokenizer.cc
new file mode 100644
index 00000000000..d9c152875c6
--- /dev/null
+++ b/chromium/net/cert/pem_tokenizer.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/pem_tokenizer.h"
+
+#include "base/base64.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+const char kPEMSearchBlock[] = "-----BEGIN ";
+const char kPEMBeginBlock[] = "-----BEGIN %s-----";
+const char kPEMEndBlock[] = "-----END %s-----";
+
+} // namespace
+
+namespace net {
+
+using base::StringPiece;
+
+struct PEMTokenizer::PEMType {
+ std::string type;
+ std::string header;
+ std::string footer;
+};
+
+PEMTokenizer::PEMTokenizer(
+ const StringPiece& str,
+ const std::vector<std::string>& allowed_block_types) {
+ Init(str, allowed_block_types);
+}
+
+PEMTokenizer::~PEMTokenizer() {
+}
+
+bool PEMTokenizer::GetNext() {
+ while (pos_ != StringPiece::npos) {
+ // Scan for the beginning of the next PEM encoded block.
+ pos_ = str_.find(kPEMSearchBlock, pos_);
+ if (pos_ == StringPiece::npos)
+ return false; // No more PEM blocks
+
+ std::vector<PEMType>::const_iterator it;
+ // Check to see if it is of an acceptable block type.
+ for (it = block_types_.begin(); it != block_types_.end(); ++it) {
+ if (!str_.substr(pos_).starts_with(it->header))
+ continue;
+
+ // Look for a footer matching the header. If none is found, then all
+ // data following this point is invalid and should not be parsed.
+ StringPiece::size_type footer_pos = str_.find(it->footer, pos_);
+ if (footer_pos == StringPiece::npos) {
+ pos_ = StringPiece::npos;
+ return false;
+ }
+
+ // Chop off the header and footer and parse the data in between.
+ StringPiece::size_type data_begin = pos_ + it->header.size();
+ pos_ = footer_pos + it->footer.size();
+ block_type_ = it->type;
+
+ StringPiece encoded = str_.substr(data_begin,
+ footer_pos - data_begin);
+ if (!base::Base64Decode(CollapseWhitespaceASCII(encoded.as_string(),
+ true), &data_)) {
+ // The most likely cause for a decode failure is a datatype that
+ // includes PEM headers, which are not supported.
+ break;
+ }
+
+ return true;
+ }
+
+ // If the block did not match any acceptable type, move past it and
+ // continue the search. Otherwise, |pos_| has been updated to the most
+ // appropriate search position to continue searching from and should not
+ // be adjusted.
+ if (it == block_types_.end())
+ pos_ += sizeof(kPEMSearchBlock);
+ }
+
+ return false;
+}
+
+void PEMTokenizer::Init(
+ const StringPiece& str,
+ const std::vector<std::string>& allowed_block_types) {
+ str_ = str;
+ pos_ = 0;
+
+ // Construct PEM header/footer strings for all the accepted types, to
+ // reduce parsing later.
+ for (std::vector<std::string>::const_iterator it =
+ allowed_block_types.begin(); it != allowed_block_types.end(); ++it) {
+ PEMType allowed_type;
+ allowed_type.type = *it;
+ allowed_type.header = base::StringPrintf(kPEMBeginBlock, it->c_str());
+ allowed_type.footer = base::StringPrintf(kPEMEndBlock, it->c_str());
+ block_types_.push_back(allowed_type);
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/cert/pem_tokenizer.h b/chromium/net/cert/pem_tokenizer.h
new file mode 100644
index 00000000000..bb41c446db7
--- /dev/null
+++ b/chromium/net/cert/pem_tokenizer.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_PEM_TOKENIZER_H_
+#define NET_CERT_PEM_TOKENIZER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// PEMTokenizer is a utility class for the parsing of data encapsulated
+// using RFC 1421, Privacy Enhancement for Internet Electronic Mail. It
+// does not implement the full specification, most notably it does not
+// support the Encapsulated Header Portion described in Section 4.4.
+class NET_EXPORT_PRIVATE PEMTokenizer {
+ public:
+ // Create a new PEMTokenizer that iterates through |str| searching for
+ // instances of PEM encoded blocks that are of the |allowed_block_types|.
+ // |str| must remain valid for the duration of the PEMTokenizer.
+ PEMTokenizer(const base::StringPiece& str,
+ const std::vector<std::string>& allowed_block_types);
+ ~PEMTokenizer();
+
+ // Attempts to decode the next PEM block in the string. Returns false if no
+ // PEM blocks can be decoded. The decoded PEM block will be available via
+ // data().
+ bool GetNext();
+
+ // Returns the PEM block type (eg: CERTIFICATE) of the last successfully
+ // decoded PEM block.
+ // GetNext() must have returned true before calling this method.
+ const std::string& block_type() const { return block_type_; }
+
+ // Returns the raw, Base64-decoded data of the last successfully decoded
+ // PEM block.
+ // GetNext() must have returned true before calling this method.
+ const std::string& data() const { return data_; }
+
+ private:
+ void Init(const base::StringPiece& str,
+ const std::vector<std::string>& allowed_block_types);
+
+ // A simple cache of the allowed PEM header and footer for a given PEM
+ // block type, so that it is only computed once.
+ struct PEMType;
+
+ // The string to search, which must remain valid for as long as this class
+ // is around.
+ base::StringPiece str_;
+
+ // The current position within |str_| that searching should begin from,
+ // or StringPiece::npos if iteration is complete
+ base::StringPiece::size_type pos_;
+
+ // The type of data that was encoded, as indicated in the PEM
+ // Pre-Encapsulation Boundary (eg: CERTIFICATE, PKCS7, or
+ // PRIVACY-ENHANCED MESSAGE).
+ std::string block_type_;
+
+ // The types of PEM blocks that are allowed. PEM blocks that are not of
+ // one of these types will be skipped.
+ std::vector<PEMType> block_types_;
+
+ // The raw (Base64-decoded) data of the last successfully decoded block.
+ std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(PEMTokenizer);
+};
+
+} // namespace net
+
+#endif // NET_CERT_PEM_TOKENIZER_H_
diff --git a/chromium/net/cert/pem_tokenizer_unittest.cc b/chromium/net/cert/pem_tokenizer_unittest.cc
new file mode 100644
index 00000000000..d5334db8999
--- /dev/null
+++ b/chromium/net/cert/pem_tokenizer_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/pem_tokenizer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(PEMTokenizerTest, BasicParsing) {
+ const char data[] =
+ "-----BEGIN EXPECTED-BLOCK-----\n"
+ "TWF0Y2hlc0FjY2VwdGVkQmxvY2tUeXBl\n"
+ "-----END EXPECTED-BLOCK-----\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("EXPECTED-BLOCK");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type());
+ EXPECT_EQ("MatchesAcceptedBlockType", tokenizer.data());
+
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, CarriageReturnLineFeeds) {
+ const char data[] =
+ "-----BEGIN EXPECTED-BLOCK-----\r\n"
+ "TWF0Y2hlc0FjY2VwdGVkQmxvY2tUeXBl\r\n"
+ "-----END EXPECTED-BLOCK-----\r\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("EXPECTED-BLOCK");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type());
+ EXPECT_EQ("MatchesAcceptedBlockType", tokenizer.data());
+
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, NoAcceptedBlockTypes) {
+ const char data[] =
+ "-----BEGIN UNEXPECTED-BLOCK-----\n"
+ "SWdub3Jlc1JlamVjdGVkQmxvY2tUeXBl\n"
+ "-----END UNEXPECTED-BLOCK-----\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("EXPECTED-BLOCK");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, MultipleAcceptedBlockTypes) {
+ const char data[] =
+ "-----BEGIN BLOCK-ONE-----\n"
+ "RW5jb2RlZERhdGFPbmU=\n"
+ "-----END BLOCK-ONE-----\n"
+ "-----BEGIN BLOCK-TWO-----\n"
+ "RW5jb2RlZERhdGFUd28=\n"
+ "-----END BLOCK-TWO-----\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("BLOCK-ONE");
+ accepted_types.push_back("BLOCK-TWO");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("BLOCK-ONE", tokenizer.block_type());
+ EXPECT_EQ("EncodedDataOne", tokenizer.data());
+
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("BLOCK-TWO", tokenizer.block_type());
+ EXPECT_EQ("EncodedDataTwo", tokenizer.data());
+
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, MissingFooter) {
+ const char data[] =
+ "-----BEGIN MISSING-FOOTER-----\n"
+ "RW5jb2RlZERhdGFPbmU=\n"
+ "-----END MISSING-FOOTER-----\n"
+ "-----BEGIN MISSING-FOOTER-----\n"
+ "RW5jb2RlZERhdGFUd28=\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("MISSING-FOOTER");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("MISSING-FOOTER", tokenizer.block_type());
+ EXPECT_EQ("EncodedDataOne", tokenizer.data());
+
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, NestedEncoding) {
+ const char data[] =
+ "-----BEGIN BLOCK-ONE-----\n"
+ "RW5jb2RlZERhdGFPbmU=\n"
+ "-----BEGIN BLOCK-TWO-----\n"
+ "RW5jb2RlZERhdGFUd28=\n"
+ "-----END BLOCK-TWO-----\n"
+ "-----END BLOCK-ONE-----\n"
+ "-----BEGIN BLOCK-ONE-----\n"
+ "RW5jb2RlZERhdGFUaHJlZQ==\n"
+ "-----END BLOCK-ONE-----\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("BLOCK-ONE");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("BLOCK-ONE", tokenizer.block_type());
+ EXPECT_EQ("EncodedDataThree", tokenizer.data());
+
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, EmptyAcceptedTypes) {
+ const char data[] =
+ "-----BEGIN BLOCK-ONE-----\n"
+ "RW5jb2RlZERhdGFPbmU=\n"
+ "-----END BLOCK-ONE-----\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+TEST(PEMTokenizerTest, BlockWithHeader) {
+ const char data[] =
+ "-----BEGIN BLOCK-ONE-----\n"
+ "Header-One: Data data data\n"
+ "Header-Two: \n"
+ " continuation\n"
+ "Header-Three: Mix-And,Match\n"
+ "\n"
+ "RW5jb2RlZERhdGFPbmU=\n"
+ "-----END BLOCK-ONE-----\n"
+ "-----BEGIN BLOCK-ONE-----\n"
+ "RW5jb2RlZERhdGFUd28=\n"
+ "-----END BLOCK-ONE-----\n";
+ base::StringPiece string_piece(data);
+ std::vector<std::string> accepted_types;
+ accepted_types.push_back("BLOCK-ONE");
+
+ PEMTokenizer tokenizer(string_piece, accepted_types);
+ EXPECT_TRUE(tokenizer.GetNext());
+
+ EXPECT_EQ("BLOCK-ONE", tokenizer.block_type());
+ EXPECT_EQ("EncodedDataTwo", tokenizer.data());
+
+ EXPECT_FALSE(tokenizer.GetNext());
+}
+
+} // namespace net
diff --git a/chromium/net/cert/single_request_cert_verifier.cc b/chromium/net/cert/single_request_cert_verifier.cc
new file mode 100644
index 00000000000..909af07d848
--- /dev/null
+++ b/chromium/net/cert/single_request_cert_verifier.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/single_request_cert_verifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+SingleRequestCertVerifier::SingleRequestCertVerifier(
+ CertVerifier* cert_verifier)
+ : cert_verifier_(cert_verifier),
+ cur_request_(NULL) {
+ DCHECK(cert_verifier_ != NULL);
+}
+
+SingleRequestCertVerifier::~SingleRequestCertVerifier() {
+ if (cur_request_) {
+ cert_verifier_->CancelRequest(cur_request_);
+ cur_request_ = NULL;
+ }
+}
+
+int SingleRequestCertVerifier::Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ const BoundNetLog& net_log) {
+ // Should not be already in use.
+ DCHECK(!cur_request_ && cur_request_callback_.is_null());
+
+ CertVerifier::RequestHandle request = NULL;
+
+ // We need to be notified of completion before |callback| is called, so that
+ // we can clear out |cur_request_*|.
+ int rv = cert_verifier_->Verify(
+ cert, hostname, flags, crl_set, verify_result,
+ base::Bind(&SingleRequestCertVerifier::OnVerifyCompletion,
+ base::Unretained(this)),
+ &request, net_log);
+
+ if (rv == ERR_IO_PENDING) {
+ // Cleared in OnVerifyCompletion().
+ cur_request_ = request;
+ cur_request_callback_ = callback;
+ }
+
+ return rv;
+}
+
+void SingleRequestCertVerifier::OnVerifyCompletion(int result) {
+ DCHECK(cur_request_ && !cur_request_callback_.is_null());
+
+ CompletionCallback callback = cur_request_callback_;
+
+ // Clear the outstanding request information.
+ cur_request_ = NULL;
+ cur_request_callback_.Reset();
+
+ // Call the user's original callback.
+ callback.Run(result);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/single_request_cert_verifier.h b/chromium/net/cert/single_request_cert_verifier.h
new file mode 100644
index 00000000000..6b192819c62
--- /dev/null
+++ b/chromium/net/cert/single_request_cert_verifier.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_SINGLE_REQUEST_CERT_VERIFIER_H_
+#define NET_CERT_SINGLE_REQUEST_CERT_VERIFIER_H_
+
+#include "net/cert/cert_verifier.h"
+
+namespace net {
+
+// This class represents the task of verifying a certificate. It wraps
+// CertVerifier to verify only a single certificate at a time and cancels this
+// request when going out of scope.
+class SingleRequestCertVerifier {
+ public:
+ // |cert_verifier| must remain valid for the lifetime of |this|.
+ explicit SingleRequestCertVerifier(CertVerifier* cert_verifier);
+
+ // If a completion callback is pending when the verifier is destroyed, the
+ // certificate verification is canceled, and the completion callback will
+ // not be called.
+ ~SingleRequestCertVerifier();
+
+ // Verifies the given certificate, filling out the |verify_result| object
+ // upon success. See CertVerifier::Verify() for details.
+ int Verify(X509Certificate* cert,
+ const std::string& hostname,
+ int flags,
+ CRLSet* crl_set,
+ CertVerifyResult* verify_result,
+ const CompletionCallback& callback,
+ const BoundNetLog& net_log);
+
+ private:
+ // Callback for when the request to |cert_verifier_| completes, so we
+ // dispatch to the user's callback.
+ void OnVerifyCompletion(int result);
+
+ // The actual certificate verifier that will handle the request.
+ CertVerifier* const cert_verifier_;
+
+ // The current request (if any).
+ CertVerifier::RequestHandle cur_request_;
+ CompletionCallback cur_request_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SingleRequestCertVerifier);
+};
+
+} // namespace net
+
+#endif // NET_CERT_SINGLE_REQUEST_CERT_VERIFIER_H_
diff --git a/chromium/net/cert/test_root_certs.cc b/chromium/net/cert/test_root_certs.cc
new file mode 100644
index 00000000000..3219f1d84c1
--- /dev/null
+++ b/chromium/net/cert/test_root_certs.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/test_root_certs.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+bool g_has_instance = false;
+
+base::LazyInstance<TestRootCerts>::Leaky
+ g_test_root_certs = LAZY_INSTANCE_INITIALIZER;
+
+CertificateList LoadCertificates(const base::FilePath& filename) {
+ std::string raw_cert;
+ if (!file_util::ReadFileToString(filename, &raw_cert)) {
+ LOG(ERROR) << "Can't load certificate " << filename.value();
+ return CertificateList();
+ }
+
+ return X509Certificate::CreateCertificateListFromBytes(
+ raw_cert.data(), raw_cert.length(), X509Certificate::FORMAT_AUTO);
+}
+
+} // namespace
+
+// static
+TestRootCerts* TestRootCerts::GetInstance() {
+ return g_test_root_certs.Pointer();
+}
+
+bool TestRootCerts::HasInstance() {
+ return g_has_instance;
+}
+
+bool TestRootCerts::AddFromFile(const base::FilePath& file) {
+ CertificateList root_certs = LoadCertificates(file);
+ if (root_certs.empty() || root_certs.size() > 1)
+ return false;
+
+ return Add(root_certs.front().get());
+}
+
+TestRootCerts::TestRootCerts() {
+ Init();
+ g_has_instance = true;
+}
+
+ScopedTestRoot::ScopedTestRoot() {}
+
+ScopedTestRoot::ScopedTestRoot(X509Certificate* cert) {
+ Reset(cert);
+}
+
+ScopedTestRoot::~ScopedTestRoot() {
+ Reset(NULL);
+}
+
+void ScopedTestRoot::Reset(X509Certificate* cert) {
+ if (cert_.get())
+ TestRootCerts::GetInstance()->Clear();
+ if (cert)
+ TestRootCerts::GetInstance()->Add(cert);
+ cert_ = cert;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/test_root_certs.h b/chromium/net/cert/test_root_certs.h
new file mode 100644
index 00000000000..22c635f9dbb
--- /dev/null
+++ b/chromium/net/cert/test_root_certs.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_TEST_ROOT_CERTS_H_
+#define NET_CERT_TEST_ROOT_CERTS_H_
+
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "build/build_config.h"
+#include "net/base/net_export.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include <list>
+#elif defined(OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#elif defined(OS_MACOSX)
+#include <CoreFoundation/CFArray.h>
+#include <Security/SecTrust.h>
+#include "base/mac/scoped_cftyperef.h"
+#endif
+
+namespace base {
+class FilePath;
+}
+
+namespace net {
+
+class X509Certificate;
+
+// TestRootCerts is a helper class for unit tests that is used to
+// artificially mark a certificate as trusted, independent of the local
+// machine configuration.
+class NET_EXPORT_PRIVATE TestRootCerts {
+ public:
+ // Obtains the Singleton instance to the trusted certificates.
+ static TestRootCerts* GetInstance();
+
+ // Returns true if an instance exists, without forcing an initialization.
+ static bool HasInstance();
+
+ // Marks |certificate| as trusted for X509Certificate::Verify(). Returns
+ // false if the certificate could not be marked trusted.
+ bool Add(X509Certificate* certificate);
+
+ // Reads a single certificate from |file| and marks it as trusted. Returns
+ // false if an error is encountered, such as being unable to read |file|
+ // or more than one certificate existing in |file|.
+ bool AddFromFile(const base::FilePath& file);
+
+ // Clears the trusted status of any certificates that were previously
+ // marked trusted via Add().
+ void Clear();
+
+ // Returns true if there are no certificates that have been marked trusted.
+ bool IsEmpty() const;
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ CFArrayRef temporary_roots() const { return temporary_roots_; }
+
+ // Modifies the root certificates of |trust_ref| to include the
+ // certificates stored in |temporary_roots_|. If IsEmpty() is true, this
+ // does not modify |trust_ref|.
+ OSStatus FixupSecTrustRef(SecTrustRef trust_ref) const;
+
+ // Configures whether or not the default/system root store should also
+ // be trusted. By default, this is true, indicating that the TestRootCerts
+ // are used in addition to OS trust store.
+ void SetAllowSystemTrust(bool allow_system_trust);
+
+#elif defined(OS_WIN)
+ HCERTSTORE temporary_roots() const { return temporary_roots_; }
+
+ // Returns an HCERTCHAINENGINE suitable to be used for certificate
+ // validation routines, or NULL to indicate that the default system chain
+ // engine is appropriate. The caller is responsible for freeing the
+ // returned HCERTCHAINENGINE.
+ HCERTCHAINENGINE GetChainEngine() const;
+#endif
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<TestRootCerts>;
+
+ TestRootCerts();
+ ~TestRootCerts();
+
+ // Performs platform-dependent initialization.
+ void Init();
+
+#if defined(USE_NSS) || defined(OS_IOS)
+ // It is necessary to maintain a cache of the original certificate trust
+ // settings, in order to restore them when Clear() is called.
+ class TrustEntry;
+ std::list<TrustEntry*> trust_cache_;
+#elif defined(OS_WIN)
+ HCERTSTORE temporary_roots_;
+#elif defined(OS_MACOSX)
+ base::ScopedCFTypeRef<CFMutableArrayRef> temporary_roots_;
+ bool allow_system_trust_;
+#endif
+
+#if defined(OS_WIN) || defined(USE_OPENSSL)
+ // True if there are no temporarily trusted root certificates.
+ bool empty_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(TestRootCerts);
+};
+
+// Scoped helper for unittests to handle safely managing trusted roots.
+class NET_EXPORT_PRIVATE ScopedTestRoot {
+ public:
+ ScopedTestRoot();
+ // Creates a ScopedTestRoot that will adds|cert| to the TestRootCerts store.
+ explicit ScopedTestRoot(X509Certificate* cert);
+ ~ScopedTestRoot();
+
+ // Assigns |cert| to be the new test root cert. If |cert| is NULL, undoes
+ // any work the ScopedTestRoot may have previously done.
+ // If |cert_| contains a certificate (due to a prior call to Reset or due to
+ // a cert being passed at construction), the existing TestRootCerts store is
+ // cleared.
+ void Reset(X509Certificate* cert);
+
+ private:
+ scoped_refptr<X509Certificate> cert_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestRoot);
+};
+
+} // namespace net
+
+#endif // NET_CERT_TEST_ROOT_CERTS_H_
diff --git a/chromium/net/cert/test_root_certs_android.cc b/chromium/net/cert/test_root_certs_android.cc
new file mode 100644
index 00000000000..73588270e95
--- /dev/null
+++ b/chromium/net/cert/test_root_certs_android.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/test_root_certs.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "net/android/network_library.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+bool TestRootCerts::Add(X509Certificate* certificate) {
+ std::string cert_bytes;
+ if (!X509Certificate::GetDEREncoded(certificate->os_cert_handle(),
+ &cert_bytes))
+ return false;
+ android::AddTestRootCertificate(
+ reinterpret_cast<const uint8*>(cert_bytes.data()), cert_bytes.size());
+ empty_ = false;
+ return true;
+}
+
+void TestRootCerts::Clear() {
+ if (empty_)
+ return;
+
+ android::ClearTestRootCertificates();
+ empty_ = true;
+}
+
+bool TestRootCerts::IsEmpty() const {
+ return empty_;
+}
+
+TestRootCerts::~TestRootCerts() {}
+
+void TestRootCerts::Init() {
+ empty_ = true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/test_root_certs_mac.cc b/chromium/net/cert/test_root_certs_mac.cc
new file mode 100644
index 00000000000..87824d4c9ed
--- /dev/null
+++ b/chromium/net/cert/test_root_certs_mac.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/test_root_certs.h"
+
+#include <Security/Security.h>
+
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+typedef OSStatus (*SecTrustSetAnchorCertificatesOnlyFuncPtr)(SecTrustRef,
+ Boolean);
+
+Boolean OurSecCertificateEqual(const void* value1, const void* value2) {
+ if (CFGetTypeID(value1) != SecCertificateGetTypeID() ||
+ CFGetTypeID(value2) != SecCertificateGetTypeID())
+ return CFEqual(value1, value2);
+ return X509Certificate::IsSameOSCert(
+ reinterpret_cast<SecCertificateRef>(const_cast<void*>(value1)),
+ reinterpret_cast<SecCertificateRef>(const_cast<void*>(value2)));
+}
+
+const void* RetainWrapper(CFAllocatorRef unused, const void* value) {
+ return CFRetain(value);
+}
+
+void ReleaseWrapper(CFAllocatorRef unused, const void* value) {
+ CFRelease(value);
+}
+
+// CFEqual prior to 10.6 only performed pointer checks on SecCertificateRefs,
+// rather than checking if they were the same (logical) certificate, so a
+// custom structure is used for the array callbacks.
+const CFArrayCallBacks kCertArrayCallbacks = {
+ 0, // version
+ RetainWrapper,
+ ReleaseWrapper,
+ CFCopyDescription,
+ OurSecCertificateEqual,
+};
+
+} // namespace
+
+bool TestRootCerts::Add(X509Certificate* certificate) {
+ if (CFArrayContainsValue(temporary_roots_,
+ CFRangeMake(0, CFArrayGetCount(temporary_roots_)),
+ certificate->os_cert_handle()))
+ return true;
+ CFArrayAppendValue(temporary_roots_, certificate->os_cert_handle());
+ return true;
+}
+
+void TestRootCerts::Clear() {
+ CFArrayRemoveAllValues(temporary_roots_);
+}
+
+bool TestRootCerts::IsEmpty() const {
+ return CFArrayGetCount(temporary_roots_) == 0;
+}
+
+OSStatus TestRootCerts::FixupSecTrustRef(SecTrustRef trust_ref) const {
+ if (IsEmpty())
+ return noErr;
+
+ // Despite SecTrustSetAnchorCertificatesOnly existing in OS X 10.6, and
+ // being documented as available, it is not actually implemented. On 10.7+,
+ // however, it always works.
+ if (base::mac::IsOSLionOrLater()) {
+ OSStatus status = SecTrustSetAnchorCertificates(trust_ref,
+ temporary_roots_);
+ if (status)
+ return status;
+ return SecTrustSetAnchorCertificatesOnly(trust_ref, !allow_system_trust_);
+ }
+
+ if (!allow_system_trust_) {
+ // Avoid any copying if system roots are not to be trusted. This acts as
+ // an exclusive list on 10.6, replacing the built-ins.
+ return SecTrustSetAnchorCertificates(trust_ref, temporary_roots_);
+ }
+
+ // Otherwise, both system trust and temporary_roots_ must be trusted.
+ // Emulate the functionality of SecTrustSetAnchorCertificatesOnly by
+ // creating a copy of the system roots and merging with temporary_roots_.
+ CFArrayRef system_roots = NULL;
+ OSStatus status = SecTrustCopyAnchorCertificates(&system_roots);
+ if (status)
+ return status;
+
+ base::ScopedCFTypeRef<CFArrayRef> scoped_system_roots(system_roots);
+ base::ScopedCFTypeRef<CFMutableArrayRef> scoped_roots(
+ CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, scoped_system_roots));
+ CFArrayAppendArray(scoped_roots, temporary_roots_,
+ CFRangeMake(0, CFArrayGetCount(temporary_roots_)));
+ return SecTrustSetAnchorCertificates(trust_ref, scoped_roots);
+}
+
+void TestRootCerts::SetAllowSystemTrust(bool allow_system_trust) {
+ allow_system_trust_ = allow_system_trust;
+}
+
+TestRootCerts::~TestRootCerts() {}
+
+void TestRootCerts::Init() {
+ temporary_roots_.reset(CFArrayCreateMutable(kCFAllocatorDefault, 0,
+ &kCertArrayCallbacks));
+ allow_system_trust_ = true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/test_root_certs_nss.cc b/chromium/net/cert/test_root_certs_nss.cc
new file mode 100644
index 00000000000..3a2f88a7968
--- /dev/null
+++ b/chromium/net/cert/test_root_certs_nss.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/test_root_certs.h"
+
+#include <cert.h>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "crypto/nss_util.h"
+#include "net/cert/x509_certificate.h"
+
+#if defined(OS_IOS)
+#include "net/cert/x509_util_ios.h"
+#endif
+
+namespace net {
+
+// TrustEntry is used to store the original CERTCertificate and CERTCertTrust
+// for a certificate whose trust status has been changed by the
+// TestRootCerts.
+class TestRootCerts::TrustEntry {
+ public:
+ // Creates a new TrustEntry by incrementing the reference to |certificate|
+ // and copying |trust|.
+ TrustEntry(CERTCertificate* certificate, const CERTCertTrust& trust);
+ ~TrustEntry();
+
+ CERTCertificate* certificate() const { return certificate_; }
+ const CERTCertTrust& trust() const { return trust_; }
+
+ private:
+ // The temporary root certificate.
+ CERTCertificate* certificate_;
+
+ // The original trust settings, before |certificate_| was manipulated to
+ // be a temporarily trusted root.
+ CERTCertTrust trust_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrustEntry);
+};
+
+TestRootCerts::TrustEntry::TrustEntry(CERTCertificate* certificate,
+ const CERTCertTrust& trust)
+ : certificate_(CERT_DupCertificate(certificate)),
+ trust_(trust) {
+}
+
+TestRootCerts::TrustEntry::~TrustEntry() {
+ CERT_DestroyCertificate(certificate_);
+}
+
+bool TestRootCerts::Add(X509Certificate* certificate) {
+#if defined(OS_IOS)
+ x509_util_ios::NSSCertificate nss_certificate(certificate->os_cert_handle());
+ CERTCertificate* cert_handle = nss_certificate.cert_handle();
+#else
+ CERTCertificate* cert_handle = certificate->os_cert_handle();
+#endif
+ // Preserve the original trust bits so that they can be restored when
+ // the certificate is removed.
+ CERTCertTrust original_trust;
+ SECStatus rv = CERT_GetCertTrust(cert_handle, &original_trust);
+ if (rv != SECSuccess) {
+ // CERT_GetCertTrust will fail if the certificate does not have any
+ // particular trust settings associated with it, and attempts to use
+ // |original_trust| later to restore the original trust settings will not
+ // cause the trust settings to be revoked. If the certificate has no
+ // particular trust settings associated with it, mark the certificate as
+ // a valid CA certificate with no specific trust.
+ rv = CERT_DecodeTrustString(&original_trust, "c,c,c");
+ }
+
+ // Change the trust bits to unconditionally trust this certificate.
+ CERTCertTrust new_trust;
+ rv = CERT_DecodeTrustString(&new_trust, "TCu,Cu,Tu");
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Cannot decode certificate trust string.";
+ return false;
+ }
+
+ rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert_handle, &new_trust);
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Cannot change certificate trust.";
+ return false;
+ }
+
+ trust_cache_.push_back(new TrustEntry(cert_handle, original_trust));
+ return true;
+}
+
+void TestRootCerts::Clear() {
+ // Restore the certificate trusts to what they were originally, before
+ // Add() was called. Work from the rear first, since if a certificate was
+ // added twice, the second entry's original trust status will be that of
+ // the first entry, while the first entry contains the desired resultant
+ // status.
+ for (std::list<TrustEntry*>::reverse_iterator it = trust_cache_.rbegin();
+ it != trust_cache_.rend(); ++it) {
+ CERTCertTrust original_trust = (*it)->trust();
+ SECStatus rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
+ (*it)->certificate(),
+ &original_trust);
+ // DCHECK(), rather than LOG(), as a failure to restore the original
+ // trust can cause flake or hard-to-trace errors in any unit tests that
+ // occur after Clear() has been called.
+ DCHECK_EQ(SECSuccess, rv) << "Cannot restore certificate trust.";
+ }
+ STLDeleteElements(&trust_cache_);
+}
+
+bool TestRootCerts::IsEmpty() const {
+ return trust_cache_.empty();
+}
+
+TestRootCerts::~TestRootCerts() {
+ Clear();
+}
+
+void TestRootCerts::Init() {
+ crypto::EnsureNSSInit();
+}
+
+} // namespace net
diff --git a/chromium/net/cert/test_root_certs_openssl.cc b/chromium/net/cert/test_root_certs_openssl.cc
new file mode 100644
index 00000000000..3d5cf3d72d1
--- /dev/null
+++ b/chromium/net/cert/test_root_certs_openssl.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/test_root_certs.h"
+
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "crypto/openssl_util.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+bool TestRootCerts::Add(X509Certificate* certificate) {
+ if (!X509_STORE_add_cert(X509Certificate::cert_store(),
+ certificate->os_cert_handle())) {
+ unsigned long error_code = ERR_peek_error();
+ if (ERR_GET_LIB(error_code) != ERR_LIB_X509 ||
+ ERR_GET_REASON(error_code) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ crypto::ClearOpenSSLERRStack(FROM_HERE);
+ return false;
+ }
+ ERR_clear_error();
+ }
+
+ empty_ = false;
+ return true;
+}
+
+void TestRootCerts::Clear() {
+ if (empty_)
+ return;
+
+ X509Certificate::ResetCertStore();
+ empty_ = true;
+}
+
+bool TestRootCerts::IsEmpty() const {
+ return empty_;
+}
+
+TestRootCerts::~TestRootCerts() {}
+
+void TestRootCerts::Init() {
+ empty_ = true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/test_root_certs_unittest.cc b/chromium/net/cert/test_root_certs_unittest.cc
new file mode 100644
index 00000000000..74c3551862b
--- /dev/null
+++ b/chromium/net/cert/test_root_certs_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_NSS) || defined(OS_IOS)
+#include <nss.h>
+#endif
+
+namespace net {
+
+namespace {
+
+// The local test root certificate.
+const char kRootCertificateFile[] = "root_ca_cert.pem";
+// A certificate issued by the local test root for 127.0.0.1.
+const char kGoodCertificateFile[] = "ok_cert.pem";
+
+} // namespace
+
+// Test basic functionality when adding from an existing X509Certificate.
+TEST(TestRootCertsTest, AddFromPointer) {
+ scoped_refptr<X509Certificate> root_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert.get());
+
+ TestRootCerts* test_roots = TestRootCerts::GetInstance();
+ ASSERT_NE(static_cast<TestRootCerts*>(NULL), test_roots);
+ EXPECT_TRUE(test_roots->IsEmpty());
+
+ EXPECT_TRUE(test_roots->Add(root_cert.get()));
+ EXPECT_FALSE(test_roots->IsEmpty());
+
+ test_roots->Clear();
+ EXPECT_TRUE(test_roots->IsEmpty());
+}
+
+// Test basic functionality when adding directly from a file, which should
+// behave the same as when adding from an existing certificate.
+TEST(TestRootCertsTest, AddFromFile) {
+ TestRootCerts* test_roots = TestRootCerts::GetInstance();
+ ASSERT_NE(static_cast<TestRootCerts*>(NULL), test_roots);
+ EXPECT_TRUE(test_roots->IsEmpty());
+
+ base::FilePath cert_path =
+ GetTestCertsDirectory().AppendASCII(kRootCertificateFile);
+ EXPECT_TRUE(test_roots->AddFromFile(cert_path));
+ EXPECT_FALSE(test_roots->IsEmpty());
+
+ test_roots->Clear();
+ EXPECT_TRUE(test_roots->IsEmpty());
+}
+
+// Test that TestRootCerts actually adds the appropriate trust status flags
+// when requested, and that the trusted status is cleared once the root is
+// removed the TestRootCerts. This test acts as a canary/sanity check for
+// the results of the rest of net_unittests, ensuring that the trust status
+// is properly being set and cleared.
+TEST(TestRootCertsTest, OverrideTrust) {
+#if defined(USE_NSS) || defined(OS_IOS)
+ if (NSS_VersionCheck("3.14.2") && !NSS_VersionCheck("3.15")) {
+ // See http://bugzil.la/863947 for details
+ LOG(INFO) << "Skipping test for NSS 3.14.2 - NSS 3.15";
+ return;
+ }
+#endif
+
+ TestRootCerts* test_roots = TestRootCerts::GetInstance();
+ ASSERT_NE(static_cast<TestRootCerts*>(NULL), test_roots);
+ EXPECT_TRUE(test_roots->IsEmpty());
+
+ scoped_refptr<X509Certificate> test_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), kGoodCertificateFile);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert.get());
+
+ // Test that the good certificate fails verification, because the root
+ // certificate should not yet be trusted.
+ int flags = 0;
+ CertVerifyResult bad_verify_result;
+ scoped_refptr<CertVerifyProc> verify_proc(CertVerifyProc::CreateDefault());
+ int bad_status = verify_proc->Verify(test_cert.get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ CertificateList(),
+ &bad_verify_result);
+ EXPECT_NE(OK, bad_status);
+ EXPECT_NE(0u, bad_verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
+
+ // Add the root certificate and mark it as trusted.
+ EXPECT_TRUE(test_roots->AddFromFile(
+ GetTestCertsDirectory().AppendASCII(kRootCertificateFile)));
+ EXPECT_FALSE(test_roots->IsEmpty());
+
+ // Test that the certificate verification now succeeds, because the
+ // TestRootCerts is successfully imbuing trust.
+ CertVerifyResult good_verify_result;
+ int good_status = verify_proc->Verify(test_cert.get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ CertificateList(),
+ &good_verify_result);
+ EXPECT_EQ(OK, good_status);
+ EXPECT_EQ(0u, good_verify_result.cert_status);
+
+ test_roots->Clear();
+ EXPECT_TRUE(test_roots->IsEmpty());
+
+ // Ensure that when the TestRootCerts is cleared, the trust settings
+ // revert to their original state, and don't linger. If trust status
+ // lingers, it will likely break other tests in net_unittests.
+ CertVerifyResult restored_verify_result;
+ int restored_status = verify_proc->Verify(test_cert.get(),
+ "127.0.0.1",
+ flags,
+ NULL,
+ CertificateList(),
+ &restored_verify_result);
+ EXPECT_NE(OK, restored_status);
+ EXPECT_NE(0u,
+ restored_verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
+ EXPECT_EQ(bad_status, restored_status);
+ EXPECT_EQ(bad_verify_result.cert_status, restored_verify_result.cert_status);
+}
+
+// TODO(rsleevi): Add tests for revocation checking via CRLs, ensuring that
+// TestRootCerts properly injects itself into the validation process. See
+// http://crbug.com/63958
+
+} // namespace net
diff --git a/chromium/net/cert/test_root_certs_win.cc b/chromium/net/cert/test_root_certs_win.cc
new file mode 100644
index 00000000000..90a21d65889
--- /dev/null
+++ b/chromium/net/cert/test_root_certs_win.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/test_root_certs.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include "base/basictypes.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/win/win_util.h"
+#include "base/win/windows_version.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+// Provides a CertDllOpenStoreProv callback provider function, to be called
+// by CertOpenStore when the CERT_STORE_PROV_SYSTEM_W store is opened. See
+// http://msdn.microsoft.com/en-us/library/aa376043(VS.85).aspx.
+BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider,
+ DWORD encoding,
+ HCRYPTPROV crypt_provider,
+ DWORD flags,
+ const void* extra,
+ HCERTSTORE memory_store,
+ PCERT_STORE_PROV_INFO store_info);
+
+// CryptoAPIInjector is used to inject a store provider function for system
+// certificate stores before the one provided internally by Crypt32.dll.
+// Once injected, there is no way to remove, so every call to open a system
+// store will be redirected to the injected function.
+struct CryptoAPIInjector {
+ // The previous default function for opening system stores. For most
+ // configurations, this should point to Crypt32's internal
+ // I_CertDllOpenSystemStoreProvW function.
+ PFN_CERT_DLL_OPEN_STORE_PROV_FUNC original_function;
+
+ // The handle that CryptoAPI uses to ensure the DLL implementing
+ // |original_function| remains loaded in memory.
+ HCRYPTOIDFUNCADDR original_handle;
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<CryptoAPIInjector>;
+
+ CryptoAPIInjector()
+ : original_function(NULL),
+ original_handle(NULL) {
+ HCRYPTOIDFUNCSET registered_functions =
+ CryptInitOIDFunctionSet(CRYPT_OID_OPEN_STORE_PROV_FUNC, 0);
+
+ // Preserve the original handler function in |original_function|. If other
+ // functions are overridden, they will also need to be preserved.
+ BOOL ok = CryptGetOIDFunctionAddress(
+ registered_functions, 0, CERT_STORE_PROV_SYSTEM_W, 0,
+ reinterpret_cast<void**>(&original_function), &original_handle);
+ DCHECK(ok);
+
+ // For now, intercept only the numeric form of the system store
+ // function, CERT_STORE_PROV_SYSTEM_W (0x0A), which is what Crypt32
+ // functionality uses exclusively. Depending on the machine that tests
+ // are being run on, it may prove necessary to also intercept
+ // sz_CERT_STORE_PROV_SYSTEM_[A/W] and CERT_STORE_PROV_SYSTEM_A, based
+ // on whether or not any third-party CryptoAPI modules have been
+ // installed.
+ const CRYPT_OID_FUNC_ENTRY kFunctionToIntercept =
+ { CERT_STORE_PROV_SYSTEM_W, &InterceptedOpenStoreW };
+
+ // Inject kFunctionToIntercept at the front of the linked list that
+ // crypt32 uses when CertOpenStore is called, replacing the existing
+ // registered function.
+ ok = CryptInstallOIDFunctionAddress(NULL, 0,
+ CRYPT_OID_OPEN_STORE_PROV_FUNC, 1,
+ &kFunctionToIntercept,
+ CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG);
+ DCHECK(ok);
+ }
+
+ // This is never called, because this object is intentionally leaked.
+ // Certificate verification happens on a non-joinable worker thread, which
+ // may still be running when ~AtExitManager is called, so the LazyInstance
+ // must be leaky.
+ ~CryptoAPIInjector() {
+ original_function = NULL;
+ CryptFreeOIDFunctionAddress(original_handle, NULL);
+ }
+};
+
+base::LazyInstance<CryptoAPIInjector>::Leaky
+ g_capi_injector = LAZY_INSTANCE_INITIALIZER;
+
+BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider,
+ DWORD encoding,
+ HCRYPTPROV crypt_provider,
+ DWORD flags,
+ const void* store_name,
+ HCERTSTORE memory_store,
+ PCERT_STORE_PROV_INFO store_info) {
+ // If the high word is all zeroes, then |store_provider| is a numeric ID.
+ // Otherwise, it's a pointer to a null-terminated ASCII string. See the
+ // documentation for CryptGetOIDFunctionAddress for more information.
+ uint32 store_as_uint = reinterpret_cast<uint32>(store_provider);
+ if (store_as_uint > 0xFFFF || store_provider != CERT_STORE_PROV_SYSTEM_W ||
+ !g_capi_injector.Get().original_function)
+ return FALSE;
+
+ BOOL ok = g_capi_injector.Get().original_function(store_provider, encoding,
+ crypt_provider, flags,
+ store_name, memory_store,
+ store_info);
+ // Only the Root store should have certificates injected. If
+ // CERT_SYSTEM_STORE_RELOCATE_FLAG is set, then |store_name| points to a
+ // CERT_SYSTEM_STORE_RELOCATE_PARA structure, rather than a
+ // NULL-terminated wide string, so check before making a string
+ // comparison.
+ if (!ok || TestRootCerts::GetInstance()->IsEmpty() ||
+ (flags & CERT_SYSTEM_STORE_RELOCATE_FLAG) ||
+ lstrcmpiW(reinterpret_cast<LPCWSTR>(store_name), L"root"))
+ return ok;
+
+ // The result of CertOpenStore with CERT_STORE_PROV_SYSTEM_W is documented
+ // to be a collection store, and that appears to hold for |memory_store|.
+ // Attempting to add an individual certificate to |memory_store| causes
+ // the request to be forwarded to the first physical store in the
+ // collection that accepts modifications, which will cause a secure
+ // confirmation dialog to be displayed, confirming the user wishes to
+ // trust the certificate. However, appending a store to the collection
+ // will merely modify the temporary collection store, and will not persist
+ // any changes to the underlying physical store. When the |memory_store| is
+ // searched to see if a certificate is in the Root store, all the
+ // underlying stores in the collection will be searched, and any certificate
+ // in temporary_roots() will be found and seen as trusted.
+ return CertAddStoreToCollection(
+ memory_store, TestRootCerts::GetInstance()->temporary_roots(), 0, 0);
+}
+
+} // namespace
+
+bool TestRootCerts::Add(X509Certificate* certificate) {
+ // Ensure that the default CryptoAPI functionality has been intercepted.
+ // If a test certificate is never added, then no interception should
+ // happen.
+ g_capi_injector.Get();
+
+ BOOL ok = CertAddCertificateContextToStore(
+ temporary_roots_, certificate->os_cert_handle(),
+ CERT_STORE_ADD_NEW, NULL);
+ if (!ok) {
+ // If the certificate is already added, return successfully.
+ return GetLastError() == CRYPT_E_EXISTS;
+ }
+
+ empty_ = false;
+ return true;
+}
+
+void TestRootCerts::Clear() {
+ empty_ = true;
+
+ PCCERT_CONTEXT prev_cert = NULL;
+ while (prev_cert = CertEnumCertificatesInStore(temporary_roots_, NULL))
+ CertDeleteCertificateFromStore(prev_cert);
+}
+
+bool TestRootCerts::IsEmpty() const {
+ return empty_;
+}
+
+HCERTCHAINENGINE TestRootCerts::GetChainEngine() const {
+ if (IsEmpty())
+ return NULL; // Default chain engine will suffice.
+
+ // Windows versions before 7 don't accept the struct size for later versions.
+ // We report the size of the old struct since we don't need the new members.
+ static const DWORD kSizeofCertChainEngineConfig =
+ SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(
+ CERT_CHAIN_ENGINE_CONFIG, CycleDetectionModulus);
+
+ // Each HCERTCHAINENGINE caches both the configured system stores and
+ // information about each chain that has been built. In order to ensure
+ // that changes to |temporary_roots_| are properly propagated and that the
+ // various caches are flushed, when at least one certificate is added,
+ // return a new chain engine for every call. Each chain engine creation
+ // should re-open the root store, ensuring the most recent changes are
+ // visible.
+ CERT_CHAIN_ENGINE_CONFIG engine_config = {
+ kSizeofCertChainEngineConfig
+ };
+ engine_config.dwFlags =
+ CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE |
+ CERT_CHAIN_ENABLE_SHARE_STORE;
+ HCERTCHAINENGINE chain_engine = NULL;
+ BOOL ok = CertCreateCertificateChainEngine(&engine_config, &chain_engine);
+ DCHECK(ok);
+ return chain_engine;
+}
+
+TestRootCerts::~TestRootCerts() {
+ CertCloseStore(temporary_roots_, 0);
+}
+
+void TestRootCerts::Init() {
+ empty_ = true;
+ temporary_roots_ = CertOpenStore(
+ CERT_STORE_PROV_MEMORY, 0, NULL,
+ CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL);
+ DCHECK(temporary_roots_);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_cert_types.cc b/chromium/net/cert/x509_cert_types.cc
new file mode 100644
index 00000000000..cfa09923f60
--- /dev/null
+++ b/chromium/net/cert/x509_cert_types.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_cert_types.h"
+
+#include <cstdlib>
+#include <cstring>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+// Helper for ParseCertificateDate. |*field| must contain at least
+// |field_len| characters. |*field| will be advanced by |field_len| on exit.
+// |*ok| is set to false if there is an error in parsing the number, but left
+// untouched otherwise. Returns the parsed integer.
+int ParseIntAndAdvance(const char** field, size_t field_len, bool* ok) {
+ int result = 0;
+ *ok &= base::StringToInt(base::StringPiece(*field, field_len), &result);
+ *field += field_len;
+ return result;
+}
+
+}
+
+CertPrincipal::CertPrincipal() {
+}
+
+CertPrincipal::CertPrincipal(const std::string& name) : common_name(name) {}
+
+CertPrincipal::~CertPrincipal() {
+}
+
+std::string CertPrincipal::GetDisplayName() const {
+ if (!common_name.empty())
+ return common_name;
+ if (!organization_names.empty())
+ return organization_names[0];
+ if (!organization_unit_names.empty())
+ return organization_unit_names[0];
+
+ return std::string();
+}
+
+CertPolicy::CertPolicy() {
+}
+
+CertPolicy::~CertPolicy() {
+}
+
+// For a denial, we consider a given |cert| to be a match to a saved denied
+// cert if the |error| intersects with the saved error status. For an
+// allowance, we consider a given |cert| to be a match to a saved allowed
+// cert if the |error| is an exact match to or subset of the errors in the
+// saved CertStatus.
+CertPolicy::Judgment CertPolicy::Check(
+ X509Certificate* cert, CertStatus error) const {
+ // It shouldn't matter which set we check first, but we check denied first
+ // in case something strange has happened.
+ bool denied = false;
+ std::map<SHA1HashValue, CertStatus, SHA1HashValueLessThan>::const_iterator
+ denied_iter = denied_.find(cert->fingerprint());
+ if ((denied_iter != denied_.end()) && (denied_iter->second & error))
+ denied = true;
+
+ std::map<SHA1HashValue, CertStatus, SHA1HashValueLessThan>::const_iterator
+ allowed_iter = allowed_.find(cert->fingerprint());
+ if ((allowed_iter != allowed_.end()) &&
+ (allowed_iter->second & error) &&
+ !(~(allowed_iter->second & error) ^ ~error)) {
+ DCHECK(!denied);
+ return ALLOWED;
+ }
+
+ if (denied)
+ return DENIED;
+ return UNKNOWN; // We don't have a policy for this cert.
+}
+
+void CertPolicy::Allow(X509Certificate* cert, CertStatus error) {
+ // Put the cert in the allowed set and (maybe) remove it from the denied set.
+ denied_.erase(cert->fingerprint());
+ // If this same cert had already been saved with a different error status,
+ // this will replace it with the new error status.
+ allowed_[cert->fingerprint()] = error;
+}
+
+void CertPolicy::Deny(X509Certificate* cert, CertStatus error) {
+ // Put the cert in the denied set and (maybe) remove it from the allowed set.
+ std::map<SHA1HashValue, CertStatus, SHA1HashValueLessThan>::const_iterator
+ allowed_iter = allowed_.find(cert->fingerprint());
+ if ((allowed_iter != allowed_.end()) && (allowed_iter->second & error))
+ allowed_.erase(cert->fingerprint());
+ denied_[cert->fingerprint()] |= error;
+}
+
+bool CertPolicy::HasAllowedCert() const {
+ return !allowed_.empty();
+}
+
+bool CertPolicy::HasDeniedCert() const {
+ return !denied_.empty();
+}
+
+bool ParseCertificateDate(const base::StringPiece& raw_date,
+ CertDateFormat format,
+ base::Time* time) {
+ size_t year_length = format == CERT_DATE_FORMAT_UTC_TIME ? 2 : 4;
+
+ if (raw_date.length() < 11 + year_length)
+ return false;
+
+ const char* field = raw_date.data();
+ bool valid = true;
+ base::Time::Exploded exploded = {0};
+
+ exploded.year = ParseIntAndAdvance(&field, year_length, &valid);
+ exploded.month = ParseIntAndAdvance(&field, 2, &valid);
+ exploded.day_of_month = ParseIntAndAdvance(&field, 2, &valid);
+ exploded.hour = ParseIntAndAdvance(&field, 2, &valid);
+ exploded.minute = ParseIntAndAdvance(&field, 2, &valid);
+ exploded.second = ParseIntAndAdvance(&field, 2, &valid);
+ if (valid && year_length == 2)
+ exploded.year += exploded.year < 50 ? 2000 : 1900;
+
+ valid &= exploded.HasValidValues();
+
+ if (!valid)
+ return false;
+
+ *time = base::Time::FromUTCExploded(exploded);
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_cert_types.h b/chromium/net/cert/x509_cert_types.h
new file mode 100644
index 00000000000..f74c82eab7b
--- /dev/null
+++ b/chromium/net/cert/x509_cert_types.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_CERT_TYPES_H_
+#define NET_CERT_X509_CERT_TYPES_H_
+
+#include <string.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+#include "net/base/hash_value.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_status_flags.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include <Security/x509defs.h>
+#endif
+
+namespace base {
+class Time;
+} // namespace base
+
+namespace net {
+
+class X509Certificate;
+
+// CertPrincipal represents the issuer or subject field of an X.509 certificate.
+struct NET_EXPORT CertPrincipal {
+ CertPrincipal();
+ explicit CertPrincipal(const std::string& name);
+ ~CertPrincipal();
+
+#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN)
+ // Parses a BER-format DistinguishedName.
+ bool ParseDistinguishedName(const void* ber_name_data, size_t length);
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // Compare this CertPrincipal with |against|, returning true if they're
+ // equal enough to be a possible match. This should NOT be used for any
+ // security relevant decisions.
+ // TODO(rsleevi): Remove once Mac client auth uses NSS for name comparison.
+ bool Matches(const CertPrincipal& against) const;
+#endif
+
+ // Returns a name that can be used to represent the issuer. It tries in this
+ // order: CN, O and OU and returns the first non-empty one found.
+ std::string GetDisplayName() const;
+
+ // The different attributes for a principal, stored in UTF-8. They may be "".
+ // Note that some of them can have several values.
+
+ std::string common_name;
+ std::string locality_name;
+ std::string state_or_province_name;
+ std::string country_name;
+
+ std::vector<std::string> street_addresses;
+ std::vector<std::string> organization_names;
+ std::vector<std::string> organization_unit_names;
+ std::vector<std::string> domain_components;
+};
+
+// This class is useful for maintaining policies about which certificates are
+// permitted or forbidden for a particular purpose.
+class NET_EXPORT CertPolicy {
+ public:
+ // The judgments this policy can reach.
+ enum Judgment {
+ // We don't have policy information for this certificate.
+ UNKNOWN,
+
+ // This certificate is allowed.
+ ALLOWED,
+
+ // This certificate is denied.
+ DENIED,
+ };
+
+ CertPolicy();
+ ~CertPolicy();
+
+ // Returns the judgment this policy makes about this certificate.
+ // For a certificate to be allowed, it must not have any *additional* errors
+ // from when it was allowed. For a certificate to be denied, it need only
+ // match *any* of the errors that caused it to be denied. We check denial
+ // first, before checking whether it's been allowed.
+ Judgment Check(X509Certificate* cert, CertStatus error) const;
+
+ // Causes the policy to allow this certificate for a given |error|.
+ void Allow(X509Certificate* cert, CertStatus error);
+
+ // Causes the policy to deny this certificate for a given |error|.
+ void Deny(X509Certificate* cert, CertStatus error);
+
+ // Returns true if this policy has allowed at least one certificate.
+ bool HasAllowedCert() const;
+
+ // Returns true if this policy has denied at least one certificate.
+ bool HasDeniedCert() const;
+
+ private:
+ // The set of fingerprints of allowed certificates.
+ std::map<SHA1HashValue, CertStatus, SHA1HashValueLessThan> allowed_;
+
+ // The set of fingerprints of denied certificates.
+ std::map<SHA1HashValue, CertStatus, SHA1HashValueLessThan> denied_;
+};
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Compares two OIDs by value.
+inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) {
+ return oid1->Length == oid2->Length &&
+ (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0);
+}
+#endif
+
+// A list of ASN.1 date/time formats that ParseCertificateDate() supports,
+// encoded in the canonical forms specified in RFC 2459/3280/5280.
+enum CertDateFormat {
+ // UTCTime: Format is YYMMDDHHMMSSZ
+ CERT_DATE_FORMAT_UTC_TIME,
+
+ // GeneralizedTime: Format is YYYYMMDDHHMMSSZ
+ CERT_DATE_FORMAT_GENERALIZED_TIME,
+};
+
+// Attempts to parse |raw_date|, an ASN.1 date/time string encoded as
+// |format|, and writes the result into |*time|. If an invalid date is
+// specified, or if parsing fails, returns false, and |*time| will not be
+// updated.
+NET_EXPORT_PRIVATE bool ParseCertificateDate(const base::StringPiece& raw_date,
+ CertDateFormat format,
+ base::Time* time);
+} // namespace net
+
+#endif // NET_CERT_X509_CERT_TYPES_H_
diff --git a/chromium/net/cert/x509_cert_types_mac.cc b/chromium/net/cert/x509_cert_types_mac.cc
new file mode 100644
index 00000000000..6439c7f3e77
--- /dev/null
+++ b/chromium/net/cert/x509_cert_types_mac.cc
@@ -0,0 +1,291 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_cert_types.h"
+
+#include <CoreServices/CoreServices.h>
+#include <Security/SecAsn1Coder.h>
+#include <Security/Security.h>
+
+#include "base/i18n/icu_string_conversions.h"
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace net {
+
+namespace {
+
+// The BER encoding of 0.9.2342.19200300.100.1.25.
+// On 10.6 and later this is available as CSSMOID_DomainComponent, which is an
+// external symbol from Security.framework. However, it appears that Apple's
+// implementation improperly encoded this on 10.6+, and even still is
+// unavailable on 10.5, so simply including the raw BER here.
+//
+// Note: CSSM is allowed to store CSSM_OIDs in any arbitrary format desired,
+// as long as the symbols are properly exposed. The fact that Apple's
+// implementation stores it in BER is an internal implementation detail
+// observed by studying libsecurity_cssm.
+const uint8 kDomainComponentData[] = {
+ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19
+};
+
+const CSSM_OID kDomainComponentOID = {
+ arraysize(kDomainComponentData),
+ const_cast<uint8*>(kDomainComponentData)
+};
+
+const CSSM_OID* kOIDs[] = {
+ &CSSMOID_CommonName,
+ &CSSMOID_LocalityName,
+ &CSSMOID_StateProvinceName,
+ &CSSMOID_CountryName,
+ &CSSMOID_StreetAddress,
+ &CSSMOID_OrganizationName,
+ &CSSMOID_OrganizationalUnitName,
+ &kDomainComponentOID,
+};
+
+// The following structs and templates work with Apple's very arcane and under-
+// documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1
+// decoder:
+// http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html
+
+// These are used to parse the contents of a raw
+// BER DistinguishedName structure.
+
+const SecAsn1Template kStringValueTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(CSSM_X509_TYPE_VALUE_PAIR, valueType), },
+ { SEC_ASN1_PRINTABLE_STRING,
+ offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
+ BER_TAG_PRINTABLE_STRING },
+ { SEC_ASN1_IA5_STRING,
+ offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
+ BER_TAG_IA5_STRING },
+ { SEC_ASN1_T61_STRING,
+ offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
+ BER_TAG_T61_STRING },
+ { SEC_ASN1_UTF8_STRING,
+ offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
+ BER_TAG_PKIX_UTF8_STRING },
+ { SEC_ASN1_BMP_STRING,
+ offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
+ BER_TAG_PKIX_BMP_STRING },
+ { SEC_ASN1_UNIVERSAL_STRING,
+ offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
+ BER_TAG_PKIX_UNIVERSAL_STRING },
+ { 0, }
+};
+
+const SecAsn1Template kKeyValuePairTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CSSM_X509_TYPE_VALUE_PAIR) },
+ { SEC_ASN1_OBJECT_ID, offsetof(CSSM_X509_TYPE_VALUE_PAIR, type), },
+ { SEC_ASN1_INLINE, 0, &kStringValueTemplate, },
+ { 0, }
+};
+
+struct KeyValuePairs {
+ CSSM_X509_TYPE_VALUE_PAIR* pairs;
+};
+
+const SecAsn1Template kKeyValuePairSetTemplate[] = {
+ { SEC_ASN1_SET_OF, offsetof(KeyValuePairs, pairs),
+ kKeyValuePairTemplate, sizeof(KeyValuePairs) }
+};
+
+struct X509Name {
+ KeyValuePairs** pairs_list;
+};
+
+const SecAsn1Template kNameTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name, pairs_list),
+ kKeyValuePairSetTemplate, sizeof(X509Name) }
+};
+
+// Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.)
+std::string DataToString(CSSM_DATA data) {
+ return std::string(
+ reinterpret_cast<std::string::value_type*>(data.Data),
+ data.Length);
+}
+
+// Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8.
+std::string Latin1DataToUTF8String(CSSM_DATA data) {
+ base::string16 utf16;
+ if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1,
+ base::OnStringConversionError::FAIL, &utf16))
+ return "";
+ return UTF16ToUTF8(utf16);
+}
+
+// Converts big-endian UTF-16 to UTF-8 in a std::string.
+// Note: The byte-order flipping is done in place on the input buffer!
+bool UTF16BigEndianToUTF8(base::char16* chars, size_t length,
+ std::string* out_string) {
+ for (size_t i = 0; i < length; i++)
+ chars[i] = EndianU16_BtoN(chars[i]);
+ return UTF16ToUTF8(chars, length, out_string);
+}
+
+// Converts big-endian UTF-32 to UTF-8 in a std::string.
+// Note: The byte-order flipping is done in place on the input buffer!
+bool UTF32BigEndianToUTF8(char32* chars, size_t length,
+ std::string* out_string) {
+ for (size_t i = 0; i < length; ++i)
+ chars[i] = EndianS32_BtoN(chars[i]);
+#if defined(WCHAR_T_IS_UTF32)
+ return WideToUTF8(reinterpret_cast<const wchar_t*>(chars),
+ length, out_string);
+#else
+#error This code doesn't handle 16-bit wchar_t.
+#endif
+}
+
+// Adds a type+value pair to the appropriate vector from a C array.
+// The array is keyed by the matching OIDs from kOIDS[].
+void AddTypeValuePair(const CSSM_OID type,
+ const std::string& value,
+ std::vector<std::string>* values[]) {
+ for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
+ if (CSSMOIDEqual(&type, kOIDs[oid])) {
+ values[oid]->push_back(value);
+ break;
+ }
+ }
+}
+
+// Stores the first string of the vector, if any, to *single_value.
+void SetSingle(const std::vector<std::string>& values,
+ std::string* single_value) {
+ // We don't expect to have more than one CN, L, S, and C.
+ LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
+ if (!values.empty())
+ *single_value = values[0];
+}
+
+bool match(const std::string& str, const std::string& against) {
+ // TODO(snej): Use the full matching rules specified in RFC 5280 sec. 7.1
+ // including trimming and case-folding: <http://www.ietf.org/rfc/rfc5280.txt>.
+ return against == str;
+}
+
+bool match(const std::vector<std::string>& rdn1,
+ const std::vector<std::string>& rdn2) {
+ // "Two relative distinguished names RDN1 and RDN2 match if they have the
+ // same number of naming attributes and for each naming attribute in RDN1
+ // there is a matching naming attribute in RDN2." --RFC 5280 sec. 7.1.
+ if (rdn1.size() != rdn2.size())
+ return false;
+ for (unsigned i1 = 0; i1 < rdn1.size(); ++i1) {
+ unsigned i2;
+ for (i2 = 0; i2 < rdn2.size(); ++i2) {
+ if (match(rdn1[i1], rdn2[i2]))
+ break;
+ }
+ if (i2 == rdn2.size())
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
+ size_t length) {
+ DCHECK(ber_name_data);
+
+ // First parse the BER |name_data| into the above structs.
+ SecAsn1CoderRef coder = NULL;
+ SecAsn1CoderCreate(&coder);
+ DCHECK(coder);
+ X509Name* name = NULL;
+ OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate,
+ &name);
+ if (err) {
+ OSSTATUS_LOG(ERROR, err) << "SecAsn1Decode";
+ SecAsn1CoderRelease(coder);
+ return false;
+ }
+
+ // Now scan the structs and add the values to my string vectors.
+ // I don't store multiple common/locality/state/country names, so use
+ // temporary vectors for those.
+ std::vector<std::string> common_names, locality_names, state_names,
+ country_names;
+ std::vector<std::string>* values[] = {
+ &common_names, &locality_names,
+ &state_names, &country_names,
+ &this->street_addresses,
+ &this->organization_names,
+ &this->organization_unit_names,
+ &this->domain_components
+ };
+ DCHECK(arraysize(kOIDs) == arraysize(values));
+
+ for (int rdn = 0; name[rdn].pairs_list; ++rdn) {
+ CSSM_X509_TYPE_VALUE_PAIR* pair;
+ for (int pair_index = 0;
+ NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs);
+ ++pair_index) {
+ switch (pair->valueType) {
+ case BER_TAG_IA5_STRING: // ASCII (that means 7-bit!)
+ case BER_TAG_PRINTABLE_STRING: // a subset of ASCII
+ case BER_TAG_PKIX_UTF8_STRING: // UTF-8
+ AddTypeValuePair(pair->type, DataToString(pair->value), values);
+ break;
+ case BER_TAG_T61_STRING: // T61, pretend it's Latin-1
+ AddTypeValuePair(pair->type,
+ Latin1DataToUTF8String(pair->value),
+ values);
+ break;
+ case BER_TAG_PKIX_BMP_STRING: { // UTF-16, big-endian
+ std::string value;
+ UTF16BigEndianToUTF8(
+ reinterpret_cast<base::char16*>(pair->value.Data),
+ pair->value.Length / sizeof(base::char16),
+ &value);
+ AddTypeValuePair(pair->type, value, values);
+ break;
+ }
+ case BER_TAG_PKIX_UNIVERSAL_STRING: { // UTF-32, big-endian
+ std::string value;
+ UTF32BigEndianToUTF8(reinterpret_cast<char32*>(pair->value.Data),
+ pair->value.Length / sizeof(char32),
+ &value);
+ AddTypeValuePair(pair->type, value, values);
+ break;
+ }
+ default:
+ DCHECK_EQ(pair->valueType, BER_TAG_UNKNOWN);
+ // We don't know what data type this is, but we'll store it as a blob.
+ // Displaying the string may not work, but at least it can be compared
+ // byte-for-byte by a Matches() call.
+ AddTypeValuePair(pair->type, DataToString(pair->value), values);
+ break;
+ }
+ }
+ }
+
+ SetSingle(common_names, &this->common_name);
+ SetSingle(locality_names, &this->locality_name);
+ SetSingle(state_names, &this->state_or_province_name);
+ SetSingle(country_names, &this->country_name);
+
+ // Releasing |coder| frees all the memory pointed to via |name|.
+ SecAsn1CoderRelease(coder);
+ return true;
+}
+
+bool CertPrincipal::Matches(const CertPrincipal& against) const {
+ return match(common_name, against.common_name) &&
+ match(locality_name, against.locality_name) &&
+ match(state_or_province_name, against.state_or_province_name) &&
+ match(country_name, against.country_name) &&
+ match(street_addresses, against.street_addresses) &&
+ match(organization_names, against.organization_names) &&
+ match(organization_unit_names, against.organization_unit_names) &&
+ match(domain_components, against.domain_components);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_cert_types_unittest.cc b/chromium/net/cert/x509_cert_types_unittest.cc
new file mode 100644
index 00000000000..38fd3e95266
--- /dev/null
+++ b/chromium/net/cert/x509_cert_types_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_cert_types.h"
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "net/test/test_certificate_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+TEST(X509TypesTest, Matching) {
+ CertPrincipal spamco;
+ spamco.common_name = "SpamCo Dept. Of Certificization";
+ spamco.country_name = "EB";
+ spamco.organization_names.push_back("SpamCo Holding Company, LLC");
+ spamco.organization_names.push_back("SpamCo Evil Masterminds");
+ spamco.organization_unit_names.push_back("Class Z Obfuscation Authority");
+ ASSERT_TRUE(spamco.Matches(spamco));
+
+ CertPrincipal bogus;
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+
+ bogus = spamco;
+ EXPECT_TRUE(bogus.Matches(spamco));
+ EXPECT_TRUE(spamco.Matches(bogus));
+
+ bogus.organization_names.erase(bogus.organization_names.begin(),
+ bogus.organization_names.end());
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+
+ bogus.organization_names.push_back("SpamCo Holding Company, LLC");
+ bogus.organization_names.push_back("SpamCo Evil Masterminds");
+ EXPECT_TRUE(bogus.Matches(spamco));
+ EXPECT_TRUE(spamco.Matches(bogus));
+
+ bogus.locality_name = "Elbosdorf";
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+
+ bogus.locality_name = "";
+ bogus.organization_unit_names.push_back("Q Division");
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+}
+#endif
+
+#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN)
+TEST(X509TypesTest, ParseDNVerisign) {
+ CertPrincipal verisign;
+ EXPECT_TRUE(verisign.ParseDistinguishedName(VerisignDN, sizeof(VerisignDN)));
+ EXPECT_EQ("", verisign.common_name);
+ EXPECT_EQ("US", verisign.country_name);
+ ASSERT_EQ(1U, verisign.organization_names.size());
+ EXPECT_EQ("VeriSign, Inc.", verisign.organization_names[0]);
+ ASSERT_EQ(1U, verisign.organization_unit_names.size());
+ EXPECT_EQ("Class 1 Public Primary Certification Authority",
+ verisign.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNStartcom) {
+ CertPrincipal startcom;
+ EXPECT_TRUE(startcom.ParseDistinguishedName(StartComDN, sizeof(StartComDN)));
+ EXPECT_EQ("StartCom Certification Authority", startcom.common_name);
+ EXPECT_EQ("IL", startcom.country_name);
+ ASSERT_EQ(1U, startcom.organization_names.size());
+ EXPECT_EQ("StartCom Ltd.", startcom.organization_names[0]);
+ ASSERT_EQ(1U, startcom.organization_unit_names.size());
+ EXPECT_EQ("Secure Digital Certificate Signing",
+ startcom.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNUserTrust) {
+ CertPrincipal usertrust;
+ EXPECT_TRUE(usertrust.ParseDistinguishedName(UserTrustDN,
+ sizeof(UserTrustDN)));
+ EXPECT_EQ("UTN-USERFirst-Client Authentication and Email",
+ usertrust.common_name);
+ EXPECT_EQ("US", usertrust.country_name);
+ EXPECT_EQ("UT", usertrust.state_or_province_name);
+ EXPECT_EQ("Salt Lake City", usertrust.locality_name);
+ ASSERT_EQ(1U, usertrust.organization_names.size());
+ EXPECT_EQ("The USERTRUST Network", usertrust.organization_names[0]);
+ ASSERT_EQ(1U, usertrust.organization_unit_names.size());
+ EXPECT_EQ("http://www.usertrust.com",
+ usertrust.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNTurkTrust) {
+ // Note: This tests parsing UTF8STRINGs.
+ CertPrincipal turktrust;
+ EXPECT_TRUE(turktrust.ParseDistinguishedName(TurkTrustDN,
+ sizeof(TurkTrustDN)));
+ EXPECT_EQ("TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı",
+ turktrust.common_name);
+ EXPECT_EQ("TR", turktrust.country_name);
+ EXPECT_EQ("Ankara", turktrust.locality_name);
+ ASSERT_EQ(1U, turktrust.organization_names.size());
+ EXPECT_EQ("TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005",
+ turktrust.organization_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNATrust) {
+ // Note: This tests parsing 16-bit BMPSTRINGs.
+ CertPrincipal atrust;
+ EXPECT_TRUE(atrust.ParseDistinguishedName(ATrustQual01DN,
+ sizeof(ATrustQual01DN)));
+ EXPECT_EQ("A-Trust-Qual-01",
+ atrust.common_name);
+ EXPECT_EQ("AT", atrust.country_name);
+ ASSERT_EQ(1U, atrust.organization_names.size());
+ EXPECT_EQ("A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH",
+ atrust.organization_names[0]);
+ ASSERT_EQ(1U, atrust.organization_unit_names.size());
+ EXPECT_EQ("A-Trust-Qual-01",
+ atrust.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNEntrust) {
+ // Note: This tests parsing T61STRINGs and fields with multiple values.
+ CertPrincipal entrust;
+ EXPECT_TRUE(entrust.ParseDistinguishedName(EntrustDN,
+ sizeof(EntrustDN)));
+ EXPECT_EQ("Entrust.net Certification Authority (2048)",
+ entrust.common_name);
+ EXPECT_EQ("", entrust.country_name);
+ ASSERT_EQ(1U, entrust.organization_names.size());
+ EXPECT_EQ("Entrust.net",
+ entrust.organization_names[0]);
+ ASSERT_EQ(2U, entrust.organization_unit_names.size());
+ EXPECT_EQ("www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)",
+ entrust.organization_unit_names[0]);
+ EXPECT_EQ("(c) 1999 Entrust.net Limited",
+ entrust.organization_unit_names[1]);
+}
+#endif
+
+const struct CertDateTestData {
+ CertDateFormat format;
+ const char* date_string;
+ bool is_valid;
+ base::Time::Exploded expected_result;
+} kCertDateTimeData[] = {
+ { CERT_DATE_FORMAT_UTC_TIME,
+ "120101000000Z",
+ true,
+ { 2012, 1, 0, 1, 0, 0, 0 } },
+ { CERT_DATE_FORMAT_GENERALIZED_TIME,
+ "20120101000000Z",
+ true,
+ { 2012, 1, 0, 1, 0, 0, 0 } },
+ { CERT_DATE_FORMAT_UTC_TIME,
+ "490101000000Z",
+ true,
+ { 2049, 1, 0, 1, 0, 0, 0 } },
+ { CERT_DATE_FORMAT_UTC_TIME,
+ "500101000000Z",
+ true,
+ { 1950, 1, 0, 1, 0, 0, 0 } },
+ { CERT_DATE_FORMAT_GENERALIZED_TIME,
+ "19500101000000Z",
+ true,
+ { 1950, 1, 0, 1, 0, 0, 0 } },
+ { CERT_DATE_FORMAT_UTC_TIME,
+ "AB0101000000Z",
+ false,
+ { 0 } },
+ { CERT_DATE_FORMAT_GENERALIZED_TIME,
+ "19AB0101000000Z",
+ false,
+ { 0 } },
+ { CERT_DATE_FORMAT_UTC_TIME,
+ "",
+ false,
+ { 0 } },
+ { CERT_DATE_FORMAT_UTC_TIME,
+ "A",
+ false,
+ { 0 } },
+ { CERT_DATE_FORMAT_GENERALIZED_TIME,
+ "20121301000000Z",
+ false,
+ { 0 } },
+ { CERT_DATE_FORMAT_GENERALIZED_TIME,
+ "20120101123000Z",
+ true,
+ { 2012, 1, 0, 1, 12, 30, 0 } },
+};
+
+// GTest pretty printer.
+void PrintTo(const CertDateTestData& data, std::ostream* os) {
+ *os << " format: " << data.format
+ << "; date string: " << base::StringPiece(data.date_string)
+ << "; valid: " << data.is_valid
+ << "; expected date: "
+ << (data.is_valid ?
+ base::Time::FromUTCExploded(data.expected_result)
+ .ToInternalValue() :
+ 0U);
+}
+
+class X509CertTypesDateTest : public testing::TestWithParam<CertDateTestData> {
+ public:
+ virtual ~X509CertTypesDateTest() {}
+ virtual void SetUp() {
+ test_data_ = GetParam();
+ }
+
+ protected:
+ CertDateTestData test_data_;
+};
+
+TEST_P(X509CertTypesDateTest, Parse) {
+ base::Time parsed_date;
+ bool parsed = ParseCertificateDate(
+ test_data_.date_string, test_data_.format, &parsed_date);
+ EXPECT_EQ(test_data_.is_valid, parsed);
+ if (!test_data_.is_valid)
+ return;
+ // Convert the expected value to a base::Time(). This ensures that systems
+ // systems that only support 32-bit times will pass the tests, by ensuring at
+ // least that the times have the same truncating behaviour.
+ // Note: Compared as internal values so that mismatches can be cleanly
+ // printed by GTest (eg: without PrintTo overrides).
+ EXPECT_EQ(base::Time::FromUTCExploded(test_data_.expected_result)
+ .ToInternalValue(),
+ parsed_date.ToInternalValue());
+}
+INSTANTIATE_TEST_CASE_P(,
+ X509CertTypesDateTest,
+ testing::ValuesIn(kCertDateTimeData));
+
+} // namespace
+
+} // namespace net
diff --git a/chromium/net/cert/x509_cert_types_win.cc b/chromium/net/cert/x509_cert_types_win.cc
new file mode 100644
index 00000000000..d99362cb921
--- /dev/null
+++ b/chromium/net/cert/x509_cert_types_win.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_cert_types.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/capi_util.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+namespace net {
+
+namespace {
+
+// A list of OIDs to decode. Any OID not on this list will be ignored for
+// purposes of parsing.
+const char* kOIDs[] = {
+ szOID_COMMON_NAME,
+ szOID_LOCALITY_NAME,
+ szOID_STATE_OR_PROVINCE_NAME,
+ szOID_COUNTRY_NAME,
+ szOID_STREET_ADDRESS,
+ szOID_ORGANIZATION_NAME,
+ szOID_ORGANIZATIONAL_UNIT_NAME,
+ szOID_DOMAIN_COMPONENT
+};
+
+// Converts the value for |attribute| to an UTF-8 string, storing the result
+// in |value|. Returns false if the string cannot be converted.
+bool GetAttributeValue(PCERT_RDN_ATTR attribute,
+ std::string* value) {
+ DWORD chars_needed = CertRDNValueToStrW(attribute->dwValueType,
+ &attribute->Value, NULL, 0);
+ if (chars_needed == 0)
+ return false;
+ if (chars_needed == 1) {
+ // The value is actually an empty string (chars_needed includes a single
+ // char for a NULL value). Don't bother converting - just clear the
+ // string.
+ value->clear();
+ return true;
+ }
+ std::wstring wide_name;
+ DWORD chars_written = CertRDNValueToStrW(
+ attribute->dwValueType, &attribute->Value,
+ WriteInto(&wide_name, chars_needed), chars_needed);
+ if (chars_written <= 1)
+ return false;
+ wide_name.resize(chars_written - 1);
+ *value = WideToUTF8(wide_name);
+ return true;
+}
+
+// Adds a type+value pair to the appropriate vector from a C array.
+// The array is keyed by the matching OIDs from kOIDS[].
+bool AddTypeValuePair(PCERT_RDN_ATTR attribute,
+ std::vector<std::string>* values[]) {
+ for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
+ if (strcmp(attribute->pszObjId, kOIDs[oid]) == 0) {
+ std::string value;
+ if (!GetAttributeValue(attribute, &value))
+ return false;
+ values[oid]->push_back(value);
+ break;
+ }
+ }
+ return true;
+}
+
+// Stores the first string of the vector, if any, to *single_value.
+void SetSingle(const std::vector<std::string>& values,
+ std::string* single_value) {
+ // We don't expect to have more than one CN, L, S, and C.
+ LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
+ if (!values.empty())
+ *single_value = values[0];
+}
+
+} // namespace
+
+bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
+ size_t length) {
+ DCHECK(ber_name_data);
+
+ CRYPT_DECODE_PARA decode_para;
+ decode_para.cbSize = sizeof(decode_para);
+ decode_para.pfnAlloc = crypto::CryptAlloc;
+ decode_para.pfnFree = crypto::CryptFree;
+ CERT_NAME_INFO* name_info = NULL;
+ DWORD name_info_size = 0;
+ BOOL rv;
+ rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ X509_NAME,
+ reinterpret_cast<const BYTE*>(ber_name_data),
+ length,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decode_para,
+ &name_info, &name_info_size);
+ if (!rv)
+ return false;
+ scoped_ptr_malloc<CERT_NAME_INFO> scoped_name_info(name_info);
+
+ std::vector<std::string> common_names, locality_names, state_names,
+ country_names;
+
+ std::vector<std::string>* values[] = {
+ &common_names, &locality_names,
+ &state_names, &country_names,
+ &this->street_addresses,
+ &this->organization_names,
+ &this->organization_unit_names,
+ &this->domain_components
+ };
+ DCHECK(arraysize(kOIDs) == arraysize(values));
+
+ for (DWORD cur_rdn = 0; cur_rdn < name_info->cRDN; ++cur_rdn) {
+ PCERT_RDN rdn = &name_info->rgRDN[cur_rdn];
+ for (DWORD cur_ava = 0; cur_ava < rdn->cRDNAttr; ++cur_ava) {
+ PCERT_RDN_ATTR ava = &rdn->rgRDNAttr[cur_ava];
+ if (!AddTypeValuePair(ava, values))
+ return false;
+ }
+ }
+
+ SetSingle(common_names, &this->common_name);
+ SetSingle(locality_names, &this->locality_name);
+ SetSingle(state_names, &this->state_or_province_name);
+ SetSingle(country_names, &this->country_name);
+ return true;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate.cc b/chromium/net/cert/x509_certificate.cc
new file mode 100644
index 00000000000..36e806ebaba
--- /dev/null
+++ b/chromium/net/cert/x509_certificate.cc
@@ -0,0 +1,734 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/metrics/histogram.h"
+#include "base/pickle.h"
+#include "base/sha1.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "net/base/net_util.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/cert/pem_tokenizer.h"
+#include "url/url_canon.h"
+
+namespace net {
+
+namespace {
+
+// Indicates the order to use when trying to decode binary data, which is
+// based on (speculation) as to what will be most common -> least common
+const X509Certificate::Format kFormatDecodePriority[] = {
+ X509Certificate::FORMAT_SINGLE_CERTIFICATE,
+ X509Certificate::FORMAT_PKCS7
+};
+
+// The PEM block header used for DER certificates
+const char kCertificateHeader[] = "CERTIFICATE";
+// The PEM block header used for PKCS#7 data
+const char kPKCS7Header[] = "PKCS7";
+
+#if !defined(USE_NSS)
+// A thread-safe cache for OS certificate handles.
+//
+// Within each of the supported underlying crypto libraries, a certificate
+// handle is represented as a ref-counted object that contains the parsed
+// data for the certificate. In addition, the underlying OS handle may also
+// contain a copy of the original ASN.1 DER used to constructed the handle.
+//
+// In order to reduce the memory usage when multiple SSL connections exist,
+// with each connection storing the server's identity certificate plus any
+// intermediates supplied, the certificate handles are cached. Any two
+// X509Certificates that were created from the same ASN.1 DER data,
+// regardless of where that data came from, will share the same underlying
+// OS certificate handle.
+class X509CertificateCache {
+ public:
+ // Performs a compare-and-swap like operation. If an OS certificate handle
+ // for the same certificate data as |*cert_handle| already exists in the
+ // cache, the original |*cert_handle| will be freed and |cert_handle|
+ // will be updated to point to a duplicated reference to the existing cached
+ // certificate, with the caller taking ownership of this duplicated handle.
+ // If an equivalent OS certificate handle is not found, a duplicated
+ // reference to |*cert_handle| will be added to the cache. In either case,
+ // upon return, the caller fully owns |*cert_handle| and is responsible for
+ // calling FreeOSCertHandle(), after first calling Remove().
+ void InsertOrUpdate(X509Certificate::OSCertHandle* cert_handle);
+
+ // Decrements the cache reference count for |cert_handle|, a handle that was
+ // previously obtained by calling InsertOrUpdate(). If this is the last
+ // cached reference held, this will remove the handle from the cache. The
+ // caller retains ownership of |cert_handle| and remains responsible for
+ // calling FreeOSCertHandle() to release the underlying OS certificate
+ void Remove(X509Certificate::OSCertHandle cert_handle);
+
+ private:
+ // A single entry in the cache. Certificates will be keyed by their SHA1
+ // fingerprints, but will not be considered equivalent unless the entire
+ // certificate data matches.
+ struct Entry {
+ Entry() : cert_handle(NULL), ref_count(0) {}
+
+ X509Certificate::OSCertHandle cert_handle;
+
+ // Increased by each call to InsertOrUpdate(), and balanced by each call
+ // to Remove(). When it equals 0, all references created by
+ // InsertOrUpdate() have been released, so the cache entry will be removed
+ // the cached OS certificate handle will be freed.
+ int ref_count;
+ };
+ typedef std::map<SHA1HashValue, Entry, SHA1HashValueLessThan> CertMap;
+
+ // Obtain an instance of X509CertificateCache via a LazyInstance.
+ X509CertificateCache() {}
+ ~X509CertificateCache() {}
+ friend struct base::DefaultLazyInstanceTraits<X509CertificateCache>;
+
+ // You must acquire this lock before using any private data of this object
+ // You must not block while holding this lock.
+ base::Lock lock_;
+
+ // The certificate cache. You must acquire |lock_| before using |cache_|.
+ CertMap cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(X509CertificateCache);
+};
+
+base::LazyInstance<X509CertificateCache>::Leaky
+ g_x509_certificate_cache = LAZY_INSTANCE_INITIALIZER;
+
+void X509CertificateCache::InsertOrUpdate(
+ X509Certificate::OSCertHandle* cert_handle) {
+ DCHECK(cert_handle);
+ SHA1HashValue fingerprint =
+ X509Certificate::CalculateFingerprint(*cert_handle);
+
+ X509Certificate::OSCertHandle old_handle = NULL;
+ {
+ base::AutoLock lock(lock_);
+ CertMap::iterator pos = cache_.find(fingerprint);
+ if (pos == cache_.end()) {
+ // A cached entry was not found, so initialize a new entry. The entry
+ // assumes ownership of the current |*cert_handle|.
+ Entry cache_entry;
+ cache_entry.cert_handle = *cert_handle;
+ cache_entry.ref_count = 0;
+ CertMap::value_type cache_value(fingerprint, cache_entry);
+ pos = cache_.insert(cache_value).first;
+ } else {
+ bool is_same_cert =
+ X509Certificate::IsSameOSCert(*cert_handle, pos->second.cert_handle);
+ if (!is_same_cert) {
+ // Two certificates don't match, due to a SHA1 hash collision. Given
+ // the low probability, the simplest solution is to not cache the
+ // certificate, which should not affect performance too negatively.
+ return;
+ }
+ // A cached entry was found and will be used instead of the caller's
+ // handle. Ensure the caller's original handle will be freed, since
+ // ownership is assumed.
+ old_handle = *cert_handle;
+ }
+ // Whether an existing cached handle or a new handle, increment the
+ // cache's reference count and return a handle that the caller can own.
+ ++pos->second.ref_count;
+ *cert_handle = X509Certificate::DupOSCertHandle(pos->second.cert_handle);
+ }
+ // If the caller's handle was replaced with a cached handle, free the
+ // original handle now. This is done outside of the lock because
+ // |old_handle| may be the only handle for this particular certificate, so
+ // freeing it may be complex or resource-intensive and does not need to
+ // be guarded by the lock.
+ if (old_handle) {
+ X509Certificate::FreeOSCertHandle(old_handle);
+ DHISTOGRAM_COUNTS("X509CertificateReuseCount", 1);
+ }
+}
+
+void X509CertificateCache::Remove(X509Certificate::OSCertHandle cert_handle) {
+ SHA1HashValue fingerprint =
+ X509Certificate::CalculateFingerprint(cert_handle);
+ base::AutoLock lock(lock_);
+
+ CertMap::iterator pos = cache_.find(fingerprint);
+ if (pos == cache_.end())
+ return; // A hash collision where the winning cert was already freed.
+
+ bool is_same_cert = X509Certificate::IsSameOSCert(cert_handle,
+ pos->second.cert_handle);
+ if (!is_same_cert)
+ return; // A hash collision where the winning cert is still around.
+
+ if (--pos->second.ref_count == 0) {
+ // The last reference to |cert_handle| has been removed, so release the
+ // Entry's OS handle and remove the Entry. The caller still holds a
+ // reference to |cert_handle| and is responsible for freeing it.
+ X509Certificate::FreeOSCertHandle(pos->second.cert_handle);
+ cache_.erase(pos);
+ }
+}
+#endif // !defined(USE_NSS)
+
+// See X509CertificateCache::InsertOrUpdate. NSS has a built-in cache, so there
+// is no point in wrapping another cache around it.
+void InsertOrUpdateCache(X509Certificate::OSCertHandle* cert_handle) {
+#if !defined(USE_NSS)
+ g_x509_certificate_cache.Pointer()->InsertOrUpdate(cert_handle);
+#endif
+}
+
+// See X509CertificateCache::Remove.
+void RemoveFromCache(X509Certificate::OSCertHandle cert_handle) {
+#if !defined(USE_NSS)
+ g_x509_certificate_cache.Pointer()->Remove(cert_handle);
+#endif
+}
+
+// Utility to split |src| on the first occurrence of |c|, if any. |right| will
+// either be empty if |c| was not found, or will contain the remainder of the
+// string including the split character itself.
+void SplitOnChar(const base::StringPiece& src,
+ char c,
+ base::StringPiece* left,
+ base::StringPiece* right) {
+ size_t pos = src.find(c);
+ if (pos == base::StringPiece::npos) {
+ *left = src;
+ right->clear();
+ } else {
+ *left = src.substr(0, pos);
+ *right = src.substr(pos);
+ }
+}
+
+} // namespace
+
+bool X509Certificate::LessThan::operator()(
+ const scoped_refptr<X509Certificate>& lhs,
+ const scoped_refptr<X509Certificate>& rhs) const {
+ if (lhs.get() == rhs.get())
+ return false;
+
+ int rv = memcmp(lhs->fingerprint_.data, rhs->fingerprint_.data,
+ sizeof(lhs->fingerprint_.data));
+ if (rv != 0)
+ return rv < 0;
+
+ rv = memcmp(lhs->ca_fingerprint_.data, rhs->ca_fingerprint_.data,
+ sizeof(lhs->ca_fingerprint_.data));
+ return rv < 0;
+}
+
+X509Certificate::X509Certificate(const std::string& subject,
+ const std::string& issuer,
+ base::Time start_date,
+ base::Time expiration_date)
+ : subject_(subject),
+ issuer_(issuer),
+ valid_start_(start_date),
+ valid_expiry_(expiration_date),
+ cert_handle_(NULL) {
+ memset(fingerprint_.data, 0, sizeof(fingerprint_.data));
+ memset(ca_fingerprint_.data, 0, sizeof(ca_fingerprint_.data));
+}
+
+// static
+X509Certificate* X509Certificate::CreateFromHandle(
+ OSCertHandle cert_handle,
+ const OSCertHandles& intermediates) {
+ DCHECK(cert_handle);
+ return new X509Certificate(cert_handle, intermediates);
+}
+
+// static
+X509Certificate* X509Certificate::CreateFromDERCertChain(
+ const std::vector<base::StringPiece>& der_certs) {
+ if (der_certs.empty())
+ return NULL;
+
+ X509Certificate::OSCertHandles intermediate_ca_certs;
+ for (size_t i = 1; i < der_certs.size(); i++) {
+ OSCertHandle handle = CreateOSCertHandleFromBytes(
+ const_cast<char*>(der_certs[i].data()), der_certs[i].size());
+ if (!handle)
+ break;
+ intermediate_ca_certs.push_back(handle);
+ }
+
+ OSCertHandle handle = NULL;
+ // Return NULL if we failed to parse any of the certs.
+ if (der_certs.size() - 1 == intermediate_ca_certs.size()) {
+ handle = CreateOSCertHandleFromBytes(
+ const_cast<char*>(der_certs[0].data()), der_certs[0].size());
+ }
+
+ X509Certificate* cert = NULL;
+ if (handle) {
+ cert = CreateFromHandle(handle, intermediate_ca_certs);
+ FreeOSCertHandle(handle);
+ }
+
+ for (size_t i = 0; i < intermediate_ca_certs.size(); i++)
+ FreeOSCertHandle(intermediate_ca_certs[i]);
+
+ return cert;
+}
+
+// static
+X509Certificate* X509Certificate::CreateFromBytes(const char* data,
+ int length) {
+ OSCertHandle cert_handle = CreateOSCertHandleFromBytes(data, length);
+ if (!cert_handle)
+ return NULL;
+
+ X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles());
+ FreeOSCertHandle(cert_handle);
+ return cert;
+}
+
+// static
+X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle,
+ PickleIterator* pickle_iter,
+ PickleType type) {
+ if (type == PICKLETYPE_CERTIFICATE_CHAIN_V3) {
+ int chain_length = 0;
+ if (!pickle_iter->ReadLength(&chain_length))
+ return NULL;
+
+ std::vector<base::StringPiece> cert_chain;
+ const char* data = NULL;
+ int data_length = 0;
+ for (int i = 0; i < chain_length; ++i) {
+ if (!pickle_iter->ReadData(&data, &data_length))
+ return NULL;
+ cert_chain.push_back(base::StringPiece(data, data_length));
+ }
+ return CreateFromDERCertChain(cert_chain);
+ }
+
+ // Legacy / Migration code. This should eventually be removed once
+ // sufficient time has passed that all pickles serialized prior to
+ // PICKLETYPE_CERTIFICATE_CHAIN_V3 have been removed.
+ OSCertHandle cert_handle = ReadOSCertHandleFromPickle(pickle_iter);
+ if (!cert_handle)
+ return NULL;
+
+ OSCertHandles intermediates;
+ uint32 num_intermediates = 0;
+ if (type != PICKLETYPE_SINGLE_CERTIFICATE) {
+ if (!pickle_iter->ReadUInt32(&num_intermediates)) {
+ FreeOSCertHandle(cert_handle);
+ return NULL;
+ }
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(__x86_64__)
+ // On 64-bit Linux (and any other 64-bit platforms), the intermediate count
+ // might really be a 64-bit field since we used to use Pickle::WriteSize(),
+ // which writes either 32 or 64 bits depending on the architecture. Since
+ // x86-64 is little-endian, if that happens, the next 32 bits will be all
+ // zeroes (the high bits) and the 32 bits we already read above are the
+ // correct value (we assume there are never more than 2^32 - 1 intermediate
+ // certificates in a chain; in practice, more than a dozen or so is
+ // basically unheard of). Since it's invalid for a certificate to start with
+ // 32 bits of zeroes, we check for that here and skip it if we find it. We
+ // save a copy of the pickle iterator to restore in case we don't get 32
+ // bits of zeroes. Now we always write 32 bits, so after a while, these old
+ // cached pickles will all get replaced.
+ // TODO(mdm): remove this compatibility code in April 2013 or so.
+ PickleIterator saved_iter = *pickle_iter;
+ uint32 zero_check = 0;
+ if (!pickle_iter->ReadUInt32(&zero_check)) {
+ // This may not be an error. If there are no intermediates, and we're
+ // reading an old 32-bit pickle, and there's nothing else after this in
+ // the pickle, we should report success. Note that it is technically
+ // possible for us to skip over zeroes that should have occurred after
+ // an empty certificate list; to avoid this going forward, only do this
+ // backward-compatibility stuff for PICKLETYPE_CERTIFICATE_CHAIN_V1
+ // which comes from the pickle version number in http_response_info.cc.
+ if (num_intermediates) {
+ FreeOSCertHandle(cert_handle);
+ return NULL;
+ }
+ }
+ if (zero_check)
+ *pickle_iter = saved_iter;
+#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && defined(__x86_64__)
+
+ for (uint32 i = 0; i < num_intermediates; ++i) {
+ OSCertHandle intermediate = ReadOSCertHandleFromPickle(pickle_iter);
+ if (!intermediate)
+ break;
+ intermediates.push_back(intermediate);
+ }
+ }
+
+ X509Certificate* cert = NULL;
+ if (intermediates.size() == num_intermediates)
+ cert = CreateFromHandle(cert_handle, intermediates);
+ FreeOSCertHandle(cert_handle);
+ for (size_t i = 0; i < intermediates.size(); ++i)
+ FreeOSCertHandle(intermediates[i]);
+
+ return cert;
+}
+
+// static
+CertificateList X509Certificate::CreateCertificateListFromBytes(
+ const char* data, int length, int format) {
+ OSCertHandles certificates;
+
+ // Check to see if it is in a PEM-encoded form. This check is performed
+ // first, as both OS X and NSS will both try to convert if they detect
+ // PEM encoding, except they don't do it consistently between the two.
+ base::StringPiece data_string(data, length);
+ std::vector<std::string> pem_headers;
+
+ // To maintain compatibility with NSS/Firefox, CERTIFICATE is a universally
+ // valid PEM block header for any format.
+ pem_headers.push_back(kCertificateHeader);
+ if (format & FORMAT_PKCS7)
+ pem_headers.push_back(kPKCS7Header);
+
+ PEMTokenizer pem_tok(data_string, pem_headers);
+ while (pem_tok.GetNext()) {
+ std::string decoded(pem_tok.data());
+
+ OSCertHandle handle = NULL;
+ if (format & FORMAT_PEM_CERT_SEQUENCE)
+ handle = CreateOSCertHandleFromBytes(decoded.c_str(), decoded.size());
+ if (handle != NULL) {
+ // Parsed a DER encoded certificate. All PEM blocks that follow must
+ // also be DER encoded certificates wrapped inside of PEM blocks.
+ format = FORMAT_PEM_CERT_SEQUENCE;
+ certificates.push_back(handle);
+ continue;
+ }
+
+ // If the first block failed to parse as a DER certificate, and
+ // formats other than PEM are acceptable, check to see if the decoded
+ // data is one of the accepted formats.
+ if (format & ~FORMAT_PEM_CERT_SEQUENCE) {
+ for (size_t i = 0; certificates.empty() &&
+ i < arraysize(kFormatDecodePriority); ++i) {
+ if (format & kFormatDecodePriority[i]) {
+ certificates = CreateOSCertHandlesFromBytes(decoded.c_str(),
+ decoded.size(), kFormatDecodePriority[i]);
+ }
+ }
+ }
+
+ // Stop parsing after the first block for any format but a sequence of
+ // PEM-encoded DER certificates. The case of FORMAT_PEM_CERT_SEQUENCE
+ // is handled above, and continues processing until a certificate fails
+ // to parse.
+ break;
+ }
+
+ // Try each of the formats, in order of parse preference, to see if |data|
+ // contains the binary representation of a Format, if it failed to parse
+ // as a PEM certificate/chain.
+ for (size_t i = 0; certificates.empty() &&
+ i < arraysize(kFormatDecodePriority); ++i) {
+ if (format & kFormatDecodePriority[i])
+ certificates = CreateOSCertHandlesFromBytes(data, length,
+ kFormatDecodePriority[i]);
+ }
+
+ CertificateList results;
+ // No certificates parsed.
+ if (certificates.empty())
+ return results;
+
+ for (OSCertHandles::iterator it = certificates.begin();
+ it != certificates.end(); ++it) {
+ X509Certificate* result = CreateFromHandle(*it, OSCertHandles());
+ results.push_back(scoped_refptr<X509Certificate>(result));
+ FreeOSCertHandle(*it);
+ }
+
+ return results;
+}
+
+void X509Certificate::Persist(Pickle* pickle) {
+ DCHECK(cert_handle_);
+ // This would be an absolutely insane number of intermediates.
+ if (intermediate_ca_certs_.size() > static_cast<size_t>(INT_MAX) - 1) {
+ NOTREACHED();
+ return;
+ }
+ if (!pickle->WriteInt(
+ static_cast<int>(intermediate_ca_certs_.size() + 1)) ||
+ !WriteOSCertHandleToPickle(cert_handle_, pickle)) {
+ NOTREACHED();
+ return;
+ }
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
+ if (!WriteOSCertHandleToPickle(intermediate_ca_certs_[i], pickle)) {
+ NOTREACHED();
+ return;
+ }
+ }
+}
+
+void X509Certificate::GetDNSNames(std::vector<std::string>* dns_names) const {
+ GetSubjectAltName(dns_names, NULL);
+ if (dns_names->empty())
+ dns_names->push_back(subject_.common_name);
+}
+
+bool X509Certificate::HasExpired() const {
+ return base::Time::Now() > valid_expiry();
+}
+
+bool X509Certificate::Equals(const X509Certificate* other) const {
+ return IsSameOSCert(cert_handle_, other->cert_handle_);
+}
+
+// static
+bool X509Certificate::VerifyHostname(
+ const std::string& hostname,
+ const std::string& cert_common_name,
+ const std::vector<std::string>& cert_san_dns_names,
+ const std::vector<std::string>& cert_san_ip_addrs) {
+ DCHECK(!hostname.empty());
+ // Perform name verification following http://tools.ietf.org/html/rfc6125.
+ // The terminology used in this method is as per that RFC:-
+ // Reference identifier == the host the local user/agent is intending to
+ // access, i.e. the thing displayed in the URL bar.
+ // Presented identifier(s) == name(s) the server knows itself as, in its cert.
+
+ // CanonicalizeHost requires surrounding brackets to parse an IPv6 address.
+ const std::string host_or_ip = hostname.find(':') != std::string::npos ?
+ "[" + hostname + "]" : hostname;
+ url_canon::CanonHostInfo host_info;
+ std::string reference_name = CanonicalizeHost(host_or_ip, &host_info);
+ // CanonicalizeHost does not normalize absolute vs relative DNS names. If
+ // the input name was absolute (included trailing .), normalize it as if it
+ // was relative.
+ if (!reference_name.empty() && *reference_name.rbegin() == '.')
+ reference_name.resize(reference_name.size() - 1);
+ if (reference_name.empty())
+ return false;
+
+ // Allow fallback to Common name matching?
+ const bool common_name_fallback = cert_san_dns_names.empty() &&
+ cert_san_ip_addrs.empty();
+
+ // Fully handle all cases where |hostname| contains an IP address.
+ if (host_info.IsIPAddress()) {
+ if (common_name_fallback &&
+ host_info.family == url_canon::CanonHostInfo::IPV4) {
+ // Fallback to Common name matching. As this is deprecated and only
+ // supported for compatibility refuse it for IPv6 addresses.
+ return reference_name == cert_common_name;
+ }
+ base::StringPiece ip_addr_string(
+ reinterpret_cast<const char*>(host_info.address),
+ host_info.AddressLength());
+ return std::find(cert_san_ip_addrs.begin(), cert_san_ip_addrs.end(),
+ ip_addr_string) != cert_san_ip_addrs.end();
+ }
+
+ // |reference_domain| is the remainder of |host| after the leading host
+ // component is stripped off, but includes the leading dot e.g.
+ // "www.f.com" -> ".f.com".
+ // If there is no meaningful domain part to |host| (e.g. it contains no dots)
+ // then |reference_domain| will be empty.
+ base::StringPiece reference_host, reference_domain;
+ SplitOnChar(reference_name, '.', &reference_host, &reference_domain);
+ bool allow_wildcards = false;
+ if (!reference_domain.empty()) {
+ DCHECK(reference_domain.starts_with("."));
+
+ // Do not allow wildcards for public/ICANN registry controlled domains -
+ // that is, prevent *.com or *.co.uk as valid presented names, but do not
+ // prevent *.appspot.com (a private registry controlled domain).
+ // In addition, unknown top-level domains (such as 'intranet' domains or
+ // new TLDs/gTLDs not yet added to the registry controlled domain dataset)
+ // are also implicitly prevented.
+ // Because |reference_domain| must contain at least one name component that
+ // is not registry controlled, this ensures that all reference domains
+ // contain at least three domain components when using wildcards.
+ size_t registry_length =
+ registry_controlled_domains::GetRegistryLength(
+ reference_name,
+ registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
+ registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+
+ // Because |reference_name| was already canonicalized, the following
+ // should never happen.
+ CHECK_NE(std::string::npos, registry_length);
+
+ // Account for the leading dot in |reference_domain|.
+ bool is_registry_controlled =
+ registry_length != 0 &&
+ registry_length == (reference_domain.size() - 1);
+
+ // Additionally, do not attempt wildcard matching for purely numeric
+ // hostnames.
+ allow_wildcards =
+ !is_registry_controlled &&
+ reference_name.find_first_not_of("0123456789.") != std::string::npos;
+ }
+
+ // Now step through the DNS names doing wild card comparison (if necessary)
+ // on each against the reference name. If subjectAltName is empty, then
+ // fallback to use the common name instead.
+ std::vector<std::string> common_name_as_vector;
+ const std::vector<std::string>* presented_names = &cert_san_dns_names;
+ if (common_name_fallback) {
+ // Note: there's a small possibility cert_common_name is an international
+ // domain name in non-standard encoding (e.g. UTF8String or BMPString
+ // instead of A-label). As common name fallback is deprecated we're not
+ // doing anything specific to deal with this.
+ common_name_as_vector.push_back(cert_common_name);
+ presented_names = &common_name_as_vector;
+ }
+ for (std::vector<std::string>::const_iterator it =
+ presented_names->begin();
+ it != presented_names->end(); ++it) {
+ // Catch badly corrupt cert names up front.
+ if (it->empty() || it->find('\0') != std::string::npos) {
+ DVLOG(1) << "Bad name in cert: " << *it;
+ continue;
+ }
+ std::string presented_name(StringToLowerASCII(*it));
+
+ // Remove trailing dot, if any.
+ if (*presented_name.rbegin() == '.')
+ presented_name.resize(presented_name.length() - 1);
+
+ // The hostname must be at least as long as the cert name it is matching,
+ // as we require the wildcard (if present) to match at least one character.
+ if (presented_name.length() > reference_name.length())
+ continue;
+
+ base::StringPiece presented_host, presented_domain;
+ SplitOnChar(presented_name, '.', &presented_host, &presented_domain);
+
+ if (presented_domain != reference_domain)
+ continue;
+
+ base::StringPiece pattern_begin, pattern_end;
+ SplitOnChar(presented_host, '*', &pattern_begin, &pattern_end);
+
+ if (pattern_end.empty()) { // No '*' in the presented_host
+ if (presented_host == reference_host)
+ return true;
+ continue;
+ }
+ pattern_end.remove_prefix(1); // move past the *
+
+ if (!allow_wildcards)
+ continue;
+
+ // * must not match a substring of an IDN A label; just a whole fragment.
+ if (reference_host.starts_with("xn--") &&
+ !(pattern_begin.empty() && pattern_end.empty()))
+ continue;
+
+ if (reference_host.starts_with(pattern_begin) &&
+ reference_host.ends_with(pattern_end))
+ return true;
+ }
+ return false;
+}
+
+bool X509Certificate::VerifyNameMatch(const std::string& hostname) const {
+ std::vector<std::string> dns_names, ip_addrs;
+ GetSubjectAltName(&dns_names, &ip_addrs);
+ return VerifyHostname(hostname, subject_.common_name, dns_names, ip_addrs);
+}
+
+// static
+bool X509Certificate::GetPEMEncodedFromDER(const std::string& der_encoded,
+ std::string* pem_encoded) {
+ if (der_encoded.empty())
+ return false;
+ std::string b64_encoded;
+ if (!base::Base64Encode(der_encoded, &b64_encoded) || b64_encoded.empty())
+ return false;
+ *pem_encoded = "-----BEGIN CERTIFICATE-----\n";
+
+ // Divide the Base-64 encoded data into 64-character chunks, as per
+ // 4.3.2.4 of RFC 1421.
+ static const size_t kChunkSize = 64;
+ size_t chunks = (b64_encoded.size() + (kChunkSize - 1)) / kChunkSize;
+ for (size_t i = 0, chunk_offset = 0; i < chunks;
+ ++i, chunk_offset += kChunkSize) {
+ pem_encoded->append(b64_encoded, chunk_offset, kChunkSize);
+ pem_encoded->append("\n");
+ }
+ pem_encoded->append("-----END CERTIFICATE-----\n");
+ return true;
+}
+
+// static
+bool X509Certificate::GetPEMEncoded(OSCertHandle cert_handle,
+ std::string* pem_encoded) {
+ std::string der_encoded;
+ if (!GetDEREncoded(cert_handle, &der_encoded))
+ return false;
+ return GetPEMEncodedFromDER(der_encoded, pem_encoded);
+}
+
+bool X509Certificate::GetPEMEncodedChain(
+ std::vector<std::string>* pem_encoded) const {
+ std::vector<std::string> encoded_chain;
+ std::string pem_data;
+ if (!GetPEMEncoded(os_cert_handle(), &pem_data))
+ return false;
+ encoded_chain.push_back(pem_data);
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
+ if (!GetPEMEncoded(intermediate_ca_certs_[i], &pem_data))
+ return false;
+ encoded_chain.push_back(pem_data);
+ }
+ pem_encoded->swap(encoded_chain);
+ return true;
+}
+
+X509Certificate::X509Certificate(OSCertHandle cert_handle,
+ const OSCertHandles& intermediates)
+ : cert_handle_(DupOSCertHandle(cert_handle)) {
+ InsertOrUpdateCache(&cert_handle_);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ // Duplicate the incoming certificate, as the caller retains ownership
+ // of |intermediates|.
+ OSCertHandle intermediate = DupOSCertHandle(intermediates[i]);
+ // Update the cache, which will assume ownership of the duplicated
+ // handle and return a suitable equivalent, potentially from the cache.
+ InsertOrUpdateCache(&intermediate);
+ intermediate_ca_certs_.push_back(intermediate);
+ }
+ // Platform-specific initialization.
+ Initialize();
+}
+
+X509Certificate::~X509Certificate() {
+ if (cert_handle_) {
+ RemoveFromCache(cert_handle_);
+ FreeOSCertHandle(cert_handle_);
+ }
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
+ RemoveFromCache(intermediate_ca_certs_[i]);
+ FreeOSCertHandle(intermediate_ca_certs_[i]);
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate.h b/chromium/net/cert/x509_certificate.h
new file mode 100644
index 00000000000..ef552431729
--- /dev/null
+++ b/chromium/net/cert/x509_certificate.h
@@ -0,0 +1,487 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_CERTIFICATE_H_
+#define NET_CERT_X509_CERTIFICATE_H_
+
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_type.h"
+#include "net/cert/x509_cert_types.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#elif defined(OS_MACOSX)
+#include <CoreFoundation/CFArray.h>
+#include <Security/SecBase.h>
+
+#elif defined(USE_OPENSSL)
+// Forward declaration; real one in <x509.h>
+typedef struct x509_st X509;
+typedef struct x509_store_st X509_STORE;
+#elif defined(USE_NSS)
+// Forward declaration; real one in <cert.h>
+struct CERTCertificateStr;
+#endif
+
+class Pickle;
+class PickleIterator;
+
+namespace net {
+
+class CRLSet;
+class CertVerifyResult;
+
+typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
+
+// X509Certificate represents a X.509 certificate, which is comprised a
+// particular identity or end-entity certificate, such as an SSL server
+// identity or an SSL client certificate, and zero or more intermediate
+// certificates that may be used to build a path to a root certificate.
+class NET_EXPORT X509Certificate
+ : public base::RefCountedThreadSafe<X509Certificate> {
+ public:
+ // An OSCertHandle is a handle to a certificate object in the underlying
+ // crypto library. We assume that OSCertHandle is a pointer type on all
+ // platforms and that NULL represents an invalid OSCertHandle.
+#if defined(OS_WIN)
+ typedef PCCERT_CONTEXT OSCertHandle;
+#elif defined(OS_MACOSX)
+ typedef SecCertificateRef OSCertHandle;
+#elif defined(USE_OPENSSL)
+ typedef X509* OSCertHandle;
+#elif defined(USE_NSS)
+ typedef struct CERTCertificateStr* OSCertHandle;
+#else
+ // TODO(ericroman): not implemented
+ typedef void* OSCertHandle;
+#endif
+
+ typedef std::vector<OSCertHandle> OSCertHandles;
+
+ enum PublicKeyType {
+ kPublicKeyTypeUnknown,
+ kPublicKeyTypeRSA,
+ kPublicKeyTypeDSA,
+ kPublicKeyTypeECDSA,
+ kPublicKeyTypeDH,
+ kPublicKeyTypeECDH
+ };
+
+ // Predicate functor used in maps when X509Certificate is used as the key.
+ class NET_EXPORT LessThan {
+ public:
+ bool operator()(const scoped_refptr<X509Certificate>& lhs,
+ const scoped_refptr<X509Certificate>& rhs) const;
+ };
+
+ enum Format {
+ // The data contains a single DER-encoded certificate, or a PEM-encoded
+ // DER certificate with the PEM encoding block name of "CERTIFICATE".
+ // Any subsequent blocks will be ignored.
+ FORMAT_SINGLE_CERTIFICATE = 1 << 0,
+
+ // The data contains a sequence of one or more PEM-encoded, DER
+ // certificates, with the PEM encoding block name of "CERTIFICATE".
+ // All PEM blocks will be parsed, until the first error is encountered.
+ FORMAT_PEM_CERT_SEQUENCE = 1 << 1,
+
+ // The data contains a PKCS#7 SignedData structure, whose certificates
+ // member is to be used to initialize the certificate and intermediates.
+ // The data may further be encoded using PEM, specifying block names of
+ // either "PKCS7" or "CERTIFICATE".
+ FORMAT_PKCS7 = 1 << 2,
+
+ // Automatically detect the format.
+ FORMAT_AUTO = FORMAT_SINGLE_CERTIFICATE | FORMAT_PEM_CERT_SEQUENCE |
+ FORMAT_PKCS7,
+ };
+
+ // PickleType is intended for deserializing certificates that were pickled
+ // by previous releases as part of a net::HttpResponseInfo.
+ // When serializing certificates to a new Pickle,
+ // PICKLETYPE_CERTIFICATE_CHAIN_V3 is always used.
+ enum PickleType {
+ // When reading a certificate from a Pickle, the Pickle only contains a
+ // single certificate.
+ PICKLETYPE_SINGLE_CERTIFICATE,
+
+ // When reading a certificate from a Pickle, the Pickle contains the
+ // the certificate plus any certificates that were stored in
+ // |intermediate_ca_certificates_| at the time it was serialized.
+ // The count of certificates is stored as a size_t, which is either 32
+ // or 64 bits.
+ PICKLETYPE_CERTIFICATE_CHAIN_V2,
+
+ // The Pickle contains the certificate and any certificates that were
+ // stored in |intermediate_ca_certs_| at the time it was serialized.
+ // The format is [int count], [data - this certificate],
+ // [data - intermediate1], ... [data - intermediateN].
+ // All certificates are stored in DER form.
+ PICKLETYPE_CERTIFICATE_CHAIN_V3,
+ };
+
+ // Creates a X509Certificate from the ground up. Used by tests that simulate
+ // SSL connections.
+ X509Certificate(const std::string& subject, const std::string& issuer,
+ base::Time start_date, base::Time expiration_date);
+
+ // Create an X509Certificate from a handle to the certificate object in the
+ // underlying crypto library. The returned pointer must be stored in a
+ // scoped_refptr<X509Certificate>.
+ static X509Certificate* CreateFromHandle(OSCertHandle cert_handle,
+ const OSCertHandles& intermediates);
+
+ // Create an X509Certificate from a chain of DER encoded certificates. The
+ // first certificate in the chain is the end-entity certificate to which a
+ // handle is returned. The other certificates in the chain are intermediate
+ // certificates. The returned pointer must be stored in a
+ // scoped_refptr<X509Certificate>.
+ static X509Certificate* CreateFromDERCertChain(
+ const std::vector<base::StringPiece>& der_certs);
+
+ // Create an X509Certificate from the DER-encoded representation.
+ // Returns NULL on failure.
+ //
+ // The returned pointer must be stored in a scoped_refptr<X509Certificate>.
+ static X509Certificate* CreateFromBytes(const char* data, int length);
+
+#if defined(USE_NSS)
+ // Create an X509Certificate from the DER-encoded representation.
+ // |nickname| can be NULL if an auto-generated nickname is desired.
+ // Returns NULL on failure. The returned pointer must be stored in a
+ // scoped_refptr<X509Certificate>.
+ //
+ // This function differs from CreateFromBytes in that it takes a
+ // nickname that will be used when the certificate is imported into PKCS#11.
+ static X509Certificate* CreateFromBytesWithNickname(const char* data,
+ int length,
+ const char* nickname);
+
+ // The default nickname of the certificate, based on the certificate type
+ // passed in. If this object was created using CreateFromBytesWithNickname,
+ // then this will return the nickname specified upon creation.
+ std::string GetDefaultNickname(CertType type) const;
+#endif
+
+ // Create an X509Certificate from the representation stored in the given
+ // pickle. The data for this object is found relative to the given
+ // pickle_iter, which should be passed to the pickle's various Read* methods.
+ // Returns NULL on failure.
+ //
+ // The returned pointer must be stored in a scoped_refptr<X509Certificate>.
+ static X509Certificate* CreateFromPickle(const Pickle& pickle,
+ PickleIterator* pickle_iter,
+ PickleType type);
+
+ // Parses all of the certificates possible from |data|. |format| is a
+ // bit-wise OR of Format, indicating the possible formats the
+ // certificates may have been serialized as. If an error occurs, an empty
+ // collection will be returned.
+ static CertificateList CreateCertificateListFromBytes(const char* data,
+ int length,
+ int format);
+
+ // Appends a representation of this object to the given pickle.
+ void Persist(Pickle* pickle);
+
+ // The serial number, DER encoded, possibly including a leading 00 byte.
+ const std::string& serial_number() const { return serial_number_; }
+
+ // The subject of the certificate. For HTTPS server certificates, this
+ // represents the web server. The common name of the subject should match
+ // the host name of the web server.
+ const CertPrincipal& subject() const { return subject_; }
+
+ // The issuer of the certificate.
+ const CertPrincipal& issuer() const { return issuer_; }
+
+ // Time period during which the certificate is valid. More precisely, this
+ // certificate is invalid before the |valid_start| date and invalid after
+ // the |valid_expiry| date.
+ // If we were unable to parse either date from the certificate (or if the cert
+ // lacks either date), the date will be null (i.e., is_null() will be true).
+ const base::Time& valid_start() const { return valid_start_; }
+ const base::Time& valid_expiry() const { return valid_expiry_; }
+
+ // The fingerprint of this certificate.
+ const SHA1HashValue& fingerprint() const { return fingerprint_; }
+
+ // The fingerprint of the intermediate CA certificates.
+ const SHA1HashValue& ca_fingerprint() const {
+ return ca_fingerprint_;
+ }
+
+ // Gets the DNS names in the certificate. Pursuant to RFC 2818, Section 3.1
+ // Server Identity, if the certificate has a subjectAltName extension of
+ // type dNSName, this method gets the DNS names in that extension.
+ // Otherwise, it gets the common name in the subject field.
+ void GetDNSNames(std::vector<std::string>* dns_names) const;
+
+ // Gets the subjectAltName extension field from the certificate, if any.
+ // For future extension; currently this only returns those name types that
+ // are required for HTTP certificate name verification - see VerifyHostname.
+ // Unrequired parameters may be passed as NULL.
+ void GetSubjectAltName(std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const;
+
+ // Convenience method that returns whether this certificate has expired as of
+ // now.
+ bool HasExpired() const;
+
+ // Returns true if this object and |other| represent the same certificate.
+ bool Equals(const X509Certificate* other) const;
+
+ // Returns intermediate certificates added via AddIntermediateCertificate().
+ // Ownership follows the "get" rule: it is the caller's responsibility to
+ // retain the elements of the result.
+ const OSCertHandles& GetIntermediateCertificates() const {
+ return intermediate_ca_certs_;
+ }
+
+#if defined(OS_MACOSX)
+ // Does this certificate's usage allow SSL client authentication?
+ bool SupportsSSLClientAuth() const;
+
+ // Returns a new CFArrayRef containing this certificate and its intermediate
+ // certificates in the form expected by Security.framework and Keychain
+ // Services, or NULL on failure.
+ // The first item in the array will be this certificate, followed by its
+ // intermediates, if any.
+ CFArrayRef CreateOSCertChainForCert() const;
+#endif
+
+ // Do any of the given issuer names appear in this cert's chain of trust?
+ // |valid_issuers| is a list of DER-encoded X.509 DistinguishedNames.
+ bool IsIssuedByEncoded(const std::vector<std::string>& valid_issuers);
+
+#if defined(OS_WIN)
+ // Returns a new PCCERT_CONTEXT containing this certificate and its
+ // intermediate certificates, or NULL on failure. The returned
+ // PCCERT_CONTEXT *MUST NOT* be stored in an X509Certificate, as this will
+ // cause os_cert_handle() to return incorrect results. This function is only
+ // necessary if the CERT_CONTEXT.hCertStore member will be accessed or
+ // enumerated, which is generally true for any CryptoAPI functions involving
+ // certificate chains, including validation or certificate display.
+ //
+ // Remarks:
+ // Depending on the CryptoAPI function, Windows may need to access the
+ // HCERTSTORE that the passed-in PCCERT_CONTEXT belongs to, such as to
+ // locate additional intermediates. However, all certificate handles are added
+ // to a NULL HCERTSTORE, allowing the system to manage the resources. As a
+ // result, intermediates for |cert_handle_| cannot be located simply via
+ // |cert_handle_->hCertStore|, as it refers to a magic value indicating
+ // "only this certificate".
+ //
+ // To avoid this problems, a new in-memory HCERTSTORE is created containing
+ // just this certificate and its intermediates. The handle to the version of
+ // the current certificate in the new HCERTSTORE is then returned, with the
+ // PCCERT_CONTEXT's HCERTSTORE set to be automatically freed when the returned
+ // certificate handle is freed.
+ //
+ // This function is only needed when the HCERTSTORE of the os_cert_handle()
+ // will be accessed, which is generally only during certificate validation
+ // or display. While the returned PCCERT_CONTEXT and its HCERTSTORE can
+ // safely be used on multiple threads if no further modifications happen, it
+ // is generally preferable for each thread that needs such a context to
+ // obtain its own, rather than risk thread-safety issues by sharing.
+ //
+ // Because of how X509Certificate caching is implemented, attempting to
+ // create an X509Certificate from the returned PCCERT_CONTEXT may result in
+ // the original handle (and thus the originall HCERTSTORE) being returned by
+ // os_cert_handle(). For this reason, the returned PCCERT_CONTEXT *MUST NOT*
+ // be stored in an X509Certificate.
+ PCCERT_CONTEXT CreateOSCertChainForCert() const;
+#endif
+
+#if defined(USE_OPENSSL)
+ // Returns a handle to a global, in-memory certificate store. We
+ // use it for test code, e.g. importing the test server's certificate.
+ static X509_STORE* cert_store();
+#endif
+
+ // Verifies that |hostname| matches this certificate.
+ // Does not verify that the certificate is valid, only that the certificate
+ // matches this host.
+ // Returns true if it matches.
+ bool VerifyNameMatch(const std::string& hostname) const;
+
+ // Obtains the DER encoded certificate data for |cert_handle|. On success,
+ // returns true and writes the DER encoded certificate to |*der_encoded|.
+ static bool GetDEREncoded(OSCertHandle cert_handle,
+ std::string* der_encoded);
+
+ // Returns the PEM encoded data from a DER encoded certificate. If the return
+ // value is true, then the PEM encoded certificate is written to
+ // |pem_encoded|.
+ static bool GetPEMEncodedFromDER(const std::string& der_encoded,
+ std::string* pem_encoded);
+
+ // Returns the PEM encoded data from an OSCertHandle. If the return value is
+ // true, then the PEM encoded certificate is written to |pem_encoded|.
+ static bool GetPEMEncoded(OSCertHandle cert_handle,
+ std::string* pem_encoded);
+
+ // Encodes the entire certificate chain (this certificate and any
+ // intermediate certificates stored in |intermediate_ca_certs_|) as a series
+ // of PEM encoded strings. Returns true if all certificates were encoded,
+ // storig the result in |*pem_encoded|, with this certificate stored as
+ // the first element.
+ bool GetPEMEncodedChain(std::vector<std::string>* pem_encoded) const;
+
+ // Sets |*size_bits| to be the length of the public key in bits, and sets
+ // |*type| to one of the |PublicKeyType| values. In case of
+ // |kPublicKeyTypeUnknown|, |*size_bits| will be set to 0.
+ static void GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type);
+
+ // Returns the OSCertHandle of this object. Because of caching, this may
+ // differ from the OSCertHandle originally supplied during initialization.
+ // Note: On Windows, CryptoAPI may return unexpected results if this handle
+ // is used across multiple threads. For more details, see
+ // CreateOSCertChainForCert().
+ OSCertHandle os_cert_handle() const { return cert_handle_; }
+
+ // Returns true if two OSCertHandles refer to identical certificates.
+ static bool IsSameOSCert(OSCertHandle a, OSCertHandle b);
+
+ // Creates an OS certificate handle from the DER-encoded representation.
+ // Returns NULL on failure.
+ static OSCertHandle CreateOSCertHandleFromBytes(const char* data,
+ int length);
+
+#if defined(USE_NSS)
+ // Creates an OS certificate handle from the DER-encoded representation.
+ // Returns NULL on failure. Sets the default nickname if |nickname| is
+ // non-NULL.
+ static OSCertHandle CreateOSCertHandleFromBytesWithNickname(
+ const char* data,
+ int length,
+ const char* nickname);
+#endif
+
+ // Creates all possible OS certificate handles from |data| encoded in a
+ // specific |format|. Returns an empty collection on failure.
+ static OSCertHandles CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ Format format);
+
+ // Duplicates (or adds a reference to) an OS certificate handle.
+ static OSCertHandle DupOSCertHandle(OSCertHandle cert_handle);
+
+ // Frees (or releases a reference to) an OS certificate handle.
+ static void FreeOSCertHandle(OSCertHandle cert_handle);
+
+ // Calculates the SHA-1 fingerprint of the certificate. Returns an empty
+ // (all zero) fingerprint on failure.
+ static SHA1HashValue CalculateFingerprint(OSCertHandle cert_handle);
+
+ // Calculates the SHA-1 fingerprint of the intermediate CA certificates.
+ // Returns an empty (all zero) fingerprint on failure.
+ static SHA1HashValue CalculateCAFingerprint(
+ const OSCertHandles& intermediates);
+
+ private:
+ friend class base::RefCountedThreadSafe<X509Certificate>;
+ friend class TestRootCerts; // For unit tests
+
+ FRIEND_TEST_ALL_PREFIXES(X509CertificateNameVerifyTest, VerifyHostname);
+ FRIEND_TEST_ALL_PREFIXES(X509CertificateTest, SerialNumbers);
+
+ // Construct an X509Certificate from a handle to the certificate object
+ // in the underlying crypto library.
+ X509Certificate(OSCertHandle cert_handle,
+ const OSCertHandles& intermediates);
+
+ ~X509Certificate();
+
+ // Common object initialization code. Called by the constructors only.
+ void Initialize();
+
+#if defined(USE_OPENSSL)
+ // Resets the store returned by cert_store() to default state. Used by
+ // TestRootCerts to undo modifications.
+ static void ResetCertStore();
+#endif
+
+ // Verifies that |hostname| matches one of the certificate names or IP
+ // addresses supplied, based on TLS name matching rules - specifically,
+ // following http://tools.ietf.org/html/rfc6125.
+ // |cert_common_name| is the Subject CN, e.g. from X509Certificate::subject().
+ // The members of |cert_san_dns_names| and |cert_san_ipaddrs| must be filled
+ // from the dNSName and iPAddress components of the subject alternative name
+ // extension, if present. Note these IP addresses are NOT ascii-encoded:
+ // they must be 4 or 16 bytes of network-ordered data, for IPv4 and IPv6
+ // addresses, respectively.
+ static bool VerifyHostname(const std::string& hostname,
+ const std::string& cert_common_name,
+ const std::vector<std::string>& cert_san_dns_names,
+ const std::vector<std::string>& cert_san_ip_addrs);
+
+ // Reads a single certificate from |pickle_iter| and returns a
+ // platform-specific certificate handle. The format of the certificate
+ // stored in |pickle_iter| is not guaranteed to be the same across different
+ // underlying cryptographic libraries, nor acceptable to CreateFromBytes().
+ // Returns an invalid handle, NULL, on failure.
+ // NOTE: This should not be used for any new code. It is provided for
+ // migration purposes and should eventually be removed.
+ static OSCertHandle ReadOSCertHandleFromPickle(PickleIterator* pickle_iter);
+
+ // Writes a single certificate to |pickle| in DER form. Returns false on
+ // failure.
+ static bool WriteOSCertHandleToPickle(OSCertHandle handle, Pickle* pickle);
+
+ // The subject of the certificate.
+ CertPrincipal subject_;
+
+ // The issuer of the certificate.
+ CertPrincipal issuer_;
+
+ // This certificate is not valid before |valid_start_|
+ base::Time valid_start_;
+
+ // This certificate is not valid after |valid_expiry_|
+ base::Time valid_expiry_;
+
+ // The fingerprint of this certificate.
+ SHA1HashValue fingerprint_;
+
+ // The fingerprint of the intermediate CA certificates.
+ SHA1HashValue ca_fingerprint_;
+
+ // The serial number of this certificate, DER encoded.
+ std::string serial_number_;
+
+ // A handle to the certificate object in the underlying crypto library.
+ OSCertHandle cert_handle_;
+
+ // Untrusted intermediate certificates associated with this certificate
+ // that may be needed for chain building.
+ OSCertHandles intermediate_ca_certs_;
+
+#if defined(USE_NSS)
+ // This stores any default nickname that has been set on the certificate
+ // at creation time with CreateFromBytesWithNickname.
+ // If this is empty, then GetDefaultNickname will return a generated name
+ // based on the type of the certificate.
+ std::string default_nickname_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(X509Certificate);
+};
+
+} // namespace net
+
+#endif // NET_CERT_X509_CERTIFICATE_H_
diff --git a/chromium/net/cert/x509_certificate_ios.cc b/chromium/net/cert/x509_certificate_ios.cc
new file mode 100644
index 00000000000..48f833f2ec7
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_ios.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#include <Security/Security.h>
+
+#include <cert.h>
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <prtime.h>
+#include <prtypes.h>
+#include <secder.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/time/time.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/ev_root_ca_metadata.h"
+#include "net/cert/x509_util_ios.h"
+#include "net/cert/x509_util_nss.h"
+
+using base::ScopedCFTypeRef;
+
+namespace net {
+namespace {
+// Returns true if a given |cert_handle| is actually a valid X.509 certificate
+// handle.
+//
+// SecCertificateCreateFromData() does not always force the immediate parsing of
+// the certificate, and as such, may return a SecCertificateRef for an
+// invalid/unparsable certificate. Force parsing to occur to ensure that the
+// SecCertificateRef is correct. On later versions where
+// SecCertificateCreateFromData() immediately parses, rather than lazily, this
+// call is cheap, as the subject is cached.
+bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
+ ScopedCFTypeRef<CFStringRef> sanity_check(
+ SecCertificateCopySubjectSummary(cert_handle));
+ return sanity_check != NULL;
+}
+} // namespace
+
+void X509Certificate::Initialize() {
+ x509_util_ios::NSSCertificate nss_cert(cert_handle_);
+ CERTCertificate* cert_handle = nss_cert.cert_handle();
+ if (cert_handle) {
+ x509_util::ParsePrincipal(&cert_handle->subject, &subject_);
+ x509_util::ParsePrincipal(&cert_handle->issuer, &issuer_);
+ x509_util::ParseDate(&cert_handle->validity.notBefore, &valid_start_);
+ x509_util::ParseDate(&cert_handle->validity.notAfter, &valid_expiry_);
+ serial_number_ = x509_util::ParseSerialNumber(cert_handle);
+ }
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+ x509_util_ios::NSSCertChain nss_chain(this);
+ // Convert to scoped CERTName* list.
+ std::vector<CERTName*> issuers;
+ crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!x509_util::GetIssuersFromEncodedList(valid_issuers,
+ arena.get(),
+ &issuers)) {
+ return false;
+ }
+ return x509_util::IsCertificateIssuedBy(
+ nss_chain.cert_chain(), issuers);
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ x509_util_ios::NSSCertificate nss_cert(cert_handle_);
+ CERTCertificate* cert_handle = nss_cert.cert_handle();
+ if (!cert_handle) {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+ return;
+ }
+ x509_util::GetSubjectAltName(cert_handle, dns_names, ip_addrs);
+}
+
+// static
+bool X509Certificate::GetDEREncoded(OSCertHandle cert_handle,
+ std::string* encoded) {
+ ScopedCFTypeRef<CFDataRef> der_data(SecCertificateCopyData(cert_handle));
+ if (!der_data)
+ return false;
+ encoded->assign(reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+ CFDataGetLength(der_data));
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+ if (CFEqual(a, b))
+ return true;
+ ScopedCFTypeRef<CFDataRef> a_data(SecCertificateCopyData(a));
+ ScopedCFTypeRef<CFDataRef> b_data(SecCertificateCopyData(b));
+ return a_data && b_data &&
+ CFDataGetLength(a_data) == CFDataGetLength(b_data) &&
+ memcmp(CFDataGetBytePtr(a_data), CFDataGetBytePtr(b_data),
+ CFDataGetLength(a_data)) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ ScopedCFTypeRef<CFDataRef> cert_data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(data), length,
+ kCFAllocatorNull));
+ if (!cert_data)
+ return NULL;
+ OSCertHandle cert_handle = SecCertificateCreateWithData(NULL, cert_data);
+ if (!cert_handle)
+ return NULL;
+ if (!IsValidOSCertHandle(cert_handle)) {
+ CFRelease(cert_handle);
+ return NULL;
+ }
+ return cert_handle;
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ Format format) {
+ return x509_util::CreateOSCertHandlesFromBytes(data, length, format);
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle handle) {
+ if (!handle)
+ return NULL;
+ return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ CFRelease(cert_handle);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(
+ OSCertHandle cert) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert));
+ if (!cert_data)
+ return sha1;
+ DCHECK(CFDataGetBytePtr(cert_data));
+ DCHECK_NE(0, CFDataGetLength(cert_data));
+ CC_SHA1(CFDataGetBytePtr(cert_data), CFDataGetLength(cert_data), sha1.data);
+
+ return sha1;
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ // The CC_SHA(3cc) man page says all CC_SHA1_xxx routines return 1, so
+ // we don't check their return values.
+ CC_SHA1_CTX sha1_ctx;
+ CC_SHA1_Init(&sha1_ctx);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ ScopedCFTypeRef<CFDataRef>
+ cert_data(SecCertificateCopyData(intermediates[i]));
+ if (!cert_data)
+ return sha1;
+ CC_SHA1_Update(&sha1_ctx,
+ CFDataGetBytePtr(cert_data),
+ CFDataGetLength(cert_data));
+ }
+ CC_SHA1_Final(sha1.data, &sha1_ctx);
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ return x509_util::ReadOSCertHandleFromPickle(pickle_iter);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert_handle));
+ if (!cert_data)
+ return false;
+
+ return pickle->WriteData(
+ reinterpret_cast<const char*>(CFDataGetBytePtr(cert_data)),
+ CFDataGetLength(cert_data));
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ x509_util_ios::NSSCertificate nss_cert(cert_handle);
+ x509_util::GetPublicKeyInfo(nss_cert.cert_handle(), size_bits, type);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate_known_roots_mac.h b/chromium/net/cert/x509_certificate_known_roots_mac.h
new file mode 100644
index 00000000000..c4375f0f61c
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_known_roots_mac.h
@@ -0,0 +1,433 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_CERTIFICATE_KNOWN_ROOTS_MAC_H_
+#define NET_CERT_X509_CERTIFICATE_KNOWN_ROOTS_MAC_H_
+
+// This is the set of Apple trusted roots from OS X 10.8.3, revision 55137.5
+// and available at:
+// http://opensource.apple.com/source/securityd/securityd-55137.5/
+//
+// Note that these *are not* trust anchors for Chromium. They are only used to
+// distinguish `real' root CAs from roots that were user-installed.
+static uint8 kKnownRootCertSHA1Hashes[][20] = {
+ {0x01, 0x68, 0x97, 0xe1, 0xa0, 0xb8, 0xf2, 0xc3, 0xb1, 0x34,
+ 0x66, 0x5c, 0x20, 0xa7, 0x27, 0xb7, 0xa1, 0x58, 0xe2, 0x8f},
+ {0x02, 0x72, 0x68, 0x29, 0x3e, 0x5f, 0x5d, 0x17, 0xaa, 0xa4,
+ 0xb3, 0xc3, 0xe6, 0x36, 0x1e, 0x1f, 0x92, 0x57, 0x5e, 0xaa},
+ {0x02, 0xfa, 0xf3, 0xe2, 0x91, 0x43, 0x54, 0x68, 0x60, 0x78,
+ 0x57, 0x69, 0x4d, 0xf5, 0xe4, 0x5b, 0x68, 0x85, 0x18, 0x68},
+ {0x04, 0x83, 0xed, 0x33, 0x99, 0xac, 0x36, 0x08, 0x05, 0x87,
+ 0x22, 0xed, 0xbc, 0x5e, 0x46, 0x00, 0xe3, 0xbe, 0xf9, 0xd7},
+ {0x05, 0x63, 0xb8, 0x63, 0x0d, 0x62, 0xd7, 0x5a, 0xbb, 0xc8,
+ 0xab, 0x1e, 0x4b, 0xdf, 0xb5, 0xa8, 0x99, 0xb2, 0x4d, 0x43},
+ {0x06, 0x08, 0x3f, 0x59, 0x3f, 0x15, 0xa1, 0x04, 0xa0, 0x69,
+ 0xa4, 0x6b, 0xa9, 0x03, 0xd0, 0x06, 0xb7, 0x97, 0x09, 0x91},
+ {0x07, 0x47, 0x22, 0x01, 0x99, 0xce, 0x74, 0xb9, 0x7c, 0xb0,
+ 0x3d, 0x79, 0xb2, 0x64, 0xa2, 0xc8, 0x55, 0xe9, 0x33, 0xff},
+ {0x07, 0xe0, 0x32, 0xe0, 0x20, 0xb7, 0x2c, 0x3f, 0x19, 0x2f,
+ 0x06, 0x28, 0xa2, 0x59, 0x3a, 0x19, 0xa7, 0x0f, 0x06, 0x9e},
+ {0x08, 0x64, 0x18, 0xe9, 0x06, 0xce, 0xe8, 0x9c, 0x23, 0x53,
+ 0xb6, 0xe2, 0x7f, 0xbd, 0x9e, 0x74, 0x39, 0xf7, 0x63, 0x16},
+ {0x0b, 0x77, 0xbe, 0xbb, 0xcb, 0x7a, 0xa2, 0x47, 0x05, 0xde,
+ 0xcc, 0x0f, 0xbd, 0x6a, 0x02, 0xfc, 0x7a, 0xbd, 0x9b, 0x52},
+ {0x0b, 0x97, 0x2c, 0x9e, 0xa6, 0xe7, 0xcc, 0x58, 0xd9, 0x3b,
+ 0x20, 0xbf, 0x71, 0xec, 0x41, 0x2e, 0x72, 0x09, 0xfa, 0xbf},
+ {0x0c, 0x08, 0xb8, 0x4a, 0x1f, 0x26, 0xf5, 0x05, 0x49, 0xda,
+ 0xab, 0x36, 0x33, 0x08, 0xd3, 0x56, 0x52, 0x68, 0xf1, 0x60},
+ {0x10, 0x1d, 0xfa, 0x3f, 0xd5, 0x0b, 0xcb, 0xbb, 0x9b, 0xb5,
+ 0x60, 0x0c, 0x19, 0x55, 0xa4, 0x1a, 0xf4, 0x73, 0x3a, 0x04},
+ {0x10, 0xf1, 0x93, 0xf3, 0x40, 0xac, 0x91, 0xd6, 0xde, 0x5f,
+ 0x1e, 0xdc, 0x00, 0x62, 0x47, 0xc4, 0xf2, 0x5d, 0x96, 0x71},
+ {0x13, 0x2d, 0x0d, 0x45, 0x53, 0x4b, 0x69, 0x97, 0xcd, 0xb2,
+ 0xd5, 0xc3, 0x39, 0xe2, 0x55, 0x76, 0x60, 0x9b, 0x5c, 0xc6},
+ {0x17, 0xc0, 0xc5, 0x9a, 0xb5, 0xd8, 0xd5, 0x85, 0x20, 0x43,
+ 0xe8, 0xec, 0x69, 0x2c, 0x40, 0x9d, 0x80, 0x62, 0xaa, 0x53},
+ {0x1b, 0x4b, 0x39, 0x61, 0x26, 0x27, 0x6b, 0x64, 0x91, 0xa2,
+ 0x68, 0x6d, 0xd7, 0x02, 0x43, 0x21, 0x2d, 0x1f, 0x1d, 0x96},
+ {0x1f, 0x49, 0x14, 0xf7, 0xd8, 0x74, 0x95, 0x1d, 0xdd, 0xae,
+ 0x02, 0xc0, 0xbe, 0xfd, 0x3a, 0x2d, 0x82, 0x75, 0x51, 0x85},
+ {0x20, 0x42, 0x85, 0xdc, 0xf7, 0xeb, 0x76, 0x41, 0x95, 0x57,
+ 0x8e, 0x13, 0x6b, 0xd4, 0xb7, 0xd1, 0xe9, 0x8e, 0x46, 0xa5},
+ {0x20, 0x99, 0x00, 0xb6, 0x3d, 0x95, 0x57, 0x28, 0x14, 0x0c,
+ 0xd1, 0x36, 0x22, 0xd8, 0xc6, 0x87, 0xa4, 0xeb, 0x00, 0x85},
+ {0x20, 0xce, 0xb1, 0xf0, 0xf5, 0x1c, 0x0e, 0x19, 0xa9, 0xf3,
+ 0x8d, 0xb1, 0xaa, 0x8e, 0x03, 0x8c, 0xaa, 0x7a, 0xc7, 0x01},
+ {0x21, 0xfc, 0xbd, 0x8e, 0x7f, 0x6c, 0xaf, 0x05, 0x1b, 0xd1,
+ 0xb3, 0x43, 0xec, 0xa8, 0xe7, 0x61, 0x47, 0xf2, 0x0f, 0x8a},
+ {0x23, 0xe5, 0x94, 0x94, 0x51, 0x95, 0xf2, 0x41, 0x48, 0x03,
+ 0xb4, 0xd5, 0x64, 0xd2, 0xa3, 0xa3, 0xf5, 0xd8, 0x8b, 0x8c},
+ {0x25, 0x01, 0x90, 0x19, 0xcf, 0xfb, 0xd9, 0x99, 0x1c, 0xb7,
+ 0x68, 0x25, 0x74, 0x8d, 0x94, 0x5f, 0x30, 0x93, 0x95, 0x42},
+ {0x25, 0x3f, 0x77, 0x5b, 0x0e, 0x77, 0x97, 0xab, 0x64, 0x5f,
+ 0x15, 0x91, 0x55, 0x97, 0xc3, 0x9e, 0x26, 0x36, 0x31, 0xd1},
+ {0x27, 0x0c, 0x50, 0x0c, 0xc6, 0xc8, 0x6e, 0xcb, 0x19, 0x80,
+ 0xbc, 0x13, 0x05, 0x43, 0x9e, 0xd2, 0x82, 0x48, 0x0b, 0xe3},
+ {0x27, 0x3e, 0xe1, 0x24, 0x57, 0xfd, 0xc4, 0xf9, 0x0c, 0x55,
+ 0xe8, 0x2b, 0x56, 0x16, 0x7f, 0x62, 0xf5, 0x32, 0xe5, 0x47},
+ {0x27, 0x96, 0xba, 0xe6, 0x3f, 0x18, 0x01, 0xe2, 0x77, 0x26,
+ 0x1b, 0xa0, 0xd7, 0x77, 0x70, 0x02, 0x8f, 0x20, 0xee, 0xe4},
+ {0x29, 0x36, 0x21, 0x02, 0x8b, 0x20, 0xed, 0x02, 0xf5, 0x66,
+ 0xc5, 0x32, 0xd1, 0xd6, 0xed, 0x90, 0x9f, 0x45, 0x00, 0x2f},
+ {0x29, 0x64, 0xb6, 0x86, 0x13, 0x5b, 0x5d, 0xfd, 0xdd, 0x32,
+ 0x53, 0xa8, 0x9b, 0xbc, 0x24, 0xd7, 0x4b, 0x08, 0xc6, 0x4d},
+ {0x2a, 0xb6, 0x28, 0x48, 0x5e, 0x78, 0xfb, 0xf3, 0xad, 0x9e,
+ 0x79, 0x10, 0xdd, 0x6b, 0xdf, 0x99, 0x72, 0x2c, 0x96, 0xe5},
+ {0x2a, 0xc8, 0xd5, 0x8b, 0x57, 0xce, 0xbf, 0x2f, 0x49, 0xaf,
+ 0xf2, 0xfc, 0x76, 0x8f, 0x51, 0x14, 0x62, 0x90, 0x7a, 0x41},
+ {0x2b, 0x84, 0xbf, 0xbb, 0x34, 0xee, 0x2e, 0xf9, 0x49, 0xfe,
+ 0x1c, 0xbe, 0x30, 0xaa, 0x02, 0x64, 0x16, 0xeb, 0x22, 0x16},
+ {0x2c, 0xeb, 0x05, 0x34, 0xad, 0x59, 0x27, 0x18, 0x0d, 0x34,
+ 0x8c, 0x5f, 0x0e, 0x05, 0x6d, 0x38, 0x2b, 0x50, 0x82, 0x87},
+ {0x2d, 0xc7, 0xab, 0xf6, 0x7e, 0x9e, 0x63, 0x9a, 0x22, 0xdb,
+ 0xdf, 0x4e, 0xee, 0x9b, 0xc5, 0x2a, 0x48, 0xa2, 0xcc, 0x2d},
+ {0x2d, 0xff, 0x63, 0x36, 0xe3, 0x3a, 0x48, 0x29, 0xaa, 0x00,
+ 0x9f, 0x01, 0xa1, 0x80, 0x1e, 0xe7, 0xeb, 0xa5, 0x82, 0xbb},
+ {0x2e, 0xf6, 0x4b, 0xba, 0x77, 0xdd, 0x37, 0x79, 0xe9, 0x1f,
+ 0xbd, 0x5a, 0x4e, 0xee, 0x63, 0x3c, 0x8a, 0x36, 0xa5, 0xb1},
+ {0x2f, 0x17, 0x3f, 0x7d, 0xe9, 0x96, 0x67, 0xaf, 0xa5, 0x7a,
+ 0xf8, 0x0a, 0xa2, 0xd1, 0xb1, 0x2f, 0xac, 0x83, 0x03, 0x38},
+ {0x30, 0x77, 0x9e, 0x93, 0x15, 0x02, 0x2e, 0x94, 0x85, 0x6a,
+ 0x3f, 0xf8, 0xbc, 0xf8, 0x15, 0xb0, 0x82, 0xf9, 0xae, 0xfd},
+ {0x31, 0x7a, 0x2a, 0xd0, 0x7f, 0x2b, 0x33, 0x5e, 0xf5, 0xa1,
+ 0xc3, 0x4e, 0x4b, 0x57, 0xe8, 0xb7, 0xd8, 0xf1, 0xfc, 0xa6},
+ {0x32, 0x3c, 0x11, 0x8e, 0x1b, 0xf7, 0xb8, 0xb6, 0x52, 0x54,
+ 0xe2, 0xe2, 0x10, 0x0d, 0xd6, 0x02, 0x90, 0x37, 0xf0, 0x96},
+ {0x33, 0x9b, 0x6b, 0x14, 0x50, 0x24, 0x9b, 0x55, 0x7a, 0x01,
+ 0x87, 0x72, 0x84, 0xd9, 0xe0, 0x2f, 0xc3, 0xd2, 0xd8, 0xe9},
+ {0x36, 0x7d, 0x4b, 0x3b, 0x4f, 0xcb, 0xbc, 0x0b, 0x76, 0x7b,
+ 0x2e, 0xc0, 0xcd, 0xb2, 0xa3, 0x6e, 0xab, 0x71, 0xa4, 0xeb},
+ {0x36, 0x86, 0x35, 0x63, 0xfd, 0x51, 0x28, 0xc7, 0xbe, 0xa6,
+ 0xf0, 0x05, 0xcf, 0xe9, 0xb4, 0x36, 0x68, 0x08, 0x6c, 0xce},
+ {0x36, 0xb1, 0x2b, 0x49, 0xf9, 0x81, 0x9e, 0xd7, 0x4c, 0x9e,
+ 0xbc, 0x38, 0x0f, 0xc6, 0x56, 0x8f, 0x5d, 0xac, 0xb2, 0xf7},
+ {0x37, 0xf7, 0x6d, 0xe6, 0x07, 0x7c, 0x90, 0xc5, 0xb1, 0x3e,
+ 0x93, 0x1a, 0xb7, 0x41, 0x10, 0xb4, 0xf2, 0xe4, 0x9a, 0x27},
+ {0x39, 0x21, 0xc1, 0x15, 0xc1, 0x5d, 0x0e, 0xca, 0x5c, 0xcb,
+ 0x5b, 0xc4, 0xf0, 0x7d, 0x21, 0xd8, 0x05, 0x0b, 0x56, 0x6a},
+ {0x39, 0x4f, 0xf6, 0x85, 0x0b, 0x06, 0xbe, 0x52, 0xe5, 0x18,
+ 0x56, 0xcc, 0x10, 0xe1, 0x80, 0xe8, 0x82, 0xb3, 0x85, 0xcc},
+ {0x3a, 0x32, 0xef, 0x7b, 0x9a, 0xb8, 0x36, 0xf8, 0x37, 0x18,
+ 0x1a, 0x4c, 0xef, 0xa3, 0x55, 0xc6, 0x46, 0x67, 0xac, 0xbf},
+ {0x3a, 0x44, 0x73, 0x5a, 0xe5, 0x81, 0x90, 0x1f, 0x24, 0x86,
+ 0x61, 0x46, 0x1e, 0x3b, 0x9c, 0xc4, 0x5f, 0xf5, 0x3a, 0x1b},
+ {0x3b, 0x16, 0x6c, 0x3b, 0x7d, 0xc4, 0xb7, 0x51, 0xc9, 0xfe,
+ 0x2a, 0xfa, 0xb9, 0x13, 0x56, 0x41, 0xe3, 0x88, 0xe1, 0x86},
+ {0x3b, 0xc0, 0x38, 0x0b, 0x33, 0xc3, 0xf6, 0xa6, 0x0c, 0x86,
+ 0x15, 0x22, 0x93, 0xd9, 0xdf, 0xf5, 0x4b, 0x81, 0xc0, 0x04},
+ {0x3b, 0xc4, 0x9f, 0x48, 0xf8, 0xf3, 0x73, 0xa0, 0x9c, 0x1e,
+ 0xbd, 0xf8, 0x5b, 0xb1, 0xc3, 0x65, 0xc7, 0xd8, 0x11, 0xb3},
+ {0x3e, 0x2b, 0xf7, 0xf2, 0x03, 0x1b, 0x96, 0xf3, 0x8c, 0xe6,
+ 0xc4, 0xd8, 0xa8, 0x5d, 0x3e, 0x2d, 0x58, 0x47, 0x6a, 0x0f},
+ {0x3f, 0x01, 0x8e, 0x6f, 0xe3, 0x36, 0x09, 0x6a, 0x79, 0x1b,
+ 0x81, 0xe7, 0x69, 0xbe, 0x70, 0x1a, 0xaf, 0x21, 0xe3, 0x07},
+ {0x40, 0x54, 0xda, 0x6f, 0x1c, 0x3f, 0x40, 0x74, 0xac, 0xed,
+ 0x0f, 0xec, 0xcd, 0xdb, 0x79, 0xd1, 0x53, 0xfb, 0x90, 0x1d},
+ {0x40, 0x9d, 0x4b, 0xd9, 0x17, 0xb5, 0x5c, 0x27, 0xb6, 0x9b,
+ 0x64, 0xcb, 0x98, 0x22, 0x44, 0x0d, 0xcd, 0x09, 0xb8, 0x89},
+ {0x40, 0xaa, 0x38, 0x73, 0x1b, 0xd1, 0x89, 0xf9, 0xcd, 0xb5,
+ 0xb9, 0xdc, 0x35, 0xe2, 0x13, 0x6f, 0x38, 0x77, 0x7a, 0xf4},
+ {0x40, 0xe7, 0x8c, 0x1d, 0x52, 0x3d, 0x1c, 0xd9, 0x95, 0x4f,
+ 0xac, 0x1a, 0x1a, 0xb3, 0xbd, 0x3c, 0xba, 0xa1, 0x5b, 0xfc},
+ {0x42, 0xf8, 0x18, 0xe8, 0x33, 0x06, 0x3b, 0xf5, 0x16, 0xc6,
+ 0x61, 0x8c, 0x1e, 0x60, 0xfd, 0x0f, 0x35, 0xc4, 0x76, 0x21},
+ {0x43, 0xd9, 0xbc, 0xb5, 0x68, 0xe0, 0x39, 0xd0, 0x73, 0xa7,
+ 0x4a, 0x71, 0xd8, 0x51, 0x1f, 0x74, 0x76, 0x08, 0x9c, 0xc3},
+ {0x43, 0xf9, 0xb1, 0x10, 0xd5, 0xba, 0xfd, 0x48, 0x22, 0x52,
+ 0x31, 0xb0, 0xd0, 0x08, 0x2b, 0x37, 0x2f, 0xef, 0x9a, 0x54},
+ {0x47, 0xbe, 0xab, 0xc9, 0x22, 0xea, 0xe8, 0x0e, 0x78, 0x78,
+ 0x34, 0x62, 0xa7, 0x9f, 0x45, 0xc2, 0x54, 0xfd, 0xe6, 0x8b},
+ {0x4a, 0x3f, 0x8d, 0x6b, 0xdc, 0x0e, 0x1e, 0xcf, 0xcd, 0x72,
+ 0xe3, 0x77, 0xde, 0xf2, 0xd7, 0xff, 0x92, 0xc1, 0x9b, 0xc7},
+ {0x4a, 0x65, 0xd5, 0xf4, 0x1d, 0xef, 0x39, 0xb8, 0xb8, 0x90,
+ 0x4a, 0x4a, 0xd3, 0x64, 0x81, 0x33, 0xcf, 0xc7, 0xa1, 0xd1},
+ {0x4a, 0xd4, 0x4d, 0x4d, 0x81, 0x2e, 0x42, 0x23, 0x2f, 0xe0,
+ 0x38, 0x76, 0x4c, 0x7b, 0x0c, 0xeb, 0x46, 0x6e, 0xef, 0x96},
+ {0x4c, 0xab, 0x31, 0xa1, 0x28, 0x34, 0x02, 0x52, 0xbc, 0xb4,
+ 0x67, 0xd6, 0x2a, 0x99, 0x63, 0x1b, 0x21, 0x77, 0x20, 0x50},
+ {0x4d, 0x23, 0x78, 0xec, 0x91, 0x95, 0x39, 0xb5, 0x00, 0x7f,
+ 0x75, 0x8f, 0x03, 0x3b, 0x21, 0x1e, 0xc5, 0x4d, 0x8b, 0xcf},
+ {0x4e, 0xb6, 0xd5, 0x78, 0x49, 0x9b, 0x1c, 0xcf, 0x5f, 0x58,
+ 0x1e, 0xad, 0x56, 0xbe, 0x3d, 0x9b, 0x67, 0x44, 0xa5, 0xe5},
+ {0x4f, 0x99, 0xaa, 0x93, 0xfb, 0x2b, 0xd1, 0x37, 0x26, 0xa1,
+ 0x99, 0x4a, 0xce, 0x7f, 0xf0, 0x05, 0xf2, 0x93, 0x5d, 0x1e},
+ {0x50, 0x30, 0x06, 0x09, 0x1d, 0x97, 0xd4, 0xf5, 0xae, 0x39,
+ 0xf7, 0xcb, 0xe7, 0x92, 0x7d, 0x7d, 0x65, 0x2d, 0x34, 0x31},
+ {0x51, 0xa4, 0x4c, 0x28, 0xf3, 0x13, 0xe3, 0xf9, 0xcb, 0x5e,
+ 0x7c, 0x0a, 0x1e, 0x0e, 0x0d, 0xd2, 0x84, 0x37, 0x58, 0xae},
+ {0x51, 0xc3, 0x24, 0x7d, 0x60, 0xf3, 0x56, 0xc7, 0xca, 0x3b,
+ 0xaf, 0x4c, 0x3f, 0x42, 0x9d, 0xac, 0x93, 0xee, 0x7b, 0x74},
+ {0x51, 0xcc, 0xa0, 0x71, 0x0a, 0xf7, 0x73, 0x3d, 0x34, 0xac,
+ 0xdc, 0x19, 0x45, 0x09, 0x9f, 0x43, 0x5c, 0x7f, 0xc5, 0x9f},
+ {0x56, 0x4b, 0x6f, 0x8c, 0x56, 0x38, 0xdc, 0x05, 0x5b, 0xba,
+ 0x2b, 0xa1, 0x39, 0x0f, 0x7e, 0x31, 0x95, 0x4a, 0x55, 0x50},
+ {0x56, 0xe0, 0xfa, 0xc0, 0x3b, 0x8f, 0x18, 0x23, 0x55, 0x18,
+ 0xe5, 0xd3, 0x11, 0xca, 0xe8, 0xc2, 0x43, 0x31, 0xab, 0x66},
+ {0x57, 0xf0, 0x3d, 0xce, 0xfb, 0x45, 0x69, 0x4c, 0x1c, 0x25,
+ 0xe6, 0xee, 0xa0, 0x2c, 0x43, 0xd7, 0x52, 0x38, 0xd3, 0xc4},
+ {0x58, 0x0f, 0x80, 0x47, 0x92, 0xab, 0xc6, 0x3b, 0xbb, 0x80,
+ 0x15, 0x4d, 0x4d, 0xfd, 0xdd, 0x8b, 0x2e, 0xf2, 0x67, 0x4e},
+ {0x58, 0x11, 0x9f, 0x0e, 0x12, 0x82, 0x87, 0xea, 0x50, 0xfd,
+ 0xd9, 0x87, 0x45, 0x6f, 0x4f, 0x78, 0xdc, 0xfa, 0xd6, 0xd4},
+ {0x59, 0x22, 0xa1, 0xe1, 0x5a, 0xea, 0x16, 0x35, 0x21, 0xf8,
+ 0x98, 0x39, 0x6a, 0x46, 0x46, 0xb0, 0x44, 0x1b, 0x0f, 0xa9},
+ {0x59, 0xaf, 0x82, 0x79, 0x91, 0x86, 0xc7, 0xb4, 0x75, 0x07,
+ 0xcb, 0xcf, 0x03, 0x57, 0x46, 0xeb, 0x04, 0xdd, 0xb7, 0x16},
+ {0x5d, 0x98, 0x9c, 0xdb, 0x15, 0x96, 0x11, 0x36, 0x51, 0x65,
+ 0x64, 0x1b, 0x56, 0x0f, 0xdb, 0xea, 0x2a, 0xc2, 0x3e, 0xf1},
+ {0x5d, 0xe8, 0x3e, 0xe8, 0x2a, 0xc5, 0x09, 0x0a, 0xea, 0x9d,
+ 0x6a, 0xc4, 0xe7, 0xa6, 0xe2, 0x13, 0xf9, 0x46, 0xe1, 0x79},
+ {0x5f, 0x3a, 0xfc, 0x0a, 0x8b, 0x64, 0xf6, 0x86, 0x67, 0x34,
+ 0x74, 0xdf, 0x7e, 0xa9, 0xa2, 0xfe, 0xf9, 0xfa, 0x7a, 0x51},
+ {0x5f, 0x3b, 0x8c, 0xf2, 0xf8, 0x10, 0xb3, 0x7d, 0x78, 0xb4,
+ 0xce, 0xec, 0x19, 0x19, 0xc3, 0x73, 0x34, 0xb9, 0xc7, 0x74},
+ {0x5f, 0x4e, 0x1f, 0xcf, 0x31, 0xb7, 0x91, 0x3b, 0x85, 0x0b,
+ 0x54, 0xf6, 0xe5, 0xff, 0x50, 0x1a, 0x2b, 0x6f, 0xc6, 0xcf},
+ {0x5f, 0xb7, 0xee, 0x06, 0x33, 0xe2, 0x59, 0xdb, 0xad, 0x0c,
+ 0x4c, 0x9a, 0xe6, 0xd3, 0x8f, 0x1a, 0x61, 0xc7, 0xdc, 0x25},
+ {0x61, 0x1e, 0x5b, 0x66, 0x2c, 0x59, 0x3a, 0x08, 0xff, 0x58,
+ 0xd1, 0x4a, 0xe2, 0x24, 0x52, 0xd1, 0x98, 0xdf, 0x6c, 0x60},
+ {0x61, 0x57, 0x3a, 0x11, 0xdf, 0x0e, 0xd8, 0x7e, 0xd5, 0x92,
+ 0x65, 0x22, 0xea, 0xd0, 0x56, 0xd7, 0x44, 0xb3, 0x23, 0x71},
+ {0x61, 0xef, 0x43, 0xd7, 0x7f, 0xca, 0xd4, 0x61, 0x51, 0xbc,
+ 0x98, 0xe0, 0xc3, 0x59, 0x12, 0xaf, 0x9f, 0xeb, 0x63, 0x11},
+ {0x62, 0x52, 0xdc, 0x40, 0xf7, 0x11, 0x43, 0xa2, 0x2f, 0xde,
+ 0x9e, 0xf7, 0x34, 0x8e, 0x06, 0x42, 0x51, 0xb1, 0x81, 0x18},
+ {0x62, 0x7f, 0x8d, 0x78, 0x27, 0x65, 0x63, 0x99, 0xd2, 0x7d,
+ 0x7f, 0x90, 0x44, 0xc9, 0xfe, 0xb3, 0xf3, 0x3e, 0xfa, 0x9a},
+ {0x66, 0x31, 0xbf, 0x9e, 0xf7, 0x4f, 0x9e, 0xb6, 0xc9, 0xd5,
+ 0xa6, 0x0c, 0xba, 0x6a, 0xbe, 0xd1, 0xf7, 0xbd, 0xef, 0x7b},
+ {0x67, 0x65, 0x0d, 0xf1, 0x7e, 0x8e, 0x7e, 0x5b, 0x82, 0x40,
+ 0xa4, 0xf4, 0x56, 0x4b, 0xcf, 0xe2, 0x3d, 0x69, 0xc6, 0xf0},
+ {0x67, 0x82, 0xaa, 0xe0, 0xed, 0xee, 0xe2, 0x1a, 0x58, 0x39,
+ 0xd3, 0xc0, 0xcd, 0x14, 0x68, 0x0a, 0x4f, 0x60, 0x14, 0x2a},
+ {0x67, 0x9a, 0x4f, 0x81, 0xfc, 0x70, 0x5d, 0xde, 0xc4, 0x19,
+ 0x77, 0x8d, 0xd2, 0xeb, 0xd8, 0x75, 0xf4, 0xc2, 0x42, 0xc6},
+ {0x69, 0xbd, 0x8c, 0xf4, 0x9c, 0xd3, 0x00, 0xfb, 0x59, 0x2e,
+ 0x17, 0x93, 0xca, 0x55, 0x6a, 0xf3, 0xec, 0xaa, 0x35, 0xfb},
+ {0x6a, 0x84, 0xfe, 0x62, 0x7e, 0xcc, 0x49, 0xa1, 0xbe, 0x02,
+ 0xe9, 0x18, 0xfa, 0xc9, 0xe1, 0xf7, 0x32, 0x80, 0x3a, 0x62},
+ {0x6b, 0x2f, 0x34, 0xad, 0x89, 0x58, 0xbe, 0x62, 0xfd, 0xb0,
+ 0x6b, 0x5c, 0xce, 0xbb, 0x9d, 0xd9, 0x4f, 0x4e, 0x39, 0xf3},
+ {0x6b, 0x81, 0x44, 0x6a, 0x5c, 0xdd, 0xf4, 0x74, 0xa0, 0xf8,
+ 0x00, 0xff, 0xbe, 0x69, 0xfd, 0x0d, 0xb6, 0x28, 0x75, 0x16},
+ {0x6e, 0x3a, 0x55, 0xa4, 0x19, 0x0c, 0x19, 0x5c, 0x93, 0x84,
+ 0x3c, 0xc0, 0xdb, 0x72, 0x2e, 0x31, 0x30, 0x61, 0xf0, 0xb1},
+ {0x70, 0x17, 0x9b, 0x86, 0x8c, 0x00, 0xa4, 0xfa, 0x60, 0x91,
+ 0x52, 0x22, 0x3f, 0x9f, 0x3e, 0x32, 0xbd, 0xe0, 0x05, 0x62},
+ {0x70, 0x43, 0x2d, 0xe4, 0x3c, 0x9d, 0x1e, 0x79, 0xc1, 0x13,
+ 0x25, 0x20, 0xbc, 0x58, 0x43, 0xf7, 0xbb, 0x7d, 0x92, 0x95},
+ {0x74, 0x20, 0x74, 0x41, 0x72, 0x9c, 0xdd, 0x92, 0xec, 0x79,
+ 0x31, 0xd8, 0x23, 0x10, 0x8d, 0xc2, 0x81, 0x92, 0xe2, 0xbb},
+ {0x74, 0x2c, 0x31, 0x92, 0xe6, 0x07, 0xe4, 0x24, 0xeb, 0x45,
+ 0x49, 0x54, 0x2b, 0xe1, 0xbb, 0xc5, 0x3e, 0x61, 0x74, 0xe2},
+ {0x74, 0x54, 0x53, 0x5c, 0x24, 0xa3, 0xa7, 0x58, 0x20, 0x7e,
+ 0x3e, 0x3e, 0xd3, 0x24, 0xf8, 0x16, 0xfb, 0x21, 0x16, 0x49},
+ {0x74, 0xa2, 0x66, 0xf0, 0x95, 0xa9, 0xa4, 0xeb, 0x95, 0x22,
+ 0x19, 0xd6, 0x05, 0xda, 0x93, 0x63, 0xf5, 0x14, 0xfa, 0xf9},
+ {0x74, 0xf8, 0xa3, 0xc3, 0xef, 0xe7, 0xb3, 0x90, 0x06, 0x4b,
+ 0x83, 0x90, 0x3c, 0x21, 0x64, 0x60, 0x20, 0xe5, 0xdf, 0xce},
+ {0x75, 0xe0, 0xab, 0xb6, 0x13, 0x85, 0x12, 0x27, 0x1c, 0x04,
+ 0xf8, 0x5f, 0xdd, 0xde, 0x38, 0xe4, 0xb7, 0x24, 0x2e, 0xfe},
+ {0x79, 0x98, 0xa3, 0x08, 0xe1, 0x4d, 0x65, 0x85, 0xe6, 0xc2,
+ 0x1e, 0x15, 0x3a, 0x71, 0x9f, 0xba, 0x5a, 0xd3, 0x4a, 0xd9},
+ {0x7e, 0x78, 0x4a, 0x10, 0x1c, 0x82, 0x65, 0xcc, 0x2d, 0xe1,
+ 0xf1, 0x6d, 0x47, 0xb4, 0x40, 0xca, 0xd9, 0x0a, 0x19, 0x45},
+ {0x7f, 0x8a, 0xb0, 0xcf, 0xd0, 0x51, 0x87, 0x6a, 0x66, 0xf3,
+ 0x36, 0x0f, 0x47, 0xc8, 0x8d, 0x8c, 0xd3, 0x35, 0xfc, 0x74},
+ {0x80, 0x1d, 0x62, 0xd0, 0x7b, 0x44, 0x9d, 0x5c, 0x5c, 0x03,
+ 0x5c, 0x98, 0xea, 0x61, 0xfa, 0x44, 0x3c, 0x2a, 0x58, 0xfe},
+ {0x80, 0x25, 0xef, 0xf4, 0x6e, 0x70, 0xc8, 0xd4, 0x72, 0x24,
+ 0x65, 0x84, 0xfe, 0x40, 0x3b, 0x8a, 0x8d, 0x6a, 0xdb, 0xf5},
+ {0x82, 0x50, 0xbe, 0xd5, 0xa2, 0x14, 0x43, 0x3a, 0x66, 0x37,
+ 0x7c, 0xbc, 0x10, 0xef, 0x83, 0xf6, 0x69, 0xda, 0x3a, 0x67},
+ {0x82, 0x68, 0x99, 0x3e, 0xda, 0xeb, 0xb1, 0xe4, 0xfb, 0x77,
+ 0x91, 0x0f, 0x12, 0xcb, 0xd6, 0xc6, 0x70, 0xf0, 0x7c, 0xea},
+ {0x85, 0x37, 0x1c, 0xa6, 0xe5, 0x50, 0x14, 0x3d, 0xce, 0x28,
+ 0x03, 0x47, 0x1b, 0xde, 0x3a, 0x09, 0xe8, 0xf8, 0x77, 0x0f},
+ {0x85, 0xa4, 0x08, 0xc0, 0x9c, 0x19, 0x3e, 0x5d, 0x51, 0x58,
+ 0x7d, 0xcd, 0xd6, 0x13, 0x30, 0xfd, 0x8c, 0xde, 0x37, 0xbf},
+ {0x85, 0xb5, 0xff, 0x67, 0x9b, 0x0c, 0x79, 0x96, 0x1f, 0xc8,
+ 0x6e, 0x44, 0x22, 0x00, 0x46, 0x13, 0xdb, 0x17, 0x92, 0x84},
+ {0x86, 0xe8, 0x17, 0xc8, 0x1a, 0x5c, 0xa6, 0x72, 0xfe, 0x00,
+ 0x0f, 0x36, 0xf8, 0x78, 0xc1, 0x95, 0x18, 0xd6, 0xf8, 0x44},
+ {0x87, 0x81, 0xc2, 0x5a, 0x96, 0xbd, 0xc2, 0xfb, 0x4c, 0x65,
+ 0x06, 0x4f, 0xf9, 0x39, 0x0b, 0x26, 0x04, 0x8a, 0x0e, 0x01},
+ {0x87, 0x82, 0xc6, 0xc3, 0x04, 0x35, 0x3b, 0xcf, 0xd2, 0x96,
+ 0x92, 0xd2, 0x59, 0x3e, 0x7d, 0x44, 0xd9, 0x34, 0xff, 0x11},
+ {0x87, 0x9f, 0x4b, 0xee, 0x05, 0xdf, 0x98, 0x58, 0x3b, 0xe3,
+ 0x60, 0xd6, 0x33, 0xe7, 0x0d, 0x3f, 0xfe, 0x98, 0x71, 0xaf},
+ {0x8b, 0xaf, 0x4c, 0x9b, 0x1d, 0xf0, 0x2a, 0x92, 0xf7, 0xda,
+ 0x12, 0x8e, 0xb9, 0x1b, 0xac, 0xf4, 0x98, 0x60, 0x4b, 0x6f},
+ {0x8c, 0x94, 0x1b, 0x34, 0xea, 0x1e, 0xa6, 0xed, 0x9a, 0xe2,
+ 0xbc, 0x54, 0xcf, 0x68, 0x72, 0x52, 0xb4, 0xc9, 0xb5, 0x61},
+ {0x8c, 0x96, 0xba, 0xeb, 0xdd, 0x2b, 0x07, 0x07, 0x48, 0xee,
+ 0x30, 0x32, 0x66, 0xa0, 0xf3, 0x98, 0x6e, 0x7c, 0xae, 0x58},
+ {0x8c, 0xc4, 0x30, 0x7b, 0xc6, 0x07, 0x55, 0xe7, 0xb2, 0x2d,
+ 0xd9, 0xf7, 0xfe, 0xa2, 0x45, 0x93, 0x6c, 0x7c, 0xf2, 0x88},
+ {0x8e, 0x5b, 0xd5, 0x0d, 0x6a, 0xe6, 0x86, 0xd6, 0x52, 0x52,
+ 0xf8, 0x43, 0xa9, 0xd4, 0xb9, 0x6d, 0x19, 0x77, 0x30, 0xab},
+ {0x90, 0x5f, 0x94, 0x2f, 0xd9, 0xf2, 0x8f, 0x67, 0x9b, 0x37,
+ 0x81, 0x80, 0xfd, 0x4f, 0x84, 0x63, 0x47, 0xf6, 0x45, 0xc1},
+ {0x90, 0xae, 0xa2, 0x69, 0x85, 0xff, 0x14, 0x80, 0x4c, 0x43,
+ 0x49, 0x52, 0xec, 0xe9, 0x60, 0x84, 0x77, 0xaf, 0x55, 0x6f},
+ {0x91, 0xc6, 0xd6, 0xee, 0x3e, 0x8a, 0xc8, 0x63, 0x84, 0xe5,
+ 0x48, 0xc2, 0x99, 0x29, 0x5c, 0x75, 0x6c, 0x81, 0x7b, 0x81},
+ {0x92, 0x5a, 0x8f, 0x8d, 0x2c, 0x6d, 0x04, 0xe0, 0x66, 0x5f,
+ 0x59, 0x6a, 0xff, 0x22, 0xd8, 0x63, 0xe8, 0x25, 0x6f, 0x3f},
+ {0x93, 0xe6, 0xab, 0x22, 0x03, 0x03, 0xb5, 0x23, 0x28, 0xdc,
+ 0xda, 0x56, 0x9e, 0xba, 0xe4, 0xd1, 0xd1, 0xcc, 0xfb, 0x65},
+ {0x94, 0x32, 0xbd, 0x9a, 0xec, 0x1d, 0x75, 0xd1, 0x70, 0x5c,
+ 0x54, 0x3a, 0xa3, 0x4c, 0x4a, 0xf6, 0xa5, 0x26, 0xc1, 0x3d},
+ {0x96, 0x56, 0xcd, 0x7b, 0x57, 0x96, 0x98, 0x95, 0xd0, 0xe1,
+ 0x41, 0x46, 0x68, 0x06, 0xfb, 0xb8, 0xc6, 0x11, 0x06, 0x87},
+ {0x96, 0x83, 0x38, 0xf1, 0x13, 0xe3, 0x6a, 0x7b, 0xab, 0xdd,
+ 0x08, 0xf7, 0x77, 0x63, 0x91, 0xa6, 0x87, 0x36, 0x58, 0x2e},
+ {0x97, 0x81, 0x79, 0x50, 0xd8, 0x1c, 0x96, 0x70, 0xcc, 0x34,
+ 0xd8, 0x09, 0xcf, 0x79, 0x44, 0x31, 0x36, 0x7e, 0xf4, 0x74},
+ {0x98, 0x45, 0xa4, 0x31, 0xd5, 0x19, 0x59, 0xca, 0xf2, 0x25,
+ 0x32, 0x2b, 0x4a, 0x4f, 0xe9, 0xf2, 0x23, 0xce, 0x6d, 0x15},
+ {0x99, 0xa6, 0x9b, 0xe6, 0x1a, 0xfe, 0x88, 0x6b, 0x4d, 0x2b,
+ 0x82, 0x00, 0x7c, 0xb8, 0x54, 0xfc, 0x31, 0x7e, 0x15, 0x39},
+ {0x9b, 0xaa, 0xe5, 0x9f, 0x56, 0xee, 0x21, 0xcb, 0x43, 0x5a,
+ 0xbe, 0x25, 0x93, 0xdf, 0xa7, 0xf0, 0x40, 0xd1, 0x1d, 0xcb},
+ {0x9f, 0xad, 0x91, 0xa6, 0xce, 0x6a, 0xc6, 0xc5, 0x00, 0x47,
+ 0xc4, 0x4e, 0xc9, 0xd4, 0xa5, 0x0d, 0x92, 0xd8, 0x49, 0x79},
+ {0xa0, 0xa1, 0xab, 0x90, 0xc9, 0xfc, 0x84, 0x7b, 0x3b, 0x12,
+ 0x61, 0xe8, 0x97, 0x7d, 0x5f, 0xd3, 0x22, 0x61, 0xd3, 0xcc},
+ {0xa1, 0xdb, 0x63, 0x93, 0x91, 0x6f, 0x17, 0xe4, 0x18, 0x55,
+ 0x09, 0x40, 0x04, 0x15, 0xc7, 0x02, 0x40, 0xb0, 0xae, 0x6b},
+ {0xa2, 0x48, 0x41, 0xab, 0xd6, 0xa0, 0xca, 0x5c, 0xcd, 0x2a,
+ 0xa3, 0xb1, 0x90, 0x70, 0x1e, 0xd6, 0x4b, 0x39, 0xfe, 0x53},
+ {0xa3, 0xf1, 0x33, 0x3f, 0xe2, 0x42, 0xbf, 0xcf, 0xc5, 0xd1,
+ 0x4e, 0x8f, 0x39, 0x42, 0x98, 0x40, 0x68, 0x10, 0xd1, 0xa0},
+ {0xa6, 0x76, 0xdb, 0xf1, 0x92, 0x48, 0xf5, 0x2c, 0x57, 0x53,
+ 0xd0, 0xda, 0xc1, 0x4c, 0x53, 0xc4, 0x74, 0xa4, 0x83, 0x5e},
+ {0xa6, 0x9a, 0x91, 0xfd, 0x05, 0x7f, 0x13, 0x6a, 0x42, 0x63,
+ 0x0b, 0xb1, 0x76, 0x0d, 0x2d, 0x51, 0x12, 0x0c, 0x16, 0x50},
+ {0xa8, 0x98, 0x5d, 0x3a, 0x65, 0xe5, 0xe5, 0xc4, 0xb2, 0xd7,
+ 0xd6, 0x6d, 0x40, 0xc6, 0xdd, 0x2f, 0xb1, 0x9c, 0x54, 0x36},
+ {0xaa, 0xdb, 0xbc, 0x22, 0x23, 0x8f, 0xc4, 0x01, 0xa1, 0x27,
+ 0xbb, 0x38, 0xdd, 0xf4, 0x1d, 0xdb, 0x08, 0x9e, 0xf0, 0x12},
+ {0xac, 0xed, 0x5f, 0x65, 0x53, 0xfd, 0x25, 0xce, 0x01, 0x5f,
+ 0x1f, 0x7a, 0x48, 0x3b, 0x6a, 0x74, 0x9f, 0x61, 0x78, 0xc6},
+ {0xad, 0x7e, 0x1c, 0x28, 0xb0, 0x64, 0xef, 0x8f, 0x60, 0x03,
+ 0x40, 0x20, 0x14, 0xc3, 0xd0, 0xe3, 0x37, 0x0e, 0xb5, 0x8a},
+ {0xae, 0x50, 0x83, 0xed, 0x7c, 0xf4, 0x5c, 0xbc, 0x8f, 0x61,
+ 0xc6, 0x21, 0xfe, 0x68, 0x5d, 0x79, 0x42, 0x21, 0x15, 0x6e},
+ {0xae, 0xc5, 0xfb, 0x3f, 0xc8, 0xe1, 0xbf, 0xc4, 0xe5, 0x4f,
+ 0x03, 0x07, 0x5a, 0x9a, 0xe8, 0x00, 0xb7, 0xf7, 0xb6, 0xfa},
+ {0xb1, 0x2e, 0x13, 0x63, 0x45, 0x86, 0xa4, 0x6f, 0x1a, 0xb2,
+ 0x60, 0x68, 0x37, 0x58, 0x2d, 0xc4, 0xac, 0xfd, 0x94, 0x97},
+ {0xb1, 0x72, 0xb1, 0xa5, 0x6d, 0x95, 0xf9, 0x1f, 0xe5, 0x02,
+ 0x87, 0xe1, 0x4d, 0x37, 0xea, 0x6a, 0x44, 0x63, 0x76, 0x8a},
+ {0xb1, 0xbc, 0x96, 0x8b, 0xd4, 0xf4, 0x9d, 0x62, 0x2a, 0xa8,
+ 0x9a, 0x81, 0xf2, 0x15, 0x01, 0x52, 0xa4, 0x1d, 0x82, 0x9c},
+ {0xb3, 0x1e, 0xb1, 0xb7, 0x40, 0xe3, 0x6c, 0x84, 0x02, 0xda,
+ 0xdc, 0x37, 0xd4, 0x4d, 0xf5, 0xd4, 0x67, 0x49, 0x52, 0xf9},
+ {0xb3, 0xea, 0xc4, 0x47, 0x76, 0xc9, 0xc8, 0x1c, 0xea, 0xf2,
+ 0x9d, 0x95, 0xb6, 0xcc, 0xa0, 0x08, 0x1b, 0x67, 0xec, 0x9d},
+ {0xb4, 0x35, 0xd4, 0xe1, 0x11, 0x9d, 0x1c, 0x66, 0x90, 0xa7,
+ 0x49, 0xeb, 0xb3, 0x94, 0xbd, 0x63, 0x7b, 0xa7, 0x82, 0xb7},
+ {0xb4, 0x57, 0x12, 0x1e, 0x63, 0x45, 0xff, 0x93, 0x5d, 0x6b,
+ 0x1c, 0xa2, 0xdd, 0xf4, 0x52, 0x3c, 0xc6, 0xd0, 0xef, 0x6b},
+ {0xb5, 0x1c, 0x06, 0x7c, 0xee, 0x2b, 0x0c, 0x3d, 0xf8, 0x55,
+ 0xab, 0x2d, 0x92, 0xf4, 0xfe, 0x39, 0xd4, 0xe7, 0x0f, 0x0e},
+ {0xb6, 0xca, 0x21, 0x5b, 0x83, 0x6c, 0x35, 0x10, 0x1d, 0xaf,
+ 0x74, 0x63, 0x90, 0x0a, 0x93, 0x68, 0x80, 0x76, 0x7a, 0xa6},
+ {0xb8, 0x01, 0x86, 0xd1, 0xeb, 0x9c, 0x86, 0xa5, 0x41, 0x04,
+ 0xcf, 0x30, 0x54, 0xf3, 0x4c, 0x52, 0xb7, 0xe5, 0x58, 0xc6},
+ {0xb8, 0x23, 0x6b, 0x00, 0x2f, 0x1d, 0x16, 0x86, 0x53, 0x01,
+ 0x55, 0x6c, 0x11, 0xa4, 0x37, 0xca, 0xeb, 0xff, 0xc3, 0xbb},
+ {0xb8, 0x6e, 0x79, 0x16, 0x20, 0xf7, 0x59, 0xf1, 0x7b, 0x8d,
+ 0x25, 0xe3, 0x8c, 0xa8, 0xbe, 0x32, 0xe7, 0xd5, 0xea, 0xc2},
+ {0xc0, 0x60, 0xed, 0x44, 0xcb, 0xd8, 0x81, 0xbd, 0x0e, 0xf8,
+ 0x6c, 0x0b, 0xa2, 0x87, 0xdd, 0xcf, 0x81, 0x67, 0x47, 0x8c},
+ {0xc8, 0xec, 0x8c, 0x87, 0x92, 0x69, 0xcb, 0x4b, 0xab, 0x39,
+ 0xe9, 0x8d, 0x7e, 0x57, 0x67, 0xf3, 0x14, 0x95, 0x73, 0x9d},
+ {0xca, 0x39, 0xd8, 0xea, 0x48, 0x22, 0x13, 0x7f, 0x33, 0x8d,
+ 0xca, 0x79, 0x56, 0x6e, 0xdd, 0xf0, 0x54, 0x7e, 0xce, 0xa7},
+ {0xca, 0x3a, 0xfb, 0xcf, 0x12, 0x40, 0x36, 0x4b, 0x44, 0xb2,
+ 0x16, 0x20, 0x88, 0x80, 0x48, 0x39, 0x19, 0x93, 0x7c, 0xf7},
+ {0xcb, 0x44, 0xa0, 0x97, 0x85, 0x7c, 0x45, 0xfa, 0x18, 0x7e,
+ 0xd9, 0x52, 0x08, 0x6c, 0xb9, 0x84, 0x1f, 0x2d, 0x51, 0xb5},
+ {0xcb, 0x65, 0x82, 0x64, 0xea, 0x8c, 0xda, 0x18, 0x6e, 0x17,
+ 0x52, 0xfb, 0x52, 0xc3, 0x97, 0x36, 0x7e, 0xa3, 0x87, 0xbe},
+ {0xcb, 0xa1, 0xc5, 0xf8, 0xb0, 0xe3, 0x5e, 0xb8, 0xb9, 0x45,
+ 0x12, 0xd3, 0xf9, 0x34, 0xa2, 0xe9, 0x06, 0x10, 0xd3, 0x36},
+ {0xcc, 0xab, 0x0e, 0xa0, 0x4c, 0x23, 0x01, 0xd6, 0x69, 0x7b,
+ 0xdd, 0x37, 0x9f, 0xcd, 0x12, 0xeb, 0x24, 0xe3, 0x94, 0x9d},
+ {0xce, 0x6a, 0x64, 0xa3, 0x09, 0xe4, 0x2f, 0xbb, 0xd9, 0x85,
+ 0x1c, 0x45, 0x3e, 0x64, 0x09, 0xea, 0xe8, 0x7d, 0x60, 0xf1},
+ {0xcf, 0x9e, 0x87, 0x6d, 0xd3, 0xeb, 0xfc, 0x42, 0x26, 0x97,
+ 0xa3, 0xb5, 0xa3, 0x7a, 0xa0, 0x76, 0xa9, 0x06, 0x23, 0x48},
+ {0xd1, 0xeb, 0x23, 0xa4, 0x6d, 0x17, 0xd6, 0x8f, 0xd9, 0x25,
+ 0x64, 0xc2, 0xf1, 0xf1, 0x60, 0x17, 0x64, 0xd8, 0xe3, 0x49},
+ {0xd2, 0x32, 0x09, 0xad, 0x23, 0xd3, 0x14, 0x23, 0x21, 0x74,
+ 0xe4, 0x0d, 0x7f, 0x9d, 0x62, 0x13, 0x97, 0x86, 0x63, 0x3a},
+ {0xd3, 0xc0, 0x63, 0xf2, 0x19, 0xed, 0x07, 0x3e, 0x34, 0xad,
+ 0x5d, 0x75, 0x0b, 0x32, 0x76, 0x29, 0xff, 0xd5, 0x9a, 0xf2},
+ {0xd4, 0x37, 0x19, 0xb5, 0x1b, 0x57, 0xca, 0x4b, 0xb8, 0x74,
+ 0x16, 0x7d, 0x47, 0x95, 0x23, 0x1d, 0x34, 0x34, 0xfd, 0xa8},
+ {0xd4, 0xde, 0x20, 0xd0, 0x5e, 0x66, 0xfc, 0x53, 0xfe, 0x1a,
+ 0x50, 0x88, 0x2c, 0x78, 0xdb, 0x28, 0x52, 0xca, 0xe4, 0x74},
+ {0xd6, 0x9b, 0x56, 0x11, 0x48, 0xf0, 0x1c, 0x77, 0xc5, 0x45,
+ 0x78, 0xc1, 0x09, 0x26, 0xdf, 0x5b, 0x85, 0x69, 0x76, 0xad},
+ {0xd6, 0xda, 0xa8, 0x20, 0x8d, 0x09, 0xd2, 0x15, 0x4d, 0x24,
+ 0xb5, 0x2f, 0xcb, 0x34, 0x6e, 0xb2, 0x58, 0xb2, 0x8a, 0x58},
+ {0xd8, 0xa6, 0x33, 0x2c, 0xe0, 0x03, 0x6f, 0xb1, 0x85, 0xf6,
+ 0x63, 0x4f, 0x7d, 0x6a, 0x06, 0x65, 0x26, 0x32, 0x28, 0x27},
+ {0xd8, 0xc5, 0x38, 0x8a, 0xb7, 0x30, 0x1b, 0x1b, 0x6e, 0xd4,
+ 0x7a, 0xe6, 0x45, 0x25, 0x3a, 0x6f, 0x9f, 0x1a, 0x27, 0x61},
+ {0xda, 0x40, 0x18, 0x8b, 0x91, 0x89, 0xa3, 0xed, 0xee, 0xae,
+ 0xda, 0x97, 0xfe, 0x2f, 0x9d, 0xf5, 0xb7, 0xd1, 0x8a, 0x41},
+ {0xda, 0xc9, 0x02, 0x4f, 0x54, 0xd8, 0xf6, 0xdf, 0x94, 0x93,
+ 0x5f, 0xb1, 0x73, 0x26, 0x38, 0xca, 0x6a, 0xd7, 0x7c, 0x13},
+ {0xde, 0x28, 0xf4, 0xa4, 0xff, 0xe5, 0xb9, 0x2f, 0xa3, 0xc5,
+ 0x03, 0xd1, 0xa3, 0x49, 0xa7, 0xf9, 0x96, 0x2a, 0x82, 0x12},
+ {0xde, 0x3f, 0x40, 0xbd, 0x50, 0x93, 0xd3, 0x9b, 0x6c, 0x60,
+ 0xf6, 0xda, 0xbc, 0x07, 0x62, 0x01, 0x00, 0x89, 0x76, 0xc9},
+ {0xde, 0x99, 0x0c, 0xed, 0x99, 0xe0, 0x43, 0x1f, 0x60, 0xed,
+ 0xc3, 0x93, 0x7e, 0x7c, 0xd5, 0xbf, 0x0e, 0xd9, 0xe5, 0xfa},
+ {0xdf, 0xdf, 0xac, 0x89, 0x47, 0xbd, 0xf7, 0x52, 0x64, 0xa9,
+ 0x23, 0x3a, 0xc1, 0x0e, 0xe3, 0xd1, 0x28, 0x33, 0xda, 0xcc},
+ {0xe0, 0x5f, 0x7c, 0x22, 0x59, 0x8c, 0x12, 0x56, 0xa7, 0xb9,
+ 0x4d, 0x92, 0xd3, 0xd1, 0x94, 0x50, 0x8c, 0x8c, 0xba, 0x71},
+ {0xe0, 0xab, 0x05, 0x94, 0x20, 0x72, 0x54, 0x93, 0x05, 0x60,
+ 0x62, 0x02, 0x36, 0x70, 0xf7, 0xcd, 0x2e, 0xfc, 0x66, 0x66},
+ {0xe1, 0x2d, 0xfb, 0x4b, 0x41, 0xd7, 0xd9, 0xc3, 0x2b, 0x30,
+ 0x51, 0x4b, 0xac, 0x1d, 0x81, 0xd8, 0x38, 0x5e, 0x2d, 0x46},
+ {0xe1, 0x9f, 0xe3, 0x0e, 0x8b, 0x84, 0x60, 0x9e, 0x80, 0x9b,
+ 0x17, 0x0d, 0x72, 0xa8, 0xc5, 0xba, 0x6e, 0x14, 0x09, 0xbd},
+ {0xe3, 0x92, 0x51, 0x2f, 0x0a, 0xcf, 0xf5, 0x05, 0xdf, 0xf6,
+ 0xde, 0x06, 0x7f, 0x75, 0x37, 0xe1, 0x65, 0xea, 0x57, 0x4b},
+ {0xe5, 0xdf, 0x74, 0x3c, 0xb6, 0x01, 0xc4, 0x9b, 0x98, 0x43,
+ 0xdc, 0xab, 0x8c, 0xe8, 0x6a, 0x81, 0x10, 0x9f, 0xe4, 0x8e},
+ {0xe6, 0x18, 0x83, 0xae, 0x84, 0xca, 0xc1, 0xc1, 0xcd, 0x52,
+ 0xad, 0xe8, 0xe9, 0x25, 0x2b, 0x45, 0xa6, 0x4f, 0xb7, 0xe2},
+ {0xe6, 0x19, 0xd2, 0x5b, 0x38, 0x0b, 0x7b, 0x13, 0xfd, 0xa3,
+ 0x3e, 0x8a, 0x58, 0xcd, 0x82, 0xd8, 0xa8, 0x8e, 0x05, 0x15},
+ {0xe7, 0xb4, 0xf6, 0x9d, 0x61, 0xec, 0x90, 0x69, 0xdb, 0x7e,
+ 0x90, 0xa7, 0x40, 0x1a, 0x3c, 0xf4, 0x7d, 0x4f, 0xe8, 0xee},
+ {0xee, 0xef, 0xaa, 0x0b, 0xcd, 0x11, 0xaf, 0x5c, 0x02, 0xfa,
+ 0x96, 0x20, 0x6a, 0xc5, 0xc6, 0x2b, 0xa7, 0x24, 0xd6, 0x0a},
+ {0xf1, 0x7f, 0x6f, 0xb6, 0x31, 0xdc, 0x99, 0xe3, 0xa3, 0xc8,
+ 0x7f, 0xfe, 0x1c, 0xf1, 0x81, 0x10, 0x88, 0xd9, 0x60, 0x33},
+ {0xf7, 0x4d, 0xac, 0xb2, 0x14, 0x14, 0xdc, 0xba, 0xab, 0x0b,
+ 0x94, 0x7c, 0x8a, 0x25, 0x7c, 0x32, 0x5c, 0xa8, 0x85, 0x50},
+ {0xf8, 0xa5, 0x4e, 0x03, 0xaa, 0xdc, 0x56, 0x92, 0xb8, 0x50,
+ 0x49, 0x6a, 0x4c, 0x46, 0x30, 0xff, 0xea, 0xa2, 0x9d, 0x83},
+ {0xf9, 0xb5, 0xb6, 0x32, 0x45, 0x5f, 0x9c, 0xbe, 0xec, 0x57,
+ 0x5f, 0x80, 0xdc, 0xe9, 0x6e, 0x2c, 0xc7, 0xb2, 0x78, 0xb7},
+ {0xfa, 0xa7, 0xd9, 0xfb, 0x31, 0xb7, 0x46, 0xf2, 0x00, 0xa8,
+ 0x5e, 0x65, 0x79, 0x76, 0x13, 0xd8, 0x16, 0xe0, 0x63, 0xb5},
+ {0xfc, 0x21, 0x9a, 0x76, 0x11, 0x2f, 0x76, 0xc1, 0xc5, 0x08,
+ 0x83, 0x3c, 0x9a, 0x2f, 0xa2, 0xba, 0x84, 0xac, 0x08, 0x7a},
+ {0xfe, 0xb8, 0xc4, 0x32, 0xdc, 0xf9, 0x76, 0x9a, 0xce, 0xae,
+ 0x3d, 0xd8, 0x90, 0x8f, 0xfd, 0x28, 0x86, 0x65, 0x64, 0x7d},
+ {0xff, 0xad, 0x0e, 0x26, 0xf0, 0x5b, 0xbc, 0xd8, 0x06, 0x3c,
+ 0xce, 0x1d, 0xfa, 0x60, 0x24, 0x5e, 0x14, 0x3d, 0x53, 0x80},
+};
+
+#endif // NET_CERT_X509_CERTIFICATE_KNOWN_ROOTS_MAC_H_
diff --git a/chromium/net/cert/x509_certificate_known_roots_win.h b/chromium/net/cert/x509_certificate_known_roots_win.h
new file mode 100644
index 00000000000..53b14c0bc91
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_known_roots_win.h
@@ -0,0 +1,726 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_CERTIFICATE_KNOWN_ROOTS_WIN_H_
+#define NET_CERT_X509_CERTIFICATE_KNOWN_ROOTS_WIN_H_
+
+// This is the set of 318 Microsoft trusted roots from 29th April 2013.
+// Extracted from
+// http://www.download.windowsupdate.com/msdownload/update/v3/
+// static/trustedr/en/authrootstl.cab
+// (sha1sum: b643e7b6591d58c175b41ce5967d9ec6eb552e4d)
+// (thisUpdate time: 2013-03-21 19:46:44)
+//
+// Note that these *are not* trust anchors for Chromium. They are only used to
+// distinguish `real' root CAs from roots that were user-installed.
+static uint8 kKnownRootCertSHA1Hashes[][20] = {
+ {0x00, 0x48, 0xf8, 0xd3, 0x7b, 0x15, 0x3f, 0x6e, 0xa2, 0x79,
+ 0x8c, 0x32, 0x3e, 0xf4, 0xf3, 0x18, 0xa5, 0x62, 0x4a, 0x9e},
+ {0x00, 0xea, 0x52, 0x2c, 0x8a, 0x9c, 0x06, 0xaa, 0x3e, 0xcc,
+ 0xe0, 0xb4, 0xfa, 0x6c, 0xdc, 0x21, 0xd9, 0x2e, 0x80, 0x99},
+ {0x01, 0x68, 0x97, 0xe1, 0xa0, 0xb8, 0xf2, 0xc3, 0xb1, 0x34,
+ 0x66, 0x5c, 0x20, 0xa7, 0x27, 0xb7, 0xa1, 0x58, 0xe2, 0x8f},
+ {0x02, 0x72, 0x68, 0x29, 0x3e, 0x5f, 0x5d, 0x17, 0xaa, 0xa4,
+ 0xb3, 0xc3, 0xe6, 0x36, 0x1e, 0x1f, 0x92, 0x57, 0x5e, 0xaa},
+ {0x02, 0xfa, 0xf3, 0xe2, 0x91, 0x43, 0x54, 0x68, 0x60, 0x78,
+ 0x57, 0x69, 0x4d, 0xf5, 0xe4, 0x5b, 0x68, 0x85, 0x18, 0x68},
+ {0x03, 0x9e, 0xed, 0xb8, 0x0b, 0xe7, 0xa0, 0x3c, 0x69, 0x53,
+ 0x89, 0x3b, 0x20, 0xd2, 0xd9, 0x32, 0x3a, 0x4c, 0x2a, 0xfd},
+ {0x04, 0x09, 0x56, 0x5b, 0x77, 0xda, 0x58, 0x2e, 0x64, 0x95,
+ 0xac, 0x00, 0x60, 0xa7, 0x23, 0x54, 0xe6, 0x4b, 0x01, 0x92},
+ {0x04, 0x46, 0xc8, 0xbb, 0x9a, 0x69, 0x83, 0xc9, 0x5c, 0x8a,
+ 0x2e, 0x54, 0x64, 0x68, 0x7c, 0x11, 0x15, 0xaa, 0xb7, 0x4a},
+ {0x04, 0x56, 0xf2, 0x3d, 0x1e, 0x9c, 0x43, 0xae, 0xcb, 0x0d,
+ 0x80, 0x7f, 0x1c, 0x06, 0x47, 0x55, 0x1a, 0x05, 0xf4, 0x56},
+ {0x04, 0x83, 0xed, 0x33, 0x99, 0xac, 0x36, 0x08, 0x05, 0x87,
+ 0x22, 0xed, 0xbc, 0x5e, 0x46, 0x00, 0xe3, 0xbe, 0xf9, 0xd7},
+ {0x04, 0x98, 0x11, 0x05, 0x6a, 0xfe, 0x9f, 0xd0, 0xf5, 0xbe,
+ 0x01, 0x68, 0x5a, 0xac, 0xe6, 0xa5, 0xd1, 0xc4, 0x45, 0x4c},
+ {0x05, 0x60, 0xa2, 0xc7, 0x38, 0xff, 0x98, 0xd1, 0x17, 0x2a,
+ 0x94, 0xfe, 0x45, 0xfb, 0x8a, 0x47, 0xd6, 0x65, 0x37, 0x1e},
+ {0x05, 0x63, 0xb8, 0x63, 0x0d, 0x62, 0xd7, 0x5a, 0xbb, 0xc8,
+ 0xab, 0x1e, 0x4b, 0xdf, 0xb5, 0xa8, 0x99, 0xb2, 0x4d, 0x43},
+ {0x06, 0x08, 0x3f, 0x59, 0x3f, 0x15, 0xa1, 0x04, 0xa0, 0x69,
+ 0xa4, 0x6b, 0xa9, 0x03, 0xd0, 0x06, 0xb7, 0x97, 0x09, 0x91},
+ {0x06, 0x14, 0x31, 0x51, 0xe0, 0x2b, 0x45, 0xdd, 0xba, 0xdd,
+ 0x5d, 0x8e, 0x56, 0x53, 0x0d, 0xaa, 0xe3, 0x28, 0xcf, 0x90},
+ {0x07, 0x47, 0x22, 0x01, 0x99, 0xce, 0x74, 0xb9, 0x7c, 0xb0,
+ 0x3d, 0x79, 0xb2, 0x64, 0xa2, 0xc8, 0x55, 0xe9, 0x33, 0xff},
+ {0x07, 0xe0, 0x32, 0xe0, 0x20, 0xb7, 0x2c, 0x3f, 0x19, 0x2f,
+ 0x06, 0x28, 0xa2, 0x59, 0x3a, 0x19, 0xa7, 0x0f, 0x06, 0x9e},
+ {0x08, 0x64, 0x18, 0xe9, 0x06, 0xce, 0xe8, 0x9c, 0x23, 0x53,
+ 0xb6, 0xe2, 0x7f, 0xbd, 0x9e, 0x74, 0x39, 0xf7, 0x63, 0x16},
+ {0x0b, 0x71, 0x99, 0xa1, 0xc7, 0xf3, 0xad, 0xdf, 0x7b, 0xa7,
+ 0xea, 0xb8, 0xeb, 0x57, 0x4a, 0xe8, 0x0d, 0x60, 0xdd, 0xde},
+ {0x0b, 0x77, 0xbe, 0xbb, 0xcb, 0x7a, 0xa2, 0x47, 0x05, 0xde,
+ 0xcc, 0x0f, 0xbd, 0x6a, 0x02, 0xfc, 0x7a, 0xbd, 0x9b, 0x52},
+ {0x0b, 0x97, 0x2c, 0x9e, 0xa6, 0xe7, 0xcc, 0x58, 0xd9, 0x3b,
+ 0x20, 0xbf, 0x71, 0xec, 0x41, 0x2e, 0x72, 0x09, 0xfa, 0xbf},
+ {0x0c, 0x62, 0x8f, 0x5c, 0x55, 0x70, 0xb1, 0xc9, 0x57, 0xfa,
+ 0xfd, 0x38, 0x3f, 0xb0, 0x3d, 0x7b, 0x7d, 0xd7, 0xb9, 0xc6},
+ {0x0c, 0xfd, 0x83, 0xdb, 0xae, 0x44, 0xb9, 0xa0, 0xc8, 0xf6,
+ 0x76, 0xf3, 0xb5, 0x70, 0x65, 0x0b, 0x94, 0xb6, 0x9d, 0xbf},
+ {0x10, 0x1d, 0xfa, 0x3f, 0xd5, 0x0b, 0xcb, 0xbb, 0x9b, 0xb5,
+ 0x60, 0x0c, 0x19, 0x55, 0xa4, 0x1a, 0xf4, 0x73, 0x3a, 0x04},
+ {0x11, 0xc5, 0xb5, 0xf7, 0x55, 0x52, 0xb0, 0x11, 0x66, 0x9c,
+ 0x2e, 0x97, 0x17, 0xde, 0x6d, 0x9b, 0xff, 0x5f, 0xa8, 0x10},
+ {0x11, 0xe1, 0x9b, 0xbc, 0x74, 0x7b, 0x1a, 0xed, 0x0d, 0xb8,
+ 0x33, 0xc9, 0x4c, 0xac, 0x6c, 0x3f, 0x85, 0xbd, 0xeb, 0xdb},
+ {0x13, 0x2d, 0x0d, 0x45, 0x53, 0x4b, 0x69, 0x97, 0xcd, 0xb2,
+ 0xd5, 0xc3, 0x39, 0xe2, 0x55, 0x76, 0x60, 0x9b, 0x5c, 0xc6},
+ {0x15, 0x03, 0x32, 0xa5, 0x8d, 0xc5, 0x91, 0xfc, 0x42, 0xd4,
+ 0xc8, 0x73, 0xff, 0x9f, 0x1f, 0x0f, 0x81, 0xd5, 0x97, 0xc9},
+ {0x16, 0xd8, 0x66, 0x35, 0xaf, 0x13, 0x41, 0xcd, 0x34, 0x79,
+ 0x94, 0x45, 0xeb, 0x60, 0x3e, 0x27, 0x37, 0x02, 0x96, 0x5d},
+ {0x18, 0xf7, 0xc1, 0xfc, 0xc3, 0x09, 0x02, 0x03, 0xfd, 0x5b,
+ 0xaa, 0x2f, 0x86, 0x1a, 0x75, 0x49, 0x76, 0xc8, 0xdd, 0x25},
+ {0x1a, 0xc9, 0x2f, 0x09, 0xea, 0x89, 0xe2, 0x8b, 0x12, 0x6d,
+ 0xfa, 0xc5, 0x1e, 0x3a, 0xf7, 0xea, 0x90, 0x95, 0xa3, 0xee},
+ {0x1b, 0x4b, 0x39, 0x61, 0x26, 0x27, 0x6b, 0x64, 0x91, 0xa2,
+ 0x68, 0x6d, 0xd7, 0x02, 0x43, 0x21, 0x2d, 0x1f, 0x1d, 0x96},
+ {0x1f, 0x49, 0x14, 0xf7, 0xd8, 0x74, 0x95, 0x1d, 0xdd, 0xae,
+ 0x02, 0xc0, 0xbe, 0xfd, 0x3a, 0x2d, 0x82, 0x75, 0x51, 0x85},
+ {0x20, 0x42, 0x85, 0xdc, 0xf7, 0xeb, 0x76, 0x41, 0x95, 0x57,
+ 0x8e, 0x13, 0x6b, 0xd4, 0xb7, 0xd1, 0xe9, 0x8e, 0x46, 0xa5},
+ {0x20, 0x99, 0x00, 0xb6, 0x3d, 0x95, 0x57, 0x28, 0x14, 0x0c,
+ 0xd1, 0x36, 0x22, 0xd8, 0xc6, 0x87, 0xa4, 0xeb, 0x00, 0x85},
+ {0x20, 0xcb, 0x59, 0x4f, 0xb4, 0xed, 0xd8, 0x95, 0x76, 0x3f,
+ 0xd5, 0x25, 0x4e, 0x95, 0x9a, 0x66, 0x74, 0xc6, 0xee, 0xb2},
+ {0x21, 0x11, 0x65, 0xca, 0x37, 0x9f, 0xbb, 0x5e, 0xd8, 0x01,
+ 0xe3, 0x1c, 0x43, 0x0a, 0x62, 0xaa, 0xc1, 0x09, 0xbc, 0xb4},
+ {0x21, 0x6b, 0x2a, 0x29, 0xe6, 0x2a, 0x00, 0xce, 0x82, 0x01,
+ 0x46, 0xd8, 0x24, 0x41, 0x41, 0xb9, 0x25, 0x11, 0xb2, 0x79},
+ {0x21, 0xfc, 0xbd, 0x8e, 0x7f, 0x6c, 0xaf, 0x05, 0x1b, 0xd1,
+ 0xb3, 0x43, 0xec, 0xa8, 0xe7, 0x61, 0x47, 0xf2, 0x0f, 0x8a},
+ {0x22, 0xd5, 0xd8, 0xdf, 0x8f, 0x02, 0x31, 0xd1, 0x8d, 0xf7,
+ 0x9d, 0xb7, 0xcf, 0x8a, 0x2d, 0x64, 0xc9, 0x3f, 0x6c, 0x3a},
+ {0x23, 0x88, 0xc9, 0xd3, 0x71, 0xcc, 0x9e, 0x96, 0x3d, 0xff,
+ 0x7d, 0x3c, 0xa7, 0xce, 0xfc, 0xd6, 0x25, 0xec, 0x19, 0x0d},
+ {0x23, 0xe5, 0x94, 0x94, 0x51, 0x95, 0xf2, 0x41, 0x48, 0x03,
+ 0xb4, 0xd5, 0x64, 0xd2, 0xa3, 0xa3, 0xf5, 0xd8, 0x8b, 0x8c},
+ {0x23, 0xe8, 0x33, 0x23, 0x3e, 0x7d, 0x0c, 0xc9, 0x2b, 0x7c,
+ 0x42, 0x79, 0xac, 0x19, 0xc2, 0xf4, 0x74, 0xd6, 0x04, 0xca},
+ {0x24, 0x5c, 0x97, 0xdf, 0x75, 0x14, 0xe7, 0xcf, 0x2d, 0xf8,
+ 0xbe, 0x72, 0xae, 0x95, 0x7b, 0x9e, 0x04, 0x74, 0x1e, 0x85},
+ {0x24, 0xa4, 0x0a, 0x1f, 0x57, 0x36, 0x43, 0xa6, 0x7f, 0x0a,
+ 0x4b, 0x07, 0x49, 0xf6, 0xa2, 0x2b, 0xf2, 0x8a, 0xbb, 0x6b},
+ {0x24, 0xba, 0x6d, 0x6c, 0x8a, 0x5b, 0x58, 0x37, 0xa4, 0x8d,
+ 0xb5, 0xfa, 0xe9, 0x19, 0xea, 0x67, 0x5c, 0x94, 0xd2, 0x17},
+ {0x25, 0x01, 0x90, 0x19, 0xcf, 0xfb, 0xd9, 0x99, 0x1c, 0xb7,
+ 0x68, 0x25, 0x74, 0x8d, 0x94, 0x5f, 0x30, 0x93, 0x95, 0x42},
+ {0x25, 0x3f, 0x77, 0x5b, 0x0e, 0x77, 0x97, 0xab, 0x64, 0x5f,
+ 0x15, 0x91, 0x55, 0x97, 0xc3, 0x9e, 0x26, 0x36, 0x31, 0xd1},
+ {0x27, 0x3e, 0xe1, 0x24, 0x57, 0xfd, 0xc4, 0xf9, 0x0c, 0x55,
+ 0xe8, 0x2b, 0x56, 0x16, 0x7f, 0x62, 0xf5, 0x32, 0xe5, 0x47},
+ {0x27, 0x96, 0xba, 0xe6, 0x3f, 0x18, 0x01, 0xe2, 0x77, 0x26,
+ 0x1b, 0xa0, 0xd7, 0x77, 0x70, 0x02, 0x8f, 0x20, 0xee, 0xe4},
+ {0x28, 0x90, 0x3a, 0x63, 0x5b, 0x52, 0x80, 0xfa, 0xe6, 0x77,
+ 0x4c, 0x0b, 0x6d, 0xa7, 0xd6, 0xba, 0xa6, 0x4a, 0xf2, 0xe8},
+ {0x29, 0x36, 0x21, 0x02, 0x8b, 0x20, 0xed, 0x02, 0xf5, 0x66,
+ 0xc5, 0x32, 0xd1, 0xd6, 0xed, 0x90, 0x9f, 0x45, 0x00, 0x2f},
+ {0x29, 0x64, 0xb6, 0x86, 0x13, 0x5b, 0x5d, 0xfd, 0xdd, 0x32,
+ 0x53, 0xa8, 0x9b, 0xbc, 0x24, 0xd7, 0x4b, 0x08, 0xc6, 0x4d},
+ {0x2a, 0xc8, 0xd5, 0x8b, 0x57, 0xce, 0xbf, 0x2f, 0x49, 0xaf,
+ 0xf2, 0xfc, 0x76, 0x8f, 0x51, 0x14, 0x62, 0x90, 0x7a, 0x41},
+ {0x2b, 0x8f, 0x1b, 0x57, 0x33, 0x0d, 0xbb, 0xa2, 0xd0, 0x7a,
+ 0x6c, 0x51, 0xf7, 0x0e, 0xe9, 0x0d, 0xda, 0xb9, 0xad, 0x8e},
+ {0x2b, 0xb1, 0xf5, 0x3e, 0x55, 0x0c, 0x1d, 0xc5, 0xf1, 0xd4,
+ 0xe6, 0xb7, 0x6a, 0x46, 0x4b, 0x55, 0x06, 0x02, 0xac, 0x21},
+ {0x2e, 0x14, 0xda, 0xec, 0x28, 0xf0, 0xfa, 0x1e, 0x8e, 0x38,
+ 0x9a, 0x4e, 0xab, 0xeb, 0x26, 0xc0, 0x0a, 0xd3, 0x83, 0xc3},
+ {0x30, 0x70, 0xf8, 0x83, 0x3e, 0x4a, 0xa6, 0x80, 0x3e, 0x09,
+ 0xa6, 0x46, 0xae, 0x3f, 0x7d, 0x8a, 0xe1, 0xfd, 0x16, 0x54},
+ {0x30, 0x77, 0x9e, 0x93, 0x15, 0x02, 0x2e, 0x94, 0x85, 0x6a,
+ 0x3f, 0xf8, 0xbc, 0xf8, 0x15, 0xb0, 0x82, 0xf9, 0xae, 0xfd},
+ {0x31, 0x7a, 0x2a, 0xd0, 0x7f, 0x2b, 0x33, 0x5e, 0xf5, 0xa1,
+ 0xc3, 0x4e, 0x4b, 0x57, 0xe8, 0xb7, 0xd8, 0xf1, 0xfc, 0xa6},
+ {0x31, 0xe2, 0xc5, 0x2c, 0xe1, 0x08, 0x9b, 0xef, 0xfd, 0xda,
+ 0xdb, 0x26, 0xdd, 0x7c, 0x78, 0x2e, 0xbc, 0x40, 0x37, 0xbd},
+ {0x32, 0x3c, 0x11, 0x8e, 0x1b, 0xf7, 0xb8, 0xb6, 0x52, 0x54,
+ 0xe2, 0xe2, 0x10, 0x0d, 0xd6, 0x02, 0x90, 0x37, 0xf0, 0x96},
+ {0x33, 0x9b, 0x6b, 0x14, 0x50, 0x24, 0x9b, 0x55, 0x7a, 0x01,
+ 0x87, 0x72, 0x84, 0xd9, 0xe0, 0x2f, 0xc3, 0xd2, 0xd8, 0xe9},
+ {0x34, 0x2c, 0xd9, 0xd3, 0x06, 0x2d, 0xa4, 0x8c, 0x34, 0x69,
+ 0x65, 0x29, 0x7f, 0x08, 0x1e, 0xbc, 0x2e, 0xf6, 0x8f, 0xdc},
+ {0x34, 0xd4, 0x99, 0x42, 0x6f, 0x9f, 0xc2, 0xbb, 0x27, 0xb0,
+ 0x75, 0xba, 0xb6, 0x82, 0xaa, 0xe5, 0xef, 0xfc, 0xba, 0x74},
+ {0x36, 0x79, 0xca, 0x35, 0x66, 0x87, 0x72, 0x30, 0x4d, 0x30,
+ 0xa5, 0xfb, 0x87, 0x3b, 0x0f, 0xa7, 0x7b, 0xb7, 0x0d, 0x54},
+ {0x36, 0x86, 0x35, 0x63, 0xfd, 0x51, 0x28, 0xc7, 0xbe, 0xa6,
+ 0xf0, 0x05, 0xcf, 0xe9, 0xb4, 0x36, 0x68, 0x08, 0x6c, 0xce},
+ {0x36, 0xb1, 0x2b, 0x49, 0xf9, 0x81, 0x9e, 0xd7, 0x4c, 0x9e,
+ 0xbc, 0x38, 0x0f, 0xc6, 0x56, 0x8f, 0x5d, 0xac, 0xb2, 0xf7},
+ {0x37, 0x9a, 0x19, 0x7b, 0x41, 0x85, 0x45, 0x35, 0x0c, 0xa6,
+ 0x03, 0x69, 0xf3, 0x3c, 0x2e, 0xaf, 0x47, 0x4f, 0x20, 0x79},
+ {0x37, 0xf7, 0x6d, 0xe6, 0x07, 0x7c, 0x90, 0xc5, 0xb1, 0x3e,
+ 0x93, 0x1a, 0xb7, 0x41, 0x10, 0xb4, 0xf2, 0xe4, 0x9a, 0x27},
+ {0x39, 0x13, 0x85, 0x3e, 0x45, 0xc4, 0x39, 0xa2, 0xda, 0x71,
+ 0x8c, 0xdf, 0xb6, 0xf3, 0xe0, 0x33, 0xe0, 0x4f, 0xee, 0x71},
+ {0x39, 0x21, 0xc1, 0x15, 0xc1, 0x5d, 0x0e, 0xca, 0x5c, 0xcb,
+ 0x5b, 0xc4, 0xf0, 0x7d, 0x21, 0xd8, 0x05, 0x0b, 0x56, 0x6a},
+ {0x39, 0x41, 0x0b, 0xc2, 0x30, 0x37, 0x48, 0x06, 0x60, 0x69,
+ 0xa7, 0x2a, 0x66, 0x4d, 0xe4, 0xc7, 0x43, 0x48, 0x12, 0x96},
+ {0x39, 0x4f, 0xf6, 0x85, 0x0b, 0x06, 0xbe, 0x52, 0xe5, 0x18,
+ 0x56, 0xcc, 0x10, 0xe1, 0x80, 0xe8, 0x82, 0xb3, 0x85, 0xcc},
+ {0x39, 0x8e, 0xbe, 0x9c, 0x0f, 0x46, 0xc0, 0x79, 0xc3, 0xc7,
+ 0xaf, 0xe0, 0x7a, 0x2f, 0xdd, 0x9f, 0xae, 0x5f, 0x8a, 0x5c},
+ {0x3a, 0x44, 0x73, 0x5a, 0xe5, 0x81, 0x90, 0x1f, 0x24, 0x86,
+ 0x61, 0x46, 0x1e, 0x3b, 0x9c, 0xc4, 0x5f, 0xf5, 0x3a, 0x1b},
+ {0x3b, 0x1e, 0xfd, 0x3a, 0x66, 0xea, 0x28, 0xb1, 0x66, 0x97,
+ 0x39, 0x47, 0x03, 0xa7, 0x2c, 0xa3, 0x40, 0xa0, 0x5b, 0xd5},
+ {0x3b, 0xc0, 0x38, 0x0b, 0x33, 0xc3, 0xf6, 0xa6, 0x0c, 0x86,
+ 0x15, 0x22, 0x93, 0xd9, 0xdf, 0xf5, 0x4b, 0x81, 0xc0, 0x04},
+ {0x3b, 0xc4, 0x9f, 0x48, 0xf8, 0xf3, 0x73, 0xa0, 0x9c, 0x1e,
+ 0xbd, 0xf8, 0x5b, 0xb1, 0xc3, 0x65, 0xc7, 0xd8, 0x11, 0xb3},
+ {0x3c, 0x71, 0xd7, 0x0e, 0x35, 0xa5, 0xda, 0xa8, 0xb2, 0xe3,
+ 0x81, 0x2d, 0xc3, 0x67, 0x74, 0x17, 0xf5, 0x99, 0x0d, 0xf3},
+ {0x3e, 0x2b, 0xf7, 0xf2, 0x03, 0x1b, 0x96, 0xf3, 0x8c, 0xe6,
+ 0xc4, 0xd8, 0xa8, 0x5d, 0x3e, 0x2d, 0x58, 0x47, 0x6a, 0x0f},
+ {0x3e, 0x42, 0xa1, 0x87, 0x06, 0xbd, 0x0c, 0x9c, 0xcf, 0x59,
+ 0x47, 0x50, 0xd2, 0xe4, 0xd6, 0xab, 0x00, 0x48, 0xfd, 0xc4},
+ {0x3e, 0x84, 0xd3, 0xbc, 0xc5, 0x44, 0xc0, 0xf6, 0xfa, 0x19,
+ 0x43, 0x5c, 0x85, 0x1f, 0x3f, 0x2f, 0xcb, 0xa8, 0xe8, 0x14},
+ {0x3f, 0x85, 0xf2, 0xbb, 0x4a, 0x62, 0xb0, 0xb5, 0x8b, 0xe1,
+ 0x61, 0x4a, 0xbb, 0x0d, 0x46, 0x31, 0xb4, 0xbe, 0xf8, 0xba},
+ {0x40, 0x54, 0xda, 0x6f, 0x1c, 0x3f, 0x40, 0x74, 0xac, 0xed,
+ 0x0f, 0xec, 0xcd, 0xdb, 0x79, 0xd1, 0x53, 0xfb, 0x90, 0x1d},
+ {0x40, 0x9d, 0x4b, 0xd9, 0x17, 0xb5, 0x5c, 0x27, 0xb6, 0x9b,
+ 0x64, 0xcb, 0x98, 0x22, 0x44, 0x0d, 0xcd, 0x09, 0xb8, 0x89},
+ {0x40, 0xe7, 0x8c, 0x1d, 0x52, 0x3d, 0x1c, 0xd9, 0x95, 0x4f,
+ 0xac, 0x1a, 0x1a, 0xb3, 0xbd, 0x3c, 0xba, 0xa1, 0x5b, 0xfc},
+ {0x42, 0xef, 0xdd, 0xe6, 0xbf, 0xf3, 0x5e, 0xd0, 0xba, 0xe6,
+ 0xac, 0xdd, 0x20, 0x4c, 0x50, 0xae, 0x86, 0xc4, 0xf4, 0xfa},
+ {0x43, 0x13, 0xbb, 0x96, 0xf1, 0xd5, 0x86, 0x9b, 0xc1, 0x4e,
+ 0x6a, 0x92, 0xf6, 0xcf, 0xf6, 0x34, 0x69, 0x87, 0x82, 0x37},
+ {0x43, 0x94, 0xce, 0x31, 0x26, 0xff, 0x1a, 0x22, 0x4c, 0xdd,
+ 0x4d, 0xee, 0xb4, 0xf4, 0xec, 0x1d, 0xa3, 0x68, 0xef, 0x6a},
+ {0x43, 0xf9, 0xb1, 0x10, 0xd5, 0xba, 0xfd, 0x48, 0x22, 0x52,
+ 0x31, 0xb0, 0xd0, 0x08, 0x2b, 0x37, 0x2f, 0xef, 0x9a, 0x54},
+ {0x44, 0x63, 0xc5, 0x31, 0xd7, 0xcc, 0xc1, 0x00, 0x67, 0x94,
+ 0x61, 0x2b, 0xb6, 0x56, 0xd3, 0xbf, 0x82, 0x57, 0x84, 0x6f},
+ {0x47, 0xbe, 0xab, 0xc9, 0x22, 0xea, 0xe8, 0x0e, 0x78, 0x78,
+ 0x34, 0x62, 0xa7, 0x9f, 0x45, 0xc2, 0x54, 0xfd, 0xe6, 0x8b},
+ {0x49, 0x0a, 0x75, 0x74, 0xde, 0x87, 0x0a, 0x47, 0xfe, 0x58,
+ 0xee, 0xf6, 0xc7, 0x6b, 0xeb, 0xc6, 0x0b, 0x12, 0x40, 0x99},
+ {0x4a, 0x05, 0x8f, 0xdf, 0xd7, 0x61, 0xdb, 0x21, 0xb0, 0xc2,
+ 0xee, 0x48, 0x57, 0x9b, 0xe2, 0x7f, 0x42, 0xa4, 0xda, 0x1c},
+ {0x4a, 0x3f, 0x8d, 0x6b, 0xdc, 0x0e, 0x1e, 0xcf, 0xcd, 0x72,
+ 0xe3, 0x77, 0xde, 0xf2, 0xd7, 0xff, 0x92, 0xc1, 0x9b, 0xc7},
+ {0x4a, 0xbd, 0xee, 0xec, 0x95, 0x0d, 0x35, 0x9c, 0x89, 0xae,
+ 0xc7, 0x52, 0xa1, 0x2c, 0x5b, 0x29, 0xf6, 0xd6, 0xaa, 0x0c},
+ {0x4e, 0xb6, 0xd5, 0x78, 0x49, 0x9b, 0x1c, 0xcf, 0x5f, 0x58,
+ 0x1e, 0xad, 0x56, 0xbe, 0x3d, 0x9b, 0x67, 0x44, 0xa5, 0xe5},
+ {0x4e, 0xfc, 0xed, 0x9c, 0x6b, 0xdd, 0x0c, 0x98, 0x5c, 0xa3,
+ 0xc7, 0xd2, 0x53, 0x06, 0x3c, 0x5b, 0xe6, 0xfc, 0x62, 0x0c},
+ {0x4f, 0x55, 0x5c, 0xe2, 0x0d, 0xcd, 0x33, 0x64, 0xe0, 0xdc,
+ 0x7c, 0x41, 0xef, 0xdd, 0x40, 0xf5, 0x03, 0x56, 0xc1, 0x22},
+ {0x4f, 0x65, 0x56, 0x63, 0x36, 0xdb, 0x65, 0x98, 0x58, 0x1d,
+ 0x58, 0x4a, 0x59, 0x6c, 0x87, 0x93, 0x4d, 0x5f, 0x2a, 0xb4},
+ {0x4f, 0x99, 0xaa, 0x93, 0xfb, 0x2b, 0xd1, 0x37, 0x26, 0xa1,
+ 0x99, 0x4a, 0xce, 0x7f, 0xf0, 0x05, 0xf2, 0x93, 0x5d, 0x1e},
+ {0x50, 0x30, 0x06, 0x09, 0x1d, 0x97, 0xd4, 0xf5, 0xae, 0x39,
+ 0xf7, 0xcb, 0xe7, 0x92, 0x7d, 0x7d, 0x65, 0x2d, 0x34, 0x31},
+ {0x51, 0xa4, 0x4c, 0x28, 0xf3, 0x13, 0xe3, 0xf9, 0xcb, 0x5e,
+ 0x7c, 0x0a, 0x1e, 0x0e, 0x0d, 0xd2, 0x84, 0x37, 0x58, 0xae},
+ {0x52, 0x41, 0x2b, 0xd6, 0x7b, 0x5a, 0x6c, 0x69, 0x52, 0x82,
+ 0x38, 0x60, 0x26, 0xf0, 0xb0, 0x53, 0xdd, 0x40, 0x0e, 0xfc},
+ {0x54, 0xf9, 0xc1, 0x63, 0x75, 0x9f, 0x19, 0x04, 0x51, 0x21,
+ 0xa3, 0x19, 0xf6, 0x4c, 0x2d, 0x05, 0x55, 0xb7, 0xe0, 0x73},
+ {0x55, 0xa6, 0x72, 0x3e, 0xcb, 0xf2, 0xec, 0xcd, 0xc3, 0x23,
+ 0x74, 0x70, 0x19, 0x9d, 0x2a, 0xbe, 0x11, 0xe3, 0x81, 0xd1},
+ {0x55, 0xc8, 0x6f, 0x74, 0x14, 0xac, 0x8b, 0xdd, 0x68, 0x14,
+ 0xf4, 0xd8, 0x6a, 0xf1, 0x5f, 0x37, 0x10, 0xe1, 0x04, 0xd0},
+ {0x56, 0xe0, 0xfa, 0xc0, 0x3b, 0x8f, 0x18, 0x23, 0x55, 0x18,
+ 0xe5, 0xd3, 0x11, 0xca, 0xe8, 0xc2, 0x43, 0x31, 0xab, 0x66},
+ {0x58, 0x11, 0x9f, 0x0e, 0x12, 0x82, 0x87, 0xea, 0x50, 0xfd,
+ 0xd9, 0x87, 0x45, 0x6f, 0x4f, 0x78, 0xdc, 0xfa, 0xd6, 0xd4},
+ {0x58, 0x5f, 0x78, 0x75, 0xbe, 0xe7, 0x43, 0x3e, 0xb0, 0x79,
+ 0xea, 0xab, 0x7d, 0x05, 0xbb, 0x0f, 0x7a, 0xf2, 0xbc, 0xcc},
+ {0x58, 0xe8, 0xab, 0xb0, 0x36, 0x15, 0x33, 0xfb, 0x80, 0xf7,
+ 0x9b, 0x1b, 0x6d, 0x29, 0xd3, 0xff, 0x8d, 0x5f, 0x00, 0xf0},
+ {0x59, 0x22, 0xa1, 0xe1, 0x5a, 0xea, 0x16, 0x35, 0x21, 0xf8,
+ 0x98, 0x39, 0x6a, 0x46, 0x46, 0xb0, 0x44, 0x1b, 0x0f, 0xa9},
+ {0x59, 0xaf, 0x82, 0x79, 0x91, 0x86, 0xc7, 0xb4, 0x75, 0x07,
+ 0xcb, 0xcf, 0x03, 0x57, 0x46, 0xeb, 0x04, 0xdd, 0xb7, 0x16},
+ {0x5a, 0x4d, 0x0e, 0x8b, 0x5f, 0xdc, 0xfd, 0xf6, 0x4e, 0x72,
+ 0x99, 0xa3, 0x6c, 0x06, 0x0d, 0xb2, 0x22, 0xca, 0x78, 0xe4},
+ {0x5a, 0x5a, 0x4d, 0xaf, 0x78, 0x61, 0x26, 0x7c, 0x4b, 0x1f,
+ 0x1e, 0x67, 0x58, 0x6b, 0xae, 0x6e, 0xd4, 0xfe, 0xb9, 0x3f},
+ {0x5d, 0x00, 0x38, 0x60, 0xf0, 0x02, 0xed, 0x82, 0x9d, 0xea,
+ 0xa4, 0x18, 0x68, 0xf7, 0x88, 0x18, 0x6d, 0x62, 0x12, 0x7f},
+ {0x5d, 0x98, 0x9c, 0xdb, 0x15, 0x96, 0x11, 0x36, 0x51, 0x65,
+ 0x64, 0x1b, 0x56, 0x0f, 0xdb, 0xea, 0x2a, 0xc2, 0x3e, 0xf1},
+ {0x5f, 0x3a, 0xfc, 0x0a, 0x8b, 0x64, 0xf6, 0x86, 0x67, 0x34,
+ 0x74, 0xdf, 0x7e, 0xa9, 0xa2, 0xfe, 0xf9, 0xfa, 0x7a, 0x51},
+ {0x5f, 0x3b, 0x8c, 0xf2, 0xf8, 0x10, 0xb3, 0x7d, 0x78, 0xb4,
+ 0xce, 0xec, 0x19, 0x19, 0xc3, 0x73, 0x34, 0xb9, 0xc7, 0x74},
+ {0x5f, 0x43, 0xe5, 0xb1, 0xbf, 0xf8, 0x78, 0x8c, 0xac, 0x1c,
+ 0xc7, 0xca, 0x4a, 0x9a, 0xc6, 0x22, 0x2b, 0xcc, 0x34, 0xc6},
+ {0x5f, 0x4e, 0x1f, 0xcf, 0x31, 0xb7, 0x91, 0x3b, 0x85, 0x0b,
+ 0x54, 0xf6, 0xe5, 0xff, 0x50, 0x1a, 0x2b, 0x6f, 0xc6, 0xcf},
+ {0x5f, 0xb7, 0xee, 0x06, 0x33, 0xe2, 0x59, 0xdb, 0xad, 0x0c,
+ 0x4c, 0x9a, 0xe6, 0xd3, 0x8f, 0x1a, 0x61, 0xc7, 0xdc, 0x25},
+ {0x60, 0xd6, 0x89, 0x74, 0xb5, 0xc2, 0x65, 0x9e, 0x8a, 0x0f,
+ 0xc1, 0x88, 0x7c, 0x88, 0xd2, 0x46, 0x69, 0x1b, 0x18, 0x2c},
+ {0x61, 0x57, 0x3a, 0x11, 0xdf, 0x0e, 0xd8, 0x7e, 0xd5, 0x92,
+ 0x65, 0x22, 0xea, 0xd0, 0x56, 0xd7, 0x44, 0xb3, 0x23, 0x71},
+ {0x61, 0xef, 0x43, 0xd7, 0x7f, 0xca, 0xd4, 0x61, 0x51, 0xbc,
+ 0x98, 0xe0, 0xc3, 0x59, 0x12, 0xaf, 0x9f, 0xeb, 0x63, 0x11},
+ {0x62, 0x52, 0xdc, 0x40, 0xf7, 0x11, 0x43, 0xa2, 0x2f, 0xde,
+ 0x9e, 0xf7, 0x34, 0x8e, 0x06, 0x42, 0x51, 0xb1, 0x81, 0x18},
+ {0x62, 0x7f, 0x8d, 0x78, 0x27, 0x65, 0x63, 0x99, 0xd2, 0x7d,
+ 0x7f, 0x90, 0x44, 0xc9, 0xfe, 0xb3, 0xf3, 0x3e, 0xfa, 0x9a},
+ {0x64, 0x90, 0x2a, 0xd7, 0x27, 0x7a, 0xf3, 0xe3, 0x2c, 0xd8,
+ 0xcc, 0x1d, 0xc7, 0x9d, 0xe1, 0xfd, 0x7f, 0x80, 0x69, 0xea},
+ {0x67, 0x24, 0x89, 0x80, 0xde, 0x77, 0x5d, 0x2c, 0x9b, 0x04,
+ 0xe4, 0x03, 0x07, 0x94, 0x0b, 0xad, 0xb3, 0x51, 0xf3, 0x95},
+ {0x67, 0x65, 0x0d, 0xf1, 0x7e, 0x8e, 0x7e, 0x5b, 0x82, 0x40,
+ 0xa4, 0xf4, 0x56, 0x4b, 0xcf, 0xe2, 0x3d, 0x69, 0xc6, 0xf0},
+ {0x67, 0x82, 0xaa, 0xe0, 0xed, 0xee, 0xe2, 0x1a, 0x58, 0x39,
+ 0xd3, 0xc0, 0xcd, 0x14, 0x68, 0x0a, 0x4f, 0x60, 0x14, 0x2a},
+ {0x67, 0x9a, 0x4f, 0x81, 0xfc, 0x70, 0x5d, 0xde, 0xc4, 0x19,
+ 0x77, 0x8d, 0xd2, 0xeb, 0xd8, 0x75, 0xf4, 0xc2, 0x42, 0xc6},
+ {0x67, 0xeb, 0x33, 0x7b, 0x68, 0x4c, 0xeb, 0x0e, 0xc2, 0xb0,
+ 0x76, 0x0a, 0xb4, 0x88, 0x27, 0x8c, 0xdd, 0x95, 0x97, 0xdd},
+ {0x68, 0x8b, 0x6e, 0xb8, 0x07, 0xe8, 0xed, 0xa5, 0xc7, 0xb1,
+ 0x7c, 0x43, 0x93, 0xd0, 0x79, 0x5f, 0x0f, 0xae, 0x15, 0x5f},
+ {0x68, 0xed, 0x18, 0xb3, 0x09, 0xcd, 0x52, 0x91, 0xc0, 0xd3,
+ 0x35, 0x7c, 0x1d, 0x11, 0x41, 0xbf, 0x88, 0x38, 0x66, 0xb1},
+ {0x69, 0xbd, 0x8c, 0xf4, 0x9c, 0xd3, 0x00, 0xfb, 0x59, 0x2e,
+ 0x17, 0x93, 0xca, 0x55, 0x6a, 0xf3, 0xec, 0xaa, 0x35, 0xfb},
+ {0x6a, 0x17, 0x45, 0x70, 0xa9, 0x16, 0xfb, 0xe8, 0x44, 0x53,
+ 0xee, 0xd3, 0xd0, 0x70, 0xa1, 0xd8, 0xda, 0x44, 0x28, 0x29},
+ {0x6a, 0x6f, 0x2a, 0x8b, 0x6e, 0x26, 0x15, 0x08, 0x8d, 0xf5,
+ 0x9c, 0xd2, 0x4c, 0x40, 0x24, 0x18, 0xae, 0x42, 0xa3, 0xf1},
+ {0x6a, 0xd2, 0x3b, 0x9d, 0xc4, 0x8e, 0x37, 0x5f, 0x85, 0x9a,
+ 0xd9, 0xca, 0xb5, 0x85, 0x32, 0x5c, 0x23, 0x89, 0x40, 0x71},
+ {0x6b, 0x2f, 0x34, 0xad, 0x89, 0x58, 0xbe, 0x62, 0xfd, 0xb0,
+ 0x6b, 0x5c, 0xce, 0xbb, 0x9d, 0xd9, 0x4f, 0x4e, 0x39, 0xf3},
+ {0x6b, 0x81, 0x44, 0x6a, 0x5c, 0xdd, 0xf4, 0x74, 0xa0, 0xf8,
+ 0x00, 0xff, 0xbe, 0x69, 0xfd, 0x0d, 0xb6, 0x28, 0x75, 0x16},
+ {0x6e, 0x3a, 0x55, 0xa4, 0x19, 0x0c, 0x19, 0x5c, 0x93, 0x84,
+ 0x3c, 0xc0, 0xdb, 0x72, 0x2e, 0x31, 0x30, 0x61, 0xf0, 0xb1},
+ {0x6f, 0x62, 0xde, 0xb8, 0x6c, 0x85, 0x58, 0x5a, 0xe4, 0x2e,
+ 0x47, 0x8d, 0xb4, 0xd7, 0x6d, 0xb3, 0x67, 0x58, 0x5a, 0xe6},
+ {0x70, 0x17, 0x9b, 0x86, 0x8c, 0x00, 0xa4, 0xfa, 0x60, 0x91,
+ 0x52, 0x22, 0x3f, 0x9f, 0x3e, 0x32, 0xbd, 0xe0, 0x05, 0x62},
+ {0x70, 0x30, 0xaa, 0xbf, 0x84, 0x32, 0xa8, 0x00, 0x66, 0x6c,
+ 0xcc, 0xc4, 0x2a, 0x88, 0x7e, 0x42, 0xb7, 0x55, 0x3e, 0x2b},
+ {0x70, 0x5d, 0x2b, 0x45, 0x65, 0xc7, 0x04, 0x7a, 0x54, 0x06,
+ 0x94, 0xa7, 0x9a, 0xf7, 0xab, 0xb8, 0x42, 0xbd, 0xc1, 0x61},
+ {0x71, 0x89, 0x9a, 0x67, 0xbf, 0x33, 0xaf, 0x31, 0xbe, 0xfd,
+ 0xc0, 0x71, 0xf8, 0xf7, 0x33, 0xb1, 0x83, 0x85, 0x63, 0x32},
+ {0x72, 0x0f, 0xc1, 0x5d, 0xdc, 0x27, 0xd4, 0x56, 0xd0, 0x98,
+ 0xfa, 0xbf, 0x3c, 0xdd, 0x78, 0xd3, 0x1e, 0xf5, 0xa8, 0xda},
+ {0x74, 0x20, 0x74, 0x41, 0x72, 0x9c, 0xdd, 0x92, 0xec, 0x79,
+ 0x31, 0xd8, 0x23, 0x10, 0x8d, 0xc2, 0x81, 0x92, 0xe2, 0xbb},
+ {0x74, 0x2c, 0x31, 0x92, 0xe6, 0x07, 0xe4, 0x24, 0xeb, 0x45,
+ 0x49, 0x54, 0x2b, 0xe1, 0xbb, 0xc5, 0x3e, 0x61, 0x74, 0xe2},
+ {0x74, 0x2c, 0xdf, 0x15, 0x94, 0x04, 0x9c, 0xbf, 0x17, 0xa2,
+ 0x04, 0x6c, 0xc6, 0x39, 0xbb, 0x38, 0x88, 0xe0, 0x2e, 0x33},
+ {0x75, 0x02, 0x51, 0xb2, 0xc6, 0x32, 0x53, 0x6f, 0x9d, 0x91,
+ 0x72, 0x79, 0x54, 0x3c, 0x13, 0x7c, 0xd7, 0x21, 0xc6, 0xe0},
+ {0x75, 0xe0, 0xab, 0xb6, 0x13, 0x85, 0x12, 0x27, 0x1c, 0x04,
+ 0xf8, 0x5f, 0xdd, 0xde, 0x38, 0xe4, 0xb7, 0x24, 0x2e, 0xfe},
+ {0x76, 0x12, 0xed, 0x9e, 0x49, 0xb3, 0x65, 0xb4, 0xda, 0xd3,
+ 0x12, 0x0c, 0x01, 0xe6, 0x03, 0x74, 0x8d, 0xae, 0x8c, 0xf0},
+ {0x76, 0x39, 0xc7, 0x18, 0x47, 0xe1, 0x51, 0xb5, 0xc7, 0xea,
+ 0x01, 0xc7, 0x58, 0xfb, 0xf1, 0x2a, 0xba, 0x29, 0x8f, 0x7a},
+ {0x76, 0xb7, 0x60, 0x96, 0xdd, 0x14, 0x56, 0x29, 0xac, 0x75,
+ 0x85, 0xd3, 0x70, 0x63, 0xc1, 0xbc, 0x47, 0x86, 0x1c, 0x8b},
+ {0x77, 0x47, 0x4f, 0xc6, 0x30, 0xe4, 0x0f, 0x4c, 0x47, 0x64,
+ 0x3f, 0x84, 0xba, 0xb8, 0xc6, 0x95, 0x4a, 0x8a, 0x41, 0xec},
+ {0x78, 0x6a, 0x74, 0xac, 0x76, 0xab, 0x14, 0x7f, 0x9c, 0x6a,
+ 0x30, 0x50, 0xba, 0x9e, 0xa8, 0x7e, 0xfe, 0x9a, 0xce, 0x3c},
+ {0x78, 0xe9, 0xdd, 0x06, 0x50, 0x62, 0x4d, 0xb9, 0xcb, 0x36,
+ 0xb5, 0x07, 0x67, 0xf2, 0x09, 0xb8, 0x43, 0xbe, 0x15, 0xb3},
+ {0x79, 0x98, 0xa3, 0x08, 0xe1, 0x4d, 0x65, 0x85, 0xe6, 0xc2,
+ 0x1e, 0x15, 0x3a, 0x71, 0x9f, 0xba, 0x5a, 0xd3, 0x4a, 0xd9},
+ {0x7a, 0x74, 0x41, 0x0f, 0xb0, 0xcd, 0x5c, 0x97, 0x2a, 0x36,
+ 0x4b, 0x71, 0xbf, 0x03, 0x1d, 0x88, 0xa6, 0x51, 0x0e, 0x9e},
+ {0x7a, 0xc5, 0xff, 0xf8, 0xdc, 0xbc, 0x55, 0x83, 0x17, 0x68,
+ 0x77, 0x07, 0x3b, 0xf7, 0x51, 0x73, 0x5e, 0x9b, 0xd3, 0x58},
+ {0x7e, 0x20, 0x69, 0x39, 0xcc, 0x5f, 0xa8, 0x83, 0x63, 0x5f,
+ 0x64, 0xc7, 0x50, 0xeb, 0xf5, 0xfd, 0xa9, 0xae, 0xe6, 0x53},
+ {0x7e, 0x78, 0x4a, 0x10, 0x1c, 0x82, 0x65, 0xcc, 0x2d, 0xe1,
+ 0xf1, 0x6d, 0x47, 0xb4, 0x40, 0xca, 0xd9, 0x0a, 0x19, 0x45},
+ {0x7e, 0xb1, 0xa0, 0x42, 0x9b, 0xe5, 0xf4, 0x28, 0xac, 0x2b,
+ 0x93, 0x97, 0x1d, 0x7c, 0x84, 0x48, 0xa5, 0x36, 0x07, 0x0c},
+ {0x7f, 0x88, 0xcd, 0x72, 0x23, 0xf3, 0xc8, 0x13, 0x81, 0x8c,
+ 0x99, 0x46, 0x14, 0xa8, 0x9c, 0x99, 0xfa, 0x3b, 0x52, 0x47},
+ {0x7f, 0x8a, 0x77, 0x83, 0x6b, 0xdc, 0x6d, 0x06, 0x8f, 0x8b,
+ 0x07, 0x37, 0xfc, 0xc5, 0x72, 0x54, 0x13, 0x06, 0x8c, 0xa4},
+ {0x7f, 0x8a, 0xb0, 0xcf, 0xd0, 0x51, 0x87, 0x6a, 0x66, 0xf3,
+ 0x36, 0x0f, 0x47, 0xc8, 0x8d, 0x8c, 0xd3, 0x35, 0xfc, 0x74},
+ {0x7f, 0xb9, 0xe2, 0xc9, 0x95, 0xc9, 0x7a, 0x93, 0x9f, 0x9e,
+ 0x81, 0xa0, 0x7a, 0xea, 0x9b, 0x4d, 0x70, 0x46, 0x34, 0x96},
+ {0x7f, 0xbb, 0x6a, 0xcd, 0x7e, 0x0a, 0xb4, 0x38, 0xda, 0xaf,
+ 0x6f, 0xd5, 0x02, 0x10, 0xd0, 0x07, 0xc6, 0xc0, 0x82, 0x9c},
+ {0x80, 0x25, 0xef, 0xf4, 0x6e, 0x70, 0xc8, 0xd4, 0x72, 0x24,
+ 0x65, 0x84, 0xfe, 0x40, 0x3b, 0x8a, 0x8d, 0x6a, 0xdb, 0xf5},
+ {0x80, 0xbf, 0x3d, 0xe9, 0xa4, 0x1d, 0x76, 0x8d, 0x19, 0x4b,
+ 0x29, 0x3c, 0x85, 0x63, 0x2c, 0xdb, 0xc8, 0xea, 0x8c, 0xf7},
+ {0x81, 0x96, 0x8b, 0x3a, 0xef, 0x1c, 0xdc, 0x70, 0xf5, 0xfa,
+ 0x32, 0x69, 0xc2, 0x92, 0xa3, 0x63, 0x5b, 0xd1, 0x23, 0xd3},
+ {0x82, 0x50, 0xbe, 0xd5, 0xa2, 0x14, 0x43, 0x3a, 0x66, 0x37,
+ 0x7c, 0xbc, 0x10, 0xef, 0x83, 0xf6, 0x69, 0xda, 0x3a, 0x67},
+ {0x83, 0x8e, 0x30, 0xf7, 0x7f, 0xdd, 0x14, 0xaa, 0x38, 0x5e,
+ 0xd1, 0x45, 0x00, 0x9c, 0x0e, 0x22, 0x36, 0x49, 0x4f, 0xaa},
+ {0x85, 0x37, 0x1c, 0xa6, 0xe5, 0x50, 0x14, 0x3d, 0xce, 0x28,
+ 0x03, 0x47, 0x1b, 0xde, 0x3a, 0x09, 0xe8, 0xf8, 0x77, 0x0f},
+ {0x85, 0xa4, 0x08, 0xc0, 0x9c, 0x19, 0x3e, 0x5d, 0x51, 0x58,
+ 0x7d, 0xcd, 0xd6, 0x13, 0x30, 0xfd, 0x8c, 0xde, 0x37, 0xbf},
+ {0x85, 0xb5, 0xff, 0x67, 0x9b, 0x0c, 0x79, 0x96, 0x1f, 0xc8,
+ 0x6e, 0x44, 0x22, 0x00, 0x46, 0x13, 0xdb, 0x17, 0x92, 0x84},
+ {0x87, 0x81, 0xc2, 0x5a, 0x96, 0xbd, 0xc2, 0xfb, 0x4c, 0x65,
+ 0x06, 0x4f, 0xf9, 0x39, 0x0b, 0x26, 0x04, 0x8a, 0x0e, 0x01},
+ {0x87, 0x82, 0xc6, 0xc3, 0x04, 0x35, 0x3b, 0xcf, 0xd2, 0x96,
+ 0x92, 0xd2, 0x59, 0x3e, 0x7d, 0x44, 0xd9, 0x34, 0xff, 0x11},
+ {0x87, 0x9f, 0x4b, 0xee, 0x05, 0xdf, 0x98, 0x58, 0x3b, 0xe3,
+ 0x60, 0xd6, 0x33, 0xe7, 0x0d, 0x3f, 0xfe, 0x98, 0x71, 0xaf},
+ {0x89, 0xc3, 0x2e, 0x6b, 0x52, 0x4e, 0x4d, 0x65, 0x38, 0x8b,
+ 0x9e, 0xce, 0xdc, 0x63, 0x71, 0x34, 0xed, 0x41, 0x93, 0xa3},
+ {0x89, 0xdf, 0x74, 0xfe, 0x5c, 0xf4, 0x0f, 0x4a, 0x80, 0xf9,
+ 0xe3, 0x37, 0x7d, 0x54, 0xda, 0x91, 0xe1, 0x01, 0x31, 0x8e},
+ {0x8b, 0x1a, 0x11, 0x06, 0xb8, 0xe2, 0x6b, 0x23, 0x29, 0x80,
+ 0xfd, 0x65, 0x2e, 0x61, 0x81, 0x37, 0x64, 0x41, 0xfd, 0x11},
+ {0x8b, 0xaf, 0x4c, 0x9b, 0x1d, 0xf0, 0x2a, 0x92, 0xf7, 0xda,
+ 0x12, 0x8e, 0xb9, 0x1b, 0xac, 0xf4, 0x98, 0x60, 0x4b, 0x6f},
+ {0x8c, 0x96, 0xba, 0xeb, 0xdd, 0x2b, 0x07, 0x07, 0x48, 0xee,
+ 0x30, 0x32, 0x66, 0xa0, 0xf3, 0x98, 0x6e, 0x7c, 0xae, 0x58},
+ {0x8c, 0xc4, 0x30, 0x7b, 0xc6, 0x07, 0x55, 0xe7, 0xb2, 0x2d,
+ 0xd9, 0xf7, 0xfe, 0xa2, 0x45, 0x93, 0x6c, 0x7c, 0xf2, 0x88},
+ {0x8c, 0xf4, 0x27, 0xfd, 0x79, 0x0c, 0x3a, 0xd1, 0x66, 0x06,
+ 0x8d, 0xe8, 0x1e, 0x57, 0xef, 0xbb, 0x93, 0x22, 0x72, 0xd4},
+ {0x8d, 0x08, 0xfc, 0x43, 0xc0, 0x77, 0x0c, 0xa8, 0x4f, 0x4d,
+ 0xcc, 0xb2, 0xd4, 0x1a, 0x5d, 0x95, 0x6d, 0x78, 0x6d, 0xc4},
+ {0x8d, 0x17, 0x84, 0xd5, 0x37, 0xf3, 0x03, 0x7d, 0xec, 0x70,
+ 0xfe, 0x57, 0x8b, 0x51, 0x9a, 0x99, 0xe6, 0x10, 0xd7, 0xb0},
+ {0x8e, 0x10, 0x32, 0xe9, 0x24, 0x59, 0x44, 0xf8, 0x47, 0x91,
+ 0x98, 0x3e, 0xc9, 0xe8, 0x29, 0xcb, 0x10, 0x59, 0xb4, 0xd3},
+ {0x8e, 0xb0, 0x3f, 0xc3, 0xcf, 0x7b, 0xb2, 0x92, 0x86, 0x62,
+ 0x68, 0xb7, 0x51, 0x22, 0x3d, 0xb5, 0x10, 0x34, 0x05, 0xcb},
+ {0x8e, 0xfd, 0xca, 0xbc, 0x93, 0xe6, 0x1e, 0x92, 0x5d, 0x4d,
+ 0x1d, 0xed, 0x18, 0x1a, 0x43, 0x20, 0xa4, 0x67, 0xa1, 0x39},
+ {0x8f, 0x43, 0x28, 0x8a, 0xd2, 0x72, 0xf3, 0x10, 0x3b, 0x6f,
+ 0xb1, 0x42, 0x84, 0x85, 0xea, 0x30, 0x14, 0xc0, 0xbc, 0xfe},
+ {0x90, 0x5f, 0x94, 0x2f, 0xd9, 0xf2, 0x8f, 0x67, 0x9b, 0x37,
+ 0x81, 0x80, 0xfd, 0x4f, 0x84, 0x63, 0x47, 0xf6, 0x45, 0xc1},
+ {0x90, 0x78, 0xc5, 0xa2, 0x8f, 0x9a, 0x43, 0x25, 0xc2, 0xa7,
+ 0xc7, 0x38, 0x13, 0xcd, 0xfe, 0x13, 0xc2, 0x0f, 0x93, 0x4e},
+ {0x90, 0xae, 0xa2, 0x69, 0x85, 0xff, 0x14, 0x80, 0x4c, 0x43,
+ 0x49, 0x52, 0xec, 0xe9, 0x60, 0x84, 0x77, 0xaf, 0x55, 0x6f},
+ {0x90, 0xde, 0xce, 0x77, 0xf8, 0xc8, 0x25, 0x34, 0x0e, 0x62,
+ 0xeb, 0xd6, 0x35, 0xe1, 0xbe, 0x20, 0xcf, 0x73, 0x27, 0xdd},
+ {0x90, 0xde, 0xde, 0x9e, 0x4c, 0x4e, 0x9f, 0x6f, 0xd8, 0x86,
+ 0x17, 0x57, 0x9d, 0xd3, 0x91, 0xbc, 0x65, 0xa6, 0x89, 0x64},
+ {0x91, 0x21, 0x98, 0xee, 0xf2, 0x3d, 0xca, 0xc4, 0x09, 0x39,
+ 0x31, 0x2f, 0xee, 0x97, 0xdd, 0x56, 0x0b, 0xae, 0x49, 0xb1},
+ {0x91, 0x58, 0xc5, 0xef, 0x98, 0x73, 0x01, 0xa8, 0x90, 0x3c,
+ 0xfd, 0xab, 0x03, 0xd7, 0x2d, 0xa1, 0xd8, 0x89, 0x09, 0xc9},
+ {0x91, 0xc6, 0xd6, 0xee, 0x3e, 0x8a, 0xc8, 0x63, 0x84, 0xe5,
+ 0x48, 0xc2, 0x99, 0x29, 0x5c, 0x75, 0x6c, 0x81, 0x7b, 0x81},
+ {0x92, 0x5a, 0x8f, 0x8d, 0x2c, 0x6d, 0x04, 0xe0, 0x66, 0x5f,
+ 0x59, 0x6a, 0xff, 0x22, 0xd8, 0x63, 0xe8, 0x25, 0x6f, 0x3f},
+ {0x93, 0xe6, 0xab, 0x22, 0x03, 0x03, 0xb5, 0x23, 0x28, 0xdc,
+ 0xda, 0x56, 0x9e, 0xba, 0xe4, 0xd1, 0xd1, 0xcc, 0xfb, 0x65},
+ {0x93, 0xf7, 0xf4, 0x8b, 0x12, 0x61, 0x94, 0x3f, 0x6a, 0x78,
+ 0x21, 0x0c, 0x52, 0xe6, 0x26, 0xdf, 0xbf, 0xbb, 0xe2, 0x60},
+ {0x96, 0x56, 0xcd, 0x7b, 0x57, 0x96, 0x98, 0x95, 0xd0, 0xe1,
+ 0x41, 0x46, 0x68, 0x06, 0xfb, 0xb8, 0xc6, 0x11, 0x06, 0x87},
+ {0x96, 0x83, 0x38, 0xf1, 0x13, 0xe3, 0x6a, 0x7b, 0xab, 0xdd,
+ 0x08, 0xf7, 0x77, 0x63, 0x91, 0xa6, 0x87, 0x36, 0x58, 0x2e},
+ {0x96, 0x97, 0x4c, 0xd6, 0xb6, 0x63, 0xa7, 0x18, 0x45, 0x26,
+ 0xb1, 0xd6, 0x48, 0xad, 0x81, 0x5c, 0xf5, 0x1e, 0x80, 0x1a},
+ {0x96, 0xc9, 0x1b, 0x0b, 0x95, 0xb4, 0x10, 0x98, 0x42, 0xfa,
+ 0xd0, 0xd8, 0x22, 0x79, 0xfe, 0x60, 0xfa, 0xb9, 0x16, 0x83},
+ {0x97, 0x1d, 0x34, 0x86, 0xfc, 0x1e, 0x8e, 0x63, 0x15, 0xf7,
+ 0xc6, 0xf2, 0xe1, 0x29, 0x67, 0xc7, 0x24, 0x34, 0x22, 0x14},
+ {0x97, 0x22, 0x6a, 0xae, 0x4a, 0x7a, 0x64, 0xa5, 0x9b, 0xd1,
+ 0x67, 0x87, 0xf2, 0x7f, 0x84, 0x1c, 0x0a, 0x00, 0x1f, 0xd0},
+ {0x97, 0x81, 0x79, 0x50, 0xd8, 0x1c, 0x96, 0x70, 0xcc, 0x34,
+ 0xd8, 0x09, 0xcf, 0x79, 0x44, 0x31, 0x36, 0x7e, 0xf4, 0x74},
+ {0x97, 0xe2, 0xe9, 0x96, 0x36, 0xa5, 0x47, 0x55, 0x4f, 0x83,
+ 0x8f, 0xba, 0x38, 0xb8, 0x2e, 0x74, 0xf8, 0x9a, 0x83, 0x0a},
+ {0x99, 0xa6, 0x9b, 0xe6, 0x1a, 0xfe, 0x88, 0x6b, 0x4d, 0x2b,
+ 0x82, 0x00, 0x7c, 0xb8, 0x54, 0xfc, 0x31, 0x7e, 0x15, 0x39},
+ {0x9b, 0xaa, 0xe5, 0x9f, 0x56, 0xee, 0x21, 0xcb, 0x43, 0x5a,
+ 0xbe, 0x25, 0x93, 0xdf, 0xa7, 0xf0, 0x40, 0xd1, 0x1d, 0xcb},
+ {0x9c, 0x61, 0x5c, 0x4d, 0x4d, 0x85, 0x10, 0x3a, 0x53, 0x26,
+ 0xc2, 0x4d, 0xba, 0xea, 0xe4, 0xa2, 0xd2, 0xd5, 0xcc, 0x97},
+ {0x9e, 0xd1, 0x80, 0x28, 0xfb, 0x1e, 0x8a, 0x97, 0x01, 0x48,
+ 0x0a, 0x78, 0x90, 0xa5, 0x9a, 0xcd, 0x73, 0xdf, 0xf8, 0x71},
+ {0x9f, 0x74, 0x4e, 0x9f, 0x2b, 0x4d, 0xba, 0xec, 0x0f, 0x31,
+ 0x2c, 0x50, 0xb6, 0x56, 0x3b, 0x8e, 0x2d, 0x93, 0xc3, 0x11},
+ {0x9f, 0xad, 0x91, 0xa6, 0xce, 0x6a, 0xc6, 0xc5, 0x00, 0x47,
+ 0xc4, 0x4e, 0xc9, 0xd4, 0xa5, 0x0d, 0x92, 0xd8, 0x49, 0x79},
+ {0x9f, 0xc7, 0x96, 0xe8, 0xf8, 0x52, 0x4f, 0x86, 0x3a, 0xe1,
+ 0x49, 0x6d, 0x38, 0x12, 0x42, 0x10, 0x5f, 0x1b, 0x78, 0xf5},
+ {0xa0, 0x73, 0xe5, 0xc5, 0xbd, 0x43, 0x61, 0x0d, 0x86, 0x4c,
+ 0x21, 0x13, 0x0a, 0x85, 0x58, 0x57, 0xcc, 0x9c, 0xea, 0x46},
+ {0xa0, 0xa1, 0xab, 0x90, 0xc9, 0xfc, 0x84, 0x7b, 0x3b, 0x12,
+ 0x61, 0xe8, 0x97, 0x7d, 0x5f, 0xd3, 0x22, 0x61, 0xd3, 0xcc},
+ {0xa0, 0xf8, 0xdb, 0x3f, 0x0b, 0xf4, 0x17, 0x69, 0x3b, 0x28,
+ 0x2e, 0xb7, 0x4a, 0x6a, 0xd8, 0x6d, 0xf9, 0xd4, 0x48, 0xa3},
+ {0xa1, 0x58, 0x51, 0x87, 0x15, 0x65, 0x86, 0xce, 0xf9, 0xc4,
+ 0x54, 0xe2, 0x2a, 0xb1, 0x5c, 0x58, 0x74, 0x56, 0x07, 0xb4},
+ {0xa1, 0xdb, 0x63, 0x93, 0x91, 0x6f, 0x17, 0xe4, 0x18, 0x55,
+ 0x09, 0x40, 0x04, 0x15, 0xc7, 0x02, 0x40, 0xb0, 0xae, 0x6b},
+ {0xa1, 0xe7, 0xc6, 0x00, 0xaa, 0x41, 0x70, 0xe5, 0xb7, 0x4b,
+ 0xc9, 0x4f, 0x9b, 0x97, 0x03, 0xed, 0xc2, 0x61, 0xb4, 0xb9},
+ {0xa3, 0x99, 0xf7, 0x6f, 0x0c, 0xbf, 0x4c, 0x9d, 0xa5, 0x5e,
+ 0x4a, 0xc2, 0x4e, 0x89, 0x60, 0x98, 0x4b, 0x29, 0x05, 0xb6},
+ {0xa3, 0xe3, 0x1e, 0x20, 0xb2, 0xe4, 0x6a, 0x32, 0x85, 0x20,
+ 0x47, 0x2d, 0x0c, 0xde, 0x95, 0x23, 0xe7, 0x26, 0x0c, 0x6d},
+ {0xa4, 0x34, 0x89, 0x15, 0x9a, 0x52, 0x0f, 0x0d, 0x93, 0xd0,
+ 0x32, 0xcc, 0xaf, 0x37, 0xe7, 0xfe, 0x20, 0xa8, 0xb4, 0x19},
+ {0xa5, 0x9c, 0x9b, 0x10, 0xec, 0x73, 0x57, 0x51, 0x5a, 0xbb,
+ 0x66, 0x0c, 0x4d, 0x94, 0xf7, 0x3b, 0x9e, 0x6e, 0x92, 0x72},
+ {0xa5, 0xec, 0x73, 0xd4, 0x8c, 0x34, 0xfc, 0xbe, 0xf1, 0x00,
+ 0x5a, 0xeb, 0x85, 0x84, 0x35, 0x24, 0xbb, 0xfa, 0xb7, 0x27},
+ {0xa6, 0x9a, 0x91, 0xfd, 0x05, 0x7f, 0x13, 0x6a, 0x42, 0x63,
+ 0x0b, 0xb1, 0x76, 0x0d, 0x2d, 0x51, 0x12, 0x0c, 0x16, 0x50},
+ {0xa7, 0xf8, 0x39, 0x0b, 0xa5, 0x77, 0x05, 0x09, 0x6f, 0xd3,
+ 0x69, 0x41, 0xd4, 0x2e, 0x71, 0x98, 0xc6, 0xd4, 0xd9, 0xd5},
+ {0xa8, 0x98, 0x5d, 0x3a, 0x65, 0xe5, 0xe5, 0xc4, 0xb2, 0xd7,
+ 0xd6, 0x6d, 0x40, 0xc6, 0xdd, 0x2f, 0xb1, 0x9c, 0x54, 0x36},
+ {0xa9, 0x62, 0x8f, 0x4b, 0x98, 0xa9, 0x1b, 0x48, 0x35, 0xba,
+ 0xd2, 0xc1, 0x46, 0x32, 0x86, 0xbb, 0x66, 0x64, 0x6a, 0x8c},
+ {0xa9, 0x82, 0x2e, 0x6c, 0x69, 0x33, 0xc6, 0x3c, 0x14, 0x8c,
+ 0x2d, 0xca, 0xa4, 0x4a, 0x5c, 0xf1, 0xaa, 0xd2, 0xc4, 0x2e},
+ {0xa9, 0xe9, 0x78, 0x08, 0x14, 0x37, 0x58, 0x88, 0xf2, 0x05,
+ 0x19, 0xb0, 0x6d, 0x2b, 0x0d, 0x2b, 0x60, 0x16, 0x90, 0x7d},
+ {0xaa, 0xdb, 0xbc, 0x22, 0x23, 0x8f, 0xc4, 0x01, 0xa1, 0x27,
+ 0xbb, 0x38, 0xdd, 0xf4, 0x1d, 0xdb, 0x08, 0x9e, 0xf0, 0x12},
+ {0xab, 0x16, 0xdd, 0x14, 0x4e, 0xcd, 0xc0, 0xfc, 0x4b, 0xaa,
+ 0xb6, 0x2e, 0xcf, 0x04, 0x08, 0x89, 0x6f, 0xde, 0x52, 0xb7},
+ {0xab, 0x48, 0xf3, 0x33, 0xdb, 0x04, 0xab, 0xb9, 0xc0, 0x72,
+ 0xda, 0x5b, 0x0c, 0xc1, 0xd0, 0x57, 0xf0, 0x36, 0x9b, 0x46},
+ {0xab, 0x9d, 0x58, 0xc0, 0x3f, 0x54, 0xb1, 0xda, 0xe3, 0xf7,
+ 0xc2, 0xd4, 0xc6, 0xc1, 0xec, 0x36, 0x94, 0x55, 0x9c, 0x37},
+ {0xac, 0xed, 0x5f, 0x65, 0x53, 0xfd, 0x25, 0xce, 0x01, 0x5f,
+ 0x1f, 0x7a, 0x48, 0x3b, 0x6a, 0x74, 0x9f, 0x61, 0x78, 0xc6},
+ {0xad, 0x7e, 0x1c, 0x28, 0xb0, 0x64, 0xef, 0x8f, 0x60, 0x03,
+ 0x40, 0x20, 0x14, 0xc3, 0xd0, 0xe3, 0x37, 0x0e, 0xb5, 0x8a},
+ {0xae, 0x3b, 0x31, 0xbf, 0x8f, 0xd8, 0x91, 0x07, 0x9c, 0xf1,
+ 0xdf, 0x34, 0xcb, 0xce, 0x6e, 0x70, 0xd3, 0x7f, 0xb5, 0xb0},
+ {0xae, 0x50, 0x83, 0xed, 0x7c, 0xf4, 0x5c, 0xbc, 0x8f, 0x61,
+ 0xc6, 0x21, 0xfe, 0x68, 0x5d, 0x79, 0x42, 0x21, 0x15, 0x6e},
+ {0xae, 0xc5, 0xfb, 0x3f, 0xc8, 0xe1, 0xbf, 0xc4, 0xe5, 0x4f,
+ 0x03, 0x07, 0x5a, 0x9a, 0xe8, 0x00, 0xb7, 0xf7, 0xb6, 0xfa},
+ {0xaf, 0xe5, 0xd2, 0x44, 0xa8, 0xd1, 0x19, 0x42, 0x30, 0xff,
+ 0x47, 0x9f, 0xe2, 0xf8, 0x97, 0xbb, 0xcd, 0x7a, 0x8c, 0xb4},
+ {0xb1, 0x2e, 0x13, 0x63, 0x45, 0x86, 0xa4, 0x6f, 0x1a, 0xb2,
+ 0x60, 0x68, 0x37, 0x58, 0x2d, 0xc4, 0xac, 0xfd, 0x94, 0x97},
+ {0xb1, 0x72, 0xb1, 0xa5, 0x6d, 0x95, 0xf9, 0x1f, 0xe5, 0x02,
+ 0x87, 0xe1, 0x4d, 0x37, 0xea, 0x6a, 0x44, 0x63, 0x76, 0x8a},
+ {0xb1, 0x9d, 0xd0, 0x96, 0xdc, 0xd4, 0xe3, 0xe0, 0xfd, 0x67,
+ 0x68, 0x85, 0x50, 0x5a, 0x67, 0x2c, 0x43, 0x8d, 0x4e, 0x9c},
+ {0xb1, 0xb2, 0x36, 0x4f, 0xd4, 0xd4, 0xf5, 0x2e, 0x89, 0xb2,
+ 0xd0, 0xfa, 0xf3, 0x3e, 0x4d, 0x62, 0xbd, 0x96, 0x99, 0x21},
+ {0xb1, 0xbc, 0x96, 0x8b, 0xd4, 0xf4, 0x9d, 0x62, 0x2a, 0xa8,
+ 0x9a, 0x81, 0xf2, 0x15, 0x01, 0x52, 0xa4, 0x1d, 0x82, 0x9c},
+ {0xb1, 0xea, 0xc3, 0xe5, 0xb8, 0x24, 0x76, 0xe9, 0xd5, 0x0b,
+ 0x1e, 0xc6, 0x7d, 0x2c, 0xc1, 0x1e, 0x12, 0xe0, 0xb4, 0x91},
+ {0xb3, 0x1e, 0xb1, 0xb7, 0x40, 0xe3, 0x6c, 0x84, 0x02, 0xda,
+ 0xdc, 0x37, 0xd4, 0x4d, 0xf5, 0xd4, 0x67, 0x49, 0x52, 0xf9},
+ {0xb3, 0x8f, 0xec, 0xec, 0x0b, 0x14, 0x8a, 0xa6, 0x86, 0xc3,
+ 0xd0, 0x0f, 0x01, 0xec, 0xc8, 0x84, 0x8e, 0x80, 0x85, 0xeb},
+ {0xb3, 0xea, 0xc4, 0x47, 0x76, 0xc9, 0xc8, 0x1c, 0xea, 0xf2,
+ 0x9d, 0x95, 0xb6, 0xcc, 0xa0, 0x08, 0x1b, 0x67, 0xec, 0x9d},
+ {0xb4, 0x35, 0xd4, 0xe1, 0x11, 0x9d, 0x1c, 0x66, 0x90, 0xa7,
+ 0x49, 0xeb, 0xb3, 0x94, 0xbd, 0x63, 0x7b, 0xa7, 0x82, 0xb7},
+ {0xb5, 0x1c, 0x06, 0x7c, 0xee, 0x2b, 0x0c, 0x3d, 0xf8, 0x55,
+ 0xab, 0x2d, 0x92, 0xf4, 0xfe, 0x39, 0xd4, 0xe7, 0x0f, 0x0e},
+ {0xb7, 0x2f, 0xff, 0x92, 0xd2, 0xce, 0x43, 0xde, 0x0a, 0x8d,
+ 0x4c, 0x54, 0x8c, 0x50, 0x37, 0x26, 0xa8, 0x1e, 0x2b, 0x93},
+ {0xb8, 0x01, 0x86, 0xd1, 0xeb, 0x9c, 0x86, 0xa5, 0x41, 0x04,
+ 0xcf, 0x30, 0x54, 0xf3, 0x4c, 0x52, 0xb7, 0xe5, 0x58, 0xc6},
+ {0xb8, 0x23, 0x6b, 0x00, 0x2f, 0x1d, 0x16, 0x86, 0x53, 0x01,
+ 0x55, 0x6c, 0x11, 0xa4, 0x37, 0xca, 0xeb, 0xff, 0xc3, 0xbb},
+ {0xb8, 0x65, 0x13, 0x0b, 0xed, 0xca, 0x38, 0xd2, 0x7f, 0x69,
+ 0x92, 0x94, 0x20, 0x77, 0x0b, 0xed, 0x86, 0xef, 0xbc, 0x10},
+ {0xb9, 0xcd, 0x0c, 0xf6, 0x98, 0x35, 0xea, 0xbf, 0x3f, 0x13,
+ 0x7f, 0x20, 0x49, 0xe4, 0xc9, 0x24, 0x87, 0x84, 0x77, 0xdb},
+ {0xbc, 0x7b, 0x3c, 0x6f, 0xef, 0x26, 0xb9, 0xf7, 0xab, 0x10,
+ 0xd7, 0xa1, 0xf6, 0xb6, 0x7c, 0x5e, 0xd2, 0xa1, 0x2d, 0x3d},
+ {0xbc, 0x92, 0x19, 0xdd, 0xc9, 0x8e, 0x14, 0xbf, 0x1a, 0x78,
+ 0x1f, 0x6e, 0x28, 0x0b, 0x04, 0xc2, 0x7f, 0x90, 0x27, 0x12},
+ {0xbe, 0x36, 0xa4, 0x56, 0x2f, 0xb2, 0xee, 0x05, 0xdb, 0xb3,
+ 0xd3, 0x23, 0x23, 0xad, 0xf4, 0x45, 0x08, 0x4e, 0xd6, 0x56},
+ {0xbe, 0xb5, 0xa9, 0x95, 0x74, 0x6b, 0x9e, 0xdf, 0x73, 0x8b,
+ 0x56, 0xe6, 0xdf, 0x43, 0x7a, 0x77, 0xbe, 0x10, 0x6b, 0x81},
+ {0xbe, 0xd5, 0x25, 0xd1, 0xac, 0x63, 0xa7, 0xfc, 0x6a, 0x66,
+ 0x0b, 0xa7, 0xa8, 0x95, 0x81, 0x8d, 0x5e, 0x8d, 0xd5, 0x64},
+ {0xc0, 0x9a, 0xb0, 0xc8, 0xad, 0x71, 0x14, 0x71, 0x4e, 0xd5,
+ 0xe2, 0x1a, 0x5a, 0x27, 0x6a, 0xdc, 0xd5, 0xe7, 0xef, 0xcb},
+ {0xc0, 0xdb, 0x57, 0x81, 0x57, 0xe9, 0xee, 0x82, 0xb5, 0x91,
+ 0x7d, 0xf0, 0xdd, 0x6d, 0x82, 0xee, 0x90, 0x39, 0xc4, 0xe2},
+ {0xc1, 0x82, 0x11, 0x32, 0x8a, 0x92, 0xb3, 0xb2, 0x38, 0x09,
+ 0xb9, 0xb5, 0xe2, 0x74, 0x0a, 0x07, 0xfb, 0x12, 0xeb, 0x5e},
+ {0xc4, 0x67, 0x4d, 0xdc, 0x6c, 0xe2, 0x96, 0x7f, 0xf9, 0xc9,
+ 0x2e, 0x07, 0x2e, 0xf8, 0xe8, 0xa7, 0xfb, 0xd6, 0xa1, 0x31},
+ {0xc7, 0x30, 0x26, 0xe3, 0x25, 0xfe, 0x21, 0x91, 0x6b, 0x55,
+ 0xc4, 0xb5, 0x3a, 0x56, 0xb1, 0x3d, 0xca, 0xf3, 0xd6, 0x25},
+ {0xc7, 0xf7, 0xcb, 0xe2, 0x02, 0x36, 0x66, 0xf9, 0x86, 0x02,
+ 0x5d, 0x4a, 0x3e, 0x31, 0x3f, 0x29, 0xeb, 0x0c, 0x5b, 0x38},
+ {0xc8, 0xec, 0x8c, 0x87, 0x92, 0x69, 0xcb, 0x4b, 0xab, 0x39,
+ 0xe9, 0x8d, 0x7e, 0x57, 0x67, 0xf3, 0x14, 0x95, 0x73, 0x9d},
+ {0xc9, 0x32, 0x1d, 0xe6, 0xb5, 0xa8, 0x26, 0x66, 0xcf, 0x69,
+ 0x71, 0xa1, 0x8a, 0x56, 0xf2, 0xd3, 0xa8, 0x67, 0x56, 0x02},
+ {0xc9, 0x3c, 0x34, 0xea, 0x90, 0xd9, 0x13, 0x0c, 0x0f, 0x03,
+ 0x00, 0x4b, 0x98, 0xbd, 0x8b, 0x35, 0x70, 0x91, 0x56, 0x11},
+ {0xc9, 0xa8, 0xb9, 0xe7, 0x55, 0x80, 0x5e, 0x58, 0xe3, 0x53,
+ 0x77, 0xa7, 0x25, 0xeb, 0xaf, 0xc3, 0x7b, 0x27, 0xcc, 0xd7},
+ {0xca, 0x3a, 0xfb, 0xcf, 0x12, 0x40, 0x36, 0x4b, 0x44, 0xb2,
+ 0x16, 0x20, 0x88, 0x80, 0x48, 0x39, 0x19, 0x93, 0x7c, 0xf7},
+ {0xca, 0xbb, 0x51, 0x67, 0x24, 0x00, 0x58, 0x8e, 0x64, 0x19,
+ 0xf1, 0xd4, 0x08, 0x78, 0xd0, 0x40, 0x3a, 0xa2, 0x02, 0x64},
+ {0xcb, 0x44, 0xa0, 0x97, 0x85, 0x7c, 0x45, 0xfa, 0x18, 0x7e,
+ 0xd9, 0x52, 0x08, 0x6c, 0xb9, 0x84, 0x1f, 0x2d, 0x51, 0xb5},
+ {0xcb, 0x65, 0x82, 0x64, 0xea, 0x8c, 0xda, 0x18, 0x6e, 0x17,
+ 0x52, 0xfb, 0x52, 0xc3, 0x97, 0x36, 0x7e, 0xa3, 0x87, 0xbe},
+ {0xcb, 0xa1, 0xc5, 0xf8, 0xb0, 0xe3, 0x5e, 0xb8, 0xb9, 0x45,
+ 0x12, 0xd3, 0xf9, 0x34, 0xa2, 0xe9, 0x06, 0x10, 0xd3, 0x36},
+ {0xcc, 0x7e, 0xa2, 0x92, 0xaf, 0x87, 0x15, 0xd7, 0x4c, 0xa4,
+ 0xb4, 0x15, 0xf3, 0x20, 0x15, 0x4b, 0x24, 0xf5, 0x65, 0xfd},
+ {0xcd, 0xd4, 0xee, 0xae, 0x60, 0x00, 0xac, 0x7f, 0x40, 0xc3,
+ 0x80, 0x2c, 0x17, 0x1e, 0x30, 0x14, 0x80, 0x30, 0xc0, 0x72},
+ {0xce, 0x6a, 0x64, 0xa3, 0x09, 0xe4, 0x2f, 0xbb, 0xd9, 0x85,
+ 0x1c, 0x45, 0x3e, 0x64, 0x09, 0xea, 0xe8, 0x7d, 0x60, 0xf1},
+ {0xce, 0xa9, 0x89, 0x0d, 0x85, 0xd8, 0x07, 0x53, 0xa6, 0x26,
+ 0x28, 0x6c, 0xda, 0xd7, 0x8c, 0xb5, 0x66, 0xd7, 0x0c, 0xf2},
+ {0xcf, 0x9e, 0x87, 0x6d, 0xd3, 0xeb, 0xfc, 0x42, 0x26, 0x97,
+ 0xa3, 0xb5, 0xa3, 0x7a, 0xa0, 0x76, 0xa9, 0x06, 0x23, 0x48},
+ {0xcf, 0xde, 0xfe, 0x10, 0x2f, 0xda, 0x05, 0xbb, 0xe4, 0xc7,
+ 0x8d, 0x2e, 0x44, 0x23, 0x58, 0x90, 0x05, 0xb2, 0x57, 0x1d},
+ {0xcf, 0xe4, 0x31, 0x3d, 0xba, 0x05, 0xb8, 0xa7, 0xc3, 0x00,
+ 0x63, 0x99, 0x5a, 0x9e, 0xb7, 0xc2, 0x47, 0xad, 0x8f, 0xd5},
+ {0xcf, 0xf3, 0x60, 0xf5, 0x24, 0xcb, 0x20, 0xf1, 0xfe, 0xad,
+ 0x89, 0x00, 0x6f, 0x7f, 0x58, 0x6a, 0x28, 0x5b, 0x2d, 0x5b},
+ {0xcf, 0xf8, 0x10, 0xfb, 0x2c, 0x4f, 0xfc, 0x01, 0x56, 0xbf,
+ 0xe1, 0xe1, 0xfa, 0xbc, 0xb4, 0x18, 0xc6, 0x8d, 0x31, 0xc5},
+ {0xd1, 0xcb, 0xca, 0x5d, 0xb2, 0xd5, 0x2a, 0x7f, 0x69, 0x3b,
+ 0x67, 0x4d, 0xe5, 0xf0, 0x5a, 0x1d, 0x0c, 0x95, 0x7d, 0xf0},
+ {0xd1, 0xeb, 0x23, 0xa4, 0x6d, 0x17, 0xd6, 0x8f, 0xd9, 0x25,
+ 0x64, 0xc2, 0xf1, 0xf1, 0x60, 0x17, 0x64, 0xd8, 0xe3, 0x49},
+ {0xd2, 0x32, 0x09, 0xad, 0x23, 0xd3, 0x14, 0x23, 0x21, 0x74,
+ 0xe4, 0x0d, 0x7f, 0x9d, 0x62, 0x13, 0x97, 0x86, 0x63, 0x3a},
+ {0xd2, 0x44, 0x1a, 0xa8, 0xc2, 0x03, 0xae, 0xca, 0xa9, 0x6e,
+ 0x50, 0x1f, 0x12, 0x4d, 0x52, 0xb6, 0x8f, 0xe4, 0xc3, 0x75},
+ {0xd2, 0x9f, 0x6c, 0x98, 0xbe, 0xfc, 0x6d, 0x98, 0x65, 0x21,
+ 0x54, 0x3e, 0xe8, 0xbe, 0x56, 0xce, 0xbc, 0x28, 0x8c, 0xf3},
+ {0xd2, 0xed, 0xf8, 0x8b, 0x41, 0xb6, 0xfe, 0x01, 0x46, 0x1d,
+ 0x6e, 0x28, 0x34, 0xec, 0x7c, 0x8f, 0x6c, 0x77, 0x72, 0x1e},
+ {0xd3, 0xc0, 0x63, 0xf2, 0x19, 0xed, 0x07, 0x3e, 0x34, 0xad,
+ 0x5d, 0x75, 0x0b, 0x32, 0x76, 0x29, 0xff, 0xd5, 0x9a, 0xf2},
+ {0xd4, 0xde, 0x20, 0xd0, 0x5e, 0x66, 0xfc, 0x53, 0xfe, 0x1a,
+ 0x50, 0x88, 0x2c, 0x78, 0xdb, 0x28, 0x52, 0xca, 0xe4, 0x74},
+ {0xd6, 0x9b, 0x56, 0x11, 0x48, 0xf0, 0x1c, 0x77, 0xc5, 0x45,
+ 0x78, 0xc1, 0x09, 0x26, 0xdf, 0x5b, 0x85, 0x69, 0x76, 0xad},
+ {0xd6, 0xbf, 0x79, 0x94, 0xf4, 0x2b, 0xe5, 0xfa, 0x29, 0xda,
+ 0x0b, 0xd7, 0x58, 0x7b, 0x59, 0x1f, 0x47, 0xa4, 0x4f, 0x22},
+ {0xd6, 0xda, 0xa8, 0x20, 0x8d, 0x09, 0xd2, 0x15, 0x4d, 0x24,
+ 0xb5, 0x2f, 0xcb, 0x34, 0x6e, 0xb2, 0x58, 0xb2, 0x8a, 0x58},
+ {0xd8, 0xa6, 0x33, 0x2c, 0xe0, 0x03, 0x6f, 0xb1, 0x85, 0xf6,
+ 0x63, 0x4f, 0x7d, 0x6a, 0x06, 0x65, 0x26, 0x32, 0x28, 0x27},
+ {0xd8, 0xc5, 0x38, 0x8a, 0xb7, 0x30, 0x1b, 0x1b, 0x6e, 0xd4,
+ 0x7a, 0xe6, 0x45, 0x25, 0x3a, 0x6f, 0x9f, 0x1a, 0x27, 0x61},
+ {0xd9, 0x04, 0x08, 0x0a, 0x49, 0x29, 0xc8, 0x38, 0xe9, 0xf1,
+ 0x85, 0xec, 0xf7, 0xa2, 0x2d, 0xef, 0x99, 0x34, 0x24, 0x07},
+ {0xda, 0x40, 0x18, 0x8b, 0x91, 0x89, 0xa3, 0xed, 0xee, 0xae,
+ 0xda, 0x97, 0xfe, 0x2f, 0x9d, 0xf5, 0xb7, 0xd1, 0x8a, 0x41},
+ {0xda, 0x8b, 0x65, 0x67, 0xef, 0x3f, 0x6e, 0x1e, 0xa2, 0x6a,
+ 0xb1, 0x46, 0xe3, 0x6c, 0xcb, 0x57, 0x28, 0x04, 0x18, 0x46},
+ {0xda, 0xc9, 0x02, 0x4f, 0x54, 0xd8, 0xf6, 0xdf, 0x94, 0x93,
+ 0x5f, 0xb1, 0x73, 0x26, 0x38, 0xca, 0x6a, 0xd7, 0x7c, 0x13},
+ {0xda, 0xfa, 0xf7, 0xfa, 0x66, 0x84, 0xec, 0x06, 0x8f, 0x14,
+ 0x50, 0xbd, 0xc7, 0xc2, 0x81, 0xa5, 0xbc, 0xa9, 0x64, 0x57},
+ {0xdb, 0xac, 0x3c, 0x7a, 0xa4, 0x25, 0x4d, 0xa1, 0xaa, 0x5c,
+ 0xaa, 0xd6, 0x84, 0x68, 0xcb, 0x88, 0xee, 0xdd, 0xee, 0xa8},
+ {0xdd, 0x83, 0xc5, 0x19, 0xd4, 0x34, 0x81, 0xfa, 0xd4, 0xc2,
+ 0x2c, 0x03, 0xd7, 0x02, 0xfe, 0x9f, 0x3b, 0x22, 0xf5, 0x17},
+ {0xdd, 0xe1, 0xd2, 0xa9, 0x01, 0x80, 0x2e, 0x1d, 0x87, 0x5e,
+ 0x84, 0xb3, 0x80, 0x7e, 0x4b, 0xb1, 0xfd, 0x99, 0x41, 0x34},
+ {0xde, 0x28, 0xf4, 0xa4, 0xff, 0xe5, 0xb9, 0x2f, 0xa3, 0xc5,
+ 0x03, 0xd1, 0xa3, 0x49, 0xa7, 0xf9, 0x96, 0x2a, 0x82, 0x12},
+ {0xde, 0x3f, 0x40, 0xbd, 0x50, 0x93, 0xd3, 0x9b, 0x6c, 0x60,
+ 0xf6, 0xda, 0xbc, 0x07, 0x62, 0x01, 0x00, 0x89, 0x76, 0xc9},
+ {0xde, 0x99, 0x0c, 0xed, 0x99, 0xe0, 0x43, 0x1f, 0x60, 0xed,
+ 0xc3, 0x93, 0x7e, 0x7c, 0xd5, 0xbf, 0x0e, 0xd9, 0xe5, 0xfa},
+ {0xdf, 0x64, 0x6d, 0xcb, 0x7b, 0x0f, 0xd3, 0xa9, 0x6a, 0xee,
+ 0x88, 0xc6, 0x4e, 0x2d, 0x67, 0x67, 0x11, 0xff, 0x9d, 0x5f},
+ {0xe0, 0x92, 0x5e, 0x18, 0xc7, 0x76, 0x5e, 0x22, 0xda, 0xbd,
+ 0x94, 0x27, 0x52, 0x9d, 0xa6, 0xaf, 0x4e, 0x06, 0x64, 0x28},
+ {0xe0, 0xab, 0x05, 0x94, 0x20, 0x72, 0x54, 0x93, 0x05, 0x60,
+ 0x62, 0x02, 0x36, 0x70, 0xf7, 0xcd, 0x2e, 0xfc, 0x66, 0x66},
+ {0xe0, 0xb4, 0x32, 0x2e, 0xb2, 0xf6, 0xa5, 0x68, 0xb6, 0x54,
+ 0x53, 0x84, 0x48, 0x18, 0x4a, 0x50, 0x36, 0x87, 0x43, 0x84},
+ {0xe1, 0x2d, 0xfb, 0x4b, 0x41, 0xd7, 0xd9, 0xc3, 0x2b, 0x30,
+ 0x51, 0x4b, 0xac, 0x1d, 0x81, 0xd8, 0x38, 0x5e, 0x2d, 0x46},
+ {0xe1, 0xa4, 0x5b, 0x14, 0x1a, 0x21, 0xda, 0x1a, 0x79, 0xf4,
+ 0x1a, 0x42, 0xa9, 0x61, 0xd6, 0x69, 0xcd, 0x06, 0x34, 0xc1},
+ {0xe3, 0x92, 0x51, 0x2f, 0x0a, 0xcf, 0xf5, 0x05, 0xdf, 0xf6,
+ 0xde, 0x06, 0x7f, 0x75, 0x37, 0xe1, 0x65, 0xea, 0x57, 0x4b},
+ {0xe3, 0xd7, 0x36, 0x06, 0x99, 0x6c, 0xdf, 0xef, 0x61, 0xfa,
+ 0x04, 0xc3, 0x35, 0xe9, 0x8e, 0xa9, 0x61, 0x04, 0x26, 0x4a},
+ {0xe5, 0xdf, 0x74, 0x3c, 0xb6, 0x01, 0xc4, 0x9b, 0x98, 0x43,
+ 0xdc, 0xab, 0x8c, 0xe8, 0x6a, 0x81, 0x10, 0x9f, 0xe4, 0x8e},
+ {0xe6, 0x19, 0xd2, 0x5b, 0x38, 0x0b, 0x7b, 0x13, 0xfd, 0xa3,
+ 0x3e, 0x8a, 0x58, 0xcd, 0x82, 0xd8, 0xa8, 0x8e, 0x05, 0x15},
+ {0xe6, 0x21, 0xf3, 0x35, 0x43, 0x79, 0x05, 0x9a, 0x4b, 0x68,
+ 0x30, 0x9d, 0x8a, 0x2f, 0x74, 0x22, 0x15, 0x87, 0xec, 0x79},
+ {0xe7, 0x07, 0x15, 0xf6, 0xf7, 0x28, 0x36, 0x5b, 0x51, 0x90,
+ 0xe2, 0x71, 0xde, 0xe4, 0xc6, 0x5e, 0xbe, 0xea, 0xca, 0xf3},
+ {0xe7, 0xb4, 0xf6, 0x9d, 0x61, 0xec, 0x90, 0x69, 0xdb, 0x7e,
+ 0x90, 0xa7, 0x40, 0x1a, 0x3c, 0xf4, 0x7d, 0x4f, 0xe8, 0xee},
+ {0xea, 0xbd, 0xa2, 0x40, 0x44, 0x0a, 0xbb, 0xd6, 0x94, 0x93,
+ 0x0a, 0x01, 0xd0, 0x97, 0x64, 0xc6, 0xc2, 0xd7, 0x79, 0x66},
+ {0xec, 0x0c, 0x37, 0x16, 0xea, 0x9e, 0xdf, 0xad, 0xd3, 0x5d,
+ 0xfb, 0xd5, 0x56, 0x08, 0xe6, 0x0a, 0x05, 0xd3, 0xcb, 0xf3},
+ {0xec, 0x93, 0xde, 0x08, 0x3c, 0x93, 0xd9, 0x33, 0xa9, 0x86,
+ 0xb3, 0xd5, 0xcd, 0xe2, 0x5a, 0xcb, 0x2f, 0xee, 0xcf, 0x8e},
+ {0xed, 0x8d, 0xc8, 0x38, 0x6c, 0x48, 0x86, 0xae, 0xee, 0x07,
+ 0x91, 0x58, 0xaa, 0xc3, 0xbf, 0xe6, 0x58, 0xe3, 0x94, 0xb4},
+ {0xed, 0xb3, 0xcb, 0x5f, 0xb4, 0x19, 0xa1, 0x85, 0x06, 0x62,
+ 0x67, 0xe5, 0x79, 0x15, 0x54, 0xe1, 0xe2, 0x8b, 0x63, 0x99},
+ {0xee, 0x29, 0xd6, 0xea, 0x98, 0xe6, 0x32, 0xc6, 0xe5, 0x27,
+ 0xe0, 0x90, 0x6f, 0x02, 0x80, 0x68, 0x8b, 0xdf, 0x44, 0xdc},
+ {0xee, 0x86, 0x93, 0x87, 0xff, 0xfd, 0x83, 0x49, 0xab, 0x5a,
+ 0xd1, 0x43, 0x22, 0x58, 0x87, 0x89, 0xa4, 0x57, 0xb0, 0x12},
+ {0xf1, 0x38, 0xa3, 0x30, 0xa4, 0xea, 0x98, 0x6b, 0xeb, 0x52,
+ 0x0b, 0xb1, 0x10, 0x35, 0x87, 0x6e, 0xfb, 0x9d, 0x7f, 0x1c},
+ {0xf1, 0x7f, 0x6f, 0xb6, 0x31, 0xdc, 0x99, 0xe3, 0xa3, 0xc8,
+ 0x7f, 0xfe, 0x1c, 0xf1, 0x81, 0x10, 0x88, 0xd9, 0x60, 0x33},
+ {0xf1, 0x8b, 0x53, 0x8d, 0x1b, 0xe9, 0x03, 0xb6, 0xa6, 0xf0,
+ 0x56, 0x43, 0x5b, 0x17, 0x15, 0x89, 0xca, 0xf3, 0x6b, 0xf2},
+ {0xf3, 0x73, 0xb3, 0x87, 0x06, 0x5a, 0x28, 0x84, 0x8a, 0xf2,
+ 0xf3, 0x4a, 0xce, 0x19, 0x2b, 0xdd, 0xc7, 0x8e, 0x9c, 0xac},
+ {0xf4, 0x40, 0x95, 0xc2, 0x38, 0xac, 0x73, 0xfc, 0x4f, 0x77,
+ 0xbf, 0x8f, 0x98, 0xdf, 0x70, 0xf8, 0xf0, 0x91, 0xbc, 0x52},
+ {0xf4, 0x8b, 0x11, 0xbf, 0xde, 0xab, 0xbe, 0x94, 0x54, 0x20,
+ 0x71, 0xe6, 0x41, 0xde, 0x6b, 0xbe, 0x88, 0x2b, 0x40, 0xb9},
+ {0xf5, 0xc2, 0x7c, 0xf5, 0xff, 0xf3, 0x02, 0x9a, 0xcf, 0x1a,
+ 0x1a, 0x4b, 0xec, 0x7e, 0xe1, 0x96, 0x4c, 0x77, 0xd7, 0x84},
+ {0xf9, 0xb5, 0xb6, 0x32, 0x45, 0x5f, 0x9c, 0xbe, 0xec, 0x57,
+ 0x5f, 0x80, 0xdc, 0xe9, 0x6e, 0x2c, 0xc7, 0xb2, 0x78, 0xb7},
+ {0xf9, 0xcd, 0x0e, 0x2c, 0xda, 0x76, 0x24, 0xc1, 0x8f, 0xbd,
+ 0xf0, 0xf0, 0xab, 0xb6, 0x45, 0xb8, 0xf7, 0xfe, 0xd5, 0x7a},
+ {0xf9, 0xdd, 0x19, 0x26, 0x6b, 0x20, 0x43, 0xf1, 0xfe, 0x4b,
+ 0x3d, 0xcb, 0x01, 0x90, 0xaf, 0xf1, 0x1f, 0x31, 0xa6, 0x9d},
+ {0xfa, 0x08, 0x82, 0x59, 0x5f, 0x9c, 0xa6, 0xa1, 0x1e, 0xcc,
+ 0xbe, 0xaf, 0x65, 0xc7, 0x64, 0xc0, 0xcc, 0xc3, 0x11, 0xd0},
+ {0xfa, 0xa7, 0xd9, 0xfb, 0x31, 0xb7, 0x46, 0xf2, 0x00, 0xa8,
+ 0x5e, 0x65, 0x79, 0x76, 0x13, 0xd8, 0x16, 0xe0, 0x63, 0xb5},
+ {0xfa, 0xaa, 0x27, 0xb8, 0xca, 0xf5, 0xfd, 0xf5, 0xcd, 0xa9,
+ 0x8a, 0xc3, 0x37, 0x85, 0x72, 0xe0, 0x4c, 0xe8, 0xf2, 0xe0},
+ {0xfa, 0xb7, 0xee, 0x36, 0x97, 0x26, 0x62, 0xfb, 0x2d, 0xb0,
+ 0x2a, 0xf6, 0xbf, 0x03, 0xfd, 0xe8, 0x7c, 0x4b, 0x2f, 0x9b},
+ {0xfd, 0x1e, 0xd1, 0xe2, 0x02, 0x1b, 0x0b, 0x9f, 0x73, 0xe8,
+ 0xeb, 0x75, 0xce, 0x23, 0x43, 0x6b, 0xbc, 0xc7, 0x46, 0xeb},
+ {0xfe, 0xb8, 0xc4, 0x32, 0xdc, 0xf9, 0x76, 0x9a, 0xce, 0xae,
+ 0x3d, 0xd8, 0x90, 0x8f, 0xfd, 0x28, 0x86, 0x65, 0x64, 0x7d},
+};
+
+#endif // NET_CERT_X509_CERTIFICATE_KNOWN_ROOTS_WIN_H_
diff --git a/chromium/net/cert/x509_certificate_mac.cc b/chromium/net/cert/x509_certificate_mac.cc
new file mode 100644
index 00000000000..2f8ce438afd
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_mac.cc
@@ -0,0 +1,611 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#include <CoreServices/CoreServices.h>
+#include <Security/Security.h>
+
+#include <cert.h>
+
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/singleton.h"
+#include "base/pickle.h"
+#include "base/sha1.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "crypto/cssm_init.h"
+#include "crypto/mac_security_services_lock.h"
+#include "crypto/nss_util.h"
+#include "net/cert/x509_util_mac.h"
+
+using base::ScopedCFTypeRef;
+using base::Time;
+
+namespace net {
+
+namespace {
+
+void GetCertDistinguishedName(
+ const x509_util::CSSMCachedCertificate& cached_cert,
+ const CSSM_OID* oid,
+ CertPrincipal* result) {
+ x509_util::CSSMFieldValue distinguished_name;
+ OSStatus status = cached_cert.GetField(oid, &distinguished_name);
+ if (status || !distinguished_name.field())
+ return;
+ result->ParseDistinguishedName(distinguished_name.field()->Data,
+ distinguished_name.field()->Length);
+}
+
+bool IsCertIssuerInEncodedList(X509Certificate::OSCertHandle cert_handle,
+ const std::vector<std::string>& issuers) {
+ x509_util::CSSMCachedCertificate cached_cert;
+ if (cached_cert.Init(cert_handle) != CSSM_OK)
+ return false;
+
+ x509_util::CSSMFieldValue distinguished_name;
+ OSStatus status = cached_cert.GetField(&CSSMOID_X509V1IssuerNameStd,
+ &distinguished_name);
+ if (status || !distinguished_name.field())
+ return false;
+
+ base::StringPiece name_piece(
+ reinterpret_cast<const char*>(distinguished_name.field()->Data),
+ static_cast<size_t>(distinguished_name.field()->Length));
+
+ for (std::vector<std::string>::const_iterator it = issuers.begin();
+ it != issuers.end(); ++it) {
+ base::StringPiece issuer_piece(*it);
+ if (name_piece == issuer_piece)
+ return true;
+ }
+
+ return false;
+}
+
+void GetCertDateForOID(const x509_util::CSSMCachedCertificate& cached_cert,
+ const CSSM_OID* oid,
+ Time* result) {
+ *result = Time::Time();
+
+ x509_util::CSSMFieldValue field;
+ OSStatus status = cached_cert.GetField(oid, &field);
+ if (status)
+ return;
+
+ const CSSM_X509_TIME* x509_time = field.GetAs<CSSM_X509_TIME>();
+ if (x509_time->timeType != BER_TAG_UTC_TIME &&
+ x509_time->timeType != BER_TAG_GENERALIZED_TIME) {
+ LOG(ERROR) << "Unsupported date/time format "
+ << x509_time->timeType;
+ return;
+ }
+
+ base::StringPiece time_string(
+ reinterpret_cast<const char*>(x509_time->time.Data),
+ x509_time->time.Length);
+ CertDateFormat format = x509_time->timeType == BER_TAG_UTC_TIME ?
+ CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
+ if (!ParseCertificateDate(time_string, format, result))
+ LOG(ERROR) << "Invalid certificate date/time " << time_string;
+}
+
+std::string GetCertSerialNumber(
+ const x509_util::CSSMCachedCertificate& cached_cert) {
+ x509_util::CSSMFieldValue serial_number;
+ OSStatus status = cached_cert.GetField(&CSSMOID_X509V1SerialNumber,
+ &serial_number);
+ if (status || !serial_number.field())
+ return std::string();
+
+ return std::string(
+ reinterpret_cast<const char*>(serial_number.field()->Data),
+ serial_number.field()->Length);
+}
+
+// Returns true if |purpose| is listed as allowed in |usage|. This
+// function also considers the "Any" purpose. If the attribute is
+// present and empty, we return false.
+bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage,
+ const CSSM_OID* purpose) {
+ for (unsigned p = 0; p < usage->numPurposes; ++p) {
+ if (CSSMOIDEqual(&usage->purposes[p], purpose))
+ return true;
+ if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ExtendedKeyUsageAny))
+ return true;
+ }
+ return false;
+}
+
+// Test that a given |cert_handle| is actually a valid X.509 certificate, and
+// return true if it is.
+//
+// On OS X, SecCertificateCreateFromData() does not return any errors if
+// called with invalid data, as long as data is present. The actual decoding
+// of the certificate does not happen until an API that requires a CSSM
+// handle is called. While SecCertificateGetCLHandle is the most likely
+// candidate, as it performs the parsing, it does not check whether the
+// parsing was actually successful. Instead, SecCertificateGetSubject is
+// used (supported since 10.3), as a means to check that the certificate
+// parsed as a valid X.509 certificate.
+bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
+ const CSSM_X509_NAME* sanity_check = NULL;
+ OSStatus status = SecCertificateGetSubject(cert_handle, &sanity_check);
+ return status == noErr && sanity_check;
+}
+
+// Parses |data| of length |length|, attempting to decode it as the specified
+// |format|. If |data| is in the specified format, any certificates contained
+// within are stored into |output|.
+void AddCertificatesFromBytes(const char* data, size_t length,
+ SecExternalFormat format,
+ X509Certificate::OSCertHandles* output) {
+ SecExternalFormat input_format = format;
+ ScopedCFTypeRef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), length,
+ kCFAllocatorNull));
+
+ CFArrayRef items = NULL;
+ OSStatus status;
+ {
+ base::AutoLock lock(crypto::GetMacSecurityServicesLock());
+ status = SecKeychainItemImport(local_data, NULL, &input_format,
+ NULL, 0, NULL, NULL, &items);
+ }
+
+ if (status) {
+ OSSTATUS_DLOG(WARNING, status)
+ << "Unable to import items from data of length " << length;
+ return;
+ }
+
+ ScopedCFTypeRef<CFArrayRef> scoped_items(items);
+ CFTypeID cert_type_id = SecCertificateGetTypeID();
+
+ for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) {
+ SecKeychainItemRef item = reinterpret_cast<SecKeychainItemRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(items, i)));
+
+ // While inputFormat implies only certificates will be imported, if/when
+ // other formats (eg: PKCS#12) are supported, this may also include
+ // private keys or other items types, so filter appropriately.
+ if (CFGetTypeID(item) == cert_type_id) {
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(item);
+ // OS X ignores |input_format| if it detects that |local_data| is PEM
+ // encoded, attempting to decode data based on internal rules for PEM
+ // block headers. If a PKCS#7 blob is encoded with a PEM block of
+ // CERTIFICATE, OS X 10.5 will return a single, invalid certificate
+ // based on the decoded data. If this happens, the certificate should
+ // not be included in |output|. Because |output| is empty,
+ // CreateCertificateListfromBytes will use PEMTokenizer to decode the
+ // data. When called again with the decoded data, OS X will honor
+ // |input_format|, causing decode to succeed. On OS X 10.6, the data
+ // is properly decoded as a PKCS#7, whether PEM or not, which avoids
+ // the need to fallback to internal decoding.
+ if (IsValidOSCertHandle(cert)) {
+ CFRetain(cert);
+ output->push_back(cert);
+ }
+ }
+ }
+}
+
+struct CSSMOIDString {
+ const CSSM_OID* oid_;
+ std::string string_;
+};
+
+typedef std::vector<CSSMOIDString> CSSMOIDStringVector;
+
+bool CERTNameToCSSMOIDVector(CERTName* name, CSSMOIDStringVector* out_values) {
+ struct OIDCSSMMap {
+ SECOidTag sec_OID_;
+ const CSSM_OID* cssm_OID_;
+ };
+
+ const OIDCSSMMap kOIDs[] = {
+ { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName },
+ { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName },
+ { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName },
+ { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName },
+ { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress },
+ { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName },
+ { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName },
+ { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier },
+ { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier },
+ { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress },
+ };
+
+ CERTRDN** rdns = name->rdns;
+ for (size_t rdn = 0; rdns[rdn]; ++rdn) {
+ CERTAVA** avas = rdns[rdn]->avas;
+ for (size_t pair = 0; avas[pair] != 0; ++pair) {
+ SECOidTag tag = CERT_GetAVATag(avas[pair]);
+ if (tag == SEC_OID_UNKNOWN) {
+ return false;
+ }
+ CSSMOIDString oidString;
+ bool found_oid = false;
+ for (size_t oid = 0; oid < ARRAYSIZE_UNSAFE(kOIDs); ++oid) {
+ if (kOIDs[oid].sec_OID_ == tag) {
+ SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
+ if (!decode_item)
+ return false;
+
+ // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
+ std::string value(reinterpret_cast<char*>(decode_item->data),
+ decode_item->len);
+ oidString.oid_ = kOIDs[oid].cssm_OID_;
+ oidString.string_ = value;
+ out_values->push_back(oidString);
+ SECITEM_FreeItem(decode_item, PR_TRUE);
+ found_oid = true;
+ break;
+ }
+ }
+ if (!found_oid) {
+ DLOG(ERROR) << "Unrecognized OID: " << tag;
+ }
+ }
+ }
+ return true;
+}
+
+class ScopedCertName {
+ public:
+ explicit ScopedCertName(CERTName* name) : name_(name) { }
+ ~ScopedCertName() {
+ if (name_) CERT_DestroyName(name_);
+ }
+ operator CERTName*() { return name_; }
+
+ private:
+ CERTName* name_;
+};
+
+class ScopedEncodedCertResults {
+ public:
+ explicit ScopedEncodedCertResults(CSSM_TP_RESULT_SET* results)
+ : results_(results) { }
+ ~ScopedEncodedCertResults() {
+ if (results_) {
+ CSSM_ENCODED_CERT* encCert =
+ reinterpret_cast<CSSM_ENCODED_CERT*>(results_->Results);
+ for (uint32 i = 0; i < results_->NumberOfResults; i++) {
+ crypto::CSSMFree(encCert[i].CertBlob.Data);
+ }
+ }
+ crypto::CSSMFree(results_->Results);
+ crypto::CSSMFree(results_);
+ }
+
+ private:
+ CSSM_TP_RESULT_SET* results_;
+};
+
+} // namespace
+
+void X509Certificate::Initialize() {
+ x509_util::CSSMCachedCertificate cached_cert;
+ if (cached_cert.Init(cert_handle_) == CSSM_OK) {
+ GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1SubjectNameStd,
+ &subject_);
+ GetCertDistinguishedName(cached_cert, &CSSMOID_X509V1IssuerNameStd,
+ &issuer_);
+ GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotBefore,
+ &valid_start_);
+ GetCertDateForOID(cached_cert, &CSSMOID_X509V1ValidityNotAfter,
+ &valid_expiry_);
+ serial_number_ = GetCertSerialNumber(cached_cert);
+ }
+
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+ if (IsCertIssuerInEncodedList(cert_handle_, valid_issuers))
+ return true;
+
+ for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
+ it != intermediate_ca_certs_.end(); ++it) {
+ if (IsCertIssuerInEncodedList(*it, valid_issuers))
+ return true;
+ }
+ return false;
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ x509_util::CSSMCachedCertificate cached_cert;
+ OSStatus status = cached_cert.Init(cert_handle_);
+ if (status)
+ return;
+ x509_util::CSSMFieldValue subject_alt_name;
+ status = cached_cert.GetField(&CSSMOID_SubjectAltName, &subject_alt_name);
+ if (status || !subject_alt_name.field())
+ return;
+ const CSSM_X509_EXTENSION* cssm_ext =
+ subject_alt_name.GetAs<CSSM_X509_EXTENSION>();
+ if (!cssm_ext || !cssm_ext->value.parsedValue)
+ return;
+ const CE_GeneralNames* alt_name =
+ reinterpret_cast<const CE_GeneralNames*>(cssm_ext->value.parsedValue);
+
+ for (size_t name = 0; name < alt_name->numNames; ++name) {
+ const CE_GeneralName& name_struct = alt_name->generalName[name];
+ const CSSM_DATA& name_data = name_struct.name;
+ // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
+ // respectively, both of which can be byte copied from
+ // CSSM_DATA::data into the appropriate output vector.
+ if (dns_names && name_struct.nameType == GNT_DNSName) {
+ dns_names->push_back(std::string(
+ reinterpret_cast<const char*>(name_data.Data),
+ name_data.Length));
+ } else if (ip_addrs && name_struct.nameType == GNT_IPAddress) {
+ ip_addrs->push_back(std::string(
+ reinterpret_cast<const char*>(name_data.Data),
+ name_data.Length));
+ }
+ }
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+ std::string* encoded) {
+ CSSM_DATA der_data;
+ if (SecCertificateGetData(cert_handle, &der_data) != noErr)
+ return false;
+ encoded->assign(reinterpret_cast<char*>(der_data.Data),
+ der_data.Length);
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+ if (CFEqual(a, b))
+ return true;
+ CSSM_DATA a_data, b_data;
+ return SecCertificateGetData(a, &a_data) == noErr &&
+ SecCertificateGetData(b, &b_data) == noErr &&
+ a_data.Length == b_data.Length &&
+ memcmp(a_data.Data, b_data.Data, a_data.Length) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ CSSM_DATA cert_data;
+ cert_data.Data = const_cast<uint8*>(reinterpret_cast<const uint8*>(data));
+ cert_data.Length = length;
+
+ OSCertHandle cert_handle = NULL;
+ OSStatus status = SecCertificateCreateFromData(&cert_data,
+ CSSM_CERT_X_509v3,
+ CSSM_CERT_ENCODING_DER,
+ &cert_handle);
+ if (status != noErr)
+ return NULL;
+ if (!IsValidOSCertHandle(cert_handle)) {
+ CFRelease(cert_handle);
+ return NULL;
+ }
+ return cert_handle;
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data, int length, Format format) {
+ OSCertHandles results;
+
+ switch (format) {
+ case FORMAT_SINGLE_CERTIFICATE: {
+ OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
+ if (handle)
+ results.push_back(handle);
+ break;
+ }
+ case FORMAT_PKCS7:
+ AddCertificatesFromBytes(data, length, kSecFormatPKCS7, &results);
+ break;
+ default:
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+
+ return results;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle handle) {
+ if (!handle)
+ return NULL;
+ return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ CFRelease(cert_handle);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(
+ OSCertHandle cert) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ CSSM_DATA cert_data;
+ OSStatus status = SecCertificateGetData(cert, &cert_data);
+ if (status)
+ return sha1;
+
+ DCHECK(cert_data.Data);
+ DCHECK_NE(cert_data.Length, 0U);
+
+ CC_SHA1(cert_data.Data, cert_data.Length, sha1.data);
+
+ return sha1;
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ // The CC_SHA(3cc) man page says all CC_SHA1_xxx routines return 1, so
+ // we don't check their return values.
+ CC_SHA1_CTX sha1_ctx;
+ CC_SHA1_Init(&sha1_ctx);
+ CSSM_DATA cert_data;
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ OSStatus status = SecCertificateGetData(intermediates[i], &cert_data);
+ if (status)
+ return sha1;
+ CC_SHA1_Update(&sha1_ctx, cert_data.Data, cert_data.Length);
+ }
+ CC_SHA1_Final(sha1.data, &sha1_ctx);
+
+ return sha1;
+}
+
+bool X509Certificate::SupportsSSLClientAuth() const {
+ x509_util::CSSMCachedCertificate cached_cert;
+ OSStatus status = cached_cert.Init(cert_handle_);
+ if (status)
+ return false;
+
+ // RFC5280 says to take the intersection of the two extensions.
+ //
+ // Our underlying crypto libraries don't expose
+ // ClientCertificateType, so for now we will not support fixed
+ // Diffie-Hellman mechanisms. For rsa_sign, we need the
+ // digitalSignature bit.
+ //
+ // In particular, if a key has the nonRepudiation bit and not the
+ // digitalSignature one, we will not offer it to the user.
+ x509_util::CSSMFieldValue key_usage;
+ status = cached_cert.GetField(&CSSMOID_KeyUsage, &key_usage);
+ if (status == CSSM_OK && key_usage.field()) {
+ const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
+ const CE_KeyUsage* key_usage_value =
+ reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue);
+ if (!((*key_usage_value) & CE_KU_DigitalSignature))
+ return false;
+ }
+
+ status = cached_cert.GetField(&CSSMOID_ExtendedKeyUsage, &key_usage);
+ if (status == CSSM_OK && key_usage.field()) {
+ const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
+ const CE_ExtendedKeyUsage* ext_key_usage =
+ reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
+ if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth))
+ return false;
+ }
+ return true;
+}
+
+CFArrayRef X509Certificate::CreateOSCertChainForCert() const {
+ CFMutableArrayRef cert_list =
+ CFArrayCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeArrayCallBacks);
+ if (!cert_list)
+ return NULL;
+
+ CFArrayAppendValue(cert_list, os_cert_handle());
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i)
+ CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]);
+
+ return cert_list;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return NULL;
+
+ return CreateOSCertHandleFromBytes(data, length);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ CSSM_DATA cert_data;
+ OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
+ if (status)
+ return false;
+
+ return pickle->WriteData(reinterpret_cast<char*>(cert_data.Data),
+ cert_data.Length);
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ // Since we might fail, set the output parameters to default values first.
+ *type = kPublicKeyTypeUnknown;
+ *size_bits = 0;
+
+ SecKeyRef key;
+ OSStatus status = SecCertificateCopyPublicKey(cert_handle, &key);
+ if (status) {
+ NOTREACHED() << "SecCertificateCopyPublicKey failed: " << status;
+ return;
+ }
+ ScopedCFTypeRef<SecKeyRef> scoped_key(key);
+
+ const CSSM_KEY* cssm_key;
+ status = SecKeyGetCSSMKey(key, &cssm_key);
+ if (status) {
+ NOTREACHED() << "SecKeyGetCSSMKey failed: " << status;
+ return;
+ }
+
+ *size_bits = cssm_key->KeyHeader.LogicalKeySizeInBits;
+
+ switch (cssm_key->KeyHeader.AlgorithmId) {
+ case CSSM_ALGID_RSA:
+ *type = kPublicKeyTypeRSA;
+ break;
+ case CSSM_ALGID_DSA:
+ *type = kPublicKeyTypeDSA;
+ break;
+ case CSSM_ALGID_ECDSA:
+ *type = kPublicKeyTypeECDSA;
+ break;
+ case CSSM_ALGID_DH:
+ *type = kPublicKeyTypeDH;
+ break;
+ default:
+ *type = kPublicKeyTypeUnknown;
+ *size_bits = 0;
+ break;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate_net_log_param.cc b/chromium/net/cert/x509_certificate_net_log_param.cc
new file mode 100644
index 00000000000..3c52b96dc07
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_net_log_param.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate_net_log_param.h"
+
+#include <string>
+#include <vector>
+
+#include "base/values.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+base::Value* NetLogX509CertificateCallback(const X509Certificate* certificate,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ base::ListValue* certs = new base::ListValue();
+ std::vector<std::string> encoded_chain;
+ certificate->GetPEMEncodedChain(&encoded_chain);
+ for (size_t i = 0; i < encoded_chain.size(); ++i)
+ certs->Append(new base::StringValue(encoded_chain[i]));
+ dict->Set("certificates", certs);
+ return dict;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate_net_log_param.h b/chromium/net/cert/x509_certificate_net_log_param.h
new file mode 100644
index 00000000000..36ae8f5ecdf
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_net_log_param.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_X509_CERT_NET_LOG_PARAM_H_
+#define NET_BASE_X509_CERT_NET_LOG_PARAM_H_
+
+#include "net/base/net_log.h"
+
+namespace net {
+
+class X509Certificate;
+
+// Creates NetLog parameter to describe an X509Certificate.
+base::Value* NetLogX509CertificateCallback(
+ const X509Certificate* certificate,
+ NetLog::LogLevel log_level);
+
+} // namespace net
+
+#endif // NET_BASE_X509_CERT_NET_LOG_PARAM_H_
diff --git a/chromium/net/cert/x509_certificate_nss.cc b/chromium/net/cert/x509_certificate_nss.cc
new file mode 100644
index 00000000000..ca256504817
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_nss.cc
@@ -0,0 +1,269 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <cert.h>
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prtime.h>
+#include <seccomon.h>
+#include <secder.h>
+#include <sechash.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/cert/x509_util_nss.h"
+
+namespace net {
+
+void X509Certificate::Initialize() {
+ x509_util::ParsePrincipal(&cert_handle_->subject, &subject_);
+ x509_util::ParsePrincipal(&cert_handle_->issuer, &issuer_);
+
+ x509_util::ParseDate(&cert_handle_->validity.notBefore, &valid_start_);
+ x509_util::ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_);
+
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+
+ serial_number_ = x509_util::ParseSerialNumber(cert_handle_);
+}
+
+// static
+X509Certificate* X509Certificate::CreateFromBytesWithNickname(
+ const char* data,
+ int length,
+ const char* nickname) {
+ OSCertHandle cert_handle = CreateOSCertHandleFromBytesWithNickname(data,
+ length,
+ nickname);
+ if (!cert_handle)
+ return NULL;
+
+ X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles());
+ FreeOSCertHandle(cert_handle);
+
+ if (nickname)
+ cert->default_nickname_ = nickname;
+
+ return cert;
+}
+
+std::string X509Certificate::GetDefaultNickname(CertType type) const {
+ if (!default_nickname_.empty())
+ return default_nickname_;
+
+ std::string result;
+ if (type == USER_CERT && cert_handle_->slot) {
+ // Find the private key for this certificate and see if it has a
+ // nickname. If there is a private key, and it has a nickname, then
+ // return that nickname.
+ SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
+ cert_handle_->slot,
+ cert_handle_,
+ NULL); // wincx
+ if (private_key) {
+ char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
+ if (private_key_nickname) {
+ result = private_key_nickname;
+ PORT_Free(private_key_nickname);
+ SECKEY_DestroyPrivateKey(private_key);
+ return result;
+ }
+ SECKEY_DestroyPrivateKey(private_key);
+ }
+ }
+
+ switch (type) {
+ case CA_CERT: {
+ char* nickname = CERT_MakeCANickname(cert_handle_);
+ result = nickname;
+ PORT_Free(nickname);
+ break;
+ }
+ case USER_CERT: {
+ std::string subject_name = subject_.GetDisplayName();
+ if (subject_name.empty()) {
+ const char* email = CERT_GetFirstEmailAddress(cert_handle_);
+ if (email)
+ subject_name = email;
+ }
+ // TODO(gspencer): Internationalize this. It's wrong to assume English
+ // here.
+ result = base::StringPrintf("%s's %s ID", subject_name.c_str(),
+ issuer_.GetDisplayName().c_str());
+ break;
+ }
+ case SERVER_CERT:
+ result = subject_.GetDisplayName();
+ break;
+ case UNKNOWN_CERT:
+ default:
+ break;
+ }
+ return result;
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ x509_util::GetSubjectAltName(cert_handle_, dns_names, ip_addrs);
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+ // Get certificate chain as scoped list of CERTCertificate objects.
+ std::vector<CERTCertificate*> cert_chain;
+ cert_chain.push_back(cert_handle_);
+ for (size_t n = 0; n < intermediate_ca_certs_.size(); ++n) {
+ cert_chain.push_back(intermediate_ca_certs_[n]);
+ }
+ // Convert encoded issuers to scoped CERTName* list.
+ std::vector<CERTName*> issuers;
+ crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!x509_util::GetIssuersFromEncodedList(valid_issuers,
+ arena.get(),
+ &issuers)) {
+ return false;
+ }
+ return x509_util::IsCertificateIssuedBy(cert_chain, issuers);
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+ std::string* encoded) {
+ if (!cert_handle->derCert.len)
+ return false;
+ encoded->assign(reinterpret_cast<char*>(cert_handle->derCert.data),
+ cert_handle->derCert.len);
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+ return a->derCert.len == b->derCert.len &&
+ memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ return CreateOSCertHandleFromBytesWithNickname(data, length, NULL);
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::CreateOSCertHandleFromBytesWithNickname(
+ const char* data,
+ int length,
+ const char* nickname) {
+ if (length < 0)
+ return NULL;
+
+ crypto::EnsureNSSInit();
+
+ if (!NSS_IsInitialized())
+ return NULL;
+
+ SECItem der_cert;
+ der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
+ der_cert.len = length;
+ der_cert.type = siDERCertBuffer;
+
+ // Parse into a certificate structure.
+ return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert,
+ const_cast<char*>(nickname),
+ PR_FALSE, PR_TRUE);
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ Format format) {
+ return x509_util::CreateOSCertHandlesFromBytes(data, length, format);
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle cert_handle) {
+ return CERT_DupCertificate(cert_handle);
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ CERT_DestroyCertificate(cert_handle);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(
+ OSCertHandle cert) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ DCHECK(NULL != cert->derCert.data);
+ DCHECK_NE(0U, cert->derCert.len);
+
+ SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data,
+ cert->derCert.data, cert->derCert.len);
+ DCHECK_EQ(SECSuccess, rv);
+
+ return sha1;
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ HASHContext* sha1_ctx = HASH_Create(HASH_AlgSHA1);
+ if (!sha1_ctx)
+ return sha1;
+ HASH_Begin(sha1_ctx);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ CERTCertificate* ca_cert = intermediates[i];
+ HASH_Update(sha1_ctx, ca_cert->derCert.data, ca_cert->derCert.len);
+ }
+ unsigned int result_len;
+ HASH_End(sha1_ctx, sha1.data, &result_len, HASH_ResultLenContext(sha1_ctx));
+ HASH_Destroy(sha1_ctx);
+
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ return x509_util::ReadOSCertHandleFromPickle(pickle_iter);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ return pickle->WriteData(
+ reinterpret_cast<const char*>(cert_handle->derCert.data),
+ cert_handle->derCert.len);
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ x509_util::GetPublicKeyInfo(cert_handle, size_bits, type);
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate_openssl.cc b/chromium/net/cert/x509_certificate_openssl.cc
new file mode 100644
index 00000000000..bdf2bf20538
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_openssl.cc
@@ -0,0 +1,520 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <openssl/asn1.h>
+#include <openssl/crypto.h>
+#include <openssl/obj_mac.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <openssl/sha.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+#include "base/memory/singleton.h"
+#include "base/pickle.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "crypto/openssl_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/cert/x509_util_openssl.h"
+
+#if defined(OS_ANDROID)
+#include "base/logging.h"
+#include "net/android/network_library.h"
+#endif
+
+namespace net {
+
+namespace {
+
+void CreateOSCertHandlesFromPKCS7Bytes(
+ const char* data, int length,
+ X509Certificate::OSCertHandles* handles) {
+ crypto::EnsureOpenSSLInit();
+ const unsigned char* der_data = reinterpret_cast<const unsigned char*>(data);
+ crypto::ScopedOpenSSL<PKCS7, PKCS7_free> pkcs7_cert(
+ d2i_PKCS7(NULL, &der_data, length));
+ if (!pkcs7_cert.get())
+ return;
+
+ STACK_OF(X509)* certs = NULL;
+ int nid = OBJ_obj2nid(pkcs7_cert.get()->type);
+ if (nid == NID_pkcs7_signed) {
+ certs = pkcs7_cert.get()->d.sign->cert;
+ } else if (nid == NID_pkcs7_signedAndEnveloped) {
+ certs = pkcs7_cert.get()->d.signed_and_enveloped->cert;
+ }
+
+ if (certs) {
+ for (int i = 0; i < sk_X509_num(certs); ++i) {
+ X509* x509_cert =
+ X509Certificate::DupOSCertHandle(sk_X509_value(certs, i));
+ handles->push_back(x509_cert);
+ }
+ }
+}
+
+void ParsePrincipalValues(X509_NAME* name,
+ int nid,
+ std::vector<std::string>* fields) {
+ for (int index = -1;
+ (index = X509_NAME_get_index_by_NID(name, nid, index)) != -1;) {
+ std::string field;
+ if (!x509_util::ParsePrincipalValueByIndex(name, index, &field))
+ break;
+ fields->push_back(field);
+ }
+}
+
+void ParsePrincipal(X509Certificate::OSCertHandle cert,
+ X509_NAME* x509_name,
+ CertPrincipal* principal) {
+ if (!x509_name)
+ return;
+
+ ParsePrincipalValues(x509_name, NID_streetAddress,
+ &principal->street_addresses);
+ ParsePrincipalValues(x509_name, NID_organizationName,
+ &principal->organization_names);
+ ParsePrincipalValues(x509_name, NID_organizationalUnitName,
+ &principal->organization_unit_names);
+ ParsePrincipalValues(x509_name, NID_domainComponent,
+ &principal->domain_components);
+
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_commonName,
+ &principal->common_name);
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_localityName,
+ &principal->locality_name);
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_stateOrProvinceName,
+ &principal->state_or_province_name);
+ x509_util::ParsePrincipalValueByNID(x509_name, NID_countryName,
+ &principal->country_name);
+}
+
+void ParseSubjectAltName(X509Certificate::OSCertHandle cert,
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addresses) {
+ DCHECK(dns_names || ip_addresses);
+ int index = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
+ X509_EXTENSION* alt_name_ext = X509_get_ext(cert, index);
+ if (!alt_name_ext)
+ return;
+
+ crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free> alt_names(
+ reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext)));
+ if (!alt_names.get())
+ return;
+
+ for (int i = 0; i < sk_GENERAL_NAME_num(alt_names.get()); ++i) {
+ const GENERAL_NAME* name = sk_GENERAL_NAME_value(alt_names.get(), i);
+ if (name->type == GEN_DNS && dns_names) {
+ const unsigned char* dns_name = ASN1_STRING_data(name->d.dNSName);
+ if (!dns_name)
+ continue;
+ int dns_name_len = ASN1_STRING_length(name->d.dNSName);
+ dns_names->push_back(
+ std::string(reinterpret_cast<const char*>(dns_name), dns_name_len));
+ } else if (name->type == GEN_IPADD && ip_addresses) {
+ const unsigned char* ip_addr = name->d.iPAddress->data;
+ if (!ip_addr)
+ continue;
+ int ip_addr_len = name->d.iPAddress->length;
+ if (ip_addr_len != static_cast<int>(kIPv4AddressSize) &&
+ ip_addr_len != static_cast<int>(kIPv6AddressSize)) {
+ // http://www.ietf.org/rfc/rfc3280.txt requires subjectAltName iPAddress
+ // to have 4 or 16 bytes, whereas in a name constraint it includes a
+ // net mask hence 8 or 32 bytes. Logging to help diagnose any mixup.
+ LOG(WARNING) << "Bad sized IP Address in cert: " << ip_addr_len;
+ continue;
+ }
+ ip_addresses->push_back(
+ std::string(reinterpret_cast<const char*>(ip_addr), ip_addr_len));
+ }
+ }
+}
+
+struct DERCache {
+ unsigned char* data;
+ int data_length;
+};
+
+void DERCache_free(void* parent, void* ptr, CRYPTO_EX_DATA* ad, int idx,
+ long argl, void* argp) {
+ DERCache* der_cache = static_cast<DERCache*>(ptr);
+ if (!der_cache)
+ return;
+ if (der_cache->data)
+ OPENSSL_free(der_cache->data);
+ OPENSSL_free(der_cache);
+}
+
+class X509InitSingleton {
+ public:
+ static X509InitSingleton* GetInstance() {
+ // We allow the X509 store to leak, because it is used from a non-joinable
+ // worker that is not stopped on shutdown, hence may still be using
+ // OpenSSL library after the AtExit runner has completed.
+ return Singleton<X509InitSingleton,
+ LeakySingletonTraits<X509InitSingleton> >::get();
+ }
+ int der_cache_ex_index() const { return der_cache_ex_index_; }
+ X509_STORE* store() const { return store_.get(); }
+
+ void ResetCertStore() {
+ store_.reset(X509_STORE_new());
+ DCHECK(store_.get());
+ X509_STORE_set_default_paths(store_.get());
+ // TODO(joth): Enable CRL (see X509_STORE_set_flags(X509_V_FLAG_CRL_CHECK)).
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<X509InitSingleton>;
+ X509InitSingleton() {
+ crypto::EnsureOpenSSLInit();
+ der_cache_ex_index_ = X509_get_ex_new_index(0, 0, 0, 0, DERCache_free);
+ DCHECK_NE(der_cache_ex_index_, -1);
+ ResetCertStore();
+ }
+
+ int der_cache_ex_index_;
+ crypto::ScopedOpenSSL<X509_STORE, X509_STORE_free> store_;
+
+ DISALLOW_COPY_AND_ASSIGN(X509InitSingleton);
+};
+
+// Takes ownership of |data| (which must have been allocated by OpenSSL).
+DERCache* SetDERCache(X509Certificate::OSCertHandle cert,
+ int x509_der_cache_index,
+ unsigned char* data,
+ int data_length) {
+ DERCache* internal_cache = static_cast<DERCache*>(
+ OPENSSL_malloc(sizeof(*internal_cache)));
+ if (!internal_cache) {
+ // We took ownership of |data|, so we must free if we can't add it to
+ // |cert|.
+ OPENSSL_free(data);
+ return NULL;
+ }
+
+ internal_cache->data = data;
+ internal_cache->data_length = data_length;
+ X509_set_ex_data(cert, x509_der_cache_index, internal_cache);
+ return internal_cache;
+}
+
+// Returns true if |der_cache| points to valid data, false otherwise.
+// (note: the DER-encoded data in |der_cache| is owned by |cert|, callers should
+// not free it).
+bool GetDERAndCacheIfNeeded(X509Certificate::OSCertHandle cert,
+ DERCache* der_cache) {
+ int x509_der_cache_index =
+ X509InitSingleton::GetInstance()->der_cache_ex_index();
+
+ // Re-encoding the DER data via i2d_X509 is an expensive operation, but it's
+ // necessary for comparing two certificates. We re-encode at most once per
+ // certificate and cache the data within the X509 cert using X509_set_ex_data.
+ DERCache* internal_cache = static_cast<DERCache*>(
+ X509_get_ex_data(cert, x509_der_cache_index));
+ if (!internal_cache) {
+ unsigned char* data = NULL;
+ int data_length = i2d_X509(cert, &data);
+ if (data_length <= 0 || !data)
+ return false;
+ internal_cache = SetDERCache(cert, x509_der_cache_index, data, data_length);
+ if (!internal_cache)
+ return false;
+ }
+ *der_cache = *internal_cache;
+ return true;
+}
+
+// Used to free a list of X509_NAMEs and the objects it points to.
+void sk_X509_NAME_free_all(STACK_OF(X509_NAME)* sk) {
+ sk_X509_NAME_pop_free(sk, X509_NAME_free);
+}
+
+} // namespace
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle cert_handle) {
+ DCHECK(cert_handle);
+ // Using X509_dup causes the entire certificate to be reparsed. This
+ // conversion, besides being non-trivial, drops any associated
+ // application-specific data set by X509_set_ex_data. Using CRYPTO_add
+ // just bumps up the ref-count for the cert, without causing any allocations
+ // or deallocations.
+ CRYPTO_add(&cert_handle->references, 1, CRYPTO_LOCK_X509);
+ return cert_handle;
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ // Decrement the ref-count for the cert and, if all references are gone,
+ // free the memory and any application-specific data associated with the
+ // certificate.
+ X509_free(cert_handle);
+}
+
+void X509Certificate::Initialize() {
+ crypto::EnsureOpenSSLInit();
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+
+ ASN1_INTEGER* serial_num = X509_get_serialNumber(cert_handle_);
+ if (serial_num) {
+ // ASN1_INTEGERS represent the decoded number, in a format internal to
+ // OpenSSL. Most notably, this may have leading zeroes stripped off for
+ // numbers whose first byte is >= 0x80. Thus, it is necessary to
+ // re-encoded the integer back into DER, which is what the interface
+ // of X509Certificate exposes, to ensure callers get the proper (DER)
+ // value.
+ int bytes_required = i2c_ASN1_INTEGER(serial_num, NULL);
+ unsigned char* buffer = reinterpret_cast<unsigned char*>(
+ WriteInto(&serial_number_, bytes_required + 1));
+ int bytes_written = i2c_ASN1_INTEGER(serial_num, &buffer);
+ DCHECK_EQ(static_cast<size_t>(bytes_written), serial_number_.size());
+ }
+
+ ParsePrincipal(cert_handle_, X509_get_subject_name(cert_handle_), &subject_);
+ ParsePrincipal(cert_handle_, X509_get_issuer_name(cert_handle_), &issuer_);
+ x509_util::ParseDate(X509_get_notBefore(cert_handle_), &valid_start_);
+ x509_util::ParseDate(X509_get_notAfter(cert_handle_), &valid_expiry_);
+}
+
+// static
+void X509Certificate::ResetCertStore() {
+ X509InitSingleton::GetInstance()->ResetCertStore();
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(OSCertHandle cert) {
+ SHA1HashValue sha1;
+ unsigned int sha1_size = static_cast<unsigned int>(sizeof(sha1.data));
+ int ret = X509_digest(cert, EVP_sha1(), sha1.data, &sha1_size);
+ CHECK(ret);
+ CHECK_EQ(sha1_size, sizeof(sha1.data));
+ return sha1;
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ SHA_CTX sha1_ctx;
+ SHA1_Init(&sha1_ctx);
+ DERCache der_cache;
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ if (!GetDERAndCacheIfNeeded(intermediates[i], &der_cache))
+ return sha1;
+ SHA1_Update(&sha1_ctx, der_cache.data, der_cache.data_length);
+ }
+ SHA1_Final(sha1.data, &sha1_ctx);
+
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ if (length < 0)
+ return NULL;
+ crypto::EnsureOpenSSLInit();
+ const unsigned char* d2i_data =
+ reinterpret_cast<const unsigned char*>(data);
+ // Don't cache this data via SetDERCache as this wire format may be not be
+ // identical from the i2d_X509 roundtrip.
+ X509* cert = d2i_X509(NULL, &d2i_data, length);
+ return cert;
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data, int length, Format format) {
+ OSCertHandles results;
+ if (length < 0)
+ return results;
+
+ switch (format) {
+ case FORMAT_SINGLE_CERTIFICATE: {
+ OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
+ if (handle)
+ results.push_back(handle);
+ break;
+ }
+ case FORMAT_PKCS7: {
+ CreateOSCertHandlesFromPKCS7Bytes(data, length, &results);
+ break;
+ }
+ default: {
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+ }
+
+ return results;
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ ParseSubjectAltName(cert_handle_, dns_names, ip_addrs);
+}
+
+// static
+X509_STORE* X509Certificate::cert_store() {
+ return X509InitSingleton::GetInstance()->store();
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+ std::string* encoded) {
+ DERCache der_cache;
+ if (!GetDERAndCacheIfNeeded(cert_handle, &der_cache))
+ return false;
+ encoded->assign(reinterpret_cast<const char*>(der_cache.data),
+ der_cache.data_length);
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+
+ // X509_cmp only checks the fingerprint, but we want to compare the whole
+ // DER data. Encoding it from OSCertHandle is an expensive operation, so we
+ // cache the DER (if not already cached via X509_set_ex_data).
+ DERCache der_cache_a, der_cache_b;
+
+ return GetDERAndCacheIfNeeded(a, &der_cache_a) &&
+ GetDERAndCacheIfNeeded(b, &der_cache_b) &&
+ der_cache_a.data_length == der_cache_b.data_length &&
+ memcmp(der_cache_a.data, der_cache_b.data, der_cache_a.data_length) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return NULL;
+
+ return CreateOSCertHandleFromBytes(data, length);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ DERCache der_cache;
+ if (!GetDERAndCacheIfNeeded(cert_handle, &der_cache))
+ return false;
+
+ return pickle->WriteData(
+ reinterpret_cast<const char*>(der_cache.data),
+ der_cache.data_length);
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ *type = kPublicKeyTypeUnknown;
+ *size_bits = 0;
+
+ crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> scoped_key(
+ X509_get_pubkey(cert_handle));
+ if (!scoped_key.get())
+ return;
+
+ CHECK(scoped_key.get());
+ EVP_PKEY* key = scoped_key.get();
+
+ switch (key->type) {
+ case EVP_PKEY_RSA:
+ *type = kPublicKeyTypeRSA;
+ *size_bits = EVP_PKEY_size(key) * 8;
+ break;
+ case EVP_PKEY_DSA:
+ *type = kPublicKeyTypeDSA;
+ *size_bits = EVP_PKEY_size(key) * 8;
+ break;
+ case EVP_PKEY_EC:
+ *type = kPublicKeyTypeECDSA;
+ *size_bits = EVP_PKEY_size(key);
+ break;
+ case EVP_PKEY_DH:
+ *type = kPublicKeyTypeDH;
+ *size_bits = EVP_PKEY_size(key) * 8;
+ break;
+ }
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+ if (valid_issuers.empty())
+ return false;
+
+ // Convert to a temporary list of X509_NAME objects.
+ // It will own the objects it points to.
+ crypto::ScopedOpenSSL<STACK_OF(X509_NAME), sk_X509_NAME_free_all>
+ issuer_names(sk_X509_NAME_new_null());
+ if (!issuer_names.get())
+ return false;
+
+ for (std::vector<std::string>::const_iterator it = valid_issuers.begin();
+ it != valid_issuers.end(); ++it) {
+ const unsigned char* p =
+ reinterpret_cast<const unsigned char*>(it->data());
+ long len = static_cast<long>(it->length());
+ X509_NAME* ca_name = d2i_X509_NAME(NULL, &p, len);
+ if (ca_name == NULL)
+ return false;
+ sk_X509_NAME_push(issuer_names.get(), ca_name);
+ }
+
+ // Create a temporary list of X509_NAME objects corresponding
+ // to the certificate chain. It doesn't own the object it points to.
+ std::vector<X509_NAME*> cert_names;
+ X509_NAME* issuer = X509_get_issuer_name(cert_handle_);
+ if (issuer == NULL)
+ return false;
+
+ cert_names.push_back(issuer);
+ for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
+ it != intermediate_ca_certs_.end(); ++it) {
+ issuer = X509_get_issuer_name(*it);
+ if (issuer == NULL)
+ return false;
+ cert_names.push_back(issuer);
+ }
+
+ // and 'cert_names'.
+ for (size_t n = 0; n < cert_names.size(); ++n) {
+ for (int m = 0; m < sk_X509_NAME_num(issuer_names.get()); ++m) {
+ X509_NAME* issuer = sk_X509_NAME_value(issuer_names.get(), m);
+ if (X509_NAME_cmp(issuer, cert_names[n]) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate_unittest.cc b/chromium/net/cert/x509_certificate_unittest.cc
new file mode 100644
index 00000000000..75ba827a18a
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_unittest.cc
@@ -0,0 +1,1147 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/asn1_util.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_certificate_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_NSS)
+#include <cert.h>
+#endif
+
+using base::HexEncode;
+using base::Time;
+
+namespace net {
+
+// Certificates for test data. They're obtained with:
+//
+// $ openssl s_client -connect [host]:443 -showcerts > /tmp/host.pem < /dev/null
+// $ openssl x509 -inform PEM -outform DER < /tmp/host.pem > /tmp/host.der
+//
+// For fingerprint
+// $ openssl x509 -inform DER -fingerprint -noout < /tmp/host.der
+
+// For valid_start, valid_expiry
+// $ openssl x509 -inform DER -text -noout < /tmp/host.der |
+// grep -A 2 Validity
+// $ date +%s -d '<date str>'
+
+// Google's cert.
+uint8 google_fingerprint[] = {
+ 0xab, 0xbe, 0x5e, 0xb4, 0x93, 0x88, 0x4e, 0xe4, 0x60, 0xc6, 0xef, 0xf8,
+ 0xea, 0xd4, 0xb1, 0x55, 0x4b, 0xc9, 0x59, 0x3c
+};
+
+// webkit.org's cert.
+uint8 webkit_fingerprint[] = {
+ 0xa1, 0x4a, 0x94, 0x46, 0x22, 0x8e, 0x70, 0x66, 0x2b, 0x94, 0xf9, 0xf8,
+ 0x57, 0x83, 0x2d, 0xa2, 0xff, 0xbc, 0x84, 0xc2
+};
+
+// thawte.com's cert (it's EV-licious!).
+uint8 thawte_fingerprint[] = {
+ 0x85, 0x04, 0x2d, 0xfd, 0x2b, 0x0e, 0xc6, 0xc8, 0xaf, 0x2d, 0x77, 0xd6,
+ 0xa1, 0x3a, 0x64, 0x04, 0x27, 0x90, 0x97, 0x37
+};
+
+// A certificate for https://www.unosoft.hu/, whose AIA extension contains
+// an LDAP URL without a host name.
+uint8 unosoft_hu_fingerprint[] = {
+ 0x32, 0xff, 0xe3, 0xbe, 0x2c, 0x3b, 0xc7, 0xca, 0xbf, 0x2d, 0x64, 0xbd,
+ 0x25, 0x66, 0xf2, 0xec, 0x8b, 0x0f, 0xbf, 0xd8
+};
+
+// The fingerprint of the Google certificate used in the parsing tests,
+// which is newer than the one included in the x509_certificate_data.h
+uint8 google_parse_fingerprint[] = {
+ 0x40, 0x50, 0x62, 0xe5, 0xbe, 0xfd, 0xe4, 0xaf, 0x97, 0xe9, 0x38, 0x2a,
+ 0xf1, 0x6c, 0xc8, 0x7c, 0x8f, 0xb7, 0xc4, 0xe2
+};
+
+// The fingerprint for the Thawte SGC certificate
+uint8 thawte_parse_fingerprint[] = {
+ 0xec, 0x07, 0x10, 0x03, 0xd8, 0xf5, 0xa3, 0x7f, 0x42, 0xc4, 0x55, 0x7f,
+ 0x65, 0x6a, 0xae, 0x86, 0x65, 0xfa, 0x4b, 0x02
+};
+
+// Dec 18 00:00:00 2009 GMT
+const double kGoogleParseValidFrom = 1261094400;
+// Dec 18 23:59:59 2011 GMT
+const double kGoogleParseValidTo = 1324252799;
+
+struct CertificateFormatTestData {
+ const char* file_name;
+ X509Certificate::Format format;
+ uint8* chain_fingerprints[3];
+};
+
+const CertificateFormatTestData FormatTestData[] = {
+ // DER Parsing - single certificate, DER encoded
+ { "google.single.der", X509Certificate::FORMAT_SINGLE_CERTIFICATE,
+ { google_parse_fingerprint,
+ NULL, } },
+ // DER parsing - single certificate, PEM encoded
+ { "google.single.pem", X509Certificate::FORMAT_SINGLE_CERTIFICATE,
+ { google_parse_fingerprint,
+ NULL, } },
+ // PEM parsing - single certificate, PEM encoded with a PEB of
+ // "CERTIFICATE"
+ { "google.single.pem", X509Certificate::FORMAT_PEM_CERT_SEQUENCE,
+ { google_parse_fingerprint,
+ NULL, } },
+ // PEM parsing - sequence of certificates, PEM encoded with a PEB of
+ // "CERTIFICATE"
+ { "google.chain.pem", X509Certificate::FORMAT_PEM_CERT_SEQUENCE,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ // PKCS#7 parsing - "degenerate" SignedData collection of certificates, DER
+ // encoding
+ { "google.binary.p7b", X509Certificate::FORMAT_PKCS7,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ // PKCS#7 parsing - "degenerate" SignedData collection of certificates, PEM
+ // encoded with a PEM PEB of "CERTIFICATE"
+ { "google.pem_cert.p7b", X509Certificate::FORMAT_PKCS7,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ // PKCS#7 parsing - "degenerate" SignedData collection of certificates, PEM
+ // encoded with a PEM PEB of "PKCS7"
+ { "google.pem_pkcs7.p7b", X509Certificate::FORMAT_PKCS7,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ // All of the above, this time using auto-detection
+ { "google.single.der", X509Certificate::FORMAT_AUTO,
+ { google_parse_fingerprint,
+ NULL, } },
+ { "google.single.pem", X509Certificate::FORMAT_AUTO,
+ { google_parse_fingerprint,
+ NULL, } },
+ { "google.chain.pem", X509Certificate::FORMAT_AUTO,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ { "google.binary.p7b", X509Certificate::FORMAT_AUTO,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ { "google.pem_cert.p7b", X509Certificate::FORMAT_AUTO,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+ { "google.pem_pkcs7.p7b", X509Certificate::FORMAT_AUTO,
+ { google_parse_fingerprint,
+ thawte_parse_fingerprint,
+ NULL, } },
+};
+
+void CheckGoogleCert(const scoped_refptr<X509Certificate>& google_cert,
+ uint8* expected_fingerprint,
+ double valid_from, double valid_to) {
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
+
+ const CertPrincipal& subject = google_cert->subject();
+ EXPECT_EQ("www.google.com", subject.common_name);
+ EXPECT_EQ("Mountain View", subject.locality_name);
+ EXPECT_EQ("California", subject.state_or_province_name);
+ EXPECT_EQ("US", subject.country_name);
+ EXPECT_EQ(0U, subject.street_addresses.size());
+ ASSERT_EQ(1U, subject.organization_names.size());
+ EXPECT_EQ("Google Inc", subject.organization_names[0]);
+ EXPECT_EQ(0U, subject.organization_unit_names.size());
+ EXPECT_EQ(0U, subject.domain_components.size());
+
+ const CertPrincipal& issuer = google_cert->issuer();
+ EXPECT_EQ("Thawte SGC CA", issuer.common_name);
+ EXPECT_EQ("", issuer.locality_name);
+ EXPECT_EQ("", issuer.state_or_province_name);
+ EXPECT_EQ("ZA", issuer.country_name);
+ EXPECT_EQ(0U, issuer.street_addresses.size());
+ ASSERT_EQ(1U, issuer.organization_names.size());
+ EXPECT_EQ("Thawte Consulting (Pty) Ltd.", issuer.organization_names[0]);
+ EXPECT_EQ(0U, issuer.organization_unit_names.size());
+ EXPECT_EQ(0U, issuer.domain_components.size());
+
+ // Use DoubleT because its epoch is the same on all platforms
+ const Time& valid_start = google_cert->valid_start();
+ EXPECT_EQ(valid_from, valid_start.ToDoubleT());
+
+ const Time& valid_expiry = google_cert->valid_expiry();
+ EXPECT_EQ(valid_to, valid_expiry.ToDoubleT());
+
+ const SHA1HashValue& fingerprint = google_cert->fingerprint();
+ for (size_t i = 0; i < 20; ++i)
+ EXPECT_EQ(expected_fingerprint[i], fingerprint.data[i]);
+
+ std::vector<std::string> dns_names;
+ google_cert->GetDNSNames(&dns_names);
+ ASSERT_EQ(1U, dns_names.size());
+ EXPECT_EQ("www.google.com", dns_names[0]);
+}
+
+TEST(X509CertificateTest, GoogleCertParsing) {
+ scoped_refptr<X509Certificate> google_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der)));
+
+ CheckGoogleCert(google_cert, google_fingerprint,
+ 1238192407, // Mar 27 22:20:07 2009 GMT
+ 1269728407); // Mar 27 22:20:07 2010 GMT
+}
+
+TEST(X509CertificateTest, WebkitCertParsing) {
+ scoped_refptr<X509Certificate> webkit_cert(X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der)));
+
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), webkit_cert);
+
+ const CertPrincipal& subject = webkit_cert->subject();
+ EXPECT_EQ("Cupertino", subject.locality_name);
+ EXPECT_EQ("California", subject.state_or_province_name);
+ EXPECT_EQ("US", subject.country_name);
+ EXPECT_EQ(0U, subject.street_addresses.size());
+ ASSERT_EQ(1U, subject.organization_names.size());
+ EXPECT_EQ("Apple Inc.", subject.organization_names[0]);
+ ASSERT_EQ(1U, subject.organization_unit_names.size());
+ EXPECT_EQ("Mac OS Forge", subject.organization_unit_names[0]);
+ EXPECT_EQ(0U, subject.domain_components.size());
+
+ const CertPrincipal& issuer = webkit_cert->issuer();
+ EXPECT_EQ("Go Daddy Secure Certification Authority", issuer.common_name);
+ EXPECT_EQ("Scottsdale", issuer.locality_name);
+ EXPECT_EQ("Arizona", issuer.state_or_province_name);
+ EXPECT_EQ("US", issuer.country_name);
+ EXPECT_EQ(0U, issuer.street_addresses.size());
+ ASSERT_EQ(1U, issuer.organization_names.size());
+ EXPECT_EQ("GoDaddy.com, Inc.", issuer.organization_names[0]);
+ ASSERT_EQ(1U, issuer.organization_unit_names.size());
+ EXPECT_EQ("http://certificates.godaddy.com/repository",
+ issuer.organization_unit_names[0]);
+ EXPECT_EQ(0U, issuer.domain_components.size());
+
+ // Use DoubleT because its epoch is the same on all platforms
+ const Time& valid_start = webkit_cert->valid_start();
+ EXPECT_EQ(1205883319, valid_start.ToDoubleT()); // Mar 18 23:35:19 2008 GMT
+
+ const Time& valid_expiry = webkit_cert->valid_expiry();
+ EXPECT_EQ(1300491319, valid_expiry.ToDoubleT()); // Mar 18 23:35:19 2011 GMT
+
+ const SHA1HashValue& fingerprint = webkit_cert->fingerprint();
+ for (size_t i = 0; i < 20; ++i)
+ EXPECT_EQ(webkit_fingerprint[i], fingerprint.data[i]);
+
+ std::vector<std::string> dns_names;
+ webkit_cert->GetDNSNames(&dns_names);
+ ASSERT_EQ(2U, dns_names.size());
+ EXPECT_EQ("*.webkit.org", dns_names[0]);
+ EXPECT_EQ("webkit.org", dns_names[1]);
+
+ // Test that the wildcard cert matches properly.
+ EXPECT_TRUE(webkit_cert->VerifyNameMatch("www.webkit.org"));
+ EXPECT_TRUE(webkit_cert->VerifyNameMatch("foo.webkit.org"));
+ EXPECT_TRUE(webkit_cert->VerifyNameMatch("webkit.org"));
+ EXPECT_FALSE(webkit_cert->VerifyNameMatch("www.webkit.com"));
+ EXPECT_FALSE(webkit_cert->VerifyNameMatch("www.foo.webkit.com"));
+}
+
+TEST(X509CertificateTest, ThawteCertParsing) {
+ scoped_refptr<X509Certificate> thawte_cert(X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der)));
+
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), thawte_cert);
+
+ const CertPrincipal& subject = thawte_cert->subject();
+ EXPECT_EQ("www.thawte.com", subject.common_name);
+ EXPECT_EQ("Mountain View", subject.locality_name);
+ EXPECT_EQ("California", subject.state_or_province_name);
+ EXPECT_EQ("US", subject.country_name);
+ EXPECT_EQ(0U, subject.street_addresses.size());
+ ASSERT_EQ(1U, subject.organization_names.size());
+ EXPECT_EQ("Thawte Inc", subject.organization_names[0]);
+ EXPECT_EQ(0U, subject.organization_unit_names.size());
+ EXPECT_EQ(0U, subject.domain_components.size());
+
+ const CertPrincipal& issuer = thawte_cert->issuer();
+ EXPECT_EQ("thawte Extended Validation SSL CA", issuer.common_name);
+ EXPECT_EQ("", issuer.locality_name);
+ EXPECT_EQ("", issuer.state_or_province_name);
+ EXPECT_EQ("US", issuer.country_name);
+ EXPECT_EQ(0U, issuer.street_addresses.size());
+ ASSERT_EQ(1U, issuer.organization_names.size());
+ EXPECT_EQ("thawte, Inc.", issuer.organization_names[0]);
+ ASSERT_EQ(1U, issuer.organization_unit_names.size());
+ EXPECT_EQ("Terms of use at https://www.thawte.com/cps (c)06",
+ issuer.organization_unit_names[0]);
+ EXPECT_EQ(0U, issuer.domain_components.size());
+
+ // Use DoubleT because its epoch is the same on all platforms
+ const Time& valid_start = thawte_cert->valid_start();
+ EXPECT_EQ(1227052800, valid_start.ToDoubleT()); // Nov 19 00:00:00 2008 GMT
+
+ const Time& valid_expiry = thawte_cert->valid_expiry();
+ EXPECT_EQ(1263772799, valid_expiry.ToDoubleT()); // Jan 17 23:59:59 2010 GMT
+
+ const SHA1HashValue& fingerprint = thawte_cert->fingerprint();
+ for (size_t i = 0; i < 20; ++i)
+ EXPECT_EQ(thawte_fingerprint[i], fingerprint.data[i]);
+
+ std::vector<std::string> dns_names;
+ thawte_cert->GetDNSNames(&dns_names);
+ ASSERT_EQ(1U, dns_names.size());
+ EXPECT_EQ("www.thawte.com", dns_names[0]);
+}
+
+// Test that all desired AttributeAndValue pairs can be extracted when only
+// a single RelativeDistinguishedName is present. "Normally" there is only
+// one AVA per RDN, but some CAs place all AVAs within a single RDN.
+// This is a regression test for http://crbug.com/101009
+TEST(X509CertificateTest, MultivalueRDN) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> multivalue_rdn_cert =
+ ImportCertFromFile(certs_dir, "multivalue_rdn.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), multivalue_rdn_cert);
+
+ const CertPrincipal& subject = multivalue_rdn_cert->subject();
+ EXPECT_EQ("Multivalue RDN Test", subject.common_name);
+ EXPECT_EQ("", subject.locality_name);
+ EXPECT_EQ("", subject.state_or_province_name);
+ EXPECT_EQ("US", subject.country_name);
+ EXPECT_EQ(0U, subject.street_addresses.size());
+ ASSERT_EQ(1U, subject.organization_names.size());
+ EXPECT_EQ("Chromium", subject.organization_names[0]);
+ ASSERT_EQ(1U, subject.organization_unit_names.size());
+ EXPECT_EQ("Chromium net_unittests", subject.organization_unit_names[0]);
+ ASSERT_EQ(1U, subject.domain_components.size());
+ EXPECT_EQ("Chromium", subject.domain_components[0]);
+}
+
+// Test that characters which would normally be escaped in the string form,
+// such as '=' or '"', are not escaped when parsed as individual components.
+// This is a regression test for http://crbug.com/102839
+TEST(X509CertificateTest, UnescapedSpecialCharacters) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> unescaped_cert =
+ ImportCertFromFile(certs_dir, "unescaped.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), unescaped_cert);
+
+ const CertPrincipal& subject = unescaped_cert->subject();
+ EXPECT_EQ("127.0.0.1", subject.common_name);
+ EXPECT_EQ("Mountain View", subject.locality_name);
+ EXPECT_EQ("California", subject.state_or_province_name);
+ EXPECT_EQ("US", subject.country_name);
+ ASSERT_EQ(1U, subject.street_addresses.size());
+ EXPECT_EQ("1600 Amphitheatre Parkway", subject.street_addresses[0]);
+ ASSERT_EQ(1U, subject.organization_names.size());
+ EXPECT_EQ("Chromium = \"net_unittests\"", subject.organization_names[0]);
+ ASSERT_EQ(2U, subject.organization_unit_names.size());
+ EXPECT_EQ("net_unittests", subject.organization_unit_names[0]);
+ EXPECT_EQ("Chromium", subject.organization_unit_names[1]);
+ EXPECT_EQ(0U, subject.domain_components.size());
+}
+
+TEST(X509CertificateTest, SerialNumbers) {
+ scoped_refptr<X509Certificate> google_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der)));
+
+ static const uint8 google_serial[16] = {
+ 0x01,0x2a,0x39,0x76,0x0d,0x3f,0x4f,0xc9,
+ 0x0b,0xe7,0xbd,0x2b,0xcf,0x95,0x2e,0x7a,
+ };
+
+ ASSERT_EQ(sizeof(google_serial), google_cert->serial_number().size());
+ EXPECT_TRUE(memcmp(google_cert->serial_number().data(), google_serial,
+ sizeof(google_serial)) == 0);
+
+ // We also want to check a serial number where the first byte is >= 0x80 in
+ // case the underlying library tries to pad it.
+ scoped_refptr<X509Certificate> paypal_null_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(paypal_null_der),
+ sizeof(paypal_null_der)));
+
+ static const uint8 paypal_null_serial[3] = {0x00, 0xf0, 0x9b};
+ ASSERT_EQ(sizeof(paypal_null_serial),
+ paypal_null_cert->serial_number().size());
+ EXPECT_TRUE(memcmp(paypal_null_cert->serial_number().data(),
+ paypal_null_serial, sizeof(paypal_null_serial)) == 0);
+}
+
+TEST(X509CertificateTest, CAFingerprints) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> server_cert =
+ ImportCertFromFile(certs_dir, "salesforce_com_test.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
+
+ scoped_refptr<X509Certificate> intermediate_cert1 =
+ ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2011.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert1);
+
+ scoped_refptr<X509Certificate> intermediate_cert2 =
+ ImportCertFromFile(certs_dir, "verisign_intermediate_ca_2016.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert2);
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(intermediate_cert1->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain1 =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ intermediates.clear();
+ intermediates.push_back(intermediate_cert2->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain2 =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ // No intermediate CA certicates.
+ intermediates.clear();
+ scoped_refptr<X509Certificate> cert_chain3 =
+ X509Certificate::CreateFromHandle(server_cert->os_cert_handle(),
+ intermediates);
+
+ static const uint8 cert_chain1_ca_fingerprint[20] = {
+ 0xc2, 0xf0, 0x08, 0x7d, 0x01, 0xe6, 0x86, 0x05, 0x3a, 0x4d,
+ 0x63, 0x3e, 0x7e, 0x70, 0xd4, 0xef, 0x65, 0xc2, 0xcc, 0x4f
+ };
+ static const uint8 cert_chain2_ca_fingerprint[20] = {
+ 0xd5, 0x59, 0xa5, 0x86, 0x66, 0x9b, 0x08, 0xf4, 0x6a, 0x30,
+ 0xa1, 0x33, 0xf8, 0xa9, 0xed, 0x3d, 0x03, 0x8e, 0x2e, 0xa8
+ };
+ // The SHA-1 hash of nothing.
+ static const uint8 cert_chain3_ca_fingerprint[20] = {
+ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
+ 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
+ };
+ EXPECT_TRUE(memcmp(cert_chain1->ca_fingerprint().data,
+ cert_chain1_ca_fingerprint, 20) == 0);
+ EXPECT_TRUE(memcmp(cert_chain2->ca_fingerprint().data,
+ cert_chain2_ca_fingerprint, 20) == 0);
+ EXPECT_TRUE(memcmp(cert_chain3->ca_fingerprint().data,
+ cert_chain3_ca_fingerprint, 20) == 0);
+}
+
+TEST(X509CertificateTest, ParseSubjectAltNames) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> san_cert =
+ ImportCertFromFile(certs_dir, "subjectAltName_sanity_check.pem");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), san_cert);
+
+ std::vector<std::string> dns_names;
+ std::vector<std::string> ip_addresses;
+ san_cert->GetSubjectAltName(&dns_names, &ip_addresses);
+
+ // Ensure that DNS names are correctly parsed.
+ ASSERT_EQ(1U, dns_names.size());
+ EXPECT_EQ("test.example", dns_names[0]);
+
+ // Ensure that both IPv4 and IPv6 addresses are correctly parsed.
+ ASSERT_EQ(2U, ip_addresses.size());
+
+ static const uint8 kIPv4Address[] = {
+ 0x7F, 0x00, 0x00, 0x02
+ };
+ ASSERT_EQ(arraysize(kIPv4Address), ip_addresses[0].size());
+ EXPECT_EQ(0, memcmp(ip_addresses[0].data(), kIPv4Address,
+ arraysize(kIPv4Address)));
+
+ static const uint8 kIPv6Address[] = {
+ 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+ ASSERT_EQ(arraysize(kIPv6Address), ip_addresses[1].size());
+ EXPECT_EQ(0, memcmp(ip_addresses[1].data(), kIPv6Address,
+ arraysize(kIPv6Address)));
+
+ // Ensure the subjectAltName dirName has not influenced the handling of
+ // the subject commonName.
+ EXPECT_EQ("127.0.0.1", san_cert->subject().common_name);
+}
+
+TEST(X509CertificateTest, ExtractSPKIFromDERCert) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> cert =
+ ImportCertFromFile(certs_dir, "nist.der");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), cert);
+
+ std::string derBytes;
+ EXPECT_TRUE(X509Certificate::GetDEREncoded(cert->os_cert_handle(),
+ &derBytes));
+
+ base::StringPiece spkiBytes;
+ EXPECT_TRUE(asn1::ExtractSPKIFromDERCert(derBytes, &spkiBytes));
+
+ uint8 hash[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<const uint8*>(spkiBytes.data()),
+ spkiBytes.size(), hash);
+
+ EXPECT_EQ(0, memcmp(hash, kNistSPKIHash, sizeof(hash)));
+}
+
+TEST(X509CertificateTest, ExtractCRLURLsFromDERCert) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> cert =
+ ImportCertFromFile(certs_dir, "nist.der");
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), cert);
+
+ std::string derBytes;
+ EXPECT_TRUE(X509Certificate::GetDEREncoded(cert->os_cert_handle(),
+ &derBytes));
+
+ std::vector<base::StringPiece> crl_urls;
+ EXPECT_TRUE(asn1::ExtractCRLURLsFromDERCert(derBytes, &crl_urls));
+
+ EXPECT_EQ(1u, crl_urls.size());
+ if (crl_urls.size() > 0) {
+ EXPECT_EQ("http://SVRSecure-G3-crl.verisign.com/SVRSecureG3.crl",
+ crl_urls[0].as_string());
+ }
+}
+
+// Tests X509CertificateCache via X509Certificate::CreateFromHandle. We
+// call X509Certificate::CreateFromHandle several times and observe whether
+// it returns a cached or new OSCertHandle.
+TEST(X509CertificateTest, Cache) {
+ X509Certificate::OSCertHandle google_cert_handle;
+ X509Certificate::OSCertHandle thawte_cert_handle;
+
+ // Add a single certificate to the certificate cache.
+ google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der));
+ scoped_refptr<X509Certificate> cert1(X509Certificate::CreateFromHandle(
+ google_cert_handle, X509Certificate::OSCertHandles()));
+ X509Certificate::FreeOSCertHandle(google_cert_handle);
+
+ // Add the same certificate, but as a new handle.
+ google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der));
+ scoped_refptr<X509Certificate> cert2(X509Certificate::CreateFromHandle(
+ google_cert_handle, X509Certificate::OSCertHandles()));
+ X509Certificate::FreeOSCertHandle(google_cert_handle);
+
+ // A new X509Certificate should be returned.
+ EXPECT_NE(cert1.get(), cert2.get());
+ // But both instances should share the underlying OS certificate handle.
+ EXPECT_EQ(cert1->os_cert_handle(), cert2->os_cert_handle());
+ EXPECT_EQ(0u, cert1->GetIntermediateCertificates().size());
+ EXPECT_EQ(0u, cert2->GetIntermediateCertificates().size());
+
+ // Add the same certificate, but this time with an intermediate. This
+ // should result in the intermediate being cached. Note that this is not
+ // a legitimate chain, but is suitable for testing.
+ google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der));
+ thawte_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(thawte_cert_handle);
+ scoped_refptr<X509Certificate> cert3(X509Certificate::CreateFromHandle(
+ google_cert_handle, intermediates));
+ X509Certificate::FreeOSCertHandle(google_cert_handle);
+ X509Certificate::FreeOSCertHandle(thawte_cert_handle);
+
+ // Test that the new certificate, even with intermediates, results in the
+ // same underlying handle being used.
+ EXPECT_EQ(cert1->os_cert_handle(), cert3->os_cert_handle());
+ // Though they use the same OS handle, the intermediates should be different.
+ EXPECT_NE(cert1->GetIntermediateCertificates().size(),
+ cert3->GetIntermediateCertificates().size());
+}
+
+TEST(X509CertificateTest, Pickle) {
+ X509Certificate::OSCertHandle google_cert_handle =
+ X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der));
+ X509Certificate::OSCertHandle thawte_cert_handle =
+ X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(thawte_cert_handle);
+ scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+ google_cert_handle, intermediates);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), cert.get());
+
+ X509Certificate::FreeOSCertHandle(google_cert_handle);
+ X509Certificate::FreeOSCertHandle(thawte_cert_handle);
+
+ Pickle pickle;
+ cert->Persist(&pickle);
+
+ PickleIterator iter(pickle);
+ scoped_refptr<X509Certificate> cert_from_pickle =
+ X509Certificate::CreateFromPickle(
+ pickle, &iter, X509Certificate::PICKLETYPE_CERTIFICATE_CHAIN_V3);
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), cert_from_pickle);
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(
+ cert->os_cert_handle(), cert_from_pickle->os_cert_handle()));
+ const X509Certificate::OSCertHandles& cert_intermediates =
+ cert->GetIntermediateCertificates();
+ const X509Certificate::OSCertHandles& pickle_intermediates =
+ cert_from_pickle->GetIntermediateCertificates();
+ ASSERT_EQ(cert_intermediates.size(), pickle_intermediates.size());
+ for (size_t i = 0; i < cert_intermediates.size(); ++i) {
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(cert_intermediates[i],
+ pickle_intermediates[i]));
+ }
+}
+
+TEST(X509CertificateTest, Policy) {
+ scoped_refptr<X509Certificate> google_cert(X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der)));
+
+ scoped_refptr<X509Certificate> webkit_cert(X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der)));
+
+ CertPolicy policy;
+
+ // To begin with, everything should be unknown.
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_FALSE(policy.HasAllowedCert());
+ EXPECT_FALSE(policy.HasDeniedCert());
+
+ // Test adding one certificate with one error.
+ policy.Allow(google_cert.get(), CERT_STATUS_DATE_INVALID);
+ EXPECT_EQ(CertPolicy::ALLOWED,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(google_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(google_cert.get(),
+ CERT_STATUS_DATE_INVALID | CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_TRUE(policy.HasAllowedCert());
+ EXPECT_FALSE(policy.HasDeniedCert());
+
+ // Test saving the same certificate with a new error.
+ policy.Allow(google_cert.get(), CERT_STATUS_AUTHORITY_INVALID);
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::ALLOWED,
+ policy.Check(google_cert.get(), CERT_STATUS_AUTHORITY_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_TRUE(policy.HasAllowedCert());
+ EXPECT_FALSE(policy.HasDeniedCert());
+
+ // Test adding one certificate with two errors.
+ policy.Allow(google_cert.get(),
+ CERT_STATUS_DATE_INVALID | CERT_STATUS_AUTHORITY_INVALID);
+ EXPECT_EQ(CertPolicy::ALLOWED,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::ALLOWED,
+ policy.Check(google_cert.get(), CERT_STATUS_AUTHORITY_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(google_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_TRUE(policy.HasAllowedCert());
+ EXPECT_FALSE(policy.HasDeniedCert());
+
+ // Test removing a certificate that was previously allowed.
+ policy.Deny(google_cert.get(), CERT_STATUS_DATE_INVALID);
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_FALSE(policy.HasAllowedCert());
+ EXPECT_TRUE(policy.HasDeniedCert());
+
+ // Test removing a certificate that was previously unknown.
+ policy.Deny(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID);
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_FALSE(policy.HasAllowedCert());
+ EXPECT_TRUE(policy.HasDeniedCert());
+
+ // Test saving a certificate that was previously denied.
+ policy.Allow(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID);
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::ALLOWED,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_TRUE(policy.HasAllowedCert());
+ EXPECT_TRUE(policy.HasDeniedCert());
+
+ // Test denying an overlapping certificate.
+ policy.Allow(google_cert.get(),
+ CERT_STATUS_COMMON_NAME_INVALID | CERT_STATUS_DATE_INVALID);
+ policy.Deny(google_cert.get(), CERT_STATUS_DATE_INVALID);
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(google_cert.get(), CERT_STATUS_DATE_INVALID));
+ EXPECT_EQ(CertPolicy::UNKNOWN,
+ policy.Check(google_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(google_cert.get(),
+ CERT_STATUS_COMMON_NAME_INVALID | CERT_STATUS_DATE_INVALID));
+
+ // Test denying an overlapping certificate (other direction).
+ policy.Allow(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID);
+ policy.Deny(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID);
+ policy.Deny(webkit_cert.get(), CERT_STATUS_DATE_INVALID);
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(webkit_cert.get(), CERT_STATUS_COMMON_NAME_INVALID));
+ EXPECT_EQ(CertPolicy::DENIED,
+ policy.Check(webkit_cert.get(), CERT_STATUS_DATE_INVALID));
+}
+
+TEST(X509CertificateTest, IntermediateCertificates) {
+ scoped_refptr<X509Certificate> webkit_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der)));
+
+ scoped_refptr<X509Certificate> thawte_cert(
+ X509Certificate::CreateFromBytes(
+ reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der)));
+
+ X509Certificate::OSCertHandle google_handle;
+ // Create object with no intermediates:
+ google_handle = X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(google_der), sizeof(google_der));
+ X509Certificate::OSCertHandles intermediates1;
+ scoped_refptr<X509Certificate> cert1;
+ cert1 = X509Certificate::CreateFromHandle(google_handle, intermediates1);
+ EXPECT_EQ(0u, cert1->GetIntermediateCertificates().size());
+
+ // Create object with 2 intermediates:
+ X509Certificate::OSCertHandles intermediates2;
+ intermediates2.push_back(webkit_cert->os_cert_handle());
+ intermediates2.push_back(thawte_cert->os_cert_handle());
+ scoped_refptr<X509Certificate> cert2;
+ cert2 = X509Certificate::CreateFromHandle(google_handle, intermediates2);
+
+ // Verify it has all the intermediates:
+ const X509Certificate::OSCertHandles& cert2_intermediates =
+ cert2->GetIntermediateCertificates();
+ ASSERT_EQ(2u, cert2_intermediates.size());
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(cert2_intermediates[0],
+ webkit_cert->os_cert_handle()));
+ EXPECT_TRUE(X509Certificate::IsSameOSCert(cert2_intermediates[1],
+ thawte_cert->os_cert_handle()));
+
+ // Cleanup
+ X509Certificate::FreeOSCertHandle(google_handle);
+}
+
+TEST(X509CertificateTest, IsIssuedByEncoded) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ // Test a client certificate from MIT.
+ scoped_refptr<X509Certificate> mit_davidben_cert(
+ ImportCertFromFile(certs_dir, "mit.davidben.der"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), mit_davidben_cert);
+
+ std::string mit_issuer(reinterpret_cast<const char*>(MITDN),
+ sizeof(MITDN));
+
+ // Test a certificate from Google, issued by Thawte
+ scoped_refptr<X509Certificate> google_cert(
+ ImportCertFromFile(certs_dir, "google.single.der"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
+
+ std::string thawte_issuer(reinterpret_cast<const char*>(ThawteDN),
+ sizeof(ThawteDN));
+
+ // Check that the David Ben certificate is issued by MIT, but not
+ // by Thawte.
+ std::vector<std::string> issuers;
+ issuers.clear();
+ issuers.push_back(mit_issuer);
+ EXPECT_TRUE(mit_davidben_cert->IsIssuedByEncoded(issuers));
+ EXPECT_FALSE(google_cert->IsIssuedByEncoded(issuers));
+
+ // Check that the Google certificate is issued by Thawte and not
+ // by MIT.
+ issuers.clear();
+ issuers.push_back(thawte_issuer);
+ EXPECT_FALSE(mit_davidben_cert->IsIssuedByEncoded(issuers));
+ EXPECT_TRUE(google_cert->IsIssuedByEncoded(issuers));
+
+ // Check that they both pass when given a list of the two issuers.
+ issuers.clear();
+ issuers.push_back(mit_issuer);
+ issuers.push_back(thawte_issuer);
+ EXPECT_TRUE(mit_davidben_cert->IsIssuedByEncoded(issuers));
+ EXPECT_TRUE(google_cert->IsIssuedByEncoded(issuers));
+}
+
+TEST(X509CertificateTest, IsIssuedByEncodedWithIntermediates) {
+ static const unsigned char kPolicyRootDN[] = {
+ 0x30, 0x1e, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41
+ };
+ static const unsigned char kPolicyIntermediateDN[] = {
+ 0x30, 0x26, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x1b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74,
+ 0x65, 0x20, 0x43, 0x41
+ };
+
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ CertificateList policy_chain = CreateCertificateListFromFile(
+ certs_dir, "explicit-policy-chain.pem", X509Certificate::FORMAT_AUTO);
+ ASSERT_EQ(3u, policy_chain.size());
+
+ // The intermediate CA certificate's policyConstraints extension has a
+ // requireExplicitPolicy field with SkipCerts=0.
+ std::string policy_intermediate_dn(
+ reinterpret_cast<const char*>(kPolicyIntermediateDN),
+ sizeof(kPolicyIntermediateDN));
+ std::string policy_root_dn(reinterpret_cast<const char*>(kPolicyRootDN),
+ sizeof(kPolicyRootDN));
+
+ X509Certificate::OSCertHandles intermediates;
+ intermediates.push_back(policy_chain[1]->os_cert_handle());
+ scoped_refptr<X509Certificate> cert_chain =
+ X509Certificate::CreateFromHandle(policy_chain[0]->os_cert_handle(),
+ intermediates);
+
+ std::vector<std::string> issuers;
+
+ // Check that the chain is issued by the intermediate.
+ issuers.clear();
+ issuers.push_back(policy_intermediate_dn);
+ EXPECT_TRUE(cert_chain->IsIssuedByEncoded(issuers));
+
+ // Check that the chain is also issued by the root.
+ issuers.clear();
+ issuers.push_back(policy_root_dn);
+ EXPECT_TRUE(cert_chain->IsIssuedByEncoded(issuers));
+
+ // Check that the chain is issued by either the intermediate or the root.
+ issuers.clear();
+ issuers.push_back(policy_intermediate_dn);
+ issuers.push_back(policy_root_dn);
+ EXPECT_TRUE(cert_chain->IsIssuedByEncoded(issuers));
+
+ // Check that an empty issuers list returns false.
+ issuers.clear();
+ EXPECT_FALSE(cert_chain->IsIssuedByEncoded(issuers));
+
+ // Check that the chain is not issued by Verisign
+ std::string mit_issuer(reinterpret_cast<const char*>(VerisignDN),
+ sizeof(VerisignDN));
+ issuers.clear();
+ issuers.push_back(mit_issuer);
+ EXPECT_FALSE(cert_chain->IsIssuedByEncoded(issuers));
+}
+
+#if defined(USE_NSS)
+TEST(X509CertificateTest, GetDefaultNickname) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "no_subject_common_name_cert.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
+
+ std::string nickname = test_cert->GetDefaultNickname(USER_CERT);
+ EXPECT_EQ("wtc@google.com's COMODO Client Authentication and "
+ "Secure Email CA ID", nickname);
+}
+#endif
+
+class X509CertificateParseTest
+ : public testing::TestWithParam<CertificateFormatTestData> {
+ public:
+ virtual ~X509CertificateParseTest() {}
+ virtual void SetUp() {
+ test_data_ = GetParam();
+ }
+ virtual void TearDown() {}
+
+ protected:
+ CertificateFormatTestData test_data_;
+};
+
+TEST_P(X509CertificateParseTest, CanParseFormat) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ CertificateList certs = CreateCertificateListFromFile(
+ certs_dir, test_data_.file_name, test_data_.format);
+ ASSERT_FALSE(certs.empty());
+ ASSERT_LE(certs.size(), arraysize(test_data_.chain_fingerprints));
+ CheckGoogleCert(certs.front(), google_parse_fingerprint,
+ kGoogleParseValidFrom, kGoogleParseValidTo);
+
+ size_t i;
+ for (i = 0; i < arraysize(test_data_.chain_fingerprints); ++i) {
+ if (test_data_.chain_fingerprints[i] == NULL) {
+ // No more test certificates expected - make sure no more were
+ // returned before marking this test a success.
+ EXPECT_EQ(i, certs.size());
+ break;
+ }
+
+ // A cert is expected - make sure that one was parsed.
+ ASSERT_LT(i, certs.size());
+
+ // Compare the parsed certificate with the expected certificate, by
+ // comparing fingerprints.
+ const X509Certificate* cert = certs[i].get();
+ const SHA1HashValue& actual_fingerprint = cert->fingerprint();
+ uint8* expected_fingerprint = test_data_.chain_fingerprints[i];
+
+ for (size_t j = 0; j < 20; ++j)
+ EXPECT_EQ(expected_fingerprint[j], actual_fingerprint.data[j]);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(, X509CertificateParseTest,
+ testing::ValuesIn(FormatTestData));
+
+struct CertificateNameVerifyTestData {
+ // true iff we expect hostname to match an entry in cert_names.
+ bool expected;
+ // The hostname to match.
+ const char* hostname;
+ // Common name, may be used if |dns_names| or |ip_addrs| are empty.
+ const char* common_name;
+ // Comma separated list of certificate names to match against. Any occurrence
+ // of '#' will be replaced with a null character before processing.
+ const char* dns_names;
+ // Comma separated list of certificate IP Addresses to match against. Each
+ // address is x prefixed 16 byte hex code for v6 or dotted-decimals for v4.
+ const char* ip_addrs;
+};
+
+// GTest 'magic' pretty-printer, so that if/when a test fails, it knows how
+// to output the parameter that was passed. Without this, it will simply
+// attempt to print out the first twenty bytes of the object, which depending
+// on platform and alignment, may result in an invalid read.
+void PrintTo(const CertificateNameVerifyTestData& data, std::ostream* os) {
+ ASSERT_TRUE(data.hostname && data.common_name);
+ // Using StringPiece to allow for optional fields being NULL.
+ *os << " expected: " << data.expected
+ << "; hostname: " << data.hostname
+ << "; common_name: " << data.common_name
+ << "; dns_names: " << base::StringPiece(data.dns_names)
+ << "; ip_addrs: " << base::StringPiece(data.ip_addrs);
+}
+
+const CertificateNameVerifyTestData kNameVerifyTestData[] = {
+ { true, "foo.com", "foo.com" },
+ { true, "f", "f" },
+ { false, "h", "i" },
+ { true, "bar.foo.com", "*.foo.com" },
+ { true, "www.test.fr", "common.name",
+ "*.test.com,*.test.co.uk,*.test.de,*.test.fr" },
+ { true, "wwW.tESt.fr", "common.name",
+ ",*.*,*.test.de,*.test.FR,www" },
+ { false, "f.uk", ".uk" },
+ { false, "w.bar.foo.com", "?.bar.foo.com" },
+ { false, "www.foo.com", "(www|ftp).foo.com" },
+ { false, "www.foo.com", "www.foo.com#" }, // # = null char.
+ { false, "www.foo.com", "", "www.foo.com#*.foo.com,#,#" },
+ { false, "www.house.example", "ww.house.example" },
+ { false, "test.org", "", "www.test.org,*.test.org,*.org" },
+ { false, "w.bar.foo.com", "w*.bar.foo.com" },
+ { false, "www.bar.foo.com", "ww*ww.bar.foo.com" },
+ { false, "wwww.bar.foo.com", "ww*ww.bar.foo.com" },
+ { true, "wwww.bar.foo.com", "w*w.bar.foo.com" },
+ { false, "wwww.bar.foo.com", "w*w.bar.foo.c0m" },
+ { true, "WALLY.bar.foo.com", "wa*.bar.foo.com" },
+ { true, "wally.bar.foo.com", "*Ly.bar.foo.com" },
+ { true, "ww%57.foo.com", "", "www.foo.com" },
+ { true, "www&.foo.com", "www%26.foo.com" },
+ // Common name must not be used if subject alternative name was provided.
+ { false, "www.test.co.jp", "www.test.co.jp",
+ "*.test.de,*.jp,www.test.co.uk,www.*.co.jp" },
+ { false, "www.bar.foo.com", "www.bar.foo.com",
+ "*.foo.com,*.*.foo.com,*.*.bar.foo.com,*..bar.foo.com," },
+ { false, "www.bath.org", "www.bath.org", "", "20.30.40.50" },
+ { false, "66.77.88.99", "www.bath.org", "www.bath.org" },
+ // IDN tests
+ { true, "xn--poema-9qae5a.com.br", "xn--poema-9qae5a.com.br" },
+ { true, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br" },
+ { false, "xn--poema-9qae5a.com.br", "", "*.xn--poema-9qae5a.com.br,"
+ "xn--poema-*.com.br,"
+ "xn--*-9qae5a.com.br,"
+ "*--poema-9qae5a.com.br" },
+ // The following are adapted from the examples quoted from
+ // http://tools.ietf.org/html/rfc6125#section-6.4.3
+ // (e.g., *.example.com would match foo.example.com but
+ // not bar.foo.example.com or example.com).
+ { true, "foo.example.com", "*.example.com" },
+ { false, "bar.foo.example.com", "*.example.com" },
+ { false, "example.com", "*.example.com" },
+ // (e.g., baz*.example.net and *baz.example.net and b*z.example.net would
+ // be taken to match baz1.example.net and foobaz.example.net and
+ // buzz.example.net, respectively
+ { true, "baz1.example.net", "baz*.example.net" },
+ { true, "foobaz.example.net", "*baz.example.net" },
+ { true, "buzz.example.net", "b*z.example.net" },
+ // Wildcards should not be valid for public registry controlled domains,
+ // and unknown/unrecognized domains, at least three domain components must
+ // be present.
+ { true, "www.test.example", "*.test.example" },
+ { true, "test.example.co.uk", "*.example.co.uk" },
+ { false, "test.example", "*.exmaple" },
+ { false, "example.co.uk", "*.co.uk" },
+ { false, "foo.com", "*.com" },
+ { false, "foo.us", "*.us" },
+ { false, "foo", "*" },
+ // IDN variants of wildcards and registry controlled domains.
+ { true, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br" },
+ { true, "test.example.xn--mgbaam7a8h", "*.example.xn--mgbaam7a8h" },
+ { false, "xn--poema-9qae5a.com.br", "*.com.br" },
+ { false, "example.xn--mgbaam7a8h", "*.xn--mgbaam7a8h" },
+ // Wildcards should be permissible for 'private' registry controlled
+ // domains.
+ { true, "www.appspot.com", "*.appspot.com" },
+ { true, "foo.s3.amazonaws.com", "*.s3.amazonaws.com" },
+ // Multiple wildcards are not valid.
+ { false, "foo.example.com", "*.*.com" },
+ { false, "foo.bar.example.com", "*.bar.*.com" },
+ // Absolute vs relative DNS name tests. Although not explicitly specified
+ // in RFC 6125, absolute reference names (those ending in a .) should
+ // match either absolute or relative presented names.
+ { true, "foo.com", "foo.com." },
+ { true, "foo.com.", "foo.com" },
+ { true, "foo.com.", "foo.com." },
+ { true, "f", "f." },
+ { true, "f.", "f" },
+ { true, "f.", "f." },
+ { true, "www-3.bar.foo.com", "*.bar.foo.com." },
+ { true, "www-3.bar.foo.com.", "*.bar.foo.com" },
+ { true, "www-3.bar.foo.com.", "*.bar.foo.com." },
+ { false, ".", "." },
+ { false, "example.com", "*.com." },
+ { false, "example.com.", "*.com" },
+ { false, "example.com.", "*.com." },
+ { false, "foo.", "*." },
+ { false, "foo", "*." },
+ { false, "foo.co.uk", "*.co.uk." },
+ { false, "foo.co.uk.", "*.co.uk." },
+ // IP addresses in common name; IPv4 only.
+ { true, "127.0.0.1", "127.0.0.1" },
+ { true, "192.168.1.1", "192.168.1.1" },
+ { true, "676768", "0.10.83.160" },
+ { true, "1.2.3", "1.2.0.3" },
+ { false, "192.169.1.1", "192.168.1.1" },
+ { false, "12.19.1.1", "12.19.1.1/255.255.255.0" },
+ { false, "FEDC:ba98:7654:3210:FEDC:BA98:7654:3210",
+ "FEDC:BA98:7654:3210:FEDC:ba98:7654:3210" },
+ { false, "1111:2222:3333:4444:5555:6666:7777:8888",
+ "1111:2222:3333:4444:5555:6666:7777:8888" },
+ { false, "::192.9.5.5", "[::192.9.5.5]" },
+ // No wildcard matching in valid IP addresses
+ { false, "::192.9.5.5", "*.9.5.5" },
+ { false, "2010:836B:4179::836B:4179", "*:836B:4179::836B:4179" },
+ { false, "192.168.1.11", "*.168.1.11" },
+ { false, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", "*.]" },
+ // IP addresses in subject alternative name (common name ignored)
+ { true, "10.1.2.3", "", "", "10.1.2.3" },
+ { true, "14.15", "", "", "14.0.0.15" },
+ { false, "10.1.2.7", "10.1.2.7", "", "10.1.2.6,10.1.2.8" },
+ { false, "10.1.2.8", "10.20.2.8", "foo" },
+ { true, "::4.5.6.7", "", "", "x00000000000000000000000004050607" },
+ { false, "::6.7.8.9", "::6.7.8.9", "::6.7.8.9",
+ "x00000000000000000000000006070808,x0000000000000000000000000607080a,"
+ "xff000000000000000000000006070809,6.7.8.9" },
+ { true, "FE80::200:f8ff:fe21:67cf", "no.common.name", "",
+ "x00000000000000000000000006070808,xfe800000000000000200f8fffe2167cf,"
+ "xff0000000000000000000000060708ff,10.0.0.1" },
+ // Numeric only hostnames (none of these are considered valid IP addresses).
+ { false, "12345.6", "12345.6" },
+ { false, "121.2.3.512", "", "1*1.2.3.512,*1.2.3.512,1*.2.3.512,*.2.3.512",
+ "121.2.3.0"},
+ { false, "1.2.3.4.5.6", "*.2.3.4.5.6" },
+ { true, "1.2.3.4.5", "", "1.2.3.4.5" },
+ // Invalid host names.
+ { false, "junk)(£)$*!@~#", "junk)(£)$*!@~#" },
+ { false, "www.*.com", "www.*.com" },
+ { false, "w$w.f.com", "w$w.f.com" },
+ { false, "nocolonallowed:example", "", "nocolonallowed:example" },
+ { false, "www-1.[::FFFF:129.144.52.38]", "*.[::FFFF:129.144.52.38]" },
+ { false, "[::4.5.6.9]", "", "", "x00000000000000000000000004050609" },
+};
+
+class X509CertificateNameVerifyTest
+ : public testing::TestWithParam<CertificateNameVerifyTestData> {
+};
+
+TEST_P(X509CertificateNameVerifyTest, VerifyHostname) {
+ CertificateNameVerifyTestData test_data = GetParam();
+
+ std::string common_name(test_data.common_name);
+ ASSERT_EQ(std::string::npos, common_name.find(','));
+ std::replace(common_name.begin(), common_name.end(), '#', '\0');
+
+ std::vector<std::string> dns_names, ip_addressses;
+ if (test_data.dns_names) {
+ // Build up the certificate DNS names list.
+ std::string dns_name_line(test_data.dns_names);
+ std::replace(dns_name_line.begin(), dns_name_line.end(), '#', '\0');
+ base::SplitString(dns_name_line, ',', &dns_names);
+ }
+
+ if (test_data.ip_addrs) {
+ // Build up the certificate IP address list.
+ std::string ip_addrs_line(test_data.ip_addrs);
+ std::vector<std::string> ip_addressses_ascii;
+ base::SplitString(ip_addrs_line, ',', &ip_addressses_ascii);
+ for (size_t i = 0; i < ip_addressses_ascii.size(); ++i) {
+ std::string& addr_ascii = ip_addressses_ascii[i];
+ ASSERT_NE(0U, addr_ascii.length());
+ if (addr_ascii[0] == 'x') { // Hex encoded address
+ addr_ascii.erase(0, 1);
+ std::vector<uint8> bytes;
+ EXPECT_TRUE(base::HexStringToBytes(addr_ascii, &bytes))
+ << "Could not parse hex address " << addr_ascii << " i = " << i;
+ ip_addressses.push_back(std::string(reinterpret_cast<char*>(&bytes[0]),
+ bytes.size()));
+ ASSERT_EQ(16U, ip_addressses.back().size()) << i;
+ } else { // Decimal groups
+ std::vector<std::string> decimals_ascii;
+ base::SplitString(addr_ascii, '.', &decimals_ascii);
+ EXPECT_EQ(4U, decimals_ascii.size()) << i;
+ std::string addr_bytes;
+ for (size_t j = 0; j < decimals_ascii.size(); ++j) {
+ int decimal_value;
+ EXPECT_TRUE(base::StringToInt(decimals_ascii[j], &decimal_value));
+ EXPECT_GE(decimal_value, 0);
+ EXPECT_LE(decimal_value, 255);
+ addr_bytes.push_back(static_cast<char>(decimal_value));
+ }
+ ip_addressses.push_back(addr_bytes);
+ ASSERT_EQ(4U, ip_addressses.back().size()) << i;
+ }
+ }
+ }
+
+ EXPECT_EQ(test_data.expected, X509Certificate::VerifyHostname(
+ test_data.hostname, common_name, dns_names, ip_addressses));
+}
+
+INSTANTIATE_TEST_CASE_P(, X509CertificateNameVerifyTest,
+ testing::ValuesIn(kNameVerifyTestData));
+
+} // namespace net
diff --git a/chromium/net/cert/x509_certificate_win.cc b/chromium/net/cert/x509_certificate_win.cc
new file mode 100644
index 00000000000..f0c9e502e46
--- /dev/null
+++ b/chromium/net/cert/x509_certificate_win.cc
@@ -0,0 +1,453 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_certificate.h"
+
+#include <blapi.h> // Implement CalculateChainFingerprint() with NSS.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/sha1.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/capi_util.h"
+#include "crypto/scoped_capi_types.h"
+#include "net/base/net_errors.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+using base::Time;
+
+namespace net {
+
+namespace {
+
+typedef crypto::ScopedCAPIHandle<
+ HCERTSTORE,
+ crypto::CAPIDestroyerWithFlags<HCERTSTORE,
+ CertCloseStore, 0> > ScopedHCERTSTORE;
+
+void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded,
+ SYSTEMTIME* system_time) {
+ system_time->wYear = exploded.year;
+ system_time->wMonth = exploded.month;
+ system_time->wDayOfWeek = exploded.day_of_week;
+ system_time->wDay = exploded.day_of_month;
+ system_time->wHour = exploded.hour;
+ system_time->wMinute = exploded.minute;
+ system_time->wSecond = exploded.second;
+ system_time->wMilliseconds = exploded.millisecond;
+}
+
+//-----------------------------------------------------------------------------
+
+// Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
+// structure and stores it in *output.
+void GetCertSubjectAltName(PCCERT_CONTEXT cert,
+ scoped_ptr_malloc<CERT_ALT_NAME_INFO>* output) {
+ PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
+ cert->pCertInfo->cExtension,
+ cert->pCertInfo->rgExtension);
+ if (!extension)
+ return;
+
+ CRYPT_DECODE_PARA decode_para;
+ decode_para.cbSize = sizeof(decode_para);
+ decode_para.pfnAlloc = crypto::CryptAlloc;
+ decode_para.pfnFree = crypto::CryptFree;
+ CERT_ALT_NAME_INFO* alt_name_info = NULL;
+ DWORD alt_name_info_size = 0;
+ BOOL rv;
+ rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ szOID_SUBJECT_ALT_NAME2,
+ extension->Value.pbData,
+ extension->Value.cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decode_para,
+ &alt_name_info,
+ &alt_name_info_size);
+ if (rv)
+ output->reset(alt_name_info);
+}
+
+void AddCertsFromStore(HCERTSTORE store,
+ X509Certificate::OSCertHandles* results) {
+ PCCERT_CONTEXT cert = NULL;
+
+ while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
+ PCCERT_CONTEXT to_add = NULL;
+ if (CertAddCertificateContextToStore(
+ NULL, // The cert won't be persisted in any cert store. This breaks
+ // any association the context currently has to |store|, which
+ // allows us, the caller, to safely close |store| without
+ // releasing the cert handles.
+ cert,
+ CERT_STORE_ADD_USE_EXISTING,
+ &to_add) && to_add != NULL) {
+ // When processing stores generated from PKCS#7/PKCS#12 files, it
+ // appears that the order returned is the inverse of the order that it
+ // appeared in the file.
+ // TODO(rsleevi): Ensure this order is consistent across all Win
+ // versions
+ results->insert(results->begin(), to_add);
+ }
+ }
+}
+
+X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) {
+ X509Certificate::OSCertHandles results;
+ CERT_BLOB data_blob;
+ data_blob.cbData = length;
+ data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data));
+
+ HCERTSTORE out_store = NULL;
+
+ DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED;
+
+ if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types,
+ CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
+ &out_store, NULL, NULL) || out_store == NULL) {
+ return results;
+ }
+
+ AddCertsFromStore(out_store, &results);
+ CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG);
+
+ return results;
+}
+
+// Given a CERT_NAME_BLOB, returns true if it appears in a given list,
+// formatted as a vector of strings holding DER-encoded X.509
+// DistinguishedName entries.
+bool IsCertNameBlobInIssuerList(
+ CERT_NAME_BLOB* name_blob,
+ const std::vector<std::string>& issuer_names) {
+ for (std::vector<std::string>::const_iterator it = issuer_names.begin();
+ it != issuer_names.end(); ++it) {
+ CERT_NAME_BLOB issuer_blob;
+ issuer_blob.pbData =
+ reinterpret_cast<BYTE*>(const_cast<char*>(it->data()));
+ issuer_blob.cbData = static_cast<DWORD>(it->length());
+
+ BOOL rb = CertCompareCertificateName(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuer_blob, name_blob);
+ if (rb)
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+void X509Certificate::Initialize() {
+ DCHECK(cert_handle_);
+ subject_.ParseDistinguishedName(cert_handle_->pCertInfo->Subject.pbData,
+ cert_handle_->pCertInfo->Subject.cbData);
+ issuer_.ParseDistinguishedName(cert_handle_->pCertInfo->Issuer.pbData,
+ cert_handle_->pCertInfo->Issuer.cbData);
+
+ valid_start_ = Time::FromFileTime(cert_handle_->pCertInfo->NotBefore);
+ valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
+
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+
+ const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
+ scoped_ptr<uint8[]> serial_bytes(new uint8[serial->cbData]);
+ for (unsigned i = 0; i < serial->cbData; i++)
+ serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
+ serial_number_ = std::string(
+ reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ if (!cert_handle_)
+ return;
+
+ scoped_ptr_malloc<CERT_ALT_NAME_INFO> alt_name_info;
+ GetCertSubjectAltName(cert_handle_, &alt_name_info);
+ CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
+ if (alt_name) {
+ int num_entries = alt_name->cAltEntry;
+ for (int i = 0; i < num_entries; i++) {
+ // dNSName is an ASN.1 IA5String representing a string of ASCII
+ // characters, so we can use WideToASCII here.
+ const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
+
+ if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
+ dns_names->push_back(WideToASCII(entry.pwszDNSName));
+ } else if (ip_addrs &&
+ entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
+ ip_addrs->push_back(std::string(
+ reinterpret_cast<const char*>(entry.IPAddress.pbData),
+ entry.IPAddress.cbData));
+ }
+ }
+ }
+}
+
+PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const {
+ // Create an in-memory certificate store to hold this certificate and
+ // any intermediate certificates in |intermediate_ca_certs_|. The store
+ // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
+ // until the PCCERT_CONTEXT is freed.
+ ScopedHCERTSTORE store(CertOpenStore(
+ CERT_STORE_PROV_MEMORY, 0, NULL,
+ CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL));
+ if (!store.get())
+ return NULL;
+
+ // NOTE: This preserves all of the properties of |os_cert_handle()| except
+ // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
+ // properties that hold access to already-opened private keys. If a handle
+ // has already been unlocked (eg: PIN prompt), then the first time that the
+ // identity is used for client auth, it may prompt the user again.
+ PCCERT_CONTEXT primary_cert;
+ BOOL ok = CertAddCertificateContextToStore(store.get(), os_cert_handle(),
+ CERT_STORE_ADD_ALWAYS,
+ &primary_cert);
+ if (!ok || !primary_cert)
+ return NULL;
+
+ for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
+ CertAddCertificateContextToStore(store.get(), intermediate_ca_certs_[i],
+ CERT_STORE_ADD_ALWAYS, NULL);
+ }
+
+ // Note: |store| is explicitly not released, as the call to CertCloseStore()
+ // when |store| goes out of scope will not actually free the store. Instead,
+ // the store will be freed when |primary_cert| is freed.
+ return primary_cert;
+}
+
+// static
+bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
+ std::string* encoded) {
+ if (!cert_handle->pbCertEncoded || !cert_handle->cbCertEncoded)
+ return false;
+ encoded->assign(reinterpret_cast<char*>(cert_handle->pbCertEncoded),
+ cert_handle->cbCertEncoded);
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+ return a->cbCertEncoded == b->cbCertEncoded &&
+ memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ OSCertHandle cert_handle = NULL;
+ if (!CertAddEncodedCertificateToStore(
+ NULL, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(data),
+ length, CERT_STORE_ADD_USE_EXISTING, &cert_handle))
+ return NULL;
+
+ return cert_handle;
+}
+
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data, int length, Format format) {
+ OSCertHandles results;
+ switch (format) {
+ case FORMAT_SINGLE_CERTIFICATE: {
+ OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
+ if (handle != NULL)
+ results.push_back(handle);
+ break;
+ }
+ case FORMAT_PKCS7:
+ results = ParsePKCS7(data, length);
+ break;
+ default:
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+
+ return results;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle cert_handle) {
+ return CertDuplicateCertificateContext(cert_handle);
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ CertFreeCertificateContext(cert_handle);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(
+ OSCertHandle cert) {
+ DCHECK(NULL != cert->pbCertEncoded);
+ DCHECK_NE(static_cast<DWORD>(0), cert->cbCertEncoded);
+
+ BOOL rv;
+ SHA1HashValue sha1;
+ DWORD sha1_size = sizeof(sha1.data);
+ rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
+ cert->cbCertEncoded, sha1.data, &sha1_size);
+ DCHECK(rv && sha1_size == sizeof(sha1.data));
+ if (!rv)
+ memset(sha1.data, 0, sizeof(sha1.data));
+ return sha1;
+}
+
+// TODO(wtc): This function is implemented with NSS low-level hash
+// functions to ensure it is fast. Reimplement this function with
+// CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead.
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ SHA1Context* sha1_ctx = SHA1_NewContext();
+ if (!sha1_ctx)
+ return sha1;
+ SHA1_Begin(sha1_ctx);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ PCCERT_CONTEXT ca_cert = intermediates[i];
+ SHA1_Update(sha1_ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded);
+ }
+ unsigned int result_len;
+ SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH);
+ SHA1_DestroyContext(sha1_ctx, PR_TRUE);
+
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return NULL;
+
+ // Legacy serialized certificates were serialized with extended attributes,
+ // rather than as DER only. As a result, these serialized certificates are
+ // not portable across platforms and may have side-effects on Windows due
+ // to extended attributes being serialized/deserialized -
+ // http://crbug.com/118706. To avoid deserializing these attributes, write
+ // the deserialized cert into a temporary cert store and then create a new
+ // cert from the DER - that is, without attributes.
+ ScopedHCERTSTORE store(
+ CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL));
+ if (!store.get())
+ return NULL;
+
+ OSCertHandle cert_handle = NULL;
+ if (!CertAddSerializedElementToStore(
+ store.get(), reinterpret_cast<const BYTE*>(data), length,
+ CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
+ NULL, reinterpret_cast<const void **>(&cert_handle))) {
+ return NULL;
+ }
+
+ std::string encoded;
+ bool ok = GetDEREncoded(cert_handle, &encoded);
+ FreeOSCertHandle(cert_handle);
+ cert_handle = NULL;
+
+ if (ok)
+ cert_handle = CreateOSCertHandleFromBytes(encoded.data(), encoded.size());
+ return cert_handle;
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ return pickle->WriteData(
+ reinterpret_cast<char*>(cert_handle->pbCertEncoded),
+ cert_handle->cbCertEncoded);
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ *type = kPublicKeyTypeUnknown;
+ *size_bits = 0;
+
+ PCCRYPT_OID_INFO oid_info = CryptFindOIDInfo(
+ CRYPT_OID_INFO_OID_KEY,
+ cert_handle->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId,
+ CRYPT_PUBKEY_ALG_OID_GROUP_ID);
+ if (!oid_info)
+ return;
+
+ CHECK_EQ(oid_info->dwGroupId,
+ static_cast<DWORD>(CRYPT_PUBKEY_ALG_OID_GROUP_ID));
+
+ *size_bits = CertGetPublicKeyLength(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert_handle->pCertInfo->SubjectPublicKeyInfo);
+
+ if (IS_SPECIAL_OID_INFO_ALGID(oid_info->Algid)) {
+ // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
+ // (0xFFFFFFFE). Need to handle it as a special case.
+ if (strcmp(oid_info->pszOID, szOID_ECC_PUBLIC_KEY) == 0) {
+ *type = kPublicKeyTypeECDSA;
+ } else {
+ NOTREACHED();
+ }
+ return;
+ }
+ switch (oid_info->Algid) {
+ case CALG_RSA_SIGN:
+ case CALG_RSA_KEYX:
+ *type = kPublicKeyTypeRSA;
+ break;
+ case CALG_DSS_SIGN:
+ *type = kPublicKeyTypeDSA;
+ break;
+ case CALG_ECDSA:
+ *type = kPublicKeyTypeECDSA;
+ break;
+ case CALG_ECDH:
+ *type = kPublicKeyTypeECDH;
+ break;
+ }
+}
+
+bool X509Certificate::IsIssuedByEncoded(
+ const std::vector<std::string>& valid_issuers) {
+
+ // If the certificate's issuer in the list?
+ if (IsCertNameBlobInIssuerList(&cert_handle_->pCertInfo->Issuer,
+ valid_issuers)) {
+ return true;
+ }
+ // Otherwise, is any of the intermediate CA subjects in the list?
+ for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
+ it != intermediate_ca_certs_.end(); ++it) {
+ if (IsCertNameBlobInIssuerList(&(*it)->pCertInfo->Issuer,
+ valid_issuers)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util.cc b/chromium/net/cert/x509_util.cc
new file mode 100644
index 00000000000..8beb5572b87
--- /dev/null
+++ b/chromium/net/cert/x509_util.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util.h"
+
+#include "base/time/time.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace x509_util {
+
+ClientCertSorter::ClientCertSorter() : now_(base::Time::Now()) {}
+
+bool ClientCertSorter::operator()(
+ const scoped_refptr<X509Certificate>& a,
+ const scoped_refptr<X509Certificate>& b) const {
+ // Certificates that are null are sorted last.
+ if (!a.get() || !b.get())
+ return a.get() && !b.get();
+
+ // Certificates that are expired/not-yet-valid are sorted last.
+ bool a_is_valid = now_ >= a->valid_start() && now_ <= a->valid_expiry();
+ bool b_is_valid = now_ >= b->valid_start() && now_ <= b->valid_expiry();
+ if (a_is_valid != b_is_valid)
+ return a_is_valid && !b_is_valid;
+
+ // Certificates with longer expirations appear as higher priority (less
+ // than) certificates with shorter expirations.
+ if (a->valid_expiry() != b->valid_expiry())
+ return a->valid_expiry() > b->valid_expiry();
+
+ // If the expiration dates are equivalent, certificates that were issued
+ // more recently should be prioritized over older certificates.
+ if (a->valid_start() != b->valid_start())
+ return a->valid_start() > b->valid_start();
+
+ // Otherwise, prefer client certificates with shorter chains.
+ const X509Certificate::OSCertHandles& a_intermediates =
+ a->GetIntermediateCertificates();
+ const X509Certificate::OSCertHandles& b_intermediates =
+ b->GetIntermediateCertificates();
+ return a_intermediates.size() < b_intermediates.size();
+}
+
+} // namespace x509_util
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util.h b/chromium/net/cert/x509_util.h
new file mode 100644
index 00000000000..8a6bae2c95f
--- /dev/null
+++ b/chromium/net/cert/x509_util.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_UTIL_H_
+#define NET_CERT_X509_UTIL_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+
+namespace crypto {
+class ECPrivateKey;
+class RSAPrivateKey;
+}
+
+namespace net {
+
+class X509Certificate;
+
+namespace x509_util {
+
+// Returns true if the times can be used to create an X.509 certificate.
+// Certificates can accept dates from Jan 1st, 1 to Dec 31, 9999. A bug in NSS
+// limited the range to 1950-9999
+// (https://bugzilla.mozilla.org/show_bug.cgi?id=786531). This function will
+// return whether it is supported by the currently used crypto library.
+NET_EXPORT_PRIVATE bool IsSupportedValidityRange(base::Time not_valid_before,
+ base::Time not_valid_after);
+
+// Creates a server bound certificate containing the public key in |key|.
+// Domain, serial number and validity period are given as
+// parameters. The certificate is signed by the private key in |key|.
+// The hashing algorithm for the signature is SHA-1.
+//
+// See Internet Draft draft-balfanz-tls-obc-00 for more details:
+// http://tools.ietf.org/html/draft-balfanz-tls-obc-00
+NET_EXPORT_PRIVATE bool CreateDomainBoundCertEC(
+ crypto::ECPrivateKey* key,
+ const std::string& domain,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after,
+ std::string* der_cert);
+
+// Create a self-signed certificate containing the public key in |key|.
+// Subject, serial number and validity period are given as parameters.
+// The certificate is signed by the private key in |key|. The hashing
+// algorithm for the signature is SHA-1.
+//
+// |subject| is a distinguished name defined in RFC4514.
+//
+// An example:
+// CN=Michael Wong,O=FooBar Corporation,DC=foobar,DC=com
+//
+// SECURITY WARNING
+//
+// Using self-signed certificates has the following security risks:
+// 1. Encryption without authentication and thus vulnerable to
+// man-in-the-middle attacks.
+// 2. Self-signed certificates cannot be revoked.
+//
+// Use this certificate only after the above risks are acknowledged.
+NET_EXPORT bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after,
+ std::string* der_cert);
+
+// Comparator for use in STL algorithms that will sort client certificates by
+// order of preference.
+// Returns true if |a| is more preferable than |b|, allowing it to be used
+// with any algorithm that compares according to strict weak ordering.
+//
+// Criteria include:
+// - Prefer certificates that have a longer validity period (later
+// expiration dates)
+// - If equal, prefer certificates that were issued more recently
+// - If equal, prefer shorter chains (if available)
+class NET_EXPORT_PRIVATE ClientCertSorter {
+ public:
+ ClientCertSorter();
+
+ bool operator()(
+ const scoped_refptr<X509Certificate>& a,
+ const scoped_refptr<X509Certificate>& b) const;
+
+ private:
+ base::Time now_;
+};
+
+} // namespace x509_util
+
+} // namespace net
+
+#endif // NET_CERT_X509_UTIL_H_
diff --git a/chromium/net/cert/x509_util_ios.cc b/chromium/net/cert/x509_util_ios.cc
new file mode 100644
index 00000000000..736c26e0438
--- /dev/null
+++ b/chromium/net/cert/x509_util_ios.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util_ios.h"
+
+#include <cert.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <nss.h>
+#include <prtypes.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "crypto/nss_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_nss.h"
+
+using base::ScopedCFTypeRef;
+
+namespace net {
+namespace x509_util_ios {
+
+namespace {
+
+// Creates an NSS certificate handle from |data|, which is |length| bytes in
+// size.
+CERTCertificate* CreateNSSCertHandleFromBytes(const char* data,
+ int length) {
+ if (length < 0)
+ return NULL;
+
+ crypto::EnsureNSSInit();
+
+ if (!NSS_IsInitialized())
+ return NULL;
+
+ SECItem der_cert;
+ der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
+ der_cert.len = length;
+ der_cert.type = siDERCertBuffer;
+
+ // Parse into a certificate structure.
+ return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL,
+ PR_FALSE, PR_TRUE);
+}
+
+} // namespace
+
+CERTCertificate* CreateNSSCertHandleFromOSHandle(
+ SecCertificateRef cert_handle) {
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert_handle));
+ return CreateNSSCertHandleFromBytes(
+ reinterpret_cast<const char*>(CFDataGetBytePtr(cert_data)),
+ CFDataGetLength(cert_data));
+}
+
+SecCertificateRef CreateOSCertHandleFromNSSHandle(
+ CERTCertificate* nss_cert_handle) {
+ return X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(nss_cert_handle->derCert.data),
+ nss_cert_handle->derCert.len);
+}
+
+X509Certificate* CreateCertFromNSSHandles(
+ CERTCertificate* cert_handle,
+ const std::vector<CERTCertificate*>& intermediates) {
+ ScopedCFTypeRef<SecCertificateRef> os_server_cert(
+ CreateOSCertHandleFromNSSHandle(cert_handle));
+ if (!os_server_cert)
+ return NULL;
+ std::vector<SecCertificateRef> os_intermediates;
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ SecCertificateRef intermediate =
+ CreateOSCertHandleFromNSSHandle(intermediates[i]);
+ if (!intermediate)
+ break;
+ os_intermediates.push_back(intermediate);
+ }
+
+ X509Certificate* cert = NULL;
+ if (intermediates.size() == os_intermediates.size()) {
+ cert = X509Certificate::CreateFromHandle(os_server_cert,
+ os_intermediates);
+ }
+
+ for (size_t i = 0; i < os_intermediates.size(); ++i)
+ CFRelease(os_intermediates[i]);
+ return cert;
+}
+
+SHA1HashValue CalculateFingerprintNSS(CERTCertificate* cert) {
+ DCHECK(cert->derCert.data);
+ DCHECK_NE(0U, cert->derCert.len);
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+ CC_SHA1(cert->derCert.data, cert->derCert.len, sha1.data);
+ return sha1;
+}
+
+// NSSCertificate implementation.
+
+NSSCertificate::NSSCertificate(SecCertificateRef cert_handle) {
+ nss_cert_handle_ = CreateNSSCertHandleFromOSHandle(cert_handle);
+ DLOG_IF(INFO, cert_handle && !nss_cert_handle_)
+ << "Could not convert SecCertificateRef to CERTCertificate*";
+}
+
+NSSCertificate::~NSSCertificate() {
+ CERT_DestroyCertificate(nss_cert_handle_);
+}
+
+CERTCertificate* NSSCertificate::cert_handle() const {
+ return nss_cert_handle_;
+}
+
+// NSSCertChain implementation
+
+NSSCertChain::NSSCertChain(X509Certificate* certificate) {
+ DCHECK(certificate);
+ certs_.push_back(CreateNSSCertHandleFromOSHandle(
+ certificate->os_cert_handle()));
+ const X509Certificate::OSCertHandles& cert_intermediates =
+ certificate->GetIntermediateCertificates();
+ for (size_t i = 0; i < cert_intermediates.size(); ++i)
+ certs_.push_back(CreateNSSCertHandleFromOSHandle(cert_intermediates[i]));
+}
+
+NSSCertChain::~NSSCertChain() {
+ for (size_t i = 0; i < certs_.size(); ++i)
+ CERT_DestroyCertificate(certs_[i]);
+}
+
+CERTCertificate* NSSCertChain::cert_handle() const {
+ return certs_.empty() ? NULL : certs_.front();
+}
+
+const std::vector<CERTCertificate*>& NSSCertChain::cert_chain() const {
+ return certs_;
+}
+
+} // namespace x509_util_ios
+} // namespace net
diff --git a/chromium/net/cert/x509_util_ios.h b/chromium/net/cert/x509_util_ios.h
new file mode 100644
index 00000000000..5a8a57601ca
--- /dev/null
+++ b/chromium/net/cert/x509_util_ios.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains functions for iOS to glue NSS and Security.framework
+// together.
+
+#ifndef NET_CERT_X509_UTIL_IOS_H_
+#define NET_CERT_X509_UTIL_IOS_H_
+
+#include <Security/Security.h>
+#include <vector>
+
+#include "net/cert/x509_cert_types.h"
+
+// Forward declaration; real one in <cert.h>
+typedef struct CERTCertificateStr CERTCertificate;
+
+namespace net {
+
+class X509Certificate;
+
+namespace x509_util_ios {
+
+// Converts a Security.framework certificate handle (SecCertificateRef) into
+// an NSS certificate handle (CERTCertificate*).
+CERTCertificate* CreateNSSCertHandleFromOSHandle(SecCertificateRef cert_handle);
+
+// Converts an NSS certificate handle (CERTCertificate*) into a
+// Security.framework handle (SecCertificateRef)
+SecCertificateRef CreateOSCertHandleFromNSSHandle(
+ CERTCertificate* nss_cert_handle);
+
+// Create a new X509Certificate from the specified NSS server cert and
+// intermediates. This is functionally equivalent to
+// X509Certificate::CreateFromHandle(), except it supports receiving
+// NSS CERTCertificate*s rather than iOS SecCertificateRefs.
+X509Certificate* CreateCertFromNSSHandles(
+ CERTCertificate* cert_handle,
+ const std::vector<CERTCertificate*>& intermediates);
+
+SHA1HashValue CalculateFingerprintNSS(CERTCertificate* cert);
+
+// This is a wrapper class around the native NSS certificate handle.
+// The constructor copies the certificate data from |cert_handle| and
+// uses the NSS library to parse it.
+class NSSCertificate {
+ public:
+ explicit NSSCertificate(SecCertificateRef cert_handle);
+ ~NSSCertificate();
+ CERTCertificate* cert_handle() const;
+ private:
+ CERTCertificate* nss_cert_handle_;
+};
+
+// A wrapper class that loads a certificate and all of its intermediates into
+// NSS. This is necessary for libpkix path building to be able to locate
+// needed intermediates.
+class NSSCertChain {
+ public:
+ explicit NSSCertChain(X509Certificate* certificate);
+ ~NSSCertChain();
+ CERTCertificate* cert_handle() const;
+ const std::vector<CERTCertificate*>& cert_chain() const;
+ private:
+ std::vector<CERTCertificate*> certs_;
+};
+
+} // namespace x509_util_ios
+} // namespace net
+
+#endif // NET_CERT_X509_UTIL_IOS_H_
diff --git a/chromium/net/cert/x509_util_mac.cc b/chromium/net/cert/x509_util_mac.cc
new file mode 100644
index 00000000000..c9aa37bc63f
--- /dev/null
+++ b/chromium/net/cert/x509_util_mac.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util_mac.h"
+
+#include "base/logging.h"
+#include "third_party/apple_apsl/cssmapplePriv.h"
+
+namespace net {
+
+namespace x509_util {
+
+namespace {
+
+// Creates a SecPolicyRef for the given OID, with optional value.
+OSStatus CreatePolicy(const CSSM_OID* policy_oid,
+ void* option_data,
+ size_t option_length,
+ SecPolicyRef* policy) {
+ SecPolicySearchRef search;
+ OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_oid, NULL,
+ &search);
+ if (err)
+ return err;
+ err = SecPolicySearchCopyNext(search, policy);
+ CFRelease(search);
+ if (err)
+ return err;
+
+ if (option_data) {
+ CSSM_DATA options_data = {
+ option_length,
+ reinterpret_cast<uint8_t*>(option_data)
+ };
+ err = SecPolicySetValue(*policy, &options_data);
+ if (err) {
+ CFRelease(*policy);
+ return err;
+ }
+ }
+ return noErr;
+}
+
+} // namespace
+
+
+OSStatus CreateSSLClientPolicy(SecPolicyRef* policy) {
+ CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options;
+ memset(&tp_ssl_options, 0, sizeof(tp_ssl_options));
+ tp_ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
+ tp_ssl_options.Flags |= CSSM_APPLE_TP_SSL_CLIENT;
+
+ return CreatePolicy(&CSSMOID_APPLE_TP_SSL, &tp_ssl_options,
+ sizeof(tp_ssl_options), policy);
+}
+
+OSStatus CreateSSLServerPolicy(const std::string& hostname,
+ SecPolicyRef* policy) {
+ CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options;
+ memset(&tp_ssl_options, 0, sizeof(tp_ssl_options));
+ tp_ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
+ if (!hostname.empty()) {
+ tp_ssl_options.ServerName = hostname.data();
+ tp_ssl_options.ServerNameLen = hostname.size();
+ }
+
+ return CreatePolicy(&CSSMOID_APPLE_TP_SSL, &tp_ssl_options,
+ sizeof(tp_ssl_options), policy);
+}
+
+OSStatus CreateBasicX509Policy(SecPolicyRef* policy) {
+ return CreatePolicy(&CSSMOID_APPLE_X509_BASIC, NULL, 0, policy);
+}
+
+OSStatus CreateRevocationPolicies(bool enable_revocation_checking,
+ bool enable_ev_checking,
+ CFMutableArrayRef policies) {
+ OSStatus status = noErr;
+
+ // In order to bypass the system revocation checking settings, the
+ // SecTrustRef must have at least one revocation policy associated with it.
+ // Since it is not known prior to verification whether the Apple TP will
+ // consider a certificate as an EV candidate, the default policy used is a
+ // CRL policy, since it does not communicate over the network.
+ // If the TP believes the leaf is an EV cert, it will explicitly add an
+ // OCSP policy to perform the online checking, and if it doesn't believe
+ // that the leaf is EV, then the default CRL policy will effectively no-op.
+ // This behaviour is used to implement EV-only revocation checking.
+ if (enable_ev_checking || enable_revocation_checking) {
+ CSSM_APPLE_TP_CRL_OPTIONS tp_crl_options;
+ memset(&tp_crl_options, 0, sizeof(tp_crl_options));
+ tp_crl_options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
+ // Only allow network CRL fetches if the caller explicitly requests
+ // online revocation checking. Note that, as of OS X 10.7.2, the system
+ // will set force this flag on according to system policies, so
+ // online revocation checks cannot be completely disabled.
+ if (enable_revocation_checking)
+ tp_crl_options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET;
+
+ SecPolicyRef crl_policy;
+ status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_CRL, &tp_crl_options,
+ sizeof(tp_crl_options), &crl_policy);
+ if (status)
+ return status;
+ CFArrayAppendValue(policies, crl_policy);
+ CFRelease(crl_policy);
+ }
+
+ // If revocation checking is explicitly enabled, then add an OCSP policy
+ // and allow network access. If both revocation checking and EV checking
+ // are disabled, then the added OCSP policy will be prevented from
+ // accessing the network. This is done because the TP will force an OCSP
+ // policy to be present when it believes the certificate is EV. If network
+ // fetching was not explicitly disabled, then it would be as if
+ // enable_ev_checking was always set to true.
+ if (enable_revocation_checking || !enable_ev_checking) {
+ CSSM_APPLE_TP_OCSP_OPTIONS tp_ocsp_options;
+ memset(&tp_ocsp_options, 0, sizeof(tp_ocsp_options));
+ tp_ocsp_options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
+
+ if (enable_revocation_checking) {
+ // The default for the OCSP policy is to fetch responses via the network,
+ // unlike the CRL policy default. The policy is further modified to
+ // prefer OCSP over CRLs, if both are specified on the certificate. This
+ // is because an OCSP response is both sufficient and typically
+ // significantly smaller than the CRL counterpart.
+ tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
+ } else {
+ // Effectively disable OCSP checking by making it impossible to get an
+ // OCSP response. Even if the Apple TP forces OCSP, no checking will
+ // be able to succeed. If this happens, the Apple TP will report an error
+ // that OCSP was unavailable, but this will be handled and suppressed in
+ // X509Certificate::Verify().
+ tp_ocsp_options.Flags = CSSM_TP_ACTION_OCSP_DISABLE_NET |
+ CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE;
+ }
+
+ SecPolicyRef ocsp_policy;
+ status = CreatePolicy(&CSSMOID_APPLE_TP_REVOCATION_OCSP, &tp_ocsp_options,
+ sizeof(tp_ocsp_options), &ocsp_policy);
+ if (status)
+ return status;
+ CFArrayAppendValue(policies, ocsp_policy);
+ CFRelease(ocsp_policy);
+ }
+
+ return status;
+}
+
+CSSMFieldValue::CSSMFieldValue()
+ : cl_handle_(CSSM_INVALID_HANDLE),
+ oid_(NULL),
+ field_(NULL) {
+}
+CSSMFieldValue::CSSMFieldValue(CSSM_CL_HANDLE cl_handle,
+ const CSSM_OID* oid,
+ CSSM_DATA_PTR field)
+ : cl_handle_(cl_handle),
+ oid_(const_cast<CSSM_OID_PTR>(oid)),
+ field_(field) {
+}
+
+CSSMFieldValue::~CSSMFieldValue() {
+ Reset(CSSM_INVALID_HANDLE, NULL, NULL);
+}
+
+void CSSMFieldValue::Reset(CSSM_CL_HANDLE cl_handle,
+ CSSM_OID_PTR oid,
+ CSSM_DATA_PTR field) {
+ if (cl_handle_ && oid_ && field_)
+ CSSM_CL_FreeFieldValue(cl_handle_, oid_, field_);
+ cl_handle_ = cl_handle;
+ oid_ = oid;
+ field_ = field;
+}
+
+CSSMCachedCertificate::CSSMCachedCertificate()
+ : cl_handle_(CSSM_INVALID_HANDLE),
+ cached_cert_handle_(CSSM_INVALID_HANDLE) {
+}
+CSSMCachedCertificate::~CSSMCachedCertificate() {
+ if (cl_handle_ && cached_cert_handle_)
+ CSSM_CL_CertAbortCache(cl_handle_, cached_cert_handle_);
+}
+
+OSStatus CSSMCachedCertificate::Init(SecCertificateRef os_cert_handle) {
+ DCHECK(!cl_handle_ && !cached_cert_handle_);
+ DCHECK(os_cert_handle);
+ CSSM_DATA cert_data;
+ OSStatus status = SecCertificateGetData(os_cert_handle, &cert_data);
+ if (status)
+ return status;
+ status = SecCertificateGetCLHandle(os_cert_handle, &cl_handle_);
+ if (status) {
+ DCHECK(!cl_handle_);
+ return status;
+ }
+
+ status = CSSM_CL_CertCache(cl_handle_, &cert_data, &cached_cert_handle_);
+ if (status)
+ DCHECK(!cached_cert_handle_);
+ return status;
+}
+
+OSStatus CSSMCachedCertificate::GetField(const CSSM_OID* field_oid,
+ CSSMFieldValue* field) const {
+ DCHECK(cl_handle_);
+ DCHECK(cached_cert_handle_);
+
+ CSSM_OID_PTR oid = const_cast<CSSM_OID_PTR>(field_oid);
+ CSSM_DATA_PTR field_ptr = NULL;
+ CSSM_HANDLE results_handle = CSSM_INVALID_HANDLE;
+ uint32 field_value_count = 0;
+ CSSM_RETURN status = CSSM_CL_CertGetFirstCachedFieldValue(
+ cl_handle_, cached_cert_handle_, oid, &results_handle,
+ &field_value_count, &field_ptr);
+ if (status)
+ return status;
+
+ // Note: |field_value_count| may be > 1, indicating that more than one
+ // value is present. This may happen with extensions, but for current
+ // usages, only the first value is returned.
+ CSSM_CL_CertAbortQuery(cl_handle_, results_handle);
+ field->Reset(cl_handle_, oid, field_ptr);
+ return CSSM_OK;
+}
+
+} // namespace x509_util
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util_mac.h b/chromium/net/cert/x509_util_mac.h
new file mode 100644
index 00000000000..caf7a281ce9
--- /dev/null
+++ b/chromium/net/cert/x509_util_mac.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_UTIL_MAC_H_
+#define NET_CERT_X509_UTIL_MAC_H_
+
+#include <CoreFoundation/CFArray.h>
+#include <Security/Security.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+namespace x509_util {
+
+// Creates a security policy for certificates used as client certificates
+// in SSL.
+// If a policy is successfully created, it will be stored in
+// |*policy| and ownership transferred to the caller.
+OSStatus NET_EXPORT CreateSSLClientPolicy(SecPolicyRef* policy);
+
+// Create an SSL server policy. While certificate name validation will be
+// performed by SecTrustEvaluate(), it has the following limitations:
+// - Doesn't support IP addresses in dotted-quad literals (127.0.0.1)
+// - Doesn't support IPv6 addresses
+// - Doesn't support the iPAddress subjectAltName
+// Providing the hostname is necessary in order to locate certain user or
+// system trust preferences, such as those created by Safari. Preferences
+// created by Keychain Access do not share this requirement.
+// On success, stores the resultant policy in |*policy| and returns noErr.
+OSStatus NET_EXPORT CreateSSLServerPolicy(const std::string& hostname,
+ SecPolicyRef* policy);
+
+// Creates a security policy for basic X.509 validation. If the policy is
+// successfully created, it will be stored in |*policy| and ownership
+// transferred to the caller.
+OSStatus NET_EXPORT CreateBasicX509Policy(SecPolicyRef* policy);
+
+// Creates security policies to control revocation checking (OCSP and CRL).
+// If |enable_revocation_checking| is true, revocation checking will be
+// explicitly enabled.
+// If |enable_revocation_checking| is false, but |enable_ev_checking| is
+// true, then the system policies for EV checking (which include checking
+// for an online OCSP response) will be permitted. However, if the OS
+// does not believe the certificate is EV, no revocation checking will be
+// performed.
+// If both are false, then the policies returned will be explicitly
+// prohibited from accessing the network or the local cache, regardless of
+// system settings.
+// If the policies are successfully created, they will be appended to
+// |policies|.
+OSStatus NET_EXPORT CreateRevocationPolicies(bool enable_revocation_checking,
+ bool enable_ev_checking,
+ CFMutableArrayRef policies);
+
+// Wrapper for a CSSM_DATA_PTR that was obtained via one of the CSSM field
+// accessors (such as CSSM_CL_CertGet[First/Next]Value or
+// CSSM_CL_CertGet[First/Next]CachedValue).
+class CSSMFieldValue {
+ public:
+ CSSMFieldValue();
+ CSSMFieldValue(CSSM_CL_HANDLE cl_handle,
+ const CSSM_OID* oid,
+ CSSM_DATA_PTR field);
+ ~CSSMFieldValue();
+
+ CSSM_OID_PTR oid() const { return oid_; }
+ CSSM_DATA_PTR field() const { return field_; }
+
+ // Returns the field as if it was an arbitrary type - most commonly, by
+ // interpreting the field as a specific CSSM/CDSA parsed type, such as
+ // CSSM_X509_SUBJECT_PUBLIC_KEY_INFO or CSSM_X509_ALGORITHM_IDENTIFIER.
+ // An added check is applied to ensure that the current field is large
+ // enough to actually contain the requested type.
+ template <typename T> const T* GetAs() const {
+ if (!field_ || field_->Length < sizeof(T))
+ return NULL;
+ return reinterpret_cast<const T*>(field_->Data);
+ }
+
+ void Reset(CSSM_CL_HANDLE cl_handle,
+ CSSM_OID_PTR oid,
+ CSSM_DATA_PTR field);
+
+ private:
+ CSSM_CL_HANDLE cl_handle_;
+ CSSM_OID_PTR oid_;
+ CSSM_DATA_PTR field_;
+
+ DISALLOW_COPY_AND_ASSIGN(CSSMFieldValue);
+};
+
+// CSSMCachedCertificate is a container class that is used to wrap the
+// CSSM_CL_CertCache APIs and provide safe and efficient access to
+// certificate fields in their CSSM form.
+//
+// To provide efficient access to certificate/CRL fields, CSSM provides an
+// API/SPI to "cache" a certificate/CRL. The exact meaning of a cached
+// certificate is not defined by CSSM, but is documented to generally be some
+// intermediate or parsed form of the certificate. In the case of Apple's
+// CSSM CL implementation, the intermediate form is the parsed certificate
+// stored in an internal format (which happens to be NSS). By caching the
+// certificate, callers that wish to access multiple fields (such as subject,
+// issuer, and validity dates) do not need to repeatedly parse the entire
+// certificate, nor are they forced to convert all fields from their NSS types
+// to their CSSM equivalents. This latter point is especially helpful when
+// running on OS X 10.5, as it will fail to convert some fields that reference
+// unsupported algorithms, such as ECC.
+class CSSMCachedCertificate {
+ public:
+ CSSMCachedCertificate();
+ ~CSSMCachedCertificate();
+
+ // Initializes the CSSMCachedCertificate by caching the specified
+ // |os_cert_handle|. On success, returns noErr.
+ // Note: Once initialized, the cached certificate should only be accessed
+ // from a single thread.
+ OSStatus Init(SecCertificateRef os_cert_handle);
+
+ // Fetches the first value for the field associated with |field_oid|.
+ // If |field_oid| is a valid OID and is present in the current certificate,
+ // returns CSSM_OK and stores the first value in |field|. If additional
+ // values are associated with |field_oid|, they are ignored.
+ OSStatus GetField(const CSSM_OID* field_oid, CSSMFieldValue* field) const;
+
+ private:
+ CSSM_CL_HANDLE cl_handle_;
+ CSSM_HANDLE cached_cert_handle_;
+};
+
+} // namespace x509_util
+
+} // namespace net
+
+#endif // NET_CERT_X509_UTIL_MAC_H_
diff --git a/chromium/net/cert/x509_util_nss.cc b/chromium/net/cert/x509_util_nss.cc
new file mode 100644
index 00000000000..f8fbd6feda9
--- /dev/null
+++ b/chromium/net/cert/x509_util_nss.cc
@@ -0,0 +1,624 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util.h"
+#include "net/cert/x509_util_nss.h"
+
+#include <cert.h> // Must be included before certdb.h
+#include <certdb.h>
+#include <cryptohi.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <secder.h>
+#include <secmod.h>
+#include <secport.h>
+
+#include "base/debug/leak_annotations.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/pickle.h"
+#include "base/strings/stringprintf.h"
+#include "crypto/ec_private_key.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/scoped_nss_types.h"
+#include "crypto/third_party/nss/chromium-nss.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace {
+
+class DomainBoundCertOIDWrapper {
+ public:
+ static DomainBoundCertOIDWrapper* GetInstance() {
+ // Instantiated as a leaky singleton to allow the singleton to be
+ // constructed on a worker thead that is not joined when a process
+ // shuts down.
+ return Singleton<DomainBoundCertOIDWrapper,
+ LeakySingletonTraits<DomainBoundCertOIDWrapper> >::get();
+ }
+
+ SECOidTag domain_bound_cert_oid_tag() const {
+ return domain_bound_cert_oid_tag_;
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<DomainBoundCertOIDWrapper>;
+
+ DomainBoundCertOIDWrapper();
+
+ SECOidTag domain_bound_cert_oid_tag_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomainBoundCertOIDWrapper);
+};
+
+DomainBoundCertOIDWrapper::DomainBoundCertOIDWrapper()
+ : domain_bound_cert_oid_tag_(SEC_OID_UNKNOWN) {
+ // 1.3.6.1.4.1.11129.2.1.6
+ // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
+ // certificateExtensions.originBoundCertificate)
+ static const uint8 kObCertOID[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x06
+ };
+ SECOidData oid_data;
+ memset(&oid_data, 0, sizeof(oid_data));
+ oid_data.oid.data = const_cast<uint8*>(kObCertOID);
+ oid_data.oid.len = sizeof(kObCertOID);
+ oid_data.offset = SEC_OID_UNKNOWN;
+ oid_data.desc = "Origin Bound Certificate";
+ oid_data.mechanism = CKM_INVALID_MECHANISM;
+ oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
+ domain_bound_cert_oid_tag_ = SECOID_AddEntry(&oid_data);
+ if (domain_bound_cert_oid_tag_ == SEC_OID_UNKNOWN)
+ LOG(ERROR) << "OB_CERT OID tag creation failed";
+}
+
+// Creates a Certificate object that may be passed to the SignCertificate
+// method to generate an X509 certificate.
+// Returns NULL if an error is encountered in the certificate creation
+// process.
+// Caller responsible for freeing returned certificate object.
+CERTCertificate* CreateCertificate(
+ SECKEYPublicKey* public_key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after) {
+ // Create info about public key.
+ CERTSubjectPublicKeyInfo* spki =
+ SECKEY_CreateSubjectPublicKeyInfo(public_key);
+ if (!spki)
+ return NULL;
+
+ // Create the certificate request.
+ CERTName* subject_name =
+ CERT_AsciiToName(const_cast<char*>(subject.c_str()));
+ CERTCertificateRequest* cert_request =
+ CERT_CreateCertificateRequest(subject_name, spki, NULL);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+
+ if (!cert_request) {
+ PRErrorCode prerr = PR_GetError();
+ LOG(ERROR) << "Failed to create certificate request: " << prerr;
+ CERT_DestroyName(subject_name);
+ return NULL;
+ }
+
+ CERTValidity* validity = CERT_CreateValidity(
+ crypto::BaseTimeToPRTime(not_valid_before),
+ crypto::BaseTimeToPRTime(not_valid_after));
+ if (!validity) {
+ PRErrorCode prerr = PR_GetError();
+ LOG(ERROR) << "Failed to create certificate validity object: " << prerr;
+ CERT_DestroyName(subject_name);
+ CERT_DestroyCertificateRequest(cert_request);
+ return NULL;
+ }
+ CERTCertificate* cert = CERT_CreateCertificate(serial_number, subject_name,
+ validity, cert_request);
+ if (!cert) {
+ PRErrorCode prerr = PR_GetError();
+ LOG(ERROR) << "Failed to create certificate: " << prerr;
+ }
+
+ // Cleanup for resources used to generate the cert.
+ CERT_DestroyName(subject_name);
+ CERT_DestroyValidity(validity);
+ CERT_DestroyCertificateRequest(cert_request);
+
+ return cert;
+}
+
+// Signs a certificate object, with |key| generating a new X509Certificate
+// and destroying the passed certificate object (even when NULL is returned).
+// The logic of this method references SignCert() in NSS utility certutil:
+// http://mxr.mozilla.org/security/ident?i=SignCert.
+// Returns true on success or false if an error is encountered in the
+// certificate signing process.
+bool SignCertificate(
+ CERTCertificate* cert,
+ SECKEYPrivateKey* key) {
+ // |arena| is used to encode the cert.
+ PLArenaPool* arena = cert->arena;
+ SECOidTag algo_id = SEC_GetSignatureAlgorithmOidTag(key->keyType,
+ SEC_OID_SHA1);
+ if (algo_id == SEC_OID_UNKNOWN)
+ return false;
+
+ SECStatus rv = SECOID_SetAlgorithmID(arena, &cert->signature, algo_id, 0);
+ if (rv != SECSuccess)
+ return false;
+
+ // Generate a cert of version 3.
+ *(cert->version.data) = 2;
+ cert->version.len = 1;
+
+ SECItem der = { siBuffer, NULL, 0 };
+
+ // Use ASN1 DER to encode the cert.
+ void* encode_result = SEC_ASN1EncodeItem(
+ NULL, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
+ if (!encode_result)
+ return false;
+
+ // Allocate space to contain the signed cert.
+ SECItem result = { siBuffer, NULL, 0 };
+
+ // Sign the ASN1 encoded cert and save it to |result|.
+ rv = DerSignData(arena, &result, &der, key, algo_id);
+ PORT_Free(der.data);
+ if (rv != SECSuccess) {
+ DLOG(ERROR) << "DerSignData: " << PORT_GetError();
+ return false;
+ }
+
+ // Save the signed result to the cert.
+ cert->derCert = result;
+
+ return true;
+}
+
+#if defined(USE_NSS) || defined(OS_IOS)
+// Callback for CERT_DecodeCertPackage(), used in
+// CreateOSCertHandlesFromBytes().
+SECStatus PR_CALLBACK CollectCertsCallback(void* arg,
+ SECItem** certs,
+ int num_certs) {
+ X509Certificate::OSCertHandles* results =
+ reinterpret_cast<X509Certificate::OSCertHandles*>(arg);
+
+ for (int i = 0; i < num_certs; ++i) {
+ X509Certificate::OSCertHandle handle =
+ X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<char*>(certs[i]->data), certs[i]->len);
+ if (handle)
+ results->push_back(handle);
+ }
+
+ return SECSuccess;
+}
+
+typedef scoped_ptr_malloc<
+ CERTName,
+ crypto::NSSDestroyer<CERTName, CERT_DestroyName> > ScopedCERTName;
+
+// Create a new CERTName object from its encoded representation.
+// |arena| is the allocation pool to use.
+// |data| points to a DER-encoded X.509 DistinguishedName.
+// Return a new CERTName pointer on success, or NULL.
+CERTName* CreateCertNameFromEncoded(PLArenaPool* arena,
+ const base::StringPiece& data) {
+ if (!arena)
+ return NULL;
+
+ ScopedCERTName name(PORT_ArenaZNew(arena, CERTName));
+ if (!name.get())
+ return NULL;
+
+ SECItem item;
+ item.len = static_cast<unsigned int>(data.length());
+ item.data = reinterpret_cast<unsigned char*>(
+ const_cast<char*>(data.data()));
+
+ SECStatus rv = SEC_ASN1DecodeItem(
+ arena, name.get(), SEC_ASN1_GET(CERT_NameTemplate), &item);
+ if (rv != SECSuccess)
+ return NULL;
+
+ return name.release();
+}
+
+#endif // defined(USE_NSS) || defined(OS_IOS)
+
+} // namespace
+
+namespace x509_util {
+
+bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after,
+ std::string* der_cert) {
+ DCHECK(key);
+ CERTCertificate* cert = CreateCertificate(key->public_key(),
+ subject,
+ serial_number,
+ not_valid_before,
+ not_valid_after);
+ if (!cert)
+ return false;
+
+ if (!SignCertificate(cert, key->key())) {
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ der_cert->assign(reinterpret_cast<char*>(cert->derCert.data),
+ cert->derCert.len);
+ CERT_DestroyCertificate(cert);
+ return true;
+}
+
+bool IsSupportedValidityRange(base::Time not_valid_before,
+ base::Time not_valid_after) {
+ CERTValidity* validity = CERT_CreateValidity(
+ crypto::BaseTimeToPRTime(not_valid_before),
+ crypto::BaseTimeToPRTime(not_valid_after));
+
+ if (!validity)
+ return false;
+
+ CERT_DestroyValidity(validity);
+ return true;
+}
+
+bool CreateDomainBoundCertEC(crypto::ECPrivateKey* key,
+ const std::string& domain,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after,
+ std::string* der_cert) {
+ DCHECK(key);
+
+ CERTCertificate* cert = CreateCertificate(key->public_key(),
+ "CN=anonymous.invalid",
+ serial_number,
+ not_valid_before,
+ not_valid_after);
+
+ if (!cert)
+ return false;
+
+ // Create opaque handle used to add extensions later.
+ void* cert_handle;
+ if ((cert_handle = CERT_StartCertExtensions(cert)) == NULL) {
+ LOG(ERROR) << "Unable to get opaque handle for adding extensions";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ // Create SECItem for IA5String encoding.
+ SECItem domain_string_item = {
+ siAsciiString,
+ (unsigned char*)domain.data(),
+ static_cast<unsigned>(domain.size())
+ };
+
+ // IA5Encode and arena allocate SECItem
+ SECItem* asn1_domain_string = SEC_ASN1EncodeItem(
+ cert->arena, NULL, &domain_string_item,
+ SEC_ASN1_GET(SEC_IA5StringTemplate));
+ if (asn1_domain_string == NULL) {
+ LOG(ERROR) << "Unable to get ASN1 encoding for domain in domain_bound_cert"
+ " extension";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ // Add the extension to the opaque handle
+ if (CERT_AddExtension(
+ cert_handle,
+ DomainBoundCertOIDWrapper::GetInstance()->domain_bound_cert_oid_tag(),
+ asn1_domain_string,
+ PR_TRUE,
+ PR_TRUE) != SECSuccess){
+ LOG(ERROR) << "Unable to add domain bound cert extension to opaque handle";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ // Copy extension into x509 cert
+ if (CERT_FinishExtensions(cert_handle) != SECSuccess){
+ LOG(ERROR) << "Unable to copy extension to X509 cert";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ if (!SignCertificate(cert, key->key())) {
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ DCHECK(cert->derCert.len);
+ // XXX copied from X509Certificate::GetDEREncoded
+ der_cert->clear();
+ der_cert->append(reinterpret_cast<char*>(cert->derCert.data),
+ cert->derCert.len);
+ CERT_DestroyCertificate(cert);
+ return true;
+}
+
+#if defined(USE_NSS) || defined(OS_IOS)
+void ParsePrincipal(CERTName* name, CertPrincipal* principal) {
+// Starting in NSS 3.15, CERTGetNameFunc takes a const CERTName* argument.
+#if NSS_VMINOR >= 15
+ typedef char* (*CERTGetNameFunc)(const CERTName* name);
+#else
+ typedef char* (*CERTGetNameFunc)(CERTName* name);
+#endif
+
+ // TODO(jcampan): add business_category and serial_number.
+ // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and
+ // CERT_GetDomainComponentName functions, but they return only the most
+ // general (the first) RDN. NSS doesn't have a function for the street
+ // address.
+ static const SECOidTag kOIDs[] = {
+ SEC_OID_AVA_STREET_ADDRESS,
+ SEC_OID_AVA_ORGANIZATION_NAME,
+ SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
+ SEC_OID_AVA_DC };
+
+ std::vector<std::string>* values[] = {
+ &principal->street_addresses,
+ &principal->organization_names,
+ &principal->organization_unit_names,
+ &principal->domain_components };
+ DCHECK_EQ(arraysize(kOIDs), arraysize(values));
+
+ CERTRDN** rdns = name->rdns;
+ for (size_t rdn = 0; rdns[rdn]; ++rdn) {
+ CERTAVA** avas = rdns[rdn]->avas;
+ for (size_t pair = 0; avas[pair] != 0; ++pair) {
+ SECOidTag tag = CERT_GetAVATag(avas[pair]);
+ for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
+ if (kOIDs[oid] == tag) {
+ SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
+ if (!decode_item)
+ break;
+ // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
+ std::string value(reinterpret_cast<char*>(decode_item->data),
+ decode_item->len);
+ values[oid]->push_back(value);
+ SECITEM_FreeItem(decode_item, PR_TRUE);
+ break;
+ }
+ }
+ }
+ }
+
+ // Get CN, L, S, and C.
+ CERTGetNameFunc get_name_funcs[4] = {
+ CERT_GetCommonName, CERT_GetLocalityName,
+ CERT_GetStateName, CERT_GetCountryName };
+ std::string* single_values[4] = {
+ &principal->common_name, &principal->locality_name,
+ &principal->state_or_province_name, &principal->country_name };
+ for (size_t i = 0; i < arraysize(get_name_funcs); ++i) {
+ char* value = get_name_funcs[i](name);
+ if (value) {
+ single_values[i]->assign(value);
+ PORT_Free(value);
+ }
+ }
+}
+
+void ParseDate(const SECItem* der_date, base::Time* result) {
+ PRTime prtime;
+ SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date);
+ DCHECK_EQ(SECSuccess, rv);
+ *result = crypto::PRTimeToBaseTime(prtime);
+}
+
+std::string ParseSerialNumber(const CERTCertificate* certificate) {
+ return std::string(reinterpret_cast<char*>(certificate->serialNumber.data),
+ certificate->serialNumber.len);
+}
+
+void GetSubjectAltName(CERTCertificate* cert_handle,
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ SECItem alt_name;
+ SECStatus rv = CERT_FindCertExtension(cert_handle,
+ SEC_OID_X509_SUBJECT_ALT_NAME,
+ &alt_name);
+ if (rv != SECSuccess)
+ return;
+
+ PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ DCHECK(arena != NULL);
+
+ CERTGeneralName* alt_name_list;
+ alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name);
+ SECITEM_FreeItem(&alt_name, PR_FALSE);
+
+ CERTGeneralName* name = alt_name_list;
+ while (name) {
+ // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
+ // respectively, both of which can be byte copied from
+ // SECItemType::data into the appropriate output vector.
+ if (dns_names && name->type == certDNSName) {
+ dns_names->push_back(std::string(
+ reinterpret_cast<char*>(name->name.other.data),
+ name->name.other.len));
+ } else if (ip_addrs && name->type == certIPAddress) {
+ ip_addrs->push_back(std::string(
+ reinterpret_cast<char*>(name->name.other.data),
+ name->name.other.len));
+ }
+ name = CERT_GetNextGeneralName(name);
+ if (name == alt_name_list)
+ break;
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+}
+
+X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ X509Certificate::Format format) {
+ X509Certificate::OSCertHandles results;
+ if (length < 0)
+ return results;
+
+ crypto::EnsureNSSInit();
+
+ if (!NSS_IsInitialized())
+ return results;
+
+ switch (format) {
+ case X509Certificate::FORMAT_SINGLE_CERTIFICATE: {
+ X509Certificate::OSCertHandle handle =
+ X509Certificate::CreateOSCertHandleFromBytes(data, length);
+ if (handle)
+ results.push_back(handle);
+ break;
+ }
+ case X509Certificate::FORMAT_PKCS7: {
+ // Make a copy since CERT_DecodeCertPackage may modify it
+ std::vector<char> data_copy(data, data + length);
+
+ SECStatus result = CERT_DecodeCertPackage(&data_copy[0],
+ length, CollectCertsCallback, &results);
+ if (result != SECSuccess)
+ results.clear();
+ break;
+ }
+ default:
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+
+ return results;
+}
+
+X509Certificate::OSCertHandle ReadOSCertHandleFromPickle(
+ PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return NULL;
+
+ return X509Certificate::CreateOSCertHandleFromBytes(data, length);
+}
+
+void GetPublicKeyInfo(CERTCertificate* handle,
+ size_t* size_bits,
+ X509Certificate::PublicKeyType* type) {
+ // Since we might fail, set the output parameters to default values first.
+ *type = X509Certificate::kPublicKeyTypeUnknown;
+ *size_bits = 0;
+
+ crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(handle));
+ if (!key.get())
+ return;
+
+ *size_bits = SECKEY_PublicKeyStrengthInBits(key.get());
+
+ switch (key->keyType) {
+ case rsaKey:
+ *type = X509Certificate::kPublicKeyTypeRSA;
+ break;
+ case dsaKey:
+ *type = X509Certificate::kPublicKeyTypeDSA;
+ break;
+ case dhKey:
+ *type = X509Certificate::kPublicKeyTypeDH;
+ break;
+ case ecKey:
+ *type = X509Certificate::kPublicKeyTypeECDSA;
+ break;
+ default:
+ *type = X509Certificate::kPublicKeyTypeUnknown;
+ *size_bits = 0;
+ break;
+ }
+}
+
+bool GetIssuersFromEncodedList(
+ const std::vector<std::string>& encoded_issuers,
+ PLArenaPool* arena,
+ std::vector<CERTName*>* out) {
+ std::vector<CERTName*> result;
+ for (size_t n = 0; n < encoded_issuers.size(); ++n) {
+ CERTName* name = CreateCertNameFromEncoded(arena, encoded_issuers[n]);
+ if (name != NULL)
+ result.push_back(name);
+ }
+
+ if (result.size() == encoded_issuers.size()) {
+ out->swap(result);
+ return true;
+ }
+
+ for (size_t n = 0; n < result.size(); ++n)
+ CERT_DestroyName(result[n]);
+ return false;
+}
+
+
+bool IsCertificateIssuedBy(const std::vector<CERTCertificate*>& cert_chain,
+ const std::vector<CERTName*>& valid_issuers) {
+ for (size_t n = 0; n < cert_chain.size(); ++n) {
+ CERTName* cert_issuer = &cert_chain[n]->issuer;
+ for (size_t i = 0; i < valid_issuers.size(); ++i) {
+ if (CERT_CompareName(valid_issuers[i], cert_issuer) == SECEqual)
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string GetUniqueNicknameForSlot(const std::string& nickname,
+ const SECItem* subject,
+ PK11SlotInfo* slot) {
+ int index = 2;
+ std::string new_name = nickname;
+ std::string temp_nickname = new_name;
+ std::string token_name;
+
+ if (!slot)
+ return new_name;
+
+ if (!PK11_IsInternalKeySlot(slot)) {
+ token_name.assign(PK11_GetTokenName(slot));
+ token_name.append(":");
+
+ temp_nickname = token_name + new_name;
+ }
+
+ while (SEC_CertNicknameConflict(temp_nickname.c_str(),
+ const_cast<SECItem*>(subject),
+ CERT_GetDefaultCertDB())) {
+ base::SStringPrintf(&new_name, "%s #%d", nickname.c_str(), index++);
+ temp_nickname = token_name + new_name;
+ }
+
+ return new_name;
+}
+
+#endif // defined(USE_NSS) || defined(OS_IOS)
+
+} // namespace x509_util
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util_nss.h b/chromium/net/cert/x509_util_nss.h
new file mode 100644
index 00000000000..877dc4856d2
--- /dev/null
+++ b/chromium/net/cert/x509_util_nss.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_UTIL_NSS_H_
+#define NET_CERT_X509_UTIL_NSS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/cert/x509_certificate.h"
+
+class PickleIterator;
+
+typedef struct CERTCertificateStr CERTCertificate;
+typedef struct CERTNameStr CERTName;
+typedef struct PK11SlotInfoStr PK11SlotInfo;
+typedef struct PLArenaPool PLArenaPool;
+typedef struct SECItemStr SECItem;
+
+namespace net {
+
+namespace x509_util {
+
+#if defined(USE_NSS) || defined(OS_IOS)
+// Parses the Principal attribute from |name| and outputs the result in
+// |principal|.
+void ParsePrincipal(CERTName* name,
+ CertPrincipal* principal);
+
+// Parses the date from |der_date| and outputs the result in |result|.
+void ParseDate(const SECItem* der_date, base::Time* result);
+
+// Parses the serial number from |certificate|.
+std::string ParseSerialNumber(const CERTCertificate* certificate);
+
+// Gets the subjectAltName extension field from the certificate, if any.
+void GetSubjectAltName(CERTCertificate* cert_handle,
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs);
+
+// Creates all possible OS certificate handles from |data| encoded in a specific
+// |format|. Returns an empty collection on failure.
+X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ X509Certificate::Format format);
+
+// Reads a single certificate from |pickle_iter| and returns a platform-specific
+// certificate handle. Returns an invalid handle, NULL, on failure.
+X509Certificate::OSCertHandle ReadOSCertHandleFromPickle(
+ PickleIterator* pickle_iter);
+
+// Sets |*size_bits| to be the length of the public key in bits, and sets
+// |*type| to one of the |PublicKeyType| values. In case of
+// |kPublicKeyTypeUnknown|, |*size_bits| will be set to 0.
+void GetPublicKeyInfo(CERTCertificate* handle,
+ size_t* size_bits,
+ X509Certificate::PublicKeyType* type);
+
+// Create a list of CERTName objects from a list of DER-encoded X.509
+// DistinguishedName items. All objects are created in a given arena.
+// |encoded_issuers| is the list of encoded DNs.
+// |arena| is the arena used for all allocations.
+// |out| will receive the result list on success.
+// Return true on success. On failure, the caller must free the
+// intermediate CERTName objects pushed to |out|.
+bool GetIssuersFromEncodedList(
+ const std::vector<std::string>& issuers,
+ PLArenaPool* arena,
+ std::vector<CERTName*>* out);
+
+// Returns true iff a certificate is issued by any of the issuers listed
+// by name in |valid_issuers|.
+// |cert_chain| is the certificate's chain.
+// |valid_issuers| is a list of strings, where each string contains
+// a DER-encoded X.509 Distinguished Name.
+bool IsCertificateIssuedBy(const std::vector<CERTCertificate*>& cert_chain,
+ const std::vector<CERTName*>& valid_issuers);
+
+// Generates a unique nickname for |slot|, returning |nickname| if it is
+// already unique.
+//
+// Note: The nickname returned will NOT include the token name, thus the
+// token name must be prepended if calling an NSS function that expects
+// <token>:<nickname>.
+// TODO(gspencer): Internationalize this: it's wrong to hard-code English.
+std::string GetUniqueNicknameForSlot(const std::string& nickname,
+ const SECItem* subject,
+ PK11SlotInfo* slot);
+#endif // defined(USE_NSS) || defined(OS_IOS)
+
+} // namespace x509_util
+
+} // namespace net
+
+#endif // NET_CERT_X509_UTIL_NSS_H_
diff --git a/chromium/net/cert/x509_util_nss_unittest.cc b/chromium/net/cert/x509_util_nss_unittest.cc
new file mode 100644
index 00000000000..968cc147ec5
--- /dev/null
+++ b/chromium/net/cert/x509_util_nss_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util.h"
+#include "net/cert/x509_util_nss.h"
+
+#include <cert.h>
+#include <secoid.h>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/ec_private_key.h"
+#include "crypto/scoped_nss_types.h"
+#include "crypto/signature_verifier.h"
+#include "net/cert/x509_certificate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+CERTCertificate* CreateNSSCertHandleFromBytes(const char* data, size_t length) {
+ SECItem der_cert;
+ der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
+ der_cert.len = length;
+ der_cert.type = siDERCertBuffer;
+
+ // Parse into a certificate structure.
+ return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL,
+ PR_FALSE, PR_TRUE);
+}
+
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+void VerifyCertificateSignature(const std::string& der_cert,
+ const std::vector<uint8>& der_spki) {
+ crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+
+ CERTSignedData sd;
+ memset(&sd, 0, sizeof(sd));
+
+ SECItem der_cert_item = {
+ siDERCertBuffer,
+ reinterpret_cast<unsigned char*>(const_cast<char*>(der_cert.data())),
+ static_cast<unsigned int>(der_cert.size())
+ };
+ SECStatus rv = SEC_ASN1DecodeItem(arena.get(), &sd,
+ SEC_ASN1_GET(CERT_SignedDataTemplate),
+ &der_cert_item);
+ ASSERT_EQ(SECSuccess, rv);
+
+ // The CERTSignedData.signatureAlgorithm is decoded, but SignatureVerifier
+ // wants the DER encoded form, so re-encode it again.
+ SECItem* signature_algorithm = SEC_ASN1EncodeItem(
+ arena.get(),
+ NULL,
+ &sd.signatureAlgorithm,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
+ ASSERT_TRUE(signature_algorithm);
+
+ crypto::SignatureVerifier verifier;
+ bool ok = verifier.VerifyInit(
+ signature_algorithm->data,
+ signature_algorithm->len,
+ sd.signature.data,
+ sd.signature.len / 8, // Signature is a BIT STRING, convert to bytes.
+ &der_spki[0],
+ der_spki.size());
+
+ ASSERT_TRUE(ok);
+ verifier.VerifyUpdate(sd.data.data,
+ sd.data.len);
+
+ ok = verifier.VerifyFinal();
+ EXPECT_TRUE(ok);
+}
+#endif // !defined(OS_WIN) && !defined(OS_MACOSX)
+
+void VerifyDomainBoundCert(const std::string& domain,
+ const std::string& der_cert) {
+ // Origin Bound Cert OID.
+ static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";
+
+ // Create object neccessary for extension lookup call.
+ SECItem extension_object = {
+ siAsciiString,
+ (unsigned char*)domain.data(),
+ static_cast<unsigned int>(domain.size())
+ };
+
+ // IA5Encode and arena allocate SECItem.
+ PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ SECItem* expected = SEC_ASN1EncodeItem(arena,
+ NULL,
+ &extension_object,
+ SEC_ASN1_GET(SEC_IA5StringTemplate));
+
+ ASSERT_NE(static_cast<SECItem*>(NULL), expected);
+
+ // Create OID SECItem.
+ SECItem ob_cert_oid = { siDEROID, NULL, 0 };
+ SECStatus ok = SEC_StringToOID(arena, &ob_cert_oid,
+ oid_string, 0);
+
+ ASSERT_EQ(SECSuccess, ok);
+
+ SECOidTag ob_cert_oid_tag = SECOID_FindOIDTag(&ob_cert_oid);
+
+ ASSERT_NE(SEC_OID_UNKNOWN, ob_cert_oid_tag);
+
+ // This test is run on Mac and Win where X509Certificate::os_cert_handle isn't
+ // an NSS type, so we have to manually create a NSS certificate object so we
+ // can use CERT_FindCertExtension. We also check the subject and validity
+ // times using NSS since X509Certificate will fail with EC certs on OSX 10.5
+ // (http://crbug.com/101231).
+ CERTCertificate* nss_cert = CreateNSSCertHandleFromBytes(
+ der_cert.data(), der_cert.size());
+
+ char* common_name = CERT_GetCommonName(&nss_cert->subject);
+ ASSERT_TRUE(common_name);
+ EXPECT_STREQ("anonymous.invalid", common_name);
+ PORT_Free(common_name);
+ EXPECT_EQ(SECSuccess, CERT_CertTimesValid(nss_cert));
+
+ // Lookup Origin Bound Cert extension in generated cert.
+ SECItem actual = { siBuffer, NULL, 0 };
+ ok = CERT_FindCertExtension(nss_cert,
+ ob_cert_oid_tag,
+ &actual);
+ CERT_DestroyCertificate(nss_cert);
+ ASSERT_EQ(SECSuccess, ok);
+
+ // Compare expected and actual extension values.
+ PRBool result = SECITEM_ItemsAreEqual(expected, &actual);
+ ASSERT_TRUE(result);
+
+ // Do Cleanup.
+ SECITEM_FreeItem(&actual, PR_FALSE);
+ PORT_FreeArena(arena, PR_FALSE);
+}
+
+} // namespace
+
+// This test creates a domain-bound cert from an EC private key and
+// then verifies the content of the certificate.
+TEST(X509UtilNSSTest, CreateDomainBoundCertEC) {
+ // Create a sample ASCII weborigin.
+ std::string domain = "weborigin.com";
+ base::Time now = base::Time::Now();
+
+ scoped_ptr<crypto::ECPrivateKey> private_key(
+ crypto::ECPrivateKey::Create());
+ std::string der_cert;
+ ASSERT_TRUE(x509_util::CreateDomainBoundCertEC(
+ private_key.get(),
+ domain, 1,
+ now,
+ now + base::TimeDelta::FromDays(1),
+ &der_cert));
+
+ VerifyDomainBoundCert(domain, der_cert);
+
+#if !defined(OS_WIN) && !defined(OS_MACOSX)
+ // signature_verifier_win and signature_verifier_mac can't handle EC certs.
+ std::vector<uint8> spki;
+ ASSERT_TRUE(private_key->ExportPublicKey(&spki));
+ VerifyCertificateSignature(der_cert, spki);
+#endif
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util_openssl.cc b/chromium/net/cert/x509_util_openssl.cc
new file mode 100644
index 00000000000..e4dec992575
--- /dev/null
+++ b/chromium/net/cert/x509_util_openssl.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util.h"
+#include "net/cert/x509_util_openssl.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/cert/x509_cert_types.h"
+
+namespace net {
+
+namespace x509_util {
+
+bool IsSupportedValidityRange(base::Time not_valid_before,
+ base::Time not_valid_after) {
+ if (not_valid_before > not_valid_after)
+ return false;
+
+ // The validity field of a certificate can only encode years 1-9999.
+
+ // Compute the base::Time values corresponding to Jan 1st,0001 and
+ // Jan 1st, 10000 respectively. Done by using the pre-computed numbers
+ // of days between these dates and the Unix epoch, i.e. Jan 1st, 1970,
+ // using the following Python script:
+ //
+ // from datetime import date as D
+ // print (D(1970,1,1)-D(1,1,1)) # -> 719162 days
+ // print (D(9999,12,31)-D(1970,1,1)) # -> 2932896 days
+ //
+ // Note: This ignores leap seconds, but should be enough in practice.
+ //
+ const int64 kDaysFromYear0001ToUnixEpoch = 719162;
+ const int64 kDaysFromUnixEpochToYear10000 = 2932896 + 1;
+ const base::Time kEpoch = base::Time::UnixEpoch();
+ const base::Time kYear0001 = kEpoch -
+ base::TimeDelta::FromDays(kDaysFromYear0001ToUnixEpoch);
+ const base::Time kYear10000 = kEpoch +
+ base::TimeDelta::FromDays(kDaysFromUnixEpochToYear10000);
+
+ if (not_valid_before < kYear0001 || not_valid_before >= kYear10000 ||
+ not_valid_after < kYear0001 || not_valid_after >= kYear10000)
+ return false;
+
+ return true;
+}
+
+bool CreateDomainBoundCertEC(
+ crypto::ECPrivateKey* key,
+ const std::string& domain,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after,
+ std::string* der_cert) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
+ const std::string& common_name,
+ uint32 serial_number,
+ base::Time not_valid_before,
+ base::Time not_valid_after,
+ std::string* der_encoded) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name,
+ int index,
+ std::string* key,
+ std::string* value) {
+ X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
+ if (!entry)
+ return false;
+
+ if (key) {
+ ASN1_OBJECT* object = X509_NAME_ENTRY_get_object(entry);
+ key->assign(OBJ_nid2sn(OBJ_obj2nid(object)));
+ }
+
+ ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
+ if (!data)
+ return false;
+
+ unsigned char* buf = NULL;
+ int len = ASN1_STRING_to_UTF8(&buf, data);
+ if (len <= 0)
+ return false;
+
+ value->assign(reinterpret_cast<const char*>(buf), len);
+ OPENSSL_free(buf);
+ return true;
+}
+
+bool ParsePrincipalValueByIndex(X509_NAME* name,
+ int index,
+ std::string* value) {
+ return ParsePrincipalKeyAndValueByIndex(name, index, NULL, value);
+}
+
+bool ParsePrincipalValueByNID(X509_NAME* name, int nid, std::string* value) {
+ int index = X509_NAME_get_index_by_NID(name, nid, -1);
+ if (index < 0)
+ return false;
+
+ return ParsePrincipalValueByIndex(name, index, value);
+}
+
+bool ParseDate(ASN1_TIME* x509_time, base::Time* time) {
+ if (!x509_time ||
+ (x509_time->type != V_ASN1_UTCTIME &&
+ x509_time->type != V_ASN1_GENERALIZEDTIME))
+ return false;
+
+ base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data),
+ x509_time->length);
+
+ CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ?
+ CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME;
+ return ParseCertificateDate(str_date, format, time);
+}
+
+} // namespace x509_util
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util_openssl.h b/chromium/net/cert/x509_util_openssl.h
new file mode 100644
index 00000000000..ec4538003f3
--- /dev/null
+++ b/chromium/net/cert/x509_util_openssl.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_CERT_X509_UTIL_OPENSSL_H_
+#define NET_CERT_X509_UTIL_OPENSSL_H_
+
+#include <openssl/asn1.h>
+#include <openssl/x509v3.h>
+
+#include <string>
+#include <vector>
+
+#include "net/base/net_export.h"
+
+namespace base {
+class Time;
+} // namespace base
+
+namespace net {
+
+// A collection of helper functions to fetch data from OpenSSL X509 certificates
+// into more convenient std / base datatypes.
+namespace x509_util {
+
+bool NET_EXPORT ParsePrincipalKeyAndValueByIndex(X509_NAME* name,
+ int index,
+ std::string* key,
+ std::string* value);
+
+bool NET_EXPORT ParsePrincipalValueByIndex(X509_NAME* name,
+ int index,
+ std::string* value);
+
+bool NET_EXPORT ParsePrincipalValueByNID(X509_NAME* name,
+ int nid,
+ std::string* value);
+
+bool NET_EXPORT ParseDate(ASN1_TIME* x509_time, base::Time* time);
+
+} // namespace x509_util
+
+} // namespace net
+
+#endif // NET_CERT_X509_UTIL_OPENSSL_H_
diff --git a/chromium/net/cert/x509_util_openssl_unittest.cc b/chromium/net/cert/x509_util_openssl_unittest.cc
new file mode 100644
index 00000000000..f237602ecbd
--- /dev/null
+++ b/chromium/net/cert/x509_util_openssl_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/ec_private_key.h"
+#include "net/cert/x509_util.h"
+#include "net/cert/x509_util_openssl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(X509UtilOpenSSLTest, IsSupportedValidityRange) {
+ base::Time now = base::Time::Now();
+ EXPECT_TRUE(x509_util::IsSupportedValidityRange(now, now));
+ EXPECT_FALSE(x509_util::IsSupportedValidityRange(
+ now, now - base::TimeDelta::FromSeconds(1)));
+
+ // See x509_util_openssl.cc to see how these were computed.
+ const int64 kDaysFromYear0001ToUnixEpoch = 719162;
+ const int64 kDaysFromUnixEpochToYear10000 = 2932896 + 1;
+
+ // When computing too_old / too_late, add one day to account for
+ // possible leap seconds.
+ base::Time too_old = base::Time::UnixEpoch() -
+ base::TimeDelta::FromDays(kDaysFromYear0001ToUnixEpoch + 1);
+
+ base::Time too_late = base::Time::UnixEpoch() +
+ base::TimeDelta::FromDays(kDaysFromUnixEpochToYear10000 + 1);
+
+ EXPECT_FALSE(x509_util::IsSupportedValidityRange(too_old, too_old));
+ EXPECT_FALSE(x509_util::IsSupportedValidityRange(too_old, now));
+
+ EXPECT_FALSE(x509_util::IsSupportedValidityRange(now, too_late));
+ EXPECT_FALSE(x509_util::IsSupportedValidityRange(too_late, too_late));
+}
+
+// For OpenSSL, x509_util::CreateDomainBoundCertEC() is not yet implemented
+// and should return false. This unit test ensures that a stub implementation
+// is present.
+TEST(X509UtilOpenSSLTest, CreateDomainBoundCertNotImplemented) {
+ std::string domain = "weborigin.com";
+ base::Time now = base::Time::Now();
+ scoped_ptr<crypto::ECPrivateKey> private_key(
+ crypto::ECPrivateKey::Create());
+ std::string der_cert;
+ EXPECT_FALSE(x509_util::CreateDomainBoundCertEC(
+ private_key.get(),
+ domain, 1,
+ now,
+ now + base::TimeDelta::FromDays(1),
+ &der_cert));
+ EXPECT_TRUE(der_cert.empty());
+
+}
+
+} // namespace net
diff --git a/chromium/net/cert/x509_util_unittest.cc b/chromium/net/cert/x509_util_unittest.cc
new file mode 100644
index 00000000000..cc13d96f1bf
--- /dev/null
+++ b/chromium/net/cert/x509_util_unittest.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/cert/x509_util.h"
+
+#include <algorithm>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "crypto/rsa_private_key.h"
+#include "net/cert/x509_certificate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace x509_util {
+
+TEST(X509UtilTest, SortClientCertificates) {
+ CertificateList certs;
+
+ const base::Time now = base::Time::Now();
+ const base::TimeDelta five_days = base::TimeDelta::FromDays(5);
+
+ certs.push_back(scoped_refptr<X509Certificate>(NULL));
+ certs.push_back(new X509Certificate(
+ "expired", "expired",
+ base::Time::UnixEpoch(), base::Time::UnixEpoch()));
+ certs.push_back(new X509Certificate(
+ "not yet valid", "not yet valid",
+ base::Time::Max(), base::Time::Max()));
+ certs.push_back(new X509Certificate(
+ "older cert", "older cert",
+ now - five_days, now + five_days));
+ certs.push_back(scoped_refptr<X509Certificate>(NULL));
+ certs.push_back(new X509Certificate(
+ "newer cert", "newer cert",
+ now - base::TimeDelta::FromDays(3), now + five_days));
+
+ std::sort(certs.begin(), certs.end(), ClientCertSorter());
+
+ ASSERT_TRUE(certs[0].get());
+ EXPECT_EQ("newer cert", certs[0]->subject().common_name);
+ ASSERT_TRUE(certs[1].get());
+ EXPECT_EQ("older cert", certs[1]->subject().common_name);
+ ASSERT_TRUE(certs[2].get());
+ EXPECT_EQ("not yet valid", certs[2]->subject().common_name);
+ ASSERT_TRUE(certs[3].get());
+ EXPECT_EQ("expired", certs[3]->subject().common_name);
+ ASSERT_FALSE(certs[4].get());
+ ASSERT_FALSE(certs[5].get());
+}
+
+#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX)
+// This test creates a self-signed cert from a private key and then verify the
+// content of the certificate.
+TEST(X509UtilTest, CreateSelfSigned) {
+ scoped_ptr<crypto::RSAPrivateKey> private_key(
+ crypto::RSAPrivateKey::Create(1024));
+
+ ASSERT_TRUE(private_key.get());
+
+ std::string der_cert;
+ ASSERT_TRUE(x509_util::CreateSelfSignedCert(
+ private_key.get(),
+ "CN=subject",
+ 1,
+ base::Time::Now(),
+ base::Time::Now() + base::TimeDelta::FromDays(1),
+ &der_cert));
+
+ scoped_refptr<X509Certificate> cert(X509Certificate::CreateFromBytes(
+ der_cert.data(), der_cert.size()));
+ ASSERT_TRUE(cert.get());
+
+ EXPECT_EQ("subject", cert->subject().GetDisplayName());
+ EXPECT_FALSE(cert->HasExpired());
+
+ cert = NULL;
+
+ const uint8 private_key_info[] = {
+ 0x30, 0x82, 0x02, 0x78, 0x02, 0x01, 0x00, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
+ 0x02, 0x62, 0x30, 0x82, 0x02, 0x5e, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b,
+ 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61,
+ 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08,
+ 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64,
+ 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4,
+ 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a,
+ 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f,
+ 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f,
+ 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17,
+ 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5,
+ 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff,
+ 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85,
+ 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9,
+ 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5,
+ 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18,
+ 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93,
+ 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0x02, 0x81, 0x80, 0x03, 0x61, 0x89,
+ 0x37, 0xcb, 0xf2, 0x98, 0xa0, 0xce, 0xb4, 0xcb,
+ 0x16, 0x13, 0xf0, 0xe6, 0xaf, 0x5c, 0xc5, 0xa7,
+ 0x69, 0x71, 0xca, 0xba, 0x8d, 0xe0, 0x4d, 0xdd,
+ 0xed, 0xb8, 0x48, 0x8b, 0x16, 0x93, 0x36, 0x95,
+ 0xc2, 0x91, 0x40, 0x65, 0x17, 0xbd, 0x7f, 0xd6,
+ 0xad, 0x9e, 0x30, 0x28, 0x46, 0xe4, 0x3e, 0xcc,
+ 0x43, 0x78, 0xf9, 0xfe, 0x1f, 0x33, 0x23, 0x1e,
+ 0x31, 0x12, 0x9d, 0x3c, 0xa7, 0x08, 0x82, 0x7b,
+ 0x7d, 0x25, 0x4e, 0x5e, 0x19, 0xa8, 0x9b, 0xed,
+ 0x86, 0xb2, 0xcb, 0x3c, 0xfe, 0x4e, 0xa1, 0xfa,
+ 0x62, 0x87, 0x3a, 0x17, 0xf7, 0x60, 0xec, 0x38,
+ 0x29, 0xe8, 0x4f, 0x34, 0x9f, 0x76, 0x9d, 0xee,
+ 0xa3, 0xf6, 0x85, 0x6b, 0x84, 0x43, 0xc9, 0x1e,
+ 0x01, 0xff, 0xfd, 0xd0, 0x29, 0x4c, 0xfa, 0x8e,
+ 0x57, 0x0c, 0xc0, 0x71, 0xa5, 0xbb, 0x88, 0x46,
+ 0x29, 0x5c, 0xc0, 0x4f, 0x01, 0x02, 0x41, 0x00,
+ 0xf5, 0x83, 0xa4, 0x64, 0x4a, 0xf2, 0xdd, 0x8c,
+ 0x2c, 0xed, 0xa8, 0xd5, 0x60, 0x5a, 0xe4, 0xc7,
+ 0xcc, 0x61, 0xcd, 0x38, 0x42, 0x20, 0xd3, 0x82,
+ 0x18, 0xf2, 0x35, 0x00, 0x72, 0x2d, 0xf7, 0x89,
+ 0x80, 0x67, 0xb5, 0x93, 0x05, 0x5f, 0xdd, 0x42,
+ 0xba, 0x16, 0x1a, 0xea, 0x15, 0xc6, 0xf0, 0xb8,
+ 0x8c, 0xbc, 0xbf, 0x54, 0x9e, 0xf1, 0xc1, 0xb2,
+ 0xb3, 0x8b, 0xb6, 0x26, 0x02, 0x30, 0xc4, 0x81,
+ 0x02, 0x41, 0x00, 0xc0, 0x60, 0x62, 0x80, 0xe1,
+ 0x22, 0x78, 0xf6, 0x9d, 0x83, 0x18, 0xeb, 0x72,
+ 0x45, 0xd7, 0xc8, 0x01, 0x7f, 0xa9, 0xca, 0x8f,
+ 0x7d, 0xd6, 0xb8, 0x31, 0x2b, 0x84, 0x7f, 0x62,
+ 0xd9, 0xa9, 0x22, 0x17, 0x7d, 0x06, 0x35, 0x6c,
+ 0xf3, 0xc1, 0x94, 0x17, 0x85, 0x5a, 0xaf, 0x9c,
+ 0x5c, 0x09, 0x3c, 0xcf, 0x2f, 0x44, 0x9d, 0xb6,
+ 0x52, 0x68, 0x5f, 0xf9, 0x59, 0xc8, 0x84, 0x2b,
+ 0x39, 0x22, 0x8f, 0x02, 0x41, 0x00, 0xb2, 0x04,
+ 0xe2, 0x0e, 0x56, 0xca, 0x03, 0x1a, 0xc0, 0xf9,
+ 0x12, 0x92, 0xa5, 0x6b, 0x42, 0xb8, 0x1c, 0xda,
+ 0x4d, 0x93, 0x9d, 0x5f, 0x6f, 0xfd, 0xc5, 0x58,
+ 0xda, 0x55, 0x98, 0x74, 0xfc, 0x28, 0x17, 0x93,
+ 0x1b, 0x75, 0x9f, 0x50, 0x03, 0x7f, 0x7e, 0xae,
+ 0xc8, 0x95, 0x33, 0x75, 0x2c, 0xd6, 0xa4, 0x35,
+ 0xb8, 0x06, 0x03, 0xba, 0x08, 0x59, 0x2b, 0x17,
+ 0x02, 0xdc, 0x4c, 0x7a, 0x50, 0x01, 0x02, 0x41,
+ 0x00, 0x9d, 0xdb, 0x39, 0x59, 0x09, 0xe4, 0x30,
+ 0xa0, 0x24, 0xf5, 0xdb, 0x2f, 0xf0, 0x2f, 0xf1,
+ 0x75, 0x74, 0x0d, 0x5e, 0xb5, 0x11, 0x73, 0xb0,
+ 0x0a, 0xaa, 0x86, 0x4c, 0x0d, 0xff, 0x7e, 0x1d,
+ 0xb4, 0x14, 0xd4, 0x09, 0x91, 0x33, 0x5a, 0xfd,
+ 0xa0, 0x58, 0x80, 0x9b, 0xbe, 0x78, 0x2e, 0x69,
+ 0x82, 0x15, 0x7c, 0x72, 0xf0, 0x7b, 0x18, 0x39,
+ 0xff, 0x6e, 0xeb, 0xc6, 0x86, 0xf5, 0xb4, 0xc7,
+ 0x6f, 0x02, 0x41, 0x00, 0x8d, 0x1a, 0x37, 0x0f,
+ 0x76, 0xc4, 0x82, 0xfa, 0x5c, 0xc3, 0x79, 0x35,
+ 0x3e, 0x70, 0x8a, 0xbf, 0x27, 0x49, 0xb0, 0x99,
+ 0x63, 0xcb, 0x77, 0x5f, 0xa8, 0x82, 0x65, 0xf6,
+ 0x03, 0x52, 0x51, 0xf1, 0xae, 0x2e, 0x05, 0xb3,
+ 0xc6, 0xa4, 0x92, 0xd1, 0xce, 0x6c, 0x72, 0xfb,
+ 0x21, 0xb3, 0x02, 0x87, 0xe4, 0xfd, 0x61, 0xca,
+ 0x00, 0x42, 0x19, 0xf0, 0xda, 0x5a, 0x53, 0xe3,
+ 0xb1, 0xc5, 0x15, 0xf3
+ };
+
+ std::vector<uint8> input;
+ input.resize(sizeof(private_key_info));
+ memcpy(&input.front(), private_key_info, sizeof(private_key_info));
+
+ private_key.reset(crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input));
+ ASSERT_TRUE(private_key.get());
+
+ ASSERT_TRUE(x509_util::CreateSelfSignedCert(
+ private_key.get(),
+ "CN=subject",
+ 1,
+ base::Time::Now(),
+ base::Time::Now() + base::TimeDelta::FromDays(1),
+ &der_cert));
+
+ cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
+ ASSERT_TRUE(cert.get());
+
+ EXPECT_EQ("subject", cert->subject().GetDisplayName());
+ EXPECT_FALSE(cert->HasExpired());
+}
+#endif
+
+} // namespace x509_util
+
+} // namespace net