summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-20 19:39:06 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-20 19:39:06 +0000
commitae780c8ff8cf7a6ee227beb8e7c6837f4933b02c (patch)
treeb452938a0277732cd8b3c55a191458ce2baaf3d1 /net
parentfe2255a16ad2c2ffb6390c1ec9d6b6bc0ae9a708 (diff)
downloadchromium_src-ae780c8ff8cf7a6ee227beb8e7c6837f4933b02c.zip
chromium_src-ae780c8ff8cf7a6ee227beb8e7c6837f4933b02c.tar.gz
chromium_src-ae780c8ff8cf7a6ee227beb8e7c6837f4933b02c.tar.bz2
net: support side-pinning of public keys.
Side-pinning allows a site to pin to a public key that is both offline and not a CA public key (without owning an intermediate CA themselves). We do this by supporting a superfluous certificate in the chain which contains a P256 public key and ECDSA signature over the leaf SPKI. BUG=none TEST=net_unittests Review URL: http://codereview.chromium.org/7951005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101993 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/transport_security_state.cc229
-rw-r--r--net/base/transport_security_state.h9
-rw-r--r--net/base/transport_security_state_unittest.cc106
-rw-r--r--net/socket/ssl_client_socket_nss.cc57
-rw-r--r--net/socket/ssl_client_socket_nss.h1
5 files changed, 402 insertions, 0 deletions
diff --git a/net/base/transport_security_state.cc b/net/base/transport_security_state.cc
index 2a7fafa..601ab35 100644
--- a/net/base/transport_security_state.cc
+++ b/net/base/transport_security_state.cc
@@ -4,6 +4,21 @@
#include "net/base/transport_security_state.h"
+#include <nspr.h>
+
+#include <cryptohi.h>
+#include <hasht.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+
+// NSS leaks #defines from its headers which will upset base/sha1.h.
+#if defined(SHA1_LENGTH)
+#undef SHA1_LENGTH
+#endif
+#if defined(SHA256_LENGTH)
+#undef SHA256_LENGTH
+#endif
+
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
@@ -275,6 +290,220 @@ bool TransportSecurityState::ParseHeader(const std::string& value,
}
}
+// Side pinning and superfluous certificates:
+//
+// In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a
+// Subject of CN=meta. When we find one we'll currently try and parse side
+// pinned key from it.
+//
+// A side pin is a key which can be pinned to, but also can be kept offline and
+// still held by the site owner. The CN=meta certificate is just a backwards
+// compatiable method of carrying a lump of bytes to the client. (We could use
+// a TLS extension just as well, but it's a lot easier for admins to add extra
+// certificates to the chain.)
+
+// A TagMap represents the simple key-value structure that we use. Keys are
+// 32-bit ints. Values are byte strings.
+typedef std::map<uint32, base::StringPiece> TagMap;
+
+// ParseTags parses a list of key-value pairs from |in| to |out| and advances
+// |in| past the data. The key-value pair data is:
+// u16le num_tags
+// u32le tag[num_tags]
+// u16le lengths[num_tags]
+// ...data...
+static bool ParseTags(base::StringPiece* in, TagMap *out) {
+ // Many part of Chrome already assume little-endian. This is just to help
+ // anyone who should try to port it in the future.
+#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
+
+ if (in->size() < sizeof(uint16))
+ return false;
+
+ uint16 num_tags_16;
+ memcpy(&num_tags_16, in->data(), sizeof(uint16));
+ in->remove_prefix(sizeof(uint16));
+ unsigned num_tags = num_tags_16;
+
+ if (in->size() < 6 * num_tags)
+ return false;
+
+ const uint32* tags = reinterpret_cast<const uint32*>(in->data());
+ const uint16* lens = reinterpret_cast<const uint16*>(
+ in->data() + 4*num_tags);
+ in->remove_prefix(6*num_tags);
+
+ uint32 prev_tag = 0;
+ for (unsigned i = 0; i < num_tags; i++) {
+ size_t len = lens[i];
+ uint32 tag = tags[i];
+
+ if (in->size() < len)
+ return false;
+ // tags must be in ascending order.
+ if (i > 0 && prev_tag >= tag)
+ return false;
+ (*out)[tag] = base::StringPiece(in->data(), len);
+ in->remove_prefix(len);
+ prev_tag = tag;
+ }
+
+ return true;
+}
+
+// GetTag extracts the data associated with |tag| in |tags|.
+static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) {
+ TagMap::const_iterator i = tags.find(tag);
+ if (i == tags.end())
+ return false;
+
+ *out = i->second;
+ return true;
+}
+
+// kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve
+// point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The
+// ASN.1 interpretation of these bytes is:
+//
+// 0:d=0 hl=2 l= 89 cons: SEQUENCE
+// 2:d=1 hl=2 l= 19 cons: SEQUENCE
+// 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
+// 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1
+// 23:d=1 hl=2 l= 66 prim: BIT STRING
+static const uint8 kP256SubjectPublicKeyInfoPrefix[] = {
+ 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,
+};
+
+// DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic
+// curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey.
+static SECKEYPublicKey* DecodeX962P256PublicKey(
+ const base::StringPiece& pubkey_bytes) {
+ // The public key is an X9.62 encoded P256 point.
+ if (pubkey_bytes.size() != 1 + 2*32)
+ return NULL;
+
+ std::string pubkey_spki(
+ reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix),
+ sizeof(kP256SubjectPublicKeyInfoPrefix));
+ pubkey_spki += pubkey_bytes.as_string();
+
+ SECItem der;
+ memset(&der, 0, sizeof(der));
+ der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data()));
+ der.len = pubkey_spki.size();
+
+ CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der);
+ if (!spki)
+ return NULL;
+ SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+
+ return public_key;
+}
+
+// These are the tag values that we use. Tags are little-endian on the wire and
+// these values correspond to the ASCII of the name.
+static const uint32 kTagALGO = 0x4f474c41;
+static const uint32 kTagP256 = 0x36353250;
+static const uint32 kTagPUBK = 0x4b425550;
+static const uint32 kTagSIG = 0x474953;
+static const uint32 kTagSPIN = 0x4e495053;
+
+// static
+bool TransportSecurityState::ParseSidePin(
+ const base::StringPiece& leaf_spki,
+ const base::StringPiece& in_side_info,
+ std::vector<SHA1Fingerprint> *out_pub_key_hash) {
+ base::StringPiece side_info(in_side_info);
+
+ TagMap outer;
+ if (!ParseTags(&side_info, &outer))
+ return false;
+ // trailing data is not allowed
+ if (side_info.size())
+ return false;
+
+ base::StringPiece side_pin_bytes;
+ if (!GetTag(kTagSPIN, outer, &side_pin_bytes))
+ return false;
+
+ bool have_parsed_a_key = false;
+ uint8 leaf_spki_hash[crypto::SHA256_LENGTH];
+ bool have_leaf_spki_hash = false;
+
+ while (side_pin_bytes.size() > 0) {
+ TagMap side_pin;
+ if (!ParseTags(&side_pin_bytes, &side_pin))
+ return false;
+
+ base::StringPiece algo, pubkey, sig;
+ if (!GetTag(kTagALGO, side_pin, &algo) ||
+ !GetTag(kTagPUBK, side_pin, &pubkey) ||
+ !GetTag(kTagSIG, side_pin, &sig)) {
+ return false;
+ }
+
+ if (algo.size() != sizeof(kTagP256) ||
+ 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) {
+ // We don't support anything but P256 at the moment.
+ continue;
+ }
+
+ if (!have_leaf_spki_hash) {
+ crypto::SHA256HashString(
+ leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash));
+ have_leaf_spki_hash = true;
+ }
+ SECItem hashitem;
+ memset(&hashitem, 0, sizeof(hashitem));
+ hashitem.data = leaf_spki_hash;
+ hashitem.len = sizeof(leaf_spki_hash);
+
+ SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey);
+ if (!secpubkey)
+ continue;
+
+ SECItem sigitem;
+ memset(&sigitem, 0, sizeof(sigitem));
+ sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data()));
+ sigitem.len = sig.size();
+
+ // |decoded_sigitem| is newly allocated, as is the data that it points to.
+ SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen(
+ &sigitem, SECKEY_SignatureLen(secpubkey));
+
+ if (!decoded_sigitem) {
+ SECKEY_DestroyPublicKey(secpubkey);
+ continue;
+ }
+
+ SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL);
+ SECKEY_DestroyPublicKey(secpubkey);
+ SECITEM_FreeItem(decoded_sigitem, PR_TRUE);
+
+ if (rv == SECSuccess) {
+ SHA1Fingerprint fpr;
+ base::SHA1HashBytes(
+ reinterpret_cast<const uint8*>(pubkey.data()),
+ pubkey.size(),
+ fpr.data);
+ out_pub_key_hash->push_back(fpr);
+ have_parsed_a_key = true;
+ }
+ }
+
+ return have_parsed_a_key;
+}
+
void TransportSecurityState::SetDelegate(
TransportSecurityState::Delegate* delegate) {
delegate_ = delegate;
diff --git a/net/base/transport_security_state.h b/net/base/transport_security_state.h
index 513ae8b..73d7bde 100644
--- a/net/base/transport_security_state.h
+++ b/net/base/transport_security_state.h
@@ -111,6 +111,15 @@ class NET_EXPORT TransportSecurityState :
int* max_age,
bool* include_subdomains);
+ // ParseSidePin attempts to parse a side pin from |side_info| which signs the
+ // SubjectPublicKeyInfo in |leaf_spki|. A side pin is a way for a site to
+ // sign their public key with a key that is offline but still controlled by
+ // them. If successful, the hash of the public key used to sign |leaf_spki|
+ // is put into |out_pub_key_hash|.
+ static bool ParseSidePin(const base::StringPiece& leaf_spki,
+ const base::StringPiece& side_info,
+ std::vector<SHA1Fingerprint> *out_pub_key_hash);
+
class Delegate {
public:
// This function may not block and may be called with internal locks held.
diff --git a/net/base/transport_security_state_unittest.cc b/net/base/transport_security_state_unittest.cc
index 1068e05..263e29a 100644
--- a/net/base/transport_security_state_unittest.cc
+++ b/net/base/transport_security_state_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/string_piece.h"
+#include "crypto/nss_util.h"
#include "net/base/transport_security_state.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -898,4 +900,108 @@ TEST_F(TransportSecurityStateTest, OverrideBuiltins) {
EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "www.google.com", true));
}
+static const uint8 kSidePinLeafSPKI[] = {
+ 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xe4,
+ 0x1d, 0xcc, 0xf2, 0x92, 0xe7, 0x7a, 0xc6, 0x36, 0xf7, 0x1a, 0x62, 0x31, 0x7d,
+ 0x37, 0xea, 0x0d, 0xa2, 0xa8, 0x12, 0x2b, 0xc2, 0x1c, 0x82, 0x3e, 0xa5, 0x70,
+ 0x4a, 0x83, 0x5d, 0x9b, 0x84, 0x82, 0x70, 0xa4, 0x88, 0x98, 0x98, 0x41, 0x29,
+ 0x31, 0xcb, 0x6e, 0x2a, 0x54, 0x65, 0x14, 0x60, 0xcc, 0x00, 0xe8, 0x10, 0x30,
+ 0x0a, 0x4a, 0xd1, 0xa7, 0x52, 0xfe, 0x2d, 0x31, 0x2a, 0x1d, 0x0d, 0x02, 0x03,
+ 0x01, 0x00, 0x01,
+};
+
+static const uint8 kSidePinInfo[] = {
+ 0x01, 0x00, 0x53, 0x50, 0x49, 0x4e, 0xa0, 0x00, 0x03, 0x00, 0x53, 0x49, 0x47,
+ 0x00, 0x50, 0x55, 0x42, 0x4b, 0x41, 0x4c, 0x47, 0x4f, 0x47, 0x00, 0x41, 0x00,
+ 0x04, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00, 0xfb, 0x26, 0xd5, 0xe8, 0x76, 0x35,
+ 0x96, 0x6d, 0x91, 0x9b, 0x5b, 0x27, 0xe6, 0x09, 0x1c, 0x7b, 0x6c, 0xcd, 0xc8,
+ 0x10, 0x25, 0x95, 0xc0, 0xa5, 0xf6, 0x6c, 0x6f, 0xfb, 0x59, 0x1e, 0x2d, 0xf4,
+ 0x02, 0x20, 0x33, 0x0a, 0xf8, 0x8b, 0x3e, 0xc4, 0xca, 0x75, 0x28, 0xdf, 0x5f,
+ 0xab, 0xe4, 0x46, 0xa0, 0xdd, 0x2d, 0xe5, 0xad, 0xc3, 0x81, 0x44, 0x70, 0xb2,
+ 0x10, 0x87, 0xe8, 0xc3, 0xd6, 0x6e, 0x12, 0x5d, 0x04, 0x67, 0x0b, 0x7d, 0xf2,
+ 0x99, 0x75, 0x57, 0x99, 0x3a, 0x98, 0xf8, 0xe4, 0xdf, 0x79, 0xdf, 0x8e, 0x02,
+ 0x2c, 0xbe, 0xd8, 0xfd, 0x75, 0x80, 0x18, 0xb1, 0x6f, 0x43, 0xd9, 0x8a, 0x79,
+ 0xc3, 0x6e, 0x18, 0xdf, 0x79, 0xc0, 0x59, 0xab, 0xd6, 0x77, 0x37, 0x6a, 0x94,
+ 0x5a, 0x7e, 0xfb, 0xa9, 0xc5, 0x54, 0x14, 0x3a, 0x7b, 0x97, 0x17, 0x2a, 0xb6,
+ 0x1e, 0x59, 0x4f, 0x2f, 0xb1, 0x15, 0x1a, 0x34, 0x50, 0x32, 0x35, 0x36,
+};
+
+static const uint8 kSidePinExpectedHash[20] = {
+ 0xb5, 0x91, 0x66, 0x47, 0x43, 0x16, 0x62, 0x86, 0xd4, 0x1e, 0x5d, 0x36, 0xe1,
+ 0xc4, 0x09, 0x3d, 0x2d, 0x1d, 0xea, 0x1e,
+};
+
+TEST_F(TransportSecurityStateTest, ParseSidePins) {
+ crypto::EnsureNSSInit();
+
+ base::StringPiece leaf_spki(reinterpret_cast<const char*>(kSidePinLeafSPKI),
+ sizeof(kSidePinLeafSPKI));
+ base::StringPiece side_info(reinterpret_cast<const char*>(kSidePinInfo),
+ sizeof(kSidePinInfo));
+
+ std::vector<SHA1Fingerprint> pub_key_hashes;
+ EXPECT_TRUE(TransportSecurityState::ParseSidePin(
+ leaf_spki, side_info, &pub_key_hashes));
+ ASSERT_EQ(1u, pub_key_hashes.size());
+ EXPECT_TRUE(0 == memcmp(pub_key_hashes[0].data, kSidePinExpectedHash,
+ sizeof(kSidePinExpectedHash)));
+}
+
+TEST_F(TransportSecurityStateTest, ParseSidePinsFailsWithBadData) {
+ crypto::EnsureNSSInit();
+
+ uint8 leaf_spki_copy[sizeof(kSidePinLeafSPKI)];
+ memcpy(leaf_spki_copy, kSidePinLeafSPKI, sizeof(leaf_spki_copy));
+
+ uint8 side_info_copy[sizeof(kSidePinInfo)];
+ memcpy(side_info_copy, kSidePinInfo, sizeof(kSidePinInfo));
+
+ base::StringPiece leaf_spki(reinterpret_cast<const char*>(leaf_spki_copy),
+ sizeof(leaf_spki_copy));
+ base::StringPiece side_info(reinterpret_cast<const char*>(side_info_copy),
+ sizeof(side_info_copy));
+ std::vector<SHA1Fingerprint> pub_key_hashes;
+
+ // Tweak |leaf_spki| and expect a failure.
+ leaf_spki_copy[10] ^= 1;
+ EXPECT_FALSE(TransportSecurityState::ParseSidePin(
+ leaf_spki, side_info, &pub_key_hashes));
+ ASSERT_EQ(0u, pub_key_hashes.size());
+
+ // Undo the change to |leaf_spki| and tweak |side_info|.
+ leaf_spki_copy[10] ^= 1;
+ side_info_copy[30] ^= 1;
+ EXPECT_FALSE(TransportSecurityState::ParseSidePin(
+ leaf_spki, side_info, &pub_key_hashes));
+ ASSERT_EQ(0u, pub_key_hashes.size());
+}
+
+TEST_F(TransportSecurityStateTest, DISABLED_ParseSidePinsFuzz) {
+ // Disabled because it's too slow for normal tests. Run manually when
+ // changing the underlying code.
+
+ crypto::EnsureNSSInit();
+
+ base::StringPiece leaf_spki(reinterpret_cast<const char*>(kSidePinLeafSPKI),
+ sizeof(kSidePinLeafSPKI));
+ uint8 side_info_copy[sizeof(kSidePinInfo)];
+ base::StringPiece side_info(reinterpret_cast<const char*>(side_info_copy),
+ sizeof(side_info_copy));
+ std::vector<SHA1Fingerprint> pub_key_hashes;
+ static const size_t bit_length = sizeof(kSidePinInfo) * 8;
+
+ for (size_t bit_to_flip = 0; bit_to_flip < bit_length; bit_to_flip++) {
+ memcpy(side_info_copy, kSidePinInfo, sizeof(kSidePinInfo));
+
+ size_t byte = bit_to_flip >> 3;
+ size_t bit = bit_to_flip & 7;
+ side_info_copy[byte] ^= (1 << bit);
+
+ EXPECT_FALSE(TransportSecurityState::ParseSidePin(
+ leaf_spki, side_info, &pub_key_hashes));
+ ASSERT_EQ(0u, pub_key_hashes.size());
+ }
+}
+
} // namespace net
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 186cc86..879689a 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -76,12 +76,14 @@
#include "crypto/rsa_private_key.h"
#include "crypto/scoped_nss_types.h"
#include "net/base/address_list.h"
+#include "net/base/asn1_util.h"
#include "net/base/cert_status_flags.h"
#include "net/base/cert_verifier.h"
#include "net/base/connection_type_histograms.h"
#include "net/base/dns_util.h"
#include "net/base/dnsrr_resolver.h"
#include "net/base/dnssec_chain_verifier.h"
+#include "net/base/transport_security_state.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
@@ -488,6 +490,11 @@ void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
ssl_info->cert = server_cert_;
ssl_info->connection_status = ssl_connection_status_;
ssl_info->public_key_hashes = server_cert_verify_result_->public_key_hashes;
+ for (std::vector<SHA1Fingerprint>::const_iterator
+ i = side_pinned_public_keys_.begin();
+ i != side_pinned_public_keys_.end(); i++) {
+ ssl_info->public_key_hashes.push_back(*i);
+ }
ssl_info->is_issued_by_known_root =
server_cert_verify_result_->is_issued_by_known_root;
@@ -1713,6 +1720,56 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
UMA_HISTOGRAM_TIMES("Net.SSLCertVerificationTimeError", verify_time);
}
+ PeerCertificateChain chain(nss_fd_);
+ for (unsigned i = 1; i < chain.size(); i++) {
+ if (strcmp(chain[i]->subjectName, "CN=meta") != 0)
+ continue;
+
+ base::StringPiece leaf_der(
+ reinterpret_cast<char*>(server_cert_nss_->derCert.data),
+ server_cert_nss_->derCert.len);
+ base::StringPiece leaf_spki;
+ if (!asn1::ExtractSPKIFromDERCert(leaf_der, &leaf_spki))
+ break;
+
+ static SECOidTag side_data_tag;
+ static bool side_data_tag_valid;
+ if (!side_data_tag_valid) {
+ // It's harmless if multiple threads enter this block concurrently.
+ static const uint8 kSideDataOID[] =
+ // 1.3.6.1.4.1.11129.2.1.4
+ // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
+ // certificateExtensions.sideData)
+ {0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x05};
+ SECOidData oid_data;
+ memset(&oid_data, 0, sizeof(oid_data));
+ oid_data.oid.data = const_cast<uint8*>(kSideDataOID);
+ oid_data.oid.len = sizeof(kSideDataOID);
+ oid_data.desc = "Certificate side data";
+ oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
+ side_data_tag = SECOID_AddEntry(&oid_data);
+ DCHECK_NE(SEC_OID_UNKNOWN, side_data_tag);
+ side_data_tag_valid = true;
+ }
+
+ SECItem side_data_item;
+ SECStatus rv = CERT_FindCertExtension(chain[i],
+ side_data_tag, &side_data_item);
+ if (rv != SECSuccess)
+ continue;
+
+ base::StringPiece side_data(
+ reinterpret_cast<char*>(side_data_item.data),
+ side_data_item.len);
+
+ if (!TransportSecurityState::ParseSidePin(
+ leaf_spki, side_data, &side_pinned_public_keys_)) {
+ LOG(WARNING) << "Side pinning data failed to parse: "
+ << host_and_port_.host();
+ }
+ break;
+ }
+
// We used to remember the intermediate CA certs in the NSS database
// persistently. However, NSS opens a connection to the SQLite database
// during NSS initialization and doesn't close the connection until NSS
diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h
index 88e8fde..0d0b342 100644
--- a/net/socket/ssl_client_socket_nss.h
+++ b/net/socket/ssl_client_socket_nss.h
@@ -219,6 +219,7 @@ class SSLClientSocketNSS : public SSLClientSocket {
// we used an SSLHostInfo's verification.
const CertVerifyResult* server_cert_verify_result_;
CertVerifyResult local_server_cert_verify_result_;
+ std::vector<SHA1Fingerprint> side_pinned_public_keys_;
int ssl_connection_status_;
// Stores client authentication information between ClientAuthHandler and