Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / net / cert / ct_serialization.cc
blob6ea3cedc20fabe7e5bacbf2a3458eafdf3d233c5
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 <stdint.h>
9 #include "base/logging.h"
11 namespace net {
13 namespace ct {
15 namespace {
17 // Note: length is always specified in bytes.
18 // Signed Certificate Timestamp (SCT) Version length
19 const size_t kVersionLength = 1;
21 // Members of a V1 SCT
22 const size_t kLogIdLength = 32;
23 const size_t kTimestampLength = 8;
24 const size_t kExtensionsLengthBytes = 2;
25 const size_t kHashAlgorithmLength = 1;
26 const size_t kSigAlgorithmLength = 1;
27 const size_t kSignatureLengthBytes = 2;
29 // Members of the digitally-signed struct of a V1 SCT
30 const size_t kSignatureTypeLength = 1;
31 const size_t kLogEntryTypeLength = 2;
32 const size_t kAsn1CertificateLengthBytes = 3;
33 const size_t kTbsCertificateLengthBytes = 3;
35 const size_t kSCTListLengthBytes = 2;
36 const size_t kSerializedSCTLengthBytes = 2;
38 // Members of digitally-signed struct of a STH
39 const size_t kTreeSizeLength = 8;
41 enum SignatureType {
42 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
43 TREE_HASH = 1,
46 // Reads a TLS-encoded variable length unsigned integer from |in|.
47 // The integer is expected to be in big-endian order, which is used by TLS.
48 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
49 // |length| indicates the size (in bytes) of the integer. On success, returns
50 // true and stores the result in |*out|.
51 template <typename T>
52 bool ReadUint(size_t length, base::StringPiece* in, T* out) {
53 if (in->size() < length)
54 return false;
55 DCHECK_LE(length, sizeof(T));
57 T result = 0;
58 for (size_t i = 0; i < length; ++i) {
59 result = (result << 8) | static_cast<unsigned char>((*in)[i]);
61 in->remove_prefix(length);
62 *out = result;
63 return true;
66 // Reads a TLS-encoded field length from |in|.
67 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
68 // |prefix_length| indicates the bytes needed to represent the length (e.g. 3)
69 // success, returns true and stores the result in |*out|.
70 bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) {
71 size_t length;
72 if (!ReadUint(prefix_length, in, &length))
73 return false;
74 *out = length;
75 return true;
78 // Reads |length| bytes from |*in|. If |*in| is too small, returns false.
79 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
80 bool ReadFixedBytes(size_t length,
81 base::StringPiece* in,
82 base::StringPiece* out) {
83 if (in->length() < length)
84 return false;
85 out->set(in->data(), length);
86 in->remove_prefix(length);
87 return true;
90 // Reads a length-prefixed variable amount of bytes from |in|, updating |out|
91 // on success. |prefix_length| indicates the number of bytes needed to represent
92 // the length.
93 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
94 bool ReadVariableBytes(size_t prefix_length,
95 base::StringPiece* in,
96 base::StringPiece* out) {
97 size_t length;
98 if (!ReadLength(prefix_length, in, &length))
99 return false;
100 return ReadFixedBytes(length, in, out);
103 // Reads a variable-length list that has been TLS encoded.
104 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
105 // |max_list_length| contains the overall length of the encoded list.
106 // |max_item_length| contains the maximum length of a single item.
107 // On success, returns true and updates |*out| with the encoded list.
108 bool ReadList(size_t max_list_length,
109 size_t max_item_length,
110 base::StringPiece* in,
111 std::vector<base::StringPiece>* out) {
112 std::vector<base::StringPiece> result;
114 base::StringPiece list_data;
115 if (!ReadVariableBytes(max_list_length, in, &list_data))
116 return false;
118 while (!list_data.empty()) {
119 base::StringPiece list_item;
120 if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) {
121 DVLOG(1) << "Failed to read item in list.";
122 return false;
124 if (list_item.empty()) {
125 DVLOG(1) << "Empty item in list";
126 return false;
128 result.push_back(list_item);
131 result.swap(*out);
132 return true;
135 // Checks and converts a hash algorithm.
136 // |in| is the numeric representation of the algorithm.
137 // If the hash algorithm value is in a set of known values, fills in |out| and
138 // returns true. Otherwise, returns false.
139 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) {
140 switch (in) {
141 case DigitallySigned::HASH_ALGO_NONE:
142 case DigitallySigned::HASH_ALGO_MD5:
143 case DigitallySigned::HASH_ALGO_SHA1:
144 case DigitallySigned::HASH_ALGO_SHA224:
145 case DigitallySigned::HASH_ALGO_SHA256:
146 case DigitallySigned::HASH_ALGO_SHA384:
147 case DigitallySigned::HASH_ALGO_SHA512:
148 break;
149 default:
150 return false;
152 *out = static_cast<DigitallySigned::HashAlgorithm>(in);
153 return true;
156 // Checks and converts a signing algorithm.
157 // |in| is the numeric representation of the algorithm.
158 // If the signing algorithm value is in a set of known values, fills in |out|
159 // and returns true. Otherwise, returns false.
160 bool ConvertSignatureAlgorithm(
161 unsigned in,
162 DigitallySigned::SignatureAlgorithm* out) {
163 switch (in) {
164 case DigitallySigned::SIG_ALGO_ANONYMOUS:
165 case DigitallySigned::SIG_ALGO_RSA:
166 case DigitallySigned::SIG_ALGO_DSA:
167 case DigitallySigned::SIG_ALGO_ECDSA:
168 break;
169 default:
170 return false;
172 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
173 return true;
176 // Writes a TLS-encoded variable length unsigned integer to |output|.
177 // |length| indicates the size (in bytes) of the integer.
178 // |value| the value itself to be written.
179 template <typename T>
180 void WriteUint(size_t length, T value, std::string* output) {
181 DCHECK_LE(length, sizeof(T));
182 DCHECK(length == sizeof(T) || value >> (length * 8) == 0);
184 for (; length > 0; --length) {
185 output->push_back((value >> ((length - 1)* 8)) & 0xFF);
189 // Writes an array to |output| from |input|.
190 // Should be used in one of two cases:
191 // * The length of |input| has already been encoded into the |output| stream.
192 // * The length of |input| is fixed and the reader is expected to specify that
193 // length when reading.
194 // If the length of |input| is dynamic and data is expected to follow it,
195 // WriteVariableBytes must be used.
196 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
197 input.AppendToString(output);
200 // Writes a variable-length array to |output|.
201 // |prefix_length| indicates the number of bytes needed to represnt the length.
202 // |input| is the array itself.
203 // If the size of |input| is less than 2^|prefix_length| - 1, encode the
204 // length and data and return true. Otherwise, return false.
205 bool WriteVariableBytes(size_t prefix_length,
206 const base::StringPiece& input,
207 std::string* output) {
208 size_t input_size = input.size();
209 size_t max_allowed_input_size =
210 static_cast<size_t>(((1 << (prefix_length * 8)) - 1));
211 if (input_size > max_allowed_input_size)
212 return false;
214 WriteUint(prefix_length, input.size(), output);
215 WriteEncodedBytes(input, output);
217 return true;
220 // Writes a LogEntry of type X.509 cert to |output|.
221 // |input| is the LogEntry containing the certificate.
222 // Returns true if the leaf_certificate in the LogEntry does not exceed
223 // kMaxAsn1CertificateLength and so can be written to |output|.
224 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
225 return WriteVariableBytes(kAsn1CertificateLengthBytes,
226 input.leaf_certificate, output);
229 // Writes a LogEntry of type PreCertificate to |output|.
230 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
231 // Returns true if the TBSCertificate component in the LogEntry does not
232 // exceed kMaxTbsCertificateLength and so can be written to |output|.
233 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) {
234 WriteEncodedBytes(
235 base::StringPiece(
236 reinterpret_cast<const char*>(input.issuer_key_hash.data),
237 kLogIdLength),
238 output);
239 return WriteVariableBytes(kTbsCertificateLengthBytes,
240 input.tbs_certificate, output);
243 } // namespace
245 bool EncodeDigitallySigned(const DigitallySigned& input,
246 std::string* output) {
247 WriteUint(kHashAlgorithmLength, input.hash_algorithm, output);
248 WriteUint(kSigAlgorithmLength, input.signature_algorithm,
249 output);
250 return WriteVariableBytes(kSignatureLengthBytes, input.signature_data,
251 output);
254 bool DecodeDigitallySigned(base::StringPiece* input,
255 DigitallySigned* output) {
256 unsigned hash_algo;
257 unsigned sig_algo;
258 base::StringPiece sig_data;
260 if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) ||
261 !ReadUint(kSigAlgorithmLength, input, &sig_algo) ||
262 !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) {
263 return false;
266 DigitallySigned result;
267 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) {
268 DVLOG(1) << "Invalid hash algorithm " << hash_algo;
269 return false;
271 if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) {
272 DVLOG(1) << "Invalid signature algorithm " << sig_algo;
273 return false;
275 sig_data.CopyToString(&result.signature_data);
277 *output = result;
278 return true;
281 bool EncodeLogEntry(const LogEntry& input, std::string* output) {
282 WriteUint(kLogEntryTypeLength, input.type, output);
283 switch (input.type) {
284 case LogEntry::LOG_ENTRY_TYPE_X509:
285 return EncodeAsn1CertLogEntry(input, output);
286 case LogEntry::LOG_ENTRY_TYPE_PRECERT:
287 return EncodePrecertLogEntry(input, output);
289 return false;
292 static void WriteTimeSinceEpoch(const base::Time& timestamp,
293 std::string* output) {
294 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
295 WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), output);
298 bool EncodeV1SCTSignedData(const base::Time& timestamp,
299 const std::string& serialized_log_entry,
300 const std::string& extensions,
301 std::string* output) {
302 WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1,
303 output);
304 WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
305 output);
306 WriteTimeSinceEpoch(timestamp, output);
307 // NOTE: serialized_log_entry must already be serialized and contain the
308 // length as the prefix.
309 WriteEncodedBytes(serialized_log_entry, output);
310 return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
313 void EncodeTreeHeadSignature(const SignedTreeHead& signed_tree_head,
314 std::string* output) {
315 WriteUint(kVersionLength, signed_tree_head.version, output);
316 WriteUint(kSignatureTypeLength, TREE_HASH, output);
317 WriteTimeSinceEpoch(signed_tree_head.timestamp, output);
318 WriteUint(kTreeSizeLength, signed_tree_head.tree_size, output);
319 WriteEncodedBytes(
320 base::StringPiece(signed_tree_head.sha256_root_hash, kSthRootHashLength),
321 output);
324 bool DecodeSCTList(base::StringPiece* input,
325 std::vector<base::StringPiece>* output) {
326 std::vector<base::StringPiece> result;
327 if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes,
328 input, &result)) {
329 return false;
332 if (!input->empty() || result.empty())
333 return false;
334 output->swap(result);
335 return true;
338 bool DecodeSignedCertificateTimestamp(
339 base::StringPiece* input,
340 scoped_refptr<SignedCertificateTimestamp>* output) {
341 scoped_refptr<SignedCertificateTimestamp> result(
342 new SignedCertificateTimestamp());
343 unsigned version;
344 if (!ReadUint(kVersionLength, input, &version))
345 return false;
346 if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
347 DVLOG(1) << "Unsupported/invalid version " << version;
348 return false;
351 result->version = SignedCertificateTimestamp::SCT_VERSION_1;
352 uint64_t timestamp;
353 base::StringPiece log_id;
354 base::StringPiece extensions;
355 if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
356 !ReadUint(kTimestampLength, input, &timestamp) ||
357 !ReadVariableBytes(kExtensionsLengthBytes, input,
358 &extensions) ||
359 !DecodeDigitallySigned(input, &result->signature)) {
360 return false;
363 if (timestamp > static_cast<uint64_t>(kint64max)) {
364 DVLOG(1) << "Timestamp value too big to cast to int64_t: " << timestamp;
365 return false;
368 log_id.CopyToString(&result->log_id);
369 extensions.CopyToString(&result->extensions);
370 result->timestamp =
371 base::Time::UnixEpoch() +
372 base::TimeDelta::FromMilliseconds(static_cast<int64_t>(timestamp));
374 output->swap(result);
375 return true;
378 bool EncodeSCTListForTesting(const base::StringPiece& sct,
379 std::string* output) {
380 std::string encoded_sct;
381 return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) &&
382 WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output);
385 } // namespace ct
387 } // namespace net