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

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: 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 // SCT Version length
wtc 2013/10/24 23:14:23 Nit: define "SCT".
Eran M. (Google) 2013/10/30 18:00:08 Done.
17 const size_t kVersionLengthInBytes = 1;
wtc 2013/10/24 23:14:23 Since all of the lengths in this file are in bytes
Eran M. (Google) 2013/10/30 18:00:08 Done.
18
19 // Members of a V1 SCT
20 const size_t kLogIdLengthInBytes = 32;
21 const size_t kTimestampLengthInBytes = 8;
22 const size_t kMaxExtensionsLengthInBytes = (1 << 16) - 1;
23 const size_t kHashAlgorithmLengthInBytes = 1;
24 const size_t kSigAlgorithmLengthInBytes = 1;
25 const size_t kMaxSignatureLengthInBytes = (1 << 16) - 1;
26
27 // Members of the digitally-signed struct of a V1 SCT
28 const size_t kSignatureTypeLengthInBytes = 1;
29 const size_t kLogEntryTypeLengthInBytes = 2;
30 const size_t kMaxAsn1CertificateLengthInBytes = (1 << 24) - 1;
31 const size_t kMaxTbsCertificateLengthInBytes = (1 << 24) - 1;
32
33 const size_t kMaxSCTListLengthInBytes = (1 << 16) - 1;
34 const size_t kMaxSerializedSCTLengthInBytes = (1 << 16) - 1;
35
36 enum SignatureType {
37 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
wtc 2013/10/24 23:14:23 Nit: I suggest also include "tree_hash(1)" for com
Eran M. (Google) 2013/10/30 18:00:08 Done.
38 };
39
40 // Returns the size, in bytes, of the length prefix needed for a field
41 // capable of being |max_field_length| long. For example, a field that can
42 // be 2^16 - 1 bytes needs 2 bytes to encode the length.
43 // This is essentially log2(max_field_length)
wtc 2013/10/24 23:14:23 It should be (log2(max_field_length) + 7)/8 becaus
Eran M. (Google) 2013/10/30 18:00:08 Good idea - switched the constants to the number o
44 size_t PrefixLength(size_t max_field_length) {
45 CHECK_GT(max_field_length, 0U);
46 size_t prefix_length = 0;
47
48 for (; max_field_length > 0; max_field_length >>= 8)
49 ++prefix_length;
50
51 return prefix_length;
52 }
53
54 // Reads a TLS-encoded variable length unsigned integer from |in|.
55 // |length| indicates the size (in bytes) of the integer. On success, returns
56 // true and stores the result in |*out|.
wtc 2013/10/24 23:14:23 Document how |in| is updated. You may want to poin
Eran M. (Google) 2013/10/30 18:00:08 Done, also added the description of the modificati
57 template <typename T>
58 bool ReadUint(size_t length, base::StringPiece* in, T* out) {
wtc 2013/10/24 23:14:23 It would be nice to assert that length <= sizeof(T
Eran M. (Google) 2013/10/30 18:00:08 Done.
59 if (in->size() < length)
60 return false;
61 T result = 0;
62 for (size_t i = 0; i < length; ++i) {
63 result = (result << 8) | static_cast<unsigned char>((*in)[i]);
64 }
wtc 2013/10/24 23:14:23 Nit: omit curly braces.
Eran M. (Google) 2013/10/30 18:00:08 I prefer to keep them in this context, as the body
65 in->remove_prefix(length);
66 *out = result;
67 return true;
68 }
69
70 // Reads a TLS-encoded field length from |in|.
71 // |max_length| indicates the maximum length supported (eg: 2^24 - 1). On
72 // success, returns true and stores the result in |*out|.
wtc 2013/10/24 23:14:23 Document how |in| is updated.
Eran M. (Google) 2013/10/30 18:00:08 Done.
73 bool ReadLength(size_t max_length, base::StringPiece* in, size_t* out) {
74 size_t prefix_length = PrefixLength(max_length);
75 size_t length;
76 if (!ReadUint(prefix_length, in, &length) || length > max_length)
wtc 2013/10/24 23:14:23 Since we only pass constants of the form 2^n - 1 (
Eran M. (Google) 2013/10/30 18:00:08 Done.
77 return false;
78 *out = length;
79 return true;
80 }
81
82 // Reads |length| bytes from |*in|. If |*in| is too small, returns false.
83 bool ReadFixedBytes(size_t length,
84 base::StringPiece* in,
85 base::StringPiece* out) {
86 if (in->length() < length)
87 return false;
88 out->set(in->data(), length);
89 in->remove_prefix(length);
90 return true;
91 }
92
93 // Reads a length-prefixed variable amount of bytes from |in|, updating |out|
94 // on success. |max_length| indicates the maximum length of the field.
95 bool ReadVariableBytes(size_t max_length,
96 base::StringPiece* in,
97 base::StringPiece* out) {
98 size_t length;
99 if (!ReadLength(max_length, in, &length))
100 return false;
101 return ReadFixedBytes(length, in, out);
102 }
103
104 // Reads a variable-length list that has been TLS encoded.
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;
113
114 base::StringPiece list_data;
115 if (!ReadVariableBytes(max_list_length, in, &list_data))
116 return false;
117
118 while (!list_data.empty()) {
119 base::StringPiece list_item;
120 if (!ReadVariableBytes(max_item_length, &list_data, &list_item)) {
121 return false;
122 }
wtc 2013/10/24 23:14:23 Nit: omit curly braces.
Eran M. (Google) 2013/10/30 18:00:08 Added a DVLOG statement, so keeping them (to be co
123 if (list_item.empty()) {
wtc 2013/10/24 23:14:23 Some types may be zero-length. But I agree it does
Eran M. (Google) 2013/10/30 18:00:08 This specifically is used to parse SCT lists, an S
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.
138 bool ConvertHashAlgorithm(int 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.
159 bool ConvertSigAlgorithm(int in, DigitallySigned::SignatureAlgorithm* out) {
wtc 2013/10/24 23:14:23 Nit: Sig => Signature
Eran M. (Google) 2013/10/30 18:00:08 Done.
160 switch (in) {
161 case DigitallySigned::SIG_ALGO_ANONYMOUS:
162 case DigitallySigned::SIG_ALGO_RSA:
163 case DigitallySigned::SIG_ALGO_DSA:
164 case DigitallySigned::SIG_ALGO_ECDSA:
165 break;
166 default:
167 return false;
168 }
169 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
170 return true;
171 }
172
173 // Checks and converts a log entry type.
174 // |in| the numeric representation of the log type.
175 // If the log type is 0 (X509 cert) or 1 (PreCertificate), fills in |out| and
176 // returns true. Otherwise, returns false.
177 bool ConvertLogEntryType(int in, LogEntry::Type* out) {
178 switch (in) {
179 case LogEntry::LOG_ENTRY_TYPE_X509:
180 case LogEntry::LOG_ENTRY_TYPE_PRECERT:
181 break;
182 default:
183 return false;
184 }
185 *out = static_cast<LogEntry::Type>(in);
186 return true;
187 }
188
189 // Writes a TLS-encoded variable length unsigned integer to |output|.
190 // |max_length| indicates the size (in bytes) of the integer.
191 // |value| the value itself to be written.
192 template <typename T>
193 void WriteUint(size_t max_length, T value, std::string* output) {
wtc 2013/10/24 23:14:23 "max_length" is a poor parameter name because it h
Eran M. (Google) 2013/10/30 18:00:08 Done.
194 DCHECK_LE(max_length, sizeof(T));
195 DCHECK(max_length == sizeof(T) || value >> (max_length * 8) == 0);
196
197 for (; max_length > 0; --max_length) {
198 output->push_back(
199 ((value & (static_cast<T>(0xFF) << ((max_length - 1) * 8)))
200 >> ((max_length - 1) * 8)));
wtc 2013/10/24 23:14:23 You don't need the left shift of 0xFF. You can do
Eran M. (Google) 2013/10/30 18:00:08 Done.
201 }
202 }
203
204 // Writes a fixed-length array to |output| from |input|.
205 void WriteFixedBytes(const base::StringPiece& input, std::string* output) {
206 input.AppendToString(output);
207 }
208
209 // Writes a variable-length array to |output|.
210 // |max_field_length| indicates the length (in bytes) of the array.
211 // |input| is the array itself.
212 // If the size of |input| is less than |max_field_length|, encode the
213 // length and data and return true. Otherwise, return false.
214 bool WriteVariableBytes(size_t max_field_length,
wtc 2013/10/24 23:14:23 Nit: this parameter's name should ideally match th
Eran M. (Google) 2013/10/30 18:00:08 Done.
215 const base::StringPiece& input,
216 std::string* output) {
217 if (input.size() > max_field_length)
218 return false;
219
220 size_t prefix_length = PrefixLength(max_field_length);
221 WriteUint(prefix_length, input.size(), output);
222 WriteFixedBytes(input, output);
223
224 return true;
225 }
226
227 // Writes a LogEntry of type X509 cert to |output|.
wtc 2013/10/24 23:14:23 Nit: X509 => X.509
Eran M. (Google) 2013/10/30 18:00:08 Done.
228 // |input| is the LogEntry containing the certificate.
229 // Returns true if the leaf_cert in the LogEntry does not exceed
230 // kMaxAsn1CertificateLengthInBytes and so can be written to |output|.
231 bool EncodeAsn1CertLogEntry(const LogEntry& input, std::string* output) {
232 return WriteVariableBytes(kMaxAsn1CertificateLengthInBytes,
233 input.leaf_cert, output);
234 }
235
236 // Writes a LogEntry of type PreCertificate to |output|.
237 // |input| is the LogEntry containing the TBSCertificate and issuer key hash.
238 // Returns true if the TBSCertificate component in the LogEntry does not
239 // exceed kMaxTbsCertificateLengthInBytes and so can be written to |output|.
240 bool EncodePrecertLogEntry(const LogEntry& input, std::string* output) {
241 WriteFixedBytes(
242 base::StringPiece(
243 reinterpret_cast<const char*>(input.issuer_key_hash.data),
244 kLogIdLengthInBytes),
245 output);
246 return WriteVariableBytes(kMaxTbsCertificateLengthInBytes,
247 input.tbs_certificate, output);
248 }
249
250 } // namespace
251
252 // If |input| is less than kMaxSignatureLengthInBytes, encodes the |input|
wtc 2013/10/24 23:14:23 Nit: If |input| is => If |input.signature_data| is
Eran M. (Google) 2013/10/30 18:00:08 Done.
253 // to |output and returns true. Otherwise, returns false.
254 bool EncodeDigitallySigned(const DigitallySigned& input,
255 std::string* output) {
256 WriteUint(kHashAlgorithmLengthInBytes, input.hash_algorithm, output);
257 WriteUint(kSigAlgorithmLengthInBytes, input.signature_algorithm,
258 output);
259 return WriteVariableBytes(kMaxSignatureLengthInBytes, input.signature_data,
260 output);
261 }
262
263 // Reads and decodes a DigitallySigned object from |input|.
264 // Returns true and fills |output| if all fields can be read, false otherwise.
265 bool DecodeDigitallySigned(base::StringPiece* input,
266 DigitallySigned* output) {
267 int hash_algo;
268 int sig_algo;
269 base::StringPiece sig_data;
270
271 if (!ReadUint(kHashAlgorithmLengthInBytes, input, &hash_algo) ||
272 !ReadUint(kSigAlgorithmLengthInBytes, input, &sig_algo) ||
wtc 2013/10/24 23:14:23 The third argument of ReadUint (as the function na
Eran M. (Google) 2013/10/30 18:00:08 Done - changed to unsigned.
273 !ReadVariableBytes(kMaxSignatureLengthInBytes, input, &sig_data)) {
274 return false;
275 }
276
277 DigitallySigned result;
278 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm)) {
279 DVLOG(1) << "Invalid hash algorithm " << hash_algo;
280 return false;
281 }
282 if (!ConvertSigAlgorithm(sig_algo, &result.signature_algorithm)) {
283 DVLOG(1) << "Invalid signature algorithm " << sig_algo;
284 return false;
285 }
286 sig_data.CopyToString(&result.signature_data);
287
288 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy
289 // of |result.signature|.
wtc 2013/10/24 23:14:23 Nit: |result.signature| => |result.signature_data|
Eran M. (Google) 2013/10/30 18:00:08 Done.
290 *output = result;
291 return true;
292 }
293
294 // Encodes the |input| LogEntry to |output|. Returns true if the entry size
295 // does not exceed allowed size in RFC6962, false otherwise.
296 bool EncodeLogEntry(const LogEntry& input, std::string* output) {
297 WriteUint(kLogEntryTypeLengthInBytes, input.type, output);
298 switch (input.type) {
299 case LogEntry::LOG_ENTRY_TYPE_X509:
300 return EncodeAsn1CertLogEntry(input, output);
301 case LogEntry::LOG_ENTRY_TYPE_PRECERT:
302 return EncodePrecertLogEntry(input, output);
303 }
304 return false;
305 }
306
307 // Encodes the data signed by an SCT into |output|. The signature included in
308 // the SCT is then verified over these bytes.
309 // |timestamp| timestamp from the SCT.
310 // |serialized_log_entry| the log entry signed by the SCT.
311 // |extensions| CT extensions.
312 // Returns true if the extensions' length does not exceed
313 // kMaxExtensionsLengthInBytes, false otherwise.
314 bool EncodeV1SCTSignedData(const base::Time& timestamp,
315 const std::string& serialized_log_entry,
316 const std::string& extensions,
317 std::string* output) {
318 WriteUint(kVersionLengthInBytes, SignedCertificateTimestamp::SCT_VERSION_1,
319 output);
320 WriteUint(kSignatureTypeLengthInBytes, SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP,
321 output);
322 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
323 WriteUint(kTimestampLengthInBytes, time_since_epoch.InMilliseconds(),
324 output);
325 WriteFixedBytes(serialized_log_entry, output);
wtc 2013/10/24 23:14:23 Using the function name "WriteFixedBytes" is a lit
Eran M. (Google) 2013/10/30 18:00:08 Done - renamed to WriteEncodedBytes (and updated t
326 return WriteVariableBytes(kMaxExtensionsLengthInBytes, extensions, output);
327 }
328
329 // Decode a list of SCTs: from a single string in |input| to a vector of
330 // individually-encoded SCTs |output|. This list is typically obtained
331 // from the CT extension in a certificate.
332 // Returns true if the list could be read and decoded successfully, false
333 // otherwise (note that the validity of each individual SCT should be checked
334 // separately).
335 bool DecodeSCTList(base::StringPiece* input,
336 std::vector<base::StringPiece>* output) {
337 std::vector<base::StringPiece> result;
338 if (!ReadList(kMaxSCTListLengthInBytes, kMaxSerializedSCTLengthInBytes,
339 input, &result)) {
340 return false;
341 }
342
343 if (!input->empty() || result.empty())
344 return false;
345 output->swap(result);
346 return true;
347 }
348
349 // Decodes a single SCT from |input| to |output|.
350 // Returns true if all fields in the SCT could be read and decoded, false
351 // otherwise.
352 bool DecodeSignedCertificateTimestamp(base::StringPiece* input,
353 SignedCertificateTimestamp* output) {
354 SignedCertificateTimestamp result;
355 int version;
356 if (!ReadUint(kVersionLengthInBytes, input, &version))
wtc 2013/10/24 23:14:23 |version| is a signed int, but ReadUint implies we
Eran M. (Google) 2013/10/30 18:00:08 Done.
357 return false;
358 if (version != SignedCertificateTimestamp::SCT_VERSION_1) {
359 DVLOG(1) << "Unsupported/invalid version " << version;
360 return false;
361 }
362
363 result.version = SignedCertificateTimestamp::SCT_VERSION_1;
364 uint64 timestamp;
365 base::StringPiece log_id;
366 base::StringPiece extensions;
367 if (!ReadFixedBytes(kLogIdLengthInBytes, input, &log_id) ||
368 !ReadUint(kTimestampLengthInBytes, input, &timestamp) ||
369 !ReadVariableBytes(kMaxExtensionsLengthInBytes, input,
370 &extensions) ||
371 !DecodeDigitallySigned(input, &result.signature)) {
372 return false;
373 }
374 log_id.CopyToString(&result.log_id);
375 extensions.CopyToString(&result.extensions);
376 result.timestamp =
377 base::Time::UnixEpoch() +
378 base::TimeDelta::FromMilliseconds(static_cast<int64>(timestamp));
wtc 2013/10/24 23:14:23 Should we validate |timestamp| before casting it t
Eran M. (Google) 2013/10/30 18:00:08 Done - verified that the value does not exceed max
379
380 // XXX(rsleevi): Declare a swap specialization to avoid the extra copy.
381 *output = result;
382 return true;
383 }
384
385 } // namespace ct
386
387 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698