xref: /aosp_15_r20/external/cronet/net/cert/ct_serialization.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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