summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreranm@google.com <eranm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-01 01:42:03 +0000
committereranm@google.com <eranm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-01 01:42:03 +0000
commit95ac16b0adc0c0b85dd2f2e66cd930bfaceaf30f (patch)
treefe723da6a2d400e8f681989b65ee05d987f94450
parentc457dcf77264057729d025b3f85921c669fc0e20 (diff)
downloadchromium_src-95ac16b0adc0c0b85dd2f2e66cd930bfaceaf30f.zip
chromium_src-95ac16b0adc0c0b85dd2f2e66cd930bfaceaf30f.tar.gz
chromium_src-95ac16b0adc0c0b85dd2f2e66cd930bfaceaf30f.tar.bz2
CT: First step towards supporting Certificate Transparency in Chrome.
This patch adds Signed Certificate Timestamp (SCT) encoding/decoding. SCT is the Certificate Transparency (CT) structure containing a proof of a public log's commitment to adding a certificate to its public repository. The next patches would be extracting the SCTs when embedded in certificates and verifying the signature from the SCT over them. BUG=309578 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=232131 Review URL: https://codereview.chromium.org/37633002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232267 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/cert/ct_serialization.cc375
-rw-r--r--net/cert/ct_serialization.h72
-rw-r--r--net/cert/ct_serialization_unittest.cc170
-rw-r--r--net/cert/signed_certificate_timestamp.cc31
-rw-r--r--net/cert/signed_certificate_timestamp.h102
-rw-r--r--net/net.gyp9
-rw-r--r--net/test/ct_test_util.cc112
-rw-r--r--net/test/ct_test_util.h35
8 files changed, 905 insertions, 1 deletions
diff --git a/net/cert/ct_serialization.cc b/net/cert/ct_serialization.cc
new file mode 100644
index 0000000..f670439
--- /dev/null
+++ b/net/cert/ct_serialization.cc
@@ -0,0 +1,375 @@
+// 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/ct_serialization.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace net {
+
+namespace ct {
+
+namespace {
+
+// Note: length is always specified in bytes.
+// Signed Certificate Timestamp (SCT) Version length
+const size_t kVersionLength = 1;
+
+// Members of a V1 SCT
+const size_t kLogIdLength = 32;
+const size_t kTimestampLength = 8;
+const size_t kExtensionsLengthBytes = 2;
+const size_t kHashAlgorithmLength = 1;
+const size_t kSigAlgorithmLength = 1;
+const size_t kSignatureLengthBytes = 2;
+
+// Members of the digitally-signed struct of a V1 SCT
+const size_t kSignatureTypeLength = 1;
+const size_t kLogEntryTypeLength = 2;
+const size_t kAsn1CertificateLengthBytes = 3;
+const size_t kTbsCertificateLengthBytes = 3;
+
+const size_t kSCTListLengthBytes = 2;
+const size_t kSerializedSCTLengthBytes = 2;
+
+enum SignatureType {
+ SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
+ TREE_HASH = 1,
+};
+
+// Reads a TLS-encoded variable length unsigned integer from |in|.
+// The integer is expected to be in big-endian order, which is used by TLS.
+// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
+// |length| indicates the size (in bytes) of the integer. On success, returns
+// true and stores the result in |*out|.
+template <typename T>
+bool ReadUint(size_t length, base::StringPiece* in, T* out) {
+ if (in->size() < length)
+ return false;
+ DCHECK_LE(length, sizeof(T));
+
+ T result = 0;
+ for (size_t i = 0; i < length; ++i) {
+ result = (result << 8) | static_cast<unsigned char>((*in)[i]);
+ }
+ in->remove_prefix(length);
+ *out = result;
+ return true;
+}
+
+// Reads a TLS-encoded field length from |in|.
+// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
+// |prefix_length| indicates the bytes needed to represent the length (e.g. 3)
+// success, returns true and stores the result in |*out|.
+bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) {
+ size_t length;
+ if (!ReadUint(prefix_length, in, &length))
+ return false;
+ *out = length;
+ return true;
+}
+
+// Reads |length| bytes from |*in|. If |*in| is too small, returns false.
+// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
+bool ReadFixedBytes(size_t length,
+ base::StringPiece* in,
+ base::StringPiece* out) {
+ if (in->length() < length)
+ return false;
+ out->set(in->data(), length);
+ in->remove_prefix(length);
+ return true;
+}
+
+// Reads a length-prefixed variable amount of bytes from |in|, updating |out|
+// on success. |prefix_length| indicates the number of bytes needed to represent
+// the length.
+// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
+bool ReadVariableBytes(size_t prefix_length,
+ base::StringPiece* in,
+ base::StringPiece* out) {
+ size_t length;
+ if (!ReadLength(prefix_length, in, &length))
+ return false;
+ return ReadFixedBytes(length, in, out);
+}
+
+// Reads a variable-length list that has been TLS encoded.
+// The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
+// |max_list_length| contains the overall length of the encoded list.
+// |max_item_length| contains the maximum length of a single item.
+// On success, returns true and updates |*out| with the encoded list.
+bool ReadList(size_t max_list_length,
+ size_t max_item_length,
+ base::StringPiece* in,
+ std::vector<base::StringPiece>* out) {
+ std::vector<base::StringPiece> result;
+
+ base::StringPiece list_data;
+ if (!ReadVariableBytes(max_list_length, in, &list_data))
+ return false;
+
+ while (!list_data.empty()) {
+ base::StringPiece list_item;
+ if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) {
+ DVLOG(1) << "Failed to read item in list.";
+ return false;
+ }
+ if (list_item.empty()) {
+ DVLOG(1) << "Empty item in list";
+ return false;
+ }
+ result.push_back(list_item);
+ }
+
+ result.swap(*out);
+ return true;
+}
+
+// Checks and converts a hash algorithm.
+// |in| is the numeric representation of the algorithm.
+// If the hash algorithm value is in a set of known values, fills in |out| and
+// returns true. Otherwise, returns false.
+bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) {
+ switch (in) {
+ case DigitallySigned::HASH_ALGO_NONE:
+ case DigitallySigned::HASH_ALGO_MD5:
+ case DigitallySigned::HASH_ALGO_SHA1:
+ case DigitallySigned::HASH_ALGO_SHA224:
+ case DigitallySigned::HASH_ALGO_SHA256:
+ case DigitallySigned::HASH_ALGO_SHA384:
+ case DigitallySigned::HASH_ALGO_SHA512:
+ break;
+ default:
+ return false;
+ }
+ *out = static_cast<DigitallySigned::HashAlgorithm>(in);
+ return true;
+}
+
+// Checks and converts a signing algorithm.
+// |in| is the numeric representation of the algorithm.
+// If the signing algorithm value is in a set of known values, fills in |out|
+// and returns true. Otherwise, returns false.
+bool ConvertSignatureAlgorithm(
+ unsigned in,
+ DigitallySigned::SignatureAlgorithm* out) {
+ switch (in) {
+ case DigitallySigned::SIG_ALGO_ANONYMOUS:
+ case DigitallySigned::SIG_ALGO_RSA:
+ case DigitallySigned::SIG_ALGO_DSA:
+ case DigitallySigned::SIG_ALGO_ECDSA:
+ break;
+ default:
+ return false;
+ }
+ *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
+ return true;
+}
+
+// Checks and converts a log entry type.
+// |in| the numeric representation of the log type.
+// If the log type is 0 (X.509 cert) or 1 (PreCertificate), fills in |out| and
+// returns true. Otherwise, returns false.
+bool ConvertLogEntryType(int in, LogEntry::Type* out) {
+ switch (in) {
+ case LogEntry::LOG_ENTRY_TYPE_X509:
+ case LogEntry::LOG_ENTRY_TYPE_PRECERT:
+ break;
+ default:
+ return false;
+ }
+ *out = static_cast<LogEntry::Type>(in);
+ return true;
+}
+
+// Writes a TLS-encoded variable length unsigned integer to |output|.
+// |length| indicates the size (in bytes) of the integer.
+// |value| the value itself to be written.
+template <typename T>
+void WriteUint(size_t length, T value, std::string* output) {
+ DCHECK_LE(length, sizeof(T));
+ DCHECK(length == sizeof(T) || value >> (length * 8) == 0);
+
+ for (; length > 0; --length) {
+ output->push_back((value >> ((length - 1)* 8)) & 0xFF);
+ }
+}
+
+// Writes an array to |output| from |input|.
+// Should be used in one of two cases:
+// * The length of |input| has already been encoded into the |output| stream.
+// * The length of |input| is fixed and the reader is expected to specify that
+// length when reading.
+// If the length of |input| is dynamic and data is expected to follow it,
+// WriteVariableBytes must be used.
+void WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
+ input.AppendToString(output);
+}
+
+// Writes a variable-length array to |output|.
+// |prefix_length| indicates the number of bytes needed to represnt the length.
+// |input| is the array itself.
+// If the size of |input| is less than 2^|prefix_length| - 1, encode the
+// length and data and return true. Otherwise, return false.
+bool WriteVariableBytes(size_t prefix_length,
+ const base::StringPiece& input,
+ std::string* output) {
+ size_t input_size = input.size();
+ size_t max_allowed_input_size =
+ static_cast<size_t>(((1 << (prefix_length * 8)) - 1));
+ if (input_size > max_allowed_input_size)
+ return false;
+
+ WriteUint(prefix_length, input.size(), output);
+ WriteEncodedBytes(input, output);
+
+ return true;
+}
+
+// Writes a LogEntry of type X.509 cert to |output|.
+// |input| is the LogEntry containing the certificate.
+// Returns true if the leaf_certificate in the LogEntry does not exceed
+// kMaxAsn1CertificateLength and so can be written to |output|.
+bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
+ return WriteVariableBytes(kAsn1CertificateLengthBytes,
+ input.leaf_certificate, output);
+}
+
+// Writes a LogEntry of type PreCertificate to |output|.
+// |input| is the LogEntry containing the TBSCertificate and issuer key hash.
+// Returns true if the TBSCertificate component in the LogEntry does not
+// exceed kMaxTbsCertificateLength and so can be written to |output|.
+bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) {
+ WriteEncodedBytes(
+ base::StringPiece(
+ reinterpret_cast<const char*>(input.issuer_key_hash.data),
+ kLogIdLength),
+ output);
+ return WriteVariableBytes(kTbsCertificateLengthBytes,
+ input.tbs_certificate, output);
+}
+
+} // namespace
+
+bool EncodeDigitallySigned(const DigitallySigned& input,
+ std::string* output) {
+ WriteUint(kHashAlgorithmLength, input.hash_algorithm, output);
+ WriteUint(kSigAlgorithmLength, input.signature_algorithm,
+ output);
+ return WriteVariableBytes(kSignatureLengthBytes, input.signature_data,
+ output);
+}
+
+bool DecodeDigitallySigned(base::StringPiece* input,
+ DigitallySigned* output) {
+ unsigned hash_algo;
+ unsigned sig_algo;
+ base::StringPiece sig_data;
+
+ if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) ||
+ !ReadUint(kSigAlgorithmLength, input, &sig_algo) ||
+ !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) {
+ return false;
+ }
+
+ DigitallySigned result;
+ if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) {
+ DVLOG(1) << "Invalid hash algorithm " << hash_algo;
+ return false;
+ }
+ if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) {
+ DVLOG(1) << "Invalid signature algorithm " << sig_algo;
+ return false;
+ }
+ sig_data.CopyToString(&result.signature_data);
+
+ *output = result;
+ return true;
+}
+
+bool EncodeLogEntry(const LogEntry& input, std::string* output) {
+ WriteUint(kLogEntryTypeLength, input.type, output);
+ switch (input.type) {
+ case LogEntry::LOG_ENTRY_TYPE_X509:
+ return EncodeAsn1CertLogEntry(input, output);
+ case LogEntry::LOG_ENTRY_TYPE_PRECERT:
+ return EncodePrecertLogEntry(input, output);
+ }
+ return false;
+}
+
+bool EncodeV1SCTSignedData(const base::Time& timestamp,
+ const std::string& serialized_log_entry,
+ const std::string& extensions,
+ std::string* output) {
+ WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1,
+ output);
+ WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
+ output);
+ base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
+ WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(),
+ output);
+ // NOTE: serialized_log_entry must already be serialized and contain the
+ // length as the prefix.
+ WriteEncodedBytes(serialized_log_entry, output);
+ return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
+}
+
+bool DecodeSCTList(base::StringPiece* input,
+ std::vector<base::StringPiece>* output) {
+ std::vector<base::StringPiece> result;
+ if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes,
+ input, &result)) {
+ return false;
+ }
+
+ if (!input->empty() || result.empty())
+ return false;
+ output->swap(result);
+ return true;
+}
+
+bool DecodeSignedCertificateTimestamp(base::StringPiece* input,
+ SignedCertificateTimestamp* output) {
+ SignedCertificateTimestamp result;
+ unsigned version;
+ if (!ReadUint(kVersionLength, input, &version))
+ return false;
+ if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
+ DVLOG(1) << "Unsupported/invalid version " << version;
+ return false;
+ }
+
+ result.version = SignedCertificateTimestamp::SCT_VERSION_1;
+ uint64 timestamp;
+ base::StringPiece log_id;
+ base::StringPiece extensions;
+ if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
+ !ReadUint(kTimestampLength, input, &timestamp) ||
+ !ReadVariableBytes(kExtensionsLengthBytes, input,
+ &extensions) ||
+ !DecodeDigitallySigned(input, &result.signature)) {
+ return false;
+ }
+
+ if (timestamp > static_cast<uint64>(kint64max)) {
+ DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp;
+ return false;
+ }
+
+ log_id.CopyToString(&result.log_id);
+ extensions.CopyToString(&result.extensions);
+ result.timestamp =
+ base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp));
+
+ *output = result;
+ return true;
+}
+
+} // namespace ct
+
+} // namespace net
diff --git a/net/cert/ct_serialization.h b/net/cert/ct_serialization.h
new file mode 100644
index 0000000..4fc74f7
--- /dev/null
+++ b/net/cert/ct_serialization.h
@@ -0,0 +1,72 @@
+// 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_CT_SERIALIZATION_H_
+#define NET_CERT_CT_SERIALIZATION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/cert/signed_certificate_timestamp.h"
+
+namespace net {
+
+// Utility functions for encoding/decoding structures used by Certificate
+// Transparency to/from the TLS wire format encoding.
+namespace ct {
+
+// If |input.signature_data| is less than kMaxSignatureLength, encodes the
+// |input| to |output| and returns true. Otherwise, returns false.
+NET_EXPORT_PRIVATE bool EncodeDigitallySigned(const DigitallySigned& input,
+ std::string* output);
+
+// Reads and decodes a DigitallySigned object from |input|.
+// The bytes read from |input| are discarded (i.e. |input|'s prefix removed)
+// Returns true and fills |output| if all fields can be read, false otherwise.
+NET_EXPORT_PRIVATE bool DecodeDigitallySigned(base::StringPiece* input,
+ DigitallySigned* output);
+
+// Encodes the |input| LogEntry to |output|. Returns true if the entry size
+// does not exceed allowed size in RFC6962, false otherwise.
+NET_EXPORT_PRIVATE bool EncodeLogEntry(const LogEntry& input,
+ std::string* output);
+
+// Encodes the data signed by a Signed Certificate Timestamp (SCT) into
+// |output|. The signature included in the SCT is then verified over these
+// bytes.
+// |timestamp| timestamp from the SCT.
+// |serialized_log_entry| the log entry signed by the SCT.
+// |extensions| CT extensions.
+// Returns true if the extensions' length does not exceed
+// kMaxExtensionsLength, false otherwise.
+NET_EXPORT_PRIVATE bool EncodeV1SCTSignedData(
+ const base::Time& timestamp,
+ const std::string& serialized_log_entry,
+ const std::string& extensions,
+ std::string* output);
+
+// Decode a list of Signed Certificate Timestamps
+// (SignedCertificateTimestampList as defined in RFC6962): from a single
+// string in |input| to a vector of individually-encoded SCTs |output|.
+// This list is typically obtained from the CT extension in a certificate.
+// Returns true if the list could be read and decoded successfully, false
+// otherwise (note that the validity of each individual SCT should be checked
+// separately).
+NET_EXPORT_PRIVATE bool DecodeSCTList(base::StringPiece* input,
+ std::vector<base::StringPiece>* output);
+
+// Decodes a single SCT from |input| to |output|.
+// Returns true if all fields in the SCT could be read and decoded, false
+// otherwise.
+NET_EXPORT_PRIVATE bool DecodeSignedCertificateTimestamp(
+ base::StringPiece* input,
+ ct::SignedCertificateTimestamp* output);
+
+} // namespace ct
+
+} // namespace net
+
+#endif // NET_CERT_CT_SERIALIZATION_H_
diff --git a/net/cert/ct_serialization_unittest.cc b/net/cert/ct_serialization_unittest.cc
new file mode 100644
index 0000000..9692fb9
--- /dev/null
+++ b/net/cert/ct_serialization_unittest.cc
@@ -0,0 +1,170 @@
+// 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/ct_serialization.h"
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/ct_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class CtSerializationTest : public ::testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ test_digitally_signed_ = ct::GetTestDigitallySigned();
+ }
+
+ protected:
+ std::string test_digitally_signed_;
+};
+
+TEST_F(CtSerializationTest, DecodesDigitallySigned) {
+ base::StringPiece digitally_signed(test_digitally_signed_);
+ ct::DigitallySigned parsed;
+
+ ASSERT_TRUE(ct::DecodeDigitallySigned(&digitally_signed, &parsed));
+ EXPECT_EQ(
+ ct::DigitallySigned::HASH_ALGO_SHA256,
+ parsed.hash_algorithm);
+
+ EXPECT_EQ(
+ ct::DigitallySigned::SIG_ALGO_ECDSA,
+ parsed.signature_algorithm);
+
+ // The encoded data contains the signature itself from the 4th byte.
+ // The first bytes are:
+ // 1 byte of hash algorithm
+ // 1 byte of signature algorithm
+ // 2 bytes - prefix containing length of the signature data.
+ EXPECT_EQ(
+ test_digitally_signed_.substr(4),
+ parsed.signature_data);
+}
+
+
+TEST_F(CtSerializationTest, FailsToDecodePartialDigitallySigned) {
+ base::StringPiece digitally_signed(test_digitally_signed_);
+ base::StringPiece partial_digitally_signed(
+ digitally_signed.substr(0, test_digitally_signed_.size() - 5));
+ ct::DigitallySigned parsed;
+
+ ASSERT_FALSE(ct::DecodeDigitallySigned(&partial_digitally_signed, &parsed));
+}
+
+
+TEST_F(CtSerializationTest, EncodesDigitallySigned) {
+ ct::DigitallySigned digitally_signed;
+ digitally_signed.hash_algorithm = ct::DigitallySigned::HASH_ALGO_SHA256;
+ digitally_signed.signature_algorithm = ct::DigitallySigned::SIG_ALGO_ECDSA;
+ digitally_signed.signature_data = test_digitally_signed_.substr(4);
+
+ std::string encoded;
+
+ ASSERT_TRUE(ct::EncodeDigitallySigned(digitally_signed, &encoded));
+ EXPECT_EQ(test_digitally_signed_, encoded);
+}
+
+
+TEST_F(CtSerializationTest, EncodesLogEntryForX509Cert) {
+ ct::LogEntry entry;
+ GetX509CertLogEntry(&entry);
+
+ std::string encoded;
+ ASSERT_TRUE(ct::EncodeLogEntry(entry, &encoded));
+ EXPECT_EQ((718U + 5U), encoded.size());
+ // First two bytes are log entry type. Next, length:
+ // Length is 718 which is 512 + 206, which is 0x2ce
+ std::string expected_prefix("\0\0\0\x2\xCE", 5);
+ // Note we use std::string comparison rather than ASSERT_STREQ due
+ // to null characters in the buffer.
+ EXPECT_EQ(expected_prefix, encoded.substr(0, 5));
+}
+
+TEST_F(CtSerializationTest, EncodesV1SCTSignedData) {
+ base::Time timestamp = base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(1348589665525);
+ std::string dummy_entry("abc");
+ std::string empty_extensions("");
+ // For now, no known failure cases.
+ std::string encoded;
+ ASSERT_TRUE(ct::EncodeV1SCTSignedData(
+ timestamp,
+ dummy_entry,
+ empty_extensions,
+ &encoded));
+ EXPECT_EQ((size_t) 15, encoded.size());
+ // Byte 0 is version, byte 1 is signature type
+ // Bytes 2-10 are timestamp
+ // Bytes 11-14 are the log signature
+ // Byte 15 is the empty extension
+ //EXPECT_EQ(0, timestamp.ToTimeT());
+ std::string expected_buffer(
+ "\x0\x0\x0\x0\x1\x39\xFE\x35\x3C\xF5\x61\x62\x63\x0\x0", 15);
+ EXPECT_EQ(expected_buffer, encoded);
+}
+
+TEST_F(CtSerializationTest, DecodesSCTList) {
+ // Two items in the list: "abc", "def"
+ base::StringPiece encoded("\x0\xa\x0\x3\x61\x62\x63\x0\x3\x64\x65\x66", 12);
+ std::vector<base::StringPiece> decoded;
+
+ ASSERT_TRUE(ct::DecodeSCTList(&encoded, &decoded));
+ ASSERT_STREQ("abc", decoded[0].data());
+ ASSERT_STREQ("def", decoded[1].data());
+}
+
+TEST_F(CtSerializationTest, FailsDecodingInvalidSCTList) {
+ // A list with one item that's too short
+ base::StringPiece encoded("\x0\xa\x0\x3\x61\x62\x63\x0\x5\x64\x65\x66", 12);
+ std::vector<base::StringPiece> decoded;
+
+ ASSERT_FALSE(ct::DecodeSCTList(&encoded, &decoded));
+}
+
+TEST_F(CtSerializationTest, DecodesSignedCertificateTimestamp) {
+ std::string encoded_test_sct(ct::GetTestSignedCertificateTimestamp());
+ base::StringPiece encoded_sct(encoded_test_sct);
+
+ ct::SignedCertificateTimestamp sct;
+ ASSERT_TRUE(ct::DecodeSignedCertificateTimestamp(&encoded_sct, &sct));
+ EXPECT_EQ(0, sct.version);
+ std::string expected_log_key(
+ "\xdf\x1c\x2e\xc1\x15\x00\x94\x52\x47\xa9\x61\x68\x32\x5d\xdc\x5c\x79\x59"
+ "\xe8\xf7\xc6\xd3\x88\xfc\x00\x2e\x0b\xbd\x3f\x74\xd7\x64",
+ 32);
+ EXPECT_EQ(expected_log_key, sct.log_id);
+ base::Time expected_time = base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(1365181456089);
+ EXPECT_EQ(expected_time, sct.timestamp);
+ // Subtracting 4 bytes for signature data (hash & sig algs),
+ // actual signature data should be 71 bytes.
+ EXPECT_EQ((size_t) 71, sct.signature.signature_data.size());
+ EXPECT_EQ(std::string(""), sct.extensions);
+}
+
+TEST_F(CtSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) {
+ // Invalid version
+ base::StringPiece invalid_version_sct("\x2\x0", 2);
+ ct::SignedCertificateTimestamp sct;
+
+ ASSERT_FALSE(
+ ct::DecodeSignedCertificateTimestamp(&invalid_version_sct, &sct));
+
+ // Valid version, invalid length (missing data)
+ base::StringPiece invalid_length_sct("\x0\xa\xb\xc", 4);
+ ASSERT_FALSE(
+ ct::DecodeSignedCertificateTimestamp(&invalid_length_sct, &sct));
+}
+
+} // namespace net
+
diff --git a/net/cert/signed_certificate_timestamp.cc b/net/cert/signed_certificate_timestamp.cc
new file mode 100644
index 0000000..0be511d
--- /dev/null
+++ b/net/cert/signed_certificate_timestamp.cc
@@ -0,0 +1,31 @@
+// 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/signed_certificate_timestamp.h"
+
+namespace net {
+
+namespace ct {
+
+SignedCertificateTimestamp::SignedCertificateTimestamp() {}
+
+SignedCertificateTimestamp::~SignedCertificateTimestamp() {}
+
+LogEntry::LogEntry() {}
+
+LogEntry::~LogEntry() {}
+
+void LogEntry::Reset() {
+ type = LogEntry::LOG_ENTRY_TYPE_X509;
+ leaf_certificate.clear();
+ tbs_certificate.clear();
+}
+
+DigitallySigned::DigitallySigned() {}
+
+DigitallySigned::~DigitallySigned() {}
+
+} // namespace ct
+
+} // namespace net
diff --git a/net/cert/signed_certificate_timestamp.h b/net/cert/signed_certificate_timestamp.h
new file mode 100644
index 0000000..6ed225c
--- /dev/null
+++ b/net/cert/signed_certificate_timestamp.h
@@ -0,0 +1,102 @@
+// 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_SIGNED_CERTIFICATE_TIMESTAMP_H_
+#define NET_CERT_SIGNED_CERTIFICATE_TIMESTAMP_H_
+
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "net/base/hash_value.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Structures related to Certificate Transparency (RFC6962).
+namespace ct {
+
+// LogEntry struct in RFC 6962, Section 3.1
+struct NET_EXPORT LogEntry {
+ // LogEntryType enum in RFC 6962, Section 3.1
+ enum Type {
+ LOG_ENTRY_TYPE_X509 = 0,
+ LOG_ENTRY_TYPE_PRECERT = 1
+ };
+
+ LogEntry();
+ ~LogEntry();
+ void Reset();
+
+ Type type;
+
+ // Set if type == LOG_ENTRY_TYPE_X509
+ std::string leaf_certificate;
+
+ // Set if type == LOG_ENTRY_TYPE_PRECERT
+ SHA256HashValue issuer_key_hash;
+ std::string tbs_certificate;
+};
+
+// Helper structure to represent Digitally Signed data, as described in
+// Sections 4.7 and 7.4.1.4.1 of RFC 5246.
+struct NET_EXPORT_PRIVATE DigitallySigned {
+ enum HashAlgorithm {
+ HASH_ALGO_NONE = 0,
+ HASH_ALGO_MD5 = 1,
+ HASH_ALGO_SHA1 = 2,
+ HASH_ALGO_SHA224 = 3,
+ HASH_ALGO_SHA256 = 4,
+ HASH_ALGO_SHA384 = 5,
+ HASH_ALGO_SHA512 = 6,
+ };
+
+ enum SignatureAlgorithm {
+ SIG_ALGO_ANONYMOUS = 0,
+ SIG_ALGO_RSA = 1,
+ SIG_ALGO_DSA = 2,
+ SIG_ALGO_ECDSA = 3
+ };
+
+ DigitallySigned();
+ ~DigitallySigned();
+
+ HashAlgorithm hash_algorithm;
+ SignatureAlgorithm signature_algorithm;
+ // 'signature' field.
+ std::string signature_data;
+};
+
+// SignedCertificateTimestamp struct in RFC 6962, Section 3.2.
+struct NET_EXPORT SignedCertificateTimestamp {
+ // Version enum in RFC 6962, Section 3.2.
+ enum Version {
+ SCT_VERSION_1 = 0,
+ };
+
+ // Source of the SCT - supplementary, not defined in CT RFC.
+ enum Origin {
+ SCT_EMBEDDED = 0,
+ SCT_FROM_TLS_HANDSHAKE = 1,
+ SCT_FROM_OCSP_RESPONSE = 2,
+ };
+
+ SignedCertificateTimestamp();
+ ~SignedCertificateTimestamp();
+
+ Version version;
+ std::string log_id;
+ base::Time timestamp;
+ std::string extensions;
+ DigitallySigned signature;
+ // The origin should not participate in equality checks
+ // as the same SCT can be provided from multiple sources.
+ Origin origin;
+};
+
+} // namespace ct
+
+} // namespace net
+
+#endif // NET_CERT_SIGNED_CERTIFICATE_TIMESTAMP_H_
diff --git a/net/net.gyp b/net/net.gyp
index 9d511d1..3729a55 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -270,6 +270,8 @@
'cert/cert_verify_result.h',
'cert/crl_set.cc',
'cert/crl_set.h',
+ 'cert/ct_serialization.cc',
+ 'cert/ct_serialization.h',
'cert/ev_root_ca_metadata.cc',
'cert/ev_root_ca_metadata.h',
'cert/jwk_serializer_nss.cc',
@@ -281,14 +283,16 @@
'cert/nss_cert_database.h',
'cert/pem_tokenizer.cc',
'cert/pem_tokenizer.h',
+ 'cert/signed_certificate_timestamp.cc',
+ 'cert/signed_certificate_timestamp.h',
'cert/single_request_cert_verifier.cc',
'cert/single_request_cert_verifier.h',
'cert/test_root_certs.cc',
'cert/test_root_certs.h',
+ 'cert/test_root_certs_android.cc',
'cert/test_root_certs_mac.cc',
'cert/test_root_certs_nss.cc',
'cert/test_root_certs_openssl.cc',
- 'cert/test_root_certs_android.cc',
'cert/test_root_certs_win.cc',
'cert/x509_cert_types.cc',
'cert/x509_cert_types.h',
@@ -1575,6 +1579,7 @@
'base/url_util_unittest.cc',
'cert/cert_verify_proc_unittest.cc',
'cert/crl_set_unittest.cc',
+ 'cert/ct_serialization_unittest.cc',
'cert/ev_root_ca_metadata_unittest.cc',
'cert/jwk_serializer_unittest.cc',
'cert/multi_threaded_cert_verifier_unittest.cc',
@@ -2311,6 +2316,8 @@
'socket/socket_test_util.h',
'test/cert_test_util.cc',
'test/cert_test_util.h',
+ 'test/ct_test_util.cc',
+ 'test/ct_test_util.h',
'test/embedded_test_server/embedded_test_server.cc',
'test/embedded_test_server/embedded_test_server.h',
'test/embedded_test_server/http_connection.cc',
diff --git a/net/test/ct_test_util.cc b/net/test/ct_test_util.cc
new file mode 100644
index 0000000..cd014e4
--- /dev/null
+++ b/net/test/ct_test_util.cc
@@ -0,0 +1,112 @@
+// 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/test/ct_test_util.h"
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/cert/ct_serialization.h"
+#include "net/cert/signed_certificate_timestamp.h"
+#include "net/cert/x509_certificate.h"
+
+namespace net {
+
+namespace ct {
+
+namespace {
+
+std::string HexToBytes(const char* hex_data) {
+ std::vector<uint8> output;
+ std::string result;
+ if (base::HexStringToBytes(hex_data, &output))
+ result.assign(reinterpret_cast<const char*>(&output[0]), output.size());
+ return result;
+}
+
+// The following test vectors are from
+// http://code.google.com/p/certificate-transparency
+
+const char kDefaultDerCert[] =
+ "308202ca30820233a003020102020106300d06092a864886f70d01010505003055310b3009"
+ "06035504061302474231243022060355040a131b4365727469666963617465205472616e73"
+ "706172656e6379204341310e300c0603550408130557616c65733110300e06035504071307"
+ "4572772057656e301e170d3132303630313030303030305a170d3232303630313030303030"
+ "305a3052310b30090603550406130247423121301f060355040a1318436572746966696361"
+ "7465205472616e73706172656e6379310e300c0603550408130557616c65733110300e0603"
+ "55040713074572772057656e30819f300d06092a864886f70d010101050003818d00308189"
+ "02818100b1fa37936111f8792da2081c3fe41925008531dc7f2c657bd9e1de4704160b4c9f"
+ "19d54ada4470404c1c51341b8f1f7538dddd28d9aca48369fc5646ddcc7617f8168aae5b41"
+ "d43331fca2dadfc804d57208949061f9eef902ca47ce88c644e000f06eeeccabdc9dd2f68a"
+ "22ccb09dc76e0dbc73527765b1a37a8c676253dcc10203010001a381ac3081a9301d060355"
+ "1d0e041604146a0d982a3b62c44b6d2ef4e9bb7a01aa9cb798e2307d0603551d2304763074"
+ "80145f9d880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603550406"
+ "1302474231243022060355040a131b4365727469666963617465205472616e73706172656e"
+ "6379204341310e300c0603550408130557616c65733110300e060355040713074572772057"
+ "656e82010030090603551d1304023000300d06092a864886f70d010105050003818100171c"
+ "d84aac414a9a030f22aac8f688b081b2709b848b4e5511406cd707fed028597a9faefc2eee"
+ "2978d633aaac14ed3235197da87e0f71b8875f1ac9e78b281749ddedd007e3ecf50645f8cb"
+ "f667256cd6a1647b5e13203bb8582de7d6696f656d1c60b95f456b7fcf338571908f1c6972"
+ "7d24c4fccd249295795814d1dac0e6";
+
+const char kDefaultKeyHash[] =
+ "2518ce9dcf869f18562d21cf7d040cbacc75371f019f8bea8cbe2f5f6619472d";
+
+const char kDefaultDerTbsCert[] =
+ "30820233a003020102020107300d06092a864886f70d01010505003055310b300906035504"
+ "061302474231243022060355040a131b4365727469666963617465205472616e7370617265"
+ "6e6379204341310e300c0603550408130557616c65733110300e0603550407130745727720"
+ "57656e301e170d3132303630313030303030305a170d3232303630313030303030305a3052"
+ "310b30090603550406130247423121301f060355040a131843657274696669636174652054"
+ "72616e73706172656e6379310e300c0603550408130557616c65733110300e060355040713"
+ "074572772057656e30819f300d06092a864886f70d010101050003818d0030818902818100"
+ "bed8893cc8f177efc548df4961443f999aeda90471992f818bf8b61d0df19d6eec3d596c9b"
+ "43e60033a501c8cffcc438f49f5edb3662aaaecf180e7c9b59fc4bd465c18c406b3b70cdde"
+ "52d5dec42aaef913c2173592c76130f2399de6ccd6e75e04ccea7d7e4bdf4bacb16b5fe697"
+ "2974bca8bcb3e8468dec941e945fdf98310203010001a381ac3081a9301d0603551d0e0416"
+ "0414a4998f6b0abefd0e549bd56f221da976d0ce57d6307d0603551d230476307480143633"
+ "1299dbdc389d1cccfe31c08b8932501a8f7ca159a4573055310b3009060355040613024742"
+ "31243022060355040a131b4365727469666963617465205472616e73706172656e63792043"
+ "41310e300c0603550408130557616c65733110300e060355040713074572772057656e8201"
+ "0030090603551d1304023000";
+
+const char kTestDigitallySigned[] =
+ "0403004730450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c208dfbfe9ef53"
+ "6cf7f2022100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc45689a2c0187ef5"
+ "a5";
+
+const char kTestSignedCertificateTimestamp[] =
+ "00df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d7640000013d"
+ "db27ded900000403004730450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c2"
+ "08dfbfe9ef536cf7f2022100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc456"
+ "89a2c0187ef5a5";
+
+} // namespace
+
+void GetX509CertLogEntry(LogEntry* entry) {
+ entry->type = ct::LogEntry::LOG_ENTRY_TYPE_X509;
+ entry->leaf_certificate = HexToBytes(kDefaultDerCert);
+}
+
+void GetPrecertLogEntry(LogEntry* entry) {
+ entry->type = ct::LogEntry::LOG_ENTRY_TYPE_PRECERT;
+ std::string issuer_hash(HexToBytes(kDefaultKeyHash));
+ memcpy(entry->issuer_key_hash.data, issuer_hash.data(), issuer_hash.size());
+ entry->tbs_certificate = HexToBytes(kDefaultDerTbsCert);
+}
+
+std::string GetTestDigitallySigned() {
+ return HexToBytes(kTestDigitallySigned);
+}
+
+std::string GetTestSignedCertificateTimestamp() {
+ return HexToBytes(kTestSignedCertificateTimestamp);
+}
+
+} // namespace ct
+
+} // namespace net
diff --git a/net/test/ct_test_util.h b/net/test/ct_test_util.h
new file mode 100644
index 0000000..79881ce
--- /dev/null
+++ b/net/test/ct_test_util.h
@@ -0,0 +1,35 @@
+// 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_CT_TEST_UTIL_H_
+#define NET_CERT_CT_TEST_UTIL_H_
+
+#include <string>
+
+namespace net {
+
+class X509Certificate;
+
+namespace ct {
+
+struct LogEntry;
+struct SignedCertificateTimestamp;
+
+// Fills |entry| with test data for an X.509 entry.
+void GetX509CertLogEntry(LogEntry* entry);
+
+// Fills |entry| with test data for a Precertificate entry.
+void GetPrecertLogEntry(LogEntry* entry);
+
+// Returns the binary representation of a test DigitallySigned
+std::string GetTestDigitallySigned();
+
+// Returns the binary representation of a test serialized SCT.
+std::string GetTestSignedCertificateTimestamp();
+
+} // namespace ct
+
+} // namespace net
+
+#endif // NET_CERT_CT_TEST_UTIL_H_