diff options
author | eranm <eranm@chromium.org> | 2016-01-12 08:00:48 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-12 16:01:54 +0000 |
commit | 57259e076ec95ad4eaadae8f443bd6bdffdba616 (patch) | |
tree | dd6aa1f6cc70a02740fd1ced4956ea4c09120640 /net | |
parent | 238b112dd7fda58a408f0c351b8b207aaa33f142 (diff) | |
download | chromium_src-57259e076ec95ad4eaadae8f443bd6bdffdba616.zip chromium_src-57259e076ec95ad4eaadae8f443bd6bdffdba616.tar.gz chromium_src-57259e076ec95ad4eaadae8f443bd6bdffdba616.tar.bz2 |
Certificate Transparency: Verify signed tree heads for empty trees.
According to RFC6962, Signed Tree Heads for trees of size 0 (i.e. the empty tree) must have, as the root hash, the hash of the empty string.
This change adds this check to the STH verification function.
BUG=563471
Review URL: https://codereview.chromium.org/1542753005
Cr-Commit-Position: refs/heads/master@{#368885}
Diffstat (limited to 'net')
-rw-r--r-- | net/cert/ct_log_verifier.cc | 17 | ||||
-rw-r--r-- | net/cert/ct_log_verifier_unittest.cc | 16 | ||||
-rw-r--r-- | net/test/ct_test_util.cc | 38 | ||||
-rw-r--r-- | net/test/ct_test_util.h | 8 |
4 files changed, 75 insertions, 4 deletions
diff --git a/net/cert/ct_log_verifier.cc b/net/cert/ct_log_verifier.cc index c33f978..3884600 100644 --- a/net/cert/ct_log_verifier.cc +++ b/net/cert/ct_log_verifier.cc @@ -4,6 +4,8 @@ #include "net/cert/ct_log_verifier.h" +#include <string.h> + #include "base/logging.h" #include "net/cert/ct_log_verifier_util.h" #include "net/cert/ct_serialization.h" @@ -12,6 +14,16 @@ namespace net { +namespace { + +// The SHA-256 hash of the empty string. +const unsigned char kSHA256EmptyStringHash[ct::kSthRootHashLength] = { + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, + 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, + 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + +} // namespace + // static scoped_refptr<const CTLogVerifier> CTLogVerifier::Create( const base::StringPiece& public_key, @@ -70,6 +82,11 @@ bool CTLogVerifier::VerifySignedTreeHead( ct::EncodeTreeHeadSignature(signed_tree_head, &serialized_data); if (VerifySignature(serialized_data, signed_tree_head.signature.signature_data)) { + if (signed_tree_head.tree_size == 0) { + // Root hash must equate SHA256 hash of the empty string. + return (memcmp(signed_tree_head.sha256_root_hash, kSHA256EmptyStringHash, + ct::kSthRootHashLength) == 0); + } return true; } return false; diff --git a/net/cert/ct_log_verifier_unittest.cc b/net/cert/ct_log_verifier_unittest.cc index 00412e5..b2cba58 100644 --- a/net/cert/ct_log_verifier_unittest.cc +++ b/net/cert/ct_log_verifier_unittest.cc @@ -274,19 +274,31 @@ TEST_F(CTLogVerifierTest, FailsInvalidLogID) { EXPECT_FALSE(log_->Verify(cert_entry, *cert_sct.get())); } -TEST_F(CTLogVerifierTest, SetsValidSTH) { +TEST_F(CTLogVerifierTest, VerifiesValidSTH) { ct::SignedTreeHead sth; ct::GetSampleSignedTreeHead(&sth); ASSERT_TRUE(log_->VerifySignedTreeHead(sth)); } -TEST_F(CTLogVerifierTest, DoesNotSetInvalidSTH) { +TEST_F(CTLogVerifierTest, DoesNotVerifyInvalidSTH) { ct::SignedTreeHead sth; ct::GetSampleSignedTreeHead(&sth); sth.sha256_root_hash[0] = '\x0'; ASSERT_FALSE(log_->VerifySignedTreeHead(sth)); } +TEST_F(CTLogVerifierTest, VerifiesValidEmptySTH) { + ct::SignedTreeHead sth; + ct::GetSampleEmptySignedTreeHead(&sth); + ASSERT_TRUE(log_->VerifySignedTreeHead(sth)); +} + +TEST_F(CTLogVerifierTest, DoesNotVerifyInvalidEmptySTH) { + ct::SignedTreeHead sth; + ct::GetBadEmptySignedTreeHead(&sth); + ASSERT_FALSE(log_->VerifySignedTreeHead(sth)); +} + // Test that excess data after the public key is rejected. TEST_F(CTLogVerifierTest, ExcessDataInPublicKey) { std::string key = ct::GetTestPublicKey(); diff --git a/net/test/ct_test_util.cc b/net/test/ct_test_util.cc index 9381934..f4ea619 100644 --- a/net/test/ct_test_util.cc +++ b/net/test/ct_test_util.cc @@ -5,7 +5,7 @@ #include "net/test/ct_test_util.h" #include <stdint.h> -#include <string> +#include <string.h> #include <vector> #include "base/base64.h" @@ -166,7 +166,7 @@ const char kSampleSTHTreeHeadSignature[] = "6c7a20022100e38464f3c0fd066257b982074f7ac87655e0c8f714768a050b4be9a7b441cb" "d3"; size_t kSampleSTHTreeSize = 21u; -int64_t kSampleSTHTimestamp = 1396877277237u; +int64_t kSampleSTHTimestamp = INT64_C(1396877277237); } // namespace @@ -266,6 +266,40 @@ void GetSampleSignedTreeHead(SignedTreeHead* sth) { GetSampleSTHTreeHeadDecodedSignature(&(sth->signature)); } +void GetSampleEmptySignedTreeHead(SignedTreeHead* sth) { + sth->version = SignedTreeHead::V1; + sth->timestamp = base::Time::UnixEpoch() + + base::TimeDelta::FromMilliseconds(INT64_C(1450443594920)); + sth->tree_size = 0; + std::string empty_root_hash = HexToBytes( + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + memcpy(sth->sha256_root_hash, empty_root_hash.c_str(), kSthRootHashLength); + + std::string tree_head_signature = HexToBytes( + "040300463044022046c26401de9416403da54762dc1f1687c38eafd791b15e484ab4c5f7" + "f52721fe02201bf537a3bbea47109fc76c2273fe0f3349f493a07de9335c266330105fb0" + "2a4a"); + base::StringPiece sp(tree_head_signature); + CHECK(DecodeDigitallySigned(&sp, &(sth->signature))); + CHECK(sp.empty()); +} + +void GetBadEmptySignedTreeHead(SignedTreeHead* sth) { + sth->version = SignedTreeHead::V1; + sth->timestamp = base::Time::UnixEpoch() + + base::TimeDelta::FromMilliseconds(INT64_C(1450870952897)); + sth->tree_size = 0; + memset(sth->sha256_root_hash, 'f', kSthRootHashLength); + + std::string tree_head_signature = HexToBytes( + "04030046304402207cab04c62dee5d1cbc95fec30cd8417313f71587b75f133ad2e6f324" + "74f164d702205e2f3a9bce46f87d7e20e951a4e955da3cb502f8717a22fabd7c5d7e1bef" + "46ea"); + base::StringPiece sp(tree_head_signature); + CHECK(DecodeDigitallySigned(&sp, &(sth->signature))); + CHECK(sp.empty()); +} + std::string GetSampleSTHSHA256RootHash() { return HexToBytes(kSampleSTHSHA256RootHash); } diff --git a/net/test/ct_test_util.h b/net/test/ct_test_util.h index 72629ab..ef4643b 100644 --- a/net/test/ct_test_util.h +++ b/net/test/ct_test_util.h @@ -71,6 +71,14 @@ std::string GetDerEncodedFakeOCSPResponseIssuerCert(); // A sample, valid STH. void GetSampleSignedTreeHead(SignedTreeHead* sth); +// A valid STH for the empty tree. +void GetSampleEmptySignedTreeHead(SignedTreeHead* sth); + +// An STH for an empty tree where the root hash is not the hash of the empty +// string, but the signature over the STH is valid. Such an STH is not valid +// according to RFC6962. +void GetBadEmptySignedTreeHead(SignedTreeHead* sth); + // The SHA256 root hash for the sample STH. std::string GetSampleSTHSHA256RootHash(); |