Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(282)

Side by Side Diff: net/cert/ct_serialization.cc

Issue 37633002: CT: First step towards supporting Certificate Transparency in Chrome. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Exporting DigitallySigned Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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, &timestamp) ||
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698