OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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> | |
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 // |max_length| indicates the maximum length supported (eg: 2^24 - 1). On | |
Ryan Sleevi
2013/10/30 20:48:27
update comment
Eran M. (Google)
2013/10/31 11:37:37
Done, same for all other comments where |max_lengt
| |
65 // success, returns true and stores the result in |*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) | |
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. |max_length| indicates the maximum length of the field. | |
88 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
89 bool ReadVariableBytes(size_t prefix_length, | |
90 base::StringPiece* in, | |
91 base::StringPiece* out) { | |
92 size_t length; | |
93 if (!ReadLength(prefix_length, in, &length)) | |
94 return false; | |
95 return ReadFixedBytes(length, in, out); | |
96 } | |
97 | |
98 // Reads a variable-length list that has been TLS encoded. | |
99 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed) | |
100 // |max_list_length| contains the overall length of the encoded list. | |
101 // |max_item_length| contains the maximum length of a single item. | |
102 // On success, returns true and updates |*out| with the encoded list. | |
103 bool ReadList(size_t max_list_length, | |
104 size_t max_item_length, | |
105 base::StringPiece* in, | |
106 std::vector<base::StringPiece>* out) { | |
107 std::vector<base::StringPiece> result; | |
108 | |
109 base::StringPiece list_data; | |
110 if (!ReadVariableBytes(max_list_length, in, &list_data)) | |
111 return false; | |
112 | |
113 while (!list_data.empty()) { | |
114 base::StringPiece list_item; | |
115 if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) { | |
116 DVLOG(1) << "Failed to read item in list."; | |
117 return false; | |
118 } | |
119 if (list_item.empty()) { | |
120 DVLOG(1) << "Empty item in list"; | |
121 return false; | |
122 } | |
123 result.push_back(list_item); | |
124 } | |
125 | |
126 result.swap(*out); | |
127 return true; | |
128 } | |
129 | |
130 // Checks and converts a hash algorithm. | |
131 // |in| is the numeric representation of the algorithm. | |
132 // If the hash algorithm value is in a set of known values, fills in |out| and | |
133 // returns true. Otherwise, returns false. | |
134 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) { | |
135 switch (in) { | |
136 case DigitallySigned::HASH_ALGO_NONE: | |
137 case DigitallySigned::HASH_ALGO_MD5: | |
138 case DigitallySigned::HASH_ALGO_SHA1: | |
139 case DigitallySigned::HASH_ALGO_SHA224: | |
140 case DigitallySigned::HASH_ALGO_SHA256: | |
141 case DigitallySigned::HASH_ALGO_SHA384: | |
142 case DigitallySigned::HASH_ALGO_SHA512: | |
143 break; | |
144 default: | |
145 return false; | |
146 } | |
147 *out = static_cast<DigitallySigned::HashAlgorithm>(in); | |
148 return true; | |
149 } | |
150 | |
151 // Checks and converts a signing algorithm. | |
152 // |in| is the numeric representation of the algorithm. | |
153 // If the signing algorithm value is in a set of known values, fills in |out| | |
154 // and returns true. Otherwise, returns false. | |
155 bool ConvertSignatureAlgorithm( | |
156 unsigned in, | |
157 DigitallySigned::SignatureAlgorithm* out) { | |
158 switch (in) { | |
159 case DigitallySigned::SIG_ALGO_ANONYMOUS: | |
160 case DigitallySigned::SIG_ALGO_RSA: | |
161 case DigitallySigned::SIG_ALGO_DSA: | |
162 case DigitallySigned::SIG_ALGO_ECDSA: | |
163 break; | |
164 default: | |
165 return false; | |
166 } | |
167 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in); | |
168 return true; | |
169 } | |
170 | |
171 // Checks and converts a log entry type. | |
172 // |in| the numeric representation of the log type. | |
173 // If the log type is 0 (X.509 cert) or 1 (PreCertificate), fills in |out| and | |
174 // returns true. Otherwise, returns false. | |
175 bool ConvertLogEntryType(int in, LogEntry::Type* out) { | |
176 switch (in) { | |
177 case LogEntry::LOG_ENTRY_TYPE_X509: | |
178 case LogEntry::LOG_ENTRY_TYPE_PRECERT: | |
179 break; | |
180 default: | |
181 return false; | |
182 } | |
183 *out = static_cast<LogEntry::Type>(in); | |
184 return true; | |
185 } | |
186 | |
187 // Writes a TLS-encoded variable length unsigned integer to |output|. | |
188 // |length| indicates the size (in bytes) of the integer. | |
189 // |value| the value itself to be written. | |
190 template <typename T> | |
191 void WriteUint(size_t length, T value, std::string* output) { | |
192 DCHECK_LE(length, sizeof(T)); | |
193 DCHECK(length == sizeof(T) || value >> (length * 8) == 0); | |
194 | |
195 for (; length > 0; --length) { | |
196 output->push_back((value >> ((length - 1)* 8)) & 0xFF); | |
197 } | |
198 } | |
199 | |
200 // Writes an array to |output| from |input|. | |
201 // Should be used in one of two cases: | |
202 // * The length of |input| has already been encoded into the |output| stream. | |
203 // * The length of |input| is fixed and the reader is expected to specify that | |
204 // length when reading. | |
205 // If the length of |input| is dynamic and data is expected to follow it, | |
206 // WriteVariableBytes must be used. | |
207 void WriteEncodedBytes(const base::StringPiece& input, std::string* output) { | |
208 input.AppendToString(output); | |
209 } | |
210 | |
211 // Writes a variable-length array to |output|. | |
212 // |max_length| indicates the length (in bytes) of the array. | |
213 // |input| is the array itself. | |
214 // If the size of |input| is less than |max_length|, encode the | |
215 // length and data and return true. Otherwise, return false. | |
216 bool WriteVariableBytes(size_t prefix_length, | |
217 const base::StringPiece& input, | |
218 std::string* output) { | |
219 if (input.size() > ((1 << (prefix_length * 8)) - 1)) | |
220 return false; | |
221 | |
222 WriteUint(prefix_length, input.size(), output); | |
223 WriteEncodedBytes(input, output); | |
224 | |
225 return true; | |
226 } | |
227 | |
228 // Writes a LogEntry of type X.509 cert to |output|. | |
229 // |input| is the LogEntry containing the certificate. | |
230 // Returns true if the leaf_certificate in the LogEntry does not exceed | |
231 // kMaxAsn1CertificateLength and so can be written to |output|. | |
232 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) { | |
233 return WriteVariableBytes(kAsn1CertificateLengthBytes, | |
234 input.leaf_certificate, output); | |
235 } | |
236 | |
237 // Writes a LogEntry of type PreCertificate to |output|. | |
238 // |input| is the LogEntry containing the TBSCertificate and issuer key hash. | |
239 // Returns true if the TBSCertificate component in the LogEntry does not | |
240 // exceed kMaxTbsCertificateLength and so can be written to |output|. | |
241 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) { | |
242 WriteEncodedBytes( | |
243 base::StringPiece( | |
244 reinterpret_cast<const char*>(input.issuer_key_hash.data), | |
245 kLogIdLength), | |
246 output); | |
247 return WriteVariableBytes(kTbsCertificateLengthBytes, | |
248 input.tbs_certificate, output); | |
249 } | |
250 | |
251 } // namespace | |
252 | |
253 bool EncodeDigitallySigned(const DigitallySigned& input, | |
254 std::string* output) { | |
255 WriteUint(kHashAlgorithmLength, input.hash_algorithm, output); | |
256 WriteUint(kSigAlgorithmLength, input.signature_algorithm, | |
257 output); | |
258 return WriteVariableBytes(kSignatureLengthBytes, input.signature_data, | |
259 output); | |
260 } | |
261 | |
262 bool DecodeDigitallySigned(base::StringPiece* input, | |
263 DigitallySigned* output) { | |
264 unsigned hash_algo; | |
265 unsigned sig_algo; | |
266 base::StringPiece sig_data; | |
267 | |
268 if (!ReadUint(kHashAlgorithmLength, input, &hash_algo) || | |
269 !ReadUint(kSigAlgorithmLength, input, &sig_algo) || | |
270 !ReadVariableBytes(kSignatureLengthBytes, input, &sig_data)) { | |
271 return false; | |
272 } | |
273 | |
274 DigitallySigned result; | |
275 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) { | |
276 DVLOG(1) << "Invalid hash algorithm " << hash_algo; | |
277 return false; | |
278 } | |
279 if (!ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) { | |
280 DVLOG(1) << "Invalid signature algorithm " << sig_algo; | |
281 return false; | |
282 } | |
283 sig_data.CopyToString(&result.signature_data); | |
284 | |
285 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy | |
Ryan Sleevi
2013/10/30 20:48:27
Just remove this comment
Eran M. (Google)
2013/10/31 11:37:37
Done.
| |
286 // of |result.signature_data|. | |
287 *output = result; | |
288 return true; | |
289 } | |
290 | |
291 bool EncodeLogEntry(const LogEntry& input, std::string* output) { | |
292 WriteUint(kLogEntryTypeLength, input.type, output); | |
293 switch (input.type) { | |
294 case LogEntry::LOG_ENTRY_TYPE_X509: | |
295 return EncodeAsn1CertLogEntry(input, output); | |
296 case LogEntry::LOG_ENTRY_TYPE_PRECERT: | |
297 return EncodePrecertLogEntry(input, output); | |
298 } | |
299 return false; | |
300 } | |
301 | |
302 bool EncodeV1SCTSignedData(const base::Time& timestamp, | |
303 const std::string& serialized_log_entry, | |
304 const std::string& extensions, | |
305 std::string* output) { | |
306 WriteUint(kVersionLength, SignedCertificateTimestamp::SCT_VERSION_1, | |
307 output); | |
308 WriteUint(kSignatureTypeLength, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP, | |
309 output); | |
310 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch(); | |
311 WriteUint(kTimestampLength, time_since_epoch.InMilliseconds(), | |
312 output); | |
313 // NOTE: serialized_log_entry must already be serialized and contain the | |
314 // length as the prefix. | |
315 WriteEncodedBytes(serialized_log_entry, output); | |
316 return WriteVariableBytes(kExtensionsLengthBytes, extensions, output); | |
317 } | |
318 | |
319 bool DecodeSCTList(base::StringPiece* input, | |
320 std::vector<base::StringPiece>* output) { | |
321 std::vector<base::StringPiece> result; | |
322 if (!ReadList(kSCTListLengthBytes, kSerializedSCTLengthBytes, | |
323 input, &result)) { | |
324 return false; | |
325 } | |
326 | |
327 if (!input->empty() || result.empty()) | |
328 return false; | |
329 output->swap(result); | |
330 return true; | |
331 } | |
332 | |
333 bool DecodeSignedCertificateTimestamp(base::StringPiece* input, | |
334 SignedCertificateTimestamp* output) { | |
335 SignedCertificateTimestamp result; | |
336 unsigned version; | |
337 if (!ReadUint(kVersionLength, input, &version)) | |
338 return false; | |
339 if (version != SignedCertificateTimestamp::SCT_VERSION_1) { | |
340 DVLOG(1) << "Unsupported/invalid version " << version; | |
341 return false; | |
342 } | |
343 | |
344 result.version = SignedCertificateTimestamp::SCT_VERSION_1; | |
345 uint64 timestamp; | |
346 base::StringPiece log_id; | |
347 base::StringPiece extensions; | |
348 if (!ReadFixedBytes(kLogIdLength, input, &log_id) || | |
349 !ReadUint(kTimestampLength, input, ×tamp) || | |
350 !ReadVariableBytes(kExtensionsLengthBytes, input, | |
351 &extensions) || | |
352 !DecodeDigitallySigned(input, &result.signature)) { | |
353 return false; | |
354 } | |
355 | |
356 if (timestamp > static_cast<uint64>(kint64max)) { | |
357 DVLOG(1) << "Timestamp value too big to cast to int64: " << timestamp; | |
358 return false; | |
359 } | |
360 | |
361 log_id.CopyToString(&result.log_id); | |
362 extensions.CopyToString(&result.extensions); | |
363 result.timestamp = | |
364 base::Time::UnixEpoch() + | |
365 base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp)); | |
366 | |
367 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy. | |
Ryan Sleevi
2013/10/30 20:48:27
same here re: comment
Eran M. (Google)
2013/10/31 11:37:37
Done.
| |
368 *output = result; | |
369 return true; | |
370 } | |
371 | |
372 } // namespace ct | |
373 | |
374 } // namespace net | |
OLD | NEW |