Index: net/base/transport_security_state_unittest.cc |
=================================================================== |
--- net/base/transport_security_state_unittest.cc (revision 150170) |
+++ net/base/transport_security_state_unittest.cc (working copy) |
@@ -6,11 +6,13 @@ |
#include <algorithm> |
#include <string> |
+#include <vector> |
#include "base/base64.h" |
#include "base/file_path.h" |
#include "base/sha1.h" |
#include "base/string_piece.h" |
+#include "crypto/sha2.h" |
#include "net/base/asn1_util.h" |
#include "net/base/cert_test_util.h" |
#include "net/base/cert_verifier.h" |
@@ -20,6 +22,7 @@ |
#include "net/base/ssl_info.h" |
#include "net/base/test_completion_callback.h" |
#include "net/base/test_root_certs.h" |
+#include "net/base/x509_cert_types.h" |
#include "net/base/x509_certificate.h" |
#include "net/http/http_util.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -91,7 +94,8 @@ |
} |
static bool GetPublicKeyHash(const net::X509Certificate::OSCertHandle& cert, |
- SHA1Fingerprint* fingerprint) { |
+ HashValue* fingerprint, |
+ HashValueTag tag) { |
std::string der_bytes; |
if (!net::X509Certificate::GetDEREncoded(cert, &der_bytes)) |
return false; |
@@ -100,28 +104,52 @@ |
if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) |
return false; |
- base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), |
- spki.size(), fingerprint->data); |
+ fingerprint->tag = tag; |
+ switch (tag) { |
+ case HASH_VALUE_SHA1: |
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), |
+ spki.size(), fingerprint->data()); |
+ break; |
+ case HASH_VALUE_SHA256: |
+ crypto::SHA256HashString(spki, fingerprint->data(), |
+ crypto::kSHA256Length); |
+ break; |
+ default: |
+ NOTREACHED() << "Unknown HashValueTag " << tag; |
+ } |
+ |
return true; |
} |
-static std::string GetPinFromCert(X509Certificate* cert) { |
- SHA1Fingerprint spki_hash; |
- EXPECT_TRUE(GetPublicKeyHash(cert->os_cert_handle(), &spki_hash)); |
+static std::string GetPinFromCert(X509Certificate* cert, HashValueTag tag) { |
+ HashValue spki_hash; |
+ EXPECT_TRUE(GetPublicKeyHash(cert->os_cert_handle(), &spki_hash, tag)); |
std::string base64; |
- base::Base64Encode(base::StringPiece(reinterpret_cast<char*>(spki_hash.data), |
- sizeof(spki_hash.data)), |
- &base64); |
- return "pin-sha1=" + HttpUtil::Quote(base64); |
+ base::Base64Encode(base::StringPiece( |
+ reinterpret_cast<char*>(spki_hash.data()), spki_hash.size()), &base64); |
+ |
+ std::string label; |
+ switch (tag) { |
+ case HASH_VALUE_SHA1: |
+ label = "pin-sha1="; |
+ break; |
+ case HASH_VALUE_SHA256: |
+ label = "pin-sha256="; |
+ break; |
+ default: |
+ NOTREACHED() << "Unknown HashValueTag " << tag; |
+ } |
+ |
+ return label + HttpUtil::Quote(base64); |
} |
-TEST_F(TransportSecurityStateTest, BogusPinsHeaders) { |
+static void TestBogusPinsHeaders(HashValueTag tag) { |
TransportSecurityState::DomainState state; |
SSLInfo ssl_info; |
ssl_info.cert = |
ImportCertFromFile(GetTestCertsDirectory(), "test_mail_google_com.pem"); |
- std::string good_pin = GetPinFromCert(ssl_info.cert); |
+ std::string good_pin = GetPinFromCert(ssl_info.cert, tag); |
base::Time now = base::Time::Now(); |
// The backup pin is fake --- it just has to not be in the chain. |
@@ -180,6 +208,14 @@ |
EXPECT_FALSE(state.include_subdomains); |
} |
+TEST_F(TransportSecurityStateTest, BogusPinsHeadersSHA1) { |
+ TestBogusPinsHeaders(HASH_VALUE_SHA1); |
+} |
+ |
+TEST_F(TransportSecurityStateTest, BogusPinsHeadersSHA256) { |
+ TestBogusPinsHeaders(HASH_VALUE_SHA256); |
+} |
+ |
TEST_F(TransportSecurityStateTest, ValidSTSHeaders) { |
TransportSecurityState::DomainState state; |
base::Time expiry; |
@@ -240,7 +276,7 @@ |
EXPECT_TRUE(state.include_subdomains); |
} |
-TEST_F(TransportSecurityStateTest, ValidPinsHeaders) { |
+static void TestValidPinsHeaders(HashValueTag tag) { |
TransportSecurityState::DomainState state; |
base::Time expiry; |
base::Time now = base::Time::Now(); |
@@ -280,7 +316,8 @@ |
// Normally, ssl_client_socket_nss would do this, but for a unit test we |
// fake it. |
ssl_info.public_key_hashes = result.public_key_hashes; |
- std::string good_pin = GetPinFromCert(ssl_info.cert); |
+ std::string good_pin = GetPinFromCert(ssl_info.cert, /*tag*/HASH_VALUE_SHA1); |
+ DLOG(WARNING) << "good pin: " << good_pin; |
// The backup pin is fake --- we just need an SPKI hash that does not match |
// the hash of any SPKI in the certificate chain. |
@@ -356,6 +393,14 @@ |
EXPECT_EQ(expiry, state.dynamic_spki_hashes_expiry); |
} |
+TEST_F(TransportSecurityStateTest, ValidPinsHeadersSHA1) { |
+ TestValidPinsHeaders(HASH_VALUE_SHA1); |
+} |
+ |
+TEST_F(TransportSecurityStateTest, ValidPinsHeadersSHA256) { |
+ TestValidPinsHeaders(HASH_VALUE_SHA256); |
+} |
+ |
TEST_F(TransportSecurityStateTest, SimpleMatches) { |
TransportSecurityState state; |
TransportSecurityState::DomainState domain_state; |
@@ -783,7 +828,7 @@ |
EXPECT_TRUE(state.GetDomainState("chrome.google.com", true, &domain_state)); |
EXPECT_TRUE(HasPins("chrome.google.com")); |
- FingerprintVector hashes; |
+ std::vector<HashValueVector> hashes; |
// Checks that a built-in list does exist. |
EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(hashes)); |
EXPECT_FALSE(HasPins("www.paypal.com")); |
@@ -829,20 +874,17 @@ |
} |
static bool AddHash(const std::string& type_and_base64, |
- FingerprintVector* out) { |
- std::string hash_str; |
- if (type_and_base64.find("sha1/") == 0 && |
- base::Base64Decode(type_and_base64.substr(5, type_and_base64.size() - 5), |
- &hash_str) && |
- hash_str.size() == base::kSHA1Length) { |
- SHA1Fingerprint hash; |
- memcpy(hash.data, hash_str.data(), sizeof(hash.data)); |
- out->push_back(hash); |
- return true; |
- } |
- return false; |
+ HashValueVector* out) { |
+ HashValue hash; |
+ |
+ if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
+ return false; |
+ |
+ out->push_back(hash); |
+ return true; |
} |
+ |
TEST_F(TransportSecurityStateTest, PinValidationWithRejectedCerts) { |
// kGoodPath is plus.google.com via Google Internet Authority. |
static const char* kGoodPath[] = { |
@@ -862,7 +904,7 @@ |
NULL, |
}; |
- std::vector<net::SHA1Fingerprint> good_hashes, bad_hashes; |
+ HashValueVector good_hashes, bad_hashes; |
for (size_t i = 0; kGoodPath[i]; i++) { |
EXPECT_TRUE(AddHash(kGoodPath[i], &good_hashes)); |
@@ -876,8 +918,13 @@ |
EXPECT_TRUE(state.GetDomainState("plus.google.com", true, &domain_state)); |
EXPECT_TRUE(domain_state.HasPins()); |
- EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(good_hashes)); |
- EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(bad_hashes)); |
+ std::vector<HashValueVector> good; |
+ good.push_back(good_hashes); |
+ EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(good)); |
+ |
+ std::vector<HashValueVector> bad; |
+ bad.push_back(bad_hashes); |
+ EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(bad)); |
} |
TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { |
@@ -898,7 +945,7 @@ |
NULL, |
}; |
- std::vector<net::SHA1Fingerprint> good_hashes, bad_hashes; |
+ HashValueVector good_hashes, bad_hashes; |
for (size_t i = 0; kGoodPath[i]; i++) { |
EXPECT_TRUE(AddHash(kGoodPath[i], &good_hashes)); |
@@ -912,8 +959,13 @@ |
EXPECT_TRUE(state.GetDomainState("blog.torproject.org", true, &domain_state)); |
EXPECT_TRUE(domain_state.HasPins()); |
- EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(good_hashes)); |
- EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(bad_hashes)); |
+ std::vector<HashValueVector> good; |
+ good.push_back(good_hashes); |
+ EXPECT_TRUE(domain_state.IsChainOfPublicKeysPermitted(good)); |
+ |
+ std::vector<HashValueVector> bad; |
+ bad.push_back(bad_hashes); |
+ EXPECT_FALSE(domain_state.IsChainOfPublicKeysPermitted(bad)); |
} |
TEST_F(TransportSecurityStateTest, OptionalHSTSCertPins) { |