• 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 enum SignatureType {
38   SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
39   TREE_HASH = 1,
40 };
41 
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|.
47 template <typename T>
ReadUint(size_t length,base::StringPiece * in,T * out)48 bool ReadUint(size_t length, base::StringPiece* in, T* out) {
49   if (in->size() < length)
50     return false;
51   DCHECK_LE(length, sizeof(T));
52 
53   T result = 0;
54   for (size_t i = 0; i < length; ++i) {
55     result = (result << 8) | static_cast<unsigned char>((*in)[i]);
56   }
57   in->remove_prefix(length);
58   *out = result;
59   return true;
60 }
61 
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|.
ReadLength(size_t prefix_length,base::StringPiece * in,size_t * out)66 bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) {
67   size_t length;
68   if (!ReadUint(prefix_length, in, &length))
69     return false;
70   *out = length;
71   return true;
72 }
73 
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)
ReadFixedBytes(size_t length,base::StringPiece * in,base::StringPiece * out)76 bool ReadFixedBytes(size_t length,
77                     base::StringPiece* in,
78                     base::StringPiece* out) {
79   if (in->length() < length)
80     return false;
81   out->set(in->data(), length);
82   in->remove_prefix(length);
83   return true;
84 }
85 
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
88 // the length.
89 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
ReadVariableBytes(size_t prefix_length,base::StringPiece * in,base::StringPiece * out)90 bool ReadVariableBytes(size_t prefix_length,
91                        base::StringPiece* in,
92                        base::StringPiece* out) {
93   size_t length;
94   if (!ReadLength(prefix_length, in, &length))
95     return false;
96   return ReadFixedBytes(length, in, out);
97 }
98 
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.
ReadList(size_t max_list_length,size_t max_item_length,base::StringPiece * in,std::vector<base::StringPiece> * out)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;
109 
110   base::StringPiece list_data;
111   if (!ReadVariableBytes(max_list_length, in, &list_data))
112     return false;
113 
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.";
118       return false;
119     }
120     if (list_item.empty()) {
121       DVLOG(1) << "Empty item in list";
122       return false;
123     }
124     result.push_back(list_item);
125   }
126 
127   result.swap(*out);
128   return true;
129 }
130 
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.
ConvertHashAlgorithm(unsigned in,DigitallySigned::HashAlgorithm * out)135 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) {
136   switch (in) {
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:
144       break;
145     default:
146       return false;
147   }
148   *out = static_cast<DigitallySigned::HashAlgorithm>(in);
149   return true;
150 }
151 
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.
ConvertSignatureAlgorithm(unsigned in,DigitallySigned::SignatureAlgorithm * out)156 bool ConvertSignatureAlgorithm(
157     unsigned in,
158     DigitallySigned::SignatureAlgorithm* out) {
159   switch (in) {
160     case DigitallySigned::SIG_ALGO_ANONYMOUS:
161     case DigitallySigned::SIG_ALGO_RSA:
162     case DigitallySigned::SIG_ALGO_DSA:
163     case DigitallySigned::SIG_ALGO_ECDSA:
164       break;
165     default:
166       return false;
167   }
168   *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
169   return true;
170 }
171 
172 // Writes a TLS-encoded variable length unsigned integer to |output|.
173 // |length| indicates the size (in bytes) of the integer.
174 // |value| the value itself to be written.
175 template <typename T>
WriteUint(size_t length,T value,std::string * output)176 void WriteUint(size_t length, T value, std::string* output) {
177   DCHECK_LE(length, sizeof(T));
178   DCHECK(length == sizeof(T) || value >> (length * 8) == 0);
179 
180   for (; length > 0; --length) {
181     output->push_back((value >> ((length - 1)* 8)) & 0xFF);
182   }
183 }
184 
185 // Writes an array to |output| from |input|.
186 // Should be used in one of two cases:
187 // * The length of |input| has already been encoded into the |output| stream.
188 // * The length of |input| is fixed and the reader is expected to specify that
189 // length when reading.
190 // If the length of |input| is dynamic and data is expected to follow it,
191 // WriteVariableBytes must be used.
WriteEncodedBytes(const base::StringPiece & input,std::string * output)192 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) {
193   input.AppendToString(output);
194 }
195 
196 // Writes a variable-length array to |output|.
197 // |prefix_length| indicates the number of bytes needed to represnt the length.
198 // |input| is the array itself.
199 // If the size of |input| is less than 2^|prefix_length| - 1, encode the
200 // length and data and return true. Otherwise, return false.
WriteVariableBytes(size_t prefix_length,const base::StringPiece & input,std::string * output)201 bool WriteVariableBytes(size_t prefix_length,
202                         const base::StringPiece& input,
203                         std::string* output) {
204   size_t input_size = input.size();
205   size_t max_allowed_input_size =
206       static_cast<size_t>(((1 << (prefix_length * 8)) - 1));
207   if (input_size > max_allowed_input_size)
208     return false;
209 
210   WriteUint(prefix_length, input.size(), output);
211   WriteEncodedBytes(input, output);
212 
213   return true;
214 }
215 
216 // Writes a LogEntry of type X.509 cert to |output|.
217 // |input| is the LogEntry containing the certificate.
218 // Returns true if the leaf_certificate in the LogEntry does not exceed
219 // kMaxAsn1CertificateLength and so can be written to |output|.
EncodeAsn1CertLogEntry(const LogEntry & input,std::string * output)220 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
221   return WriteVariableBytes(kAsn1CertificateLengthBytes,
222                             input.leaf_certificate, output);
223 }
224 
225 // Writes a LogEntry of type PreCertificate to |output|.
226 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
227 // Returns true if the TBSCertificate component in the LogEntry does not
228 // exceed kMaxTbsCertificateLength and so can be written to |output|.
EncodePrecertLogEntry(const LogEntry & input,std::string * output)229 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) {
230   WriteEncodedBytes(
231       base::StringPiece(
232           reinterpret_cast<const char*>(input.issuer_key_hash.data),
233           kLogIdLength),
234       output);
235   return WriteVariableBytes(kTbsCertificateLengthBytes,
236                             input.tbs_certificate, output);
237 }
238 
239 }  // namespace
240 
EncodeDigitallySigned(const DigitallySigned & input,std::string * output)241 bool EncodeDigitallySigned(const DigitallySigned& input,
242                            std::string* output) {
243   WriteUint(kHashAlgorithmLength, input.hash_algorithm, output);
244   WriteUint(kSigAlgorithmLength, input.signature_algorithm,
245             output);
246   return WriteVariableBytes(kSignatureLengthBytes, input.signature_data,
247                             output);
248 }
249 
DecodeDigitallySigned(base::StringPiece * input,DigitallySigned * output)250 bool DecodeDigitallySigned(base::StringPiece* input,
251                            DigitallySigned* output) {
252   unsigned hash_algo;
253   unsigned sig_algo;
254   base::StringPiece sig_data;
255 
256   if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) ||
257       !ReadUint(kSigAlgorithmLength, input, &sig_algo) ||
258       !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) {
259     return false;
260   }
261 
262   DigitallySigned result;
263   if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) {
264     DVLOG(1) << "Invalid hash algorithm " << hash_algo;
265     return false;
266   }
267   if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) {
268     DVLOG(1) << "Invalid signature algorithm " << sig_algo;
269     return false;
270   }
271   sig_data.CopyToString(&result.signature_data);
272 
273   *output = result;
274   return true;
275 }
276 
EncodeLogEntry(const LogEntry & input,std::string * output)277 bool EncodeLogEntry(const LogEntry& input, std::string* output) {
278   WriteUint(kLogEntryTypeLength, input.type, output);
279   switch (input.type) {
280     case LogEntry::LOG_ENTRY_TYPE_X509:
281       return EncodeAsn1CertLogEntry(input, output);
282     case LogEntry::LOG_ENTRY_TYPE_PRECERT:
283       return EncodePrecertLogEntry(input, output);
284   }
285   return false;
286 }
287 
EncodeV1SCTSignedData(const base::Time & timestamp,const std::string & serialized_log_entry,const std::string & extensions,std::string * output)288 bool EncodeV1SCTSignedData(const base::Time& timestamp,
289                            const std::string& serialized_log_entry,
290                            const std::string& extensions,
291                            std::string* output) {
292   WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1,
293             output);
294   WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
295             output);
296   base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
297   WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(),
298             output);
299   // NOTE: serialized_log_entry must already be serialized and contain the
300   // length as the prefix.
301   WriteEncodedBytes(serialized_log_entry, output);
302   return WriteVariableBytes(kExtensionsLengthBytes, extensions, output);
303 }
304 
DecodeSCTList(base::StringPiece * input,std::vector<base::StringPiece> * output)305 bool DecodeSCTList(base::StringPiece* input,
306                    std::vector<base::StringPiece>* output) {
307   std::vector<base::StringPiece> result;
308   if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes,
309                 input, &result)) {
310     return false;
311   }
312 
313   if (!input->empty() || result.empty())
314     return false;
315   output->swap(result);
316   return true;
317 }
318 
DecodeSignedCertificateTimestamp(base::StringPiece * input,scoped_refptr<SignedCertificateTimestamp> * output)319 bool DecodeSignedCertificateTimestamp(
320     base::StringPiece* input,
321     scoped_refptr<SignedCertificateTimestamp>* output) {
322   scoped_refptr<SignedCertificateTimestamp> result(
323       new SignedCertificateTimestamp());
324   unsigned version;
325   if (!ReadUint(kVersionLength, input, &version))
326     return false;
327   if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
328     DVLOG(1) << "Unsupported/invalid version " << version;
329     return false;
330   }
331 
332   result->version = SignedCertificateTimestamp::SCT_VERSION_1;
333   uint64 timestamp;
334   base::StringPiece log_id;
335   base::StringPiece extensions;
336   if (!ReadFixedBytes(kLogIdLength, input, &log_id) ||
337       !ReadUint(kTimestampLength, input, &timestamp) ||
338       !ReadVariableBytes(kExtensionsLengthBytes, input,
339                          &extensions) ||
340       !DecodeDigitallySigned(input, &result->signature)) {
341     return false;
342   }
343 
344   if (timestamp > static_cast<uint64>(kint64max)) {
345     DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp;
346     return false;
347   }
348 
349   log_id.CopyToString(&result->log_id);
350   extensions.CopyToString(&result->extensions);
351   result->timestamp =
352       base::Time::UnixEpoch() +
353       base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp));
354 
355   output->swap(result);
356   return true;
357 }
358 
EncodeSCTListForTesting(const base::StringPiece & sct,std::string * output)359 bool EncodeSCTListForTesting(const base::StringPiece& sct,
360                              std::string* output) {
361   std::string encoded_sct;
362   return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) &&
363       WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output);
364 }
365 
366 }  // namespace ct
367 
368 }  // namespace net
369