1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/cert/ct_serialization.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
16 // Note: length is always specified in bytes.
17 // Signed Certificate Timestamp (SCT) Version length
18 const size_t kVersionLength = 1;
20 // Members of a V1 SCT
21 const size_t kLogIdLength = 32;
22 const size_t kTimestampLength = 8;
23 const size_t kExtensionsLengthBytes = 2;
24 const size_t kHashAlgorithmLength = 1;
25 const size_t kSigAlgorithmLength = 1;
26 const size_t kSignatureLengthBytes = 2;
28 // Members of the digitally-signed struct of a V1 SCT
29 const size_t kSignatureTypeLength = 1;
30 const size_t kLogEntryTypeLength = 2;
31 const size_t kAsn1CertificateLengthBytes = 3;
32 const size_t kTbsCertificateLengthBytes = 3;
34 const size_t kSCTListLengthBytes = 2;
35 const size_t kSerializedSCTLengthBytes = 2;
38 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
42 // Reads a TLS-encoded variable length unsigned integer from |in|.
43 // The integer is expected to be in big-endian order, which is used by TLS.
44 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
45 // |length| indicates the size (in bytes) of the integer. On success, returns
46 // true and stores the result in |*out|.
48 bool ReadUint(size_t length, base::StringPiece* in, T* out) {
49 if (in->size() < length)
51 DCHECK_LE(length, sizeof(T));
54 for (size_t i = 0; i < length; ++i) {
55 result = (result << 8) | static_cast<unsigned char>((*in)[i]);
57 in->remove_prefix(length);
62 // Reads a TLS-encoded field length from |in|.
63 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
64 // |prefix_length| indicates the bytes needed to represent the length (e.g. 3)
65 // success, returns true and stores the result in |*out|.
66 bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) {
68 if (!ReadUint(prefix_length, in, &length))
74 // Reads |length| bytes from |*in|. If |*in| is too small, returns false.
75 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
76 bool ReadFixedBytes(size_t length,
77 base::StringPiece* in,
78 base::StringPiece* out) {
79 if (in->length() < length)
81 out->set(in->data(), length);
82 in->remove_prefix(length);
86 // Reads a length-prefixed variable amount of bytes from |in|, updating |out|
87 // on success. |prefix_length| indicates the number of bytes needed to represent
89 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
90 bool ReadVariableBytes(size_t prefix_length,
91 base::StringPiece* in,
92 base::StringPiece* out) {
94 if (!ReadLength(prefix_length, in, &length))
96 return ReadFixedBytes(length, in, out);
99 // Reads a variable-length list that has been TLS encoded.
100 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
101 // |max_list_length| contains the overall length of the encoded list.
102 // |max_item_length| contains the maximum length of a single item.
103 // On success, returns true and updates |*out| with the encoded list.
104 bool ReadList(size_t max_list_length,
105 size_t max_item_length,
106 base::StringPiece* in,
107 std::vector<base::StringPiece>* out) {
108 std::vector<base::StringPiece> result;
110 base::StringPiece list_data;
111 if (!ReadVariableBytes(max_list_length, in, &list_data))
114 while (!list_data.empty()) {
115 base::StringPiece list_item;
116 if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) {
117 DVLOG(1) << "Failed to read item in list.";
120 if (list_item.empty()) {
121 DVLOG(1) << "Empty item in list";
124 result.push_back(list_item);
131 // Checks and converts a hash algorithm.
132 // |in| is the numeric representation of the algorithm.
133 // If the hash algorithm value is in a set of known values, fills in |out| and
134 // returns true. Otherwise, returns false.
135 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) {
137 case DigitallySigned::HASH_ALGO_NONE:
138 case DigitallySigned::HASH_ALGO_MD5:
139 case DigitallySigned::HASH_ALGO_SHA1:
140 case DigitallySigned::HASH_ALGO_SHA224:
141 case DigitallySigned::HASH_ALGO_SHA256:
142 case DigitallySigned::HASH_ALGO_SHA384:
143 case DigitallySigned::HASH_ALGO_SHA512:
148 *out = static_cast<DigitallySigned::HashAlgorithm>(in);
152 // Checks and converts a signing algorithm.
153 // |in| is the numeric representation of the algorithm.
154 // If the signing algorithm value is in a set of known values, fills in |out|
155 // and returns true. Otherwise, returns false.
156 bool ConvertSignatureAlgorithm(
158 DigitallySigned::SignatureAlgorithm* out) {
160 case DigitallySigned::SIG_ALGO_ANONYMOUS:
161 case DigitallySigned::SIG_ALGO_RSA:
162 case DigitallySigned::SIG_ALGO_DSA:
163 case DigitallySigned::SIG_ALGO_ECDSA:
168 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
172 // Checks and converts a log entry type.
173 // |in| the numeric representation of the log type.
174 // If the log type is 0 (X.509 cert) or 1 (PreCertificate), fills in |out| and
175 // returns true. Otherwise, returns false.
176 bool ConvertLogEntryType(int in, LogEntry::Type* out) {
178 case LogEntry::LOG_ENTRY_TYPE_X509:
179 case LogEntry::LOG_ENTRY_TYPE_PRECERT:
184 *out = static_cast<LogEntry::Type>(in);
188 // Writes a TLS-encoded variable length unsigned integer to |output|.
189 // |length| indicates the size (in bytes) of the integer.
190 // |value| the value itself to be written.
191 template <typename T>
192 void WriteUint(size_t length, T value, std::string* output) {
193 DCHECK_LE(length, sizeof(T));
194 DCHECK(length == sizeof(T) || value >> (length * 8) == 0);
196 for (; length > 0; --length) {
197 output->push_back((value >> ((length - 1)* 8)) & 0xFF);
201 // Writes an array to |output| from |input|.
202 // Should be used in one of two cases:
203 // * The length of |input| has already been encoded into the |output| stream.
204 // * The length of |input| is fixed and the reader is expected to specify that
205 // length when reading.
206 // If the length of |input| is dynamic and data is expected to follow it,
207 // WriteVariableBytes must be used.
208 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
209 input.AppendToString(output);
212 // Writes a variable-length array to |output|.
213 // |prefix_length| indicates the number of bytes needed to represnt the length.
214 // |input| is the array itself.
215 // If the size of |input| is less than 2^|prefix_length| - 1, encode the
216 // length and data and return true. Otherwise, return false.
217 bool WriteVariableBytes(size_t prefix_length,
218 const base::StringPiece& input,
219 std::string* output) {
220 size_t input_size = input.size();
221 size_t max_allowed_input_size =
222 static_cast<size_t>(((1 << (prefix_length * 8)) - 1));
223 if (input_size > max_allowed_input_size)
226 WriteUint(prefix_length, input.size(), output);
227 WriteEncodedBytes(input, output);
232 // Writes a LogEntry of type X.509 cert to |output|.
233 // |input| is the LogEntry containing the certificate.
234 // Returns true if the leaf_certificate in the LogEntry does not exceed
235 // kMaxAsn1CertificateLength and so can be written to |output|.
236 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
237 return WriteVariableBytes(kAsn1CertificateLengthBytes,
238 input.leaf_certificate, output);
241 // Writes a LogEntry of type PreCertificate to |output|.
242 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
243 // Returns true if the TBSCertificate component in the LogEntry does not
244 // exceed kMaxTbsCertificateLength and so can be written to |output|.
245 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) {
248 reinterpret_cast<const char*>(input.issuer_key_hash.data),
251 return WriteVariableBytes(kTbsCertificateLengthBytes,
252 input.tbs_certificate, output);
257 bool EncodeDigitallySigned(const DigitallySigned& input,
258 std::string* output) {
259 WriteUint(kHashAlgorithmLength, input.hash_algorithm, output);
260 WriteUint(kSigAlgorithmLength, input.signature_algorithm,
262 return WriteVariableBytes(kSignatureLengthBytes, input.signature_data,
266 bool DecodeDigitallySigned(base::StringPiece* input,
267 DigitallySigned* output) {
270 base::StringPiece sig_data;
272 if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) ||
273 !ReadUint(kSigAlgorithmLength, input, &sig_algo) ||
274 !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) {
278 DigitallySigned result;
279 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) {
280 DVLOG(1) << "Invalid hash algorithm " << hash_algo;
283 if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) {
284 DVLOG(1) << "Invalid signature algorithm " << sig_algo;
287 sig_data.CopyToString(&result.signature_data);
293 bool EncodeLogEntry(const LogEntry& input, std::string* output) {
294 WriteUint(kLogEntryTypeLength, input.type, output);
295 switch (input.type) {
296 case LogEntry::LOG_ENTRY_TYPE_X509:
297 return EncodeAsn1CertLogEntry(input, output);
298 case LogEntry::LOG_ENTRY_TYPE_PRECERT:
299 return EncodePrecertLogEntry(input, output);
304 bool EncodeV1SCTSignedData(const base::Time& timestamp,
305 const std::string& serialized_log_entry,
306 const std::string& extensions,
307 std::string* output) {
308 WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1,
310 WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
312 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
313 WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(),
315 // NOTE: serialized_log_entry must already be serialized and contain the
316 // length as the prefix.
317 WriteEncodedBytes(serialized_log_entry, output);
318 return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
321 bool DecodeSCTList(base::StringPiece* input,
322 std::vector<base::StringPiece>* output) {
323 std::vector<base::StringPiece> result;
324 if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes,
329 if (!input->empty() || result.empty())
331 output->swap(result);
335 bool DecodeSignedCertificateTimestamp(base::StringPiece* input,
336 SignedCertificateTimestamp* output) {
337 SignedCertificateTimestamp result;
339 if (!ReadUint(kVersionLength, input, &version))
341 if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
342 DVLOG(1) << "Unsupported/invalid version " << version;
346 result.version = SignedCertificateTimestamp::SCT_VERSION_1;
348 base::StringPiece log_id;
349 base::StringPiece extensions;
350 if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
351 !ReadUint(kTimestampLength, input, ×tamp) ||
352 !ReadVariableBytes(kExtensionsLengthBytes, input,
354 !DecodeDigitallySigned(input, &result.signature)) {
358 if (timestamp > static_cast<uint64>(kint64max)) {
359 DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp;
363 log_id.CopyToString(&result.log_id);
364 extensions.CopyToString(&result.extensions);
366 base::Time::UnixEpoch() +
367 base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp));