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