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