1 // Copyright 2013 The Chromium Authors
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 <string_view>
8
9 #include "base/logging.h"
10 #include "base/numerics/checked_math.h"
11 #include "crypto/sha2.h"
12 #include "net/cert/merkle_tree_leaf.h"
13 #include "net/cert/signed_certificate_timestamp.h"
14 #include "net/cert/signed_tree_head.h"
15 #include "third_party/boringssl/src/include/openssl/bytestring.h"
16
17 namespace net::ct {
18
19 namespace {
20
21 const size_t kLogIdLength = crypto::kSHA256Length;
22
23 enum SignatureType {
24 SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP = 0,
25 TREE_HASH = 1,
26 };
27
28 // Reads a variable-length SCT list that has been TLS encoded.
29 // The bytes read from |in| are discarded (i.e. |in|'s prefix removed)
30 // |max_list_length| contains the overall length of the encoded list.
31 // |max_item_length| contains the maximum length of a single item.
32 // On success, returns true and updates |*out| with the encoded list.
ReadSCTList(CBS * in,std::vector<std::string_view> * out)33 bool ReadSCTList(CBS* in, std::vector<std::string_view>* out) {
34 std::vector<std::string_view> result;
35
36 CBS sct_list_data;
37
38 if (!CBS_get_u16_length_prefixed(in, &sct_list_data))
39 return false;
40
41 while (CBS_len(&sct_list_data) != 0) {
42 CBS sct_list_item;
43 if (!CBS_get_u16_length_prefixed(&sct_list_data, &sct_list_item) ||
44 CBS_len(&sct_list_item) == 0) {
45 return false;
46 }
47
48 result.emplace_back(reinterpret_cast<const char*>(CBS_data(&sct_list_item)),
49 CBS_len(&sct_list_item));
50 }
51
52 result.swap(*out);
53 return true;
54 }
55
56 // Checks and converts a hash algorithm.
57 // |in| is the numeric representation of the algorithm.
58 // If the hash algorithm value is in a set of known values, fills in |out| and
59 // returns true. Otherwise, returns false.
ConvertHashAlgorithm(unsigned in,DigitallySigned::HashAlgorithm * out)60 bool ConvertHashAlgorithm(unsigned in, DigitallySigned::HashAlgorithm* out) {
61 switch (in) {
62 case DigitallySigned::HASH_ALGO_NONE:
63 case DigitallySigned::HASH_ALGO_MD5:
64 case DigitallySigned::HASH_ALGO_SHA1:
65 case DigitallySigned::HASH_ALGO_SHA224:
66 case DigitallySigned::HASH_ALGO_SHA256:
67 case DigitallySigned::HASH_ALGO_SHA384:
68 case DigitallySigned::HASH_ALGO_SHA512:
69 break;
70 default:
71 return false;
72 }
73 *out = static_cast<DigitallySigned::HashAlgorithm>(in);
74 return true;
75 }
76
77 // Checks and converts a signing algorithm.
78 // |in| is the numeric representation of the algorithm.
79 // If the signing algorithm value is in a set of known values, fills in |out|
80 // and returns true. Otherwise, returns false.
ConvertSignatureAlgorithm(unsigned in,DigitallySigned::SignatureAlgorithm * out)81 bool ConvertSignatureAlgorithm(
82 unsigned in,
83 DigitallySigned::SignatureAlgorithm* out) {
84 switch (in) {
85 case DigitallySigned::SIG_ALGO_ANONYMOUS:
86 case DigitallySigned::SIG_ALGO_RSA:
87 case DigitallySigned::SIG_ALGO_DSA:
88 case DigitallySigned::SIG_ALGO_ECDSA:
89 break;
90 default:
91 return false;
92 }
93 *out = static_cast<DigitallySigned::SignatureAlgorithm>(in);
94 return true;
95 }
96
97 // Writes a SignedEntryData of type X.509 cert to |*output|.
98 // |input| is the SignedEntryData containing the certificate.
99 // Returns true if the leaf_certificate in the SignedEntryData does not exceed
100 // kMaxAsn1CertificateLength and so can be written to |output|.
EncodeAsn1CertSignedEntry(const SignedEntryData & input,CBB * output)101 bool EncodeAsn1CertSignedEntry(const SignedEntryData& input, CBB* output) {
102 CBB child;
103 return CBB_add_u24_length_prefixed(output, &child) &&
104 CBB_add_bytes(
105 &child,
106 reinterpret_cast<const uint8_t*>(input.leaf_certificate.data()),
107 input.leaf_certificate.size()) &&
108 CBB_flush(output);
109 }
110
111 // Writes a SignedEntryData of type PreCertificate to |*output|.
112 // |input| is the SignedEntryData containing the TBSCertificate and issuer key
113 // hash. Returns true if the TBSCertificate component in the SignedEntryData
114 // does not exceed kMaxTbsCertificateLength and so can be written to |output|.
EncodePrecertSignedEntry(const SignedEntryData & input,CBB * output)115 bool EncodePrecertSignedEntry(const SignedEntryData& input, CBB* output) {
116 CBB child;
117 return CBB_add_bytes(
118 output,
119 reinterpret_cast<const uint8_t*>(input.issuer_key_hash.data),
120 kLogIdLength) &&
121 CBB_add_u24_length_prefixed(output, &child) &&
122 CBB_add_bytes(
123 &child,
124 reinterpret_cast<const uint8_t*>(input.tbs_certificate.data()),
125 input.tbs_certificate.size()) &&
126 CBB_flush(output);
127 }
128
129 } // namespace
130
EncodeDigitallySigned(const DigitallySigned & input,CBB * output_cbb)131 bool EncodeDigitallySigned(const DigitallySigned& input, CBB* output_cbb) {
132 CBB child;
133 return CBB_add_u8(output_cbb, input.hash_algorithm) &&
134 CBB_add_u8(output_cbb, input.signature_algorithm) &&
135 CBB_add_u16_length_prefixed(output_cbb, &child) &&
136 CBB_add_bytes(
137 &child,
138 reinterpret_cast<const uint8_t*>(input.signature_data.data()),
139 input.signature_data.size()) &&
140 CBB_flush(output_cbb);
141 }
142
EncodeDigitallySigned(const DigitallySigned & input,std::string * output)143 bool EncodeDigitallySigned(const DigitallySigned& input,
144 std::string* output) {
145 bssl::ScopedCBB output_cbb;
146 if (!CBB_init(output_cbb.get(), 64) ||
147 !EncodeDigitallySigned(input, output_cbb.get()) ||
148 !CBB_flush(output_cbb.get())) {
149 return false;
150 }
151
152 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
153 CBB_len(output_cbb.get()));
154 return true;
155 }
156
DecodeDigitallySigned(CBS * input,DigitallySigned * output)157 bool DecodeDigitallySigned(CBS* input, DigitallySigned* output) {
158 uint8_t hash_algo;
159 uint8_t sig_algo;
160 CBS sig_data;
161
162 if (!CBS_get_u8(input, &hash_algo) || !CBS_get_u8(input, &sig_algo) ||
163 !CBS_get_u16_length_prefixed(input, &sig_data)) {
164 return false;
165 }
166
167 DigitallySigned result;
168 if (!ConvertHashAlgorithm(hash_algo, &result.hash_algorithm) ||
169 !ConvertSignatureAlgorithm(sig_algo, &result.signature_algorithm)) {
170 return false;
171 }
172
173 result.signature_data.assign(
174 reinterpret_cast<const char*>(CBS_data(&sig_data)), CBS_len(&sig_data));
175
176 *output = result;
177 return true;
178 }
179
DecodeDigitallySigned(std::string_view * input,DigitallySigned * output)180 bool DecodeDigitallySigned(std::string_view* input, DigitallySigned* output) {
181 CBS input_cbs;
182 CBS_init(&input_cbs, reinterpret_cast<const uint8_t*>(input->data()),
183 input->size());
184 bool result = DecodeDigitallySigned(&input_cbs, output);
185 input->remove_prefix(input->size() - CBS_len(&input_cbs));
186 return result;
187 }
188
EncodeSignedEntry(const SignedEntryData & input,CBB * output)189 static bool EncodeSignedEntry(const SignedEntryData& input, CBB* output) {
190 if (!CBB_add_u16(output, input.type)) {
191 return false;
192 }
193 switch (input.type) {
194 case SignedEntryData::LOG_ENTRY_TYPE_X509:
195 return EncodeAsn1CertSignedEntry(input, output);
196 case SignedEntryData::LOG_ENTRY_TYPE_PRECERT:
197 return EncodePrecertSignedEntry(input, output);
198 }
199 return false;
200 }
201
EncodeSignedEntry(const SignedEntryData & input,std::string * output)202 bool EncodeSignedEntry(const SignedEntryData& input, std::string* output) {
203 bssl::ScopedCBB output_cbb;
204
205 if (!CBB_init(output_cbb.get(), 64) ||
206 !EncodeSignedEntry(input, output_cbb.get()) ||
207 !CBB_flush(output_cbb.get())) {
208 return false;
209 }
210
211 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
212 CBB_len(output_cbb.get()));
213 return true;
214 }
215
ReadTimeSinceEpoch(CBS * input,base::Time * output)216 static bool ReadTimeSinceEpoch(CBS* input, base::Time* output) {
217 uint64_t time_since_epoch = 0;
218 if (!CBS_get_u64(input, &time_since_epoch))
219 return false;
220
221 base::CheckedNumeric<int64_t> time_since_epoch_signed = time_since_epoch;
222
223 if (!time_since_epoch_signed.IsValid()) {
224 return false;
225 }
226
227 *output = base::Time::UnixEpoch() +
228 base::Milliseconds(int64_t{time_since_epoch_signed.ValueOrDie()});
229
230 return true;
231 }
232
WriteTimeSinceEpoch(const base::Time & timestamp,CBB * output)233 static bool WriteTimeSinceEpoch(const base::Time& timestamp, CBB* output) {
234 base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch();
235 return CBB_add_u64(output, time_since_epoch.InMilliseconds());
236 }
237
EncodeTreeLeaf(const MerkleTreeLeaf & leaf,std::string * output)238 bool EncodeTreeLeaf(const MerkleTreeLeaf& leaf, std::string* output) {
239 bssl::ScopedCBB output_cbb;
240 CBB child;
241 if (!CBB_init(output_cbb.get(), 64) ||
242 !CBB_add_u8(output_cbb.get(), 0) || // version: 1
243 !CBB_add_u8(output_cbb.get(), 0) || // type: timestamped entry
244 !WriteTimeSinceEpoch(leaf.timestamp, output_cbb.get()) ||
245 !EncodeSignedEntry(leaf.signed_entry, output_cbb.get()) ||
246 !CBB_add_u16_length_prefixed(output_cbb.get(), &child) ||
247 !CBB_add_bytes(&child,
248 reinterpret_cast<const uint8_t*>(leaf.extensions.data()),
249 leaf.extensions.size()) ||
250 !CBB_flush(output_cbb.get())) {
251 return false;
252 }
253 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
254 CBB_len(output_cbb.get()));
255 return true;
256 }
257
EncodeV1SCTSignedData(const base::Time & timestamp,const std::string & serialized_log_entry,const std::string & extensions,std::string * output)258 bool EncodeV1SCTSignedData(const base::Time& timestamp,
259 const std::string& serialized_log_entry,
260 const std::string& extensions,
261 std::string* output) {
262 bssl::ScopedCBB output_cbb;
263 CBB child;
264 if (!CBB_init(output_cbb.get(), 64) ||
265 !CBB_add_u8(output_cbb.get(), SignedCertificateTimestamp::V1) ||
266 !CBB_add_u8(output_cbb.get(), SIGNATURE_TYPE_CERTIFICATE_TIMESTAMP) ||
267 !WriteTimeSinceEpoch(timestamp, output_cbb.get()) ||
268 // NOTE: serialized_log_entry must already be serialized and contain the
269 // length as the prefix.
270 !CBB_add_bytes(
271 output_cbb.get(),
272 reinterpret_cast<const uint8_t*>(serialized_log_entry.data()),
273 serialized_log_entry.size()) ||
274 !CBB_add_u16_length_prefixed(output_cbb.get(), &child) ||
275 !CBB_add_bytes(&child,
276 reinterpret_cast<const uint8_t*>(extensions.data()),
277 extensions.size()) ||
278 !CBB_flush(output_cbb.get())) {
279 return false;
280 }
281 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
282 CBB_len(output_cbb.get()));
283 return true;
284 }
285
EncodeTreeHeadSignature(const SignedTreeHead & signed_tree_head,std::string * output)286 bool EncodeTreeHeadSignature(const SignedTreeHead& signed_tree_head,
287 std::string* output) {
288 bssl::ScopedCBB output_cbb;
289 if (!CBB_init(output_cbb.get(), 64) ||
290 !CBB_add_u8(output_cbb.get(), signed_tree_head.version) ||
291 !CBB_add_u8(output_cbb.get(), TREE_HASH) ||
292 !WriteTimeSinceEpoch(signed_tree_head.timestamp, output_cbb.get()) ||
293 !CBB_add_u64(output_cbb.get(), signed_tree_head.tree_size) ||
294 !CBB_add_bytes(
295 output_cbb.get(),
296 reinterpret_cast<const uint8_t*>(signed_tree_head.sha256_root_hash),
297 kSthRootHashLength)) {
298 return false;
299 }
300 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
301 CBB_len(output_cbb.get()));
302 return true;
303 }
304
DecodeSCTList(std::string_view input,std::vector<std::string_view> * output)305 bool DecodeSCTList(std::string_view input,
306 std::vector<std::string_view>* output) {
307 std::vector<std::string_view> result;
308 CBS input_cbs;
309 CBS_init(&input_cbs, reinterpret_cast<const uint8_t*>(input.data()),
310 input.size());
311 if (!ReadSCTList(&input_cbs, &result) || CBS_len(&input_cbs) != 0 ||
312 result.empty()) {
313 return false;
314 }
315
316 output->swap(result);
317 return true;
318 }
319
DecodeSignedCertificateTimestamp(std::string_view * input,scoped_refptr<SignedCertificateTimestamp> * output)320 bool DecodeSignedCertificateTimestamp(
321 std::string_view* input,
322 scoped_refptr<SignedCertificateTimestamp>* output) {
323 auto result = base::MakeRefCounted<SignedCertificateTimestamp>();
324 uint8_t version;
325 CBS input_cbs;
326 CBS_init(&input_cbs, reinterpret_cast<const uint8_t*>(input->data()),
327 input->size());
328 if (!CBS_get_u8(&input_cbs, &version) ||
329 version != SignedCertificateTimestamp::V1) {
330 return false;
331 }
332
333 result->version = SignedCertificateTimestamp::V1;
334 CBS log_id;
335 CBS extensions;
336 if (!CBS_get_bytes(&input_cbs, &log_id, kLogIdLength) ||
337 !ReadTimeSinceEpoch(&input_cbs, &result->timestamp) ||
338 !CBS_get_u16_length_prefixed(&input_cbs, &extensions) ||
339 !DecodeDigitallySigned(&input_cbs, &result->signature)) {
340 return false;
341 }
342
343 result->log_id.assign(reinterpret_cast<const char*>(CBS_data(&log_id)),
344 CBS_len(&log_id));
345 result->extensions.assign(
346 reinterpret_cast<const char*>(CBS_data(&extensions)),
347 CBS_len(&extensions));
348 output->swap(result);
349 input->remove_prefix(input->size() - CBS_len(&input_cbs));
350 return true;
351 }
352
EncodeSignedCertificateTimestamp(const scoped_refptr<ct::SignedCertificateTimestamp> & input,std::string * output)353 bool EncodeSignedCertificateTimestamp(
354 const scoped_refptr<ct::SignedCertificateTimestamp>& input,
355 std::string* output) {
356 // This function only supports serialization of V1 SCTs.
357 DCHECK_EQ(SignedCertificateTimestamp::V1, input->version);
358 DCHECK_EQ(kLogIdLength, input->log_id.size());
359
360 bssl::ScopedCBB output_cbb;
361 CBB child;
362 if (!CBB_init(output_cbb.get(), 64) ||
363 !CBB_add_u8(output_cbb.get(), input->version) ||
364 !CBB_add_bytes(output_cbb.get(),
365 reinterpret_cast<const uint8_t*>(input->log_id.data()),
366 kLogIdLength) ||
367 !WriteTimeSinceEpoch(input->timestamp, output_cbb.get()) ||
368 !CBB_add_u16_length_prefixed(output_cbb.get(), &child) ||
369 !CBB_add_bytes(&child,
370 reinterpret_cast<const uint8_t*>(input->extensions.data()),
371 input->extensions.size()) ||
372 !EncodeDigitallySigned(input->signature, output_cbb.get()) ||
373 !CBB_flush(output_cbb.get())) {
374 return false;
375 }
376 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
377 CBB_len(output_cbb.get()));
378 return true;
379 }
380
EncodeSCTListForTesting(const std::vector<std::string> & scts,std::string * output)381 bool EncodeSCTListForTesting(const std::vector<std::string>& scts,
382 std::string* output) {
383 bssl::ScopedCBB output_cbb;
384 CBB output_child;
385 if (!CBB_init(output_cbb.get(), 64) ||
386 !CBB_add_u16_length_prefixed(output_cbb.get(), &output_child)) {
387 return false;
388 }
389
390 for (const std::string& sct : scts) {
391 bssl::ScopedCBB encoded_sct;
392 CBB encoded_sct_child;
393 if (!CBB_init(encoded_sct.get(), 64) ||
394 !CBB_add_u16_length_prefixed(encoded_sct.get(), &encoded_sct_child) ||
395 !CBB_add_bytes(&encoded_sct_child,
396 reinterpret_cast<const uint8_t*>(sct.data()),
397 sct.size()) ||
398 !CBB_flush(encoded_sct.get()) ||
399 !CBB_add_bytes(&output_child, CBB_data(encoded_sct.get()),
400 CBB_len(encoded_sct.get()))) {
401 return false;
402 }
403 }
404
405 if (!CBB_flush(output_cbb.get())) {
406 return false;
407 }
408 output->append(reinterpret_cast<const char*>(CBB_data(output_cbb.get())),
409 CBB_len(output_cbb.get()));
410 return true;
411 }
412
413 } // namespace net::ct
414