xref: /aosp_15_r20/external/cronet/net/cert/ct_objects_extractor.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_objects_extractor.h"
6 
7 #include <string.h>
8 
9 #include <string_view>
10 
11 #include "base/hash/sha1.h"
12 #include "base/logging.h"
13 #include "base/strings/string_util.h"
14 #include "crypto/sha2.h"
15 #include "net/cert/asn1_util.h"
16 #include "net/cert/signed_certificate_timestamp.h"
17 #include "net/cert/x509_util.h"
18 #include "third_party/boringssl/src/include/openssl/bytestring.h"
19 #include "third_party/boringssl/src/include/openssl/mem.h"
20 
21 namespace net::ct {
22 
23 namespace {
24 
25 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.5 - OCSP SingleExtension for
26 // X.509v3 Certificate Transparency Signed Certificate Timestamp List, see
27 // Section 3.3 of RFC6962.
28 const uint8_t kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
29                                      0xD6, 0x79, 0x02, 0x04, 0x05};
30 
31 // The wire form of the OID 1.3.6.1.5.5.7.48.1.1. See RFC 6960.
32 const uint8_t kOCSPBasicResponseOid[] = {0x2b, 0x06, 0x01, 0x05, 0x05,
33                                          0x07, 0x30, 0x01, 0x01};
34 
35 // The wire form of the OID 1.3.14.3.2.26.
36 const uint8_t kSHA1Oid[] = {0x2b, 0x0e, 0x03, 0x02, 0x1a};
37 
38 // The wire form of the OID 2.16.840.1.101.3.4.2.1.
39 const uint8_t kSHA256Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65,
40                               0x03, 0x04, 0x02, 0x01};
41 
StringEqualToCBS(const std::string & value1,const CBS * value2)42 bool StringEqualToCBS(const std::string& value1, const CBS* value2) {
43   if (CBS_len(value2) != value1.size())
44     return false;
45   return memcmp(value1.data(), CBS_data(value2), CBS_len(value2)) == 0;
46 }
47 
SkipElements(CBS * cbs,int count)48 bool SkipElements(CBS* cbs, int count) {
49   for (int i = 0; i < count; i++) {
50     if (!CBS_get_any_asn1_element(cbs, nullptr, nullptr, nullptr))
51       return false;
52   }
53   return true;
54 }
55 
SkipOptionalElement(CBS * cbs,unsigned tag)56 bool SkipOptionalElement(CBS* cbs, unsigned tag) {
57   CBS unused;
58   return !CBS_peek_asn1_tag(cbs, tag) || CBS_get_asn1(cbs, &unused, tag);
59 }
60 
61 // Copies all the bytes in |outer| which are before |inner| to |out|. |inner|
62 // must be a subset of |outer|.
CopyBefore(const CBS & outer,const CBS & inner,CBB * out)63 bool CopyBefore(const CBS& outer, const CBS& inner, CBB* out) {
64   CHECK_LE(CBS_data(&outer), CBS_data(&inner));
65   CHECK_LE(CBS_data(&inner) + CBS_len(&inner),
66            CBS_data(&outer) + CBS_len(&outer));
67 
68   return !!CBB_add_bytes(out, CBS_data(&outer),
69                          CBS_data(&inner) - CBS_data(&outer));
70 }
71 
72 // Copies all the bytes in |outer| which are after |inner| to |out|. |inner|
73 // must be a subset of |outer|.
CopyAfter(const CBS & outer,const CBS & inner,CBB * out)74 bool CopyAfter(const CBS& outer, const CBS& inner, CBB* out) {
75   CHECK_LE(CBS_data(&outer), CBS_data(&inner));
76   CHECK_LE(CBS_data(&inner) + CBS_len(&inner),
77            CBS_data(&outer) + CBS_len(&outer));
78 
79   return !!CBB_add_bytes(
80       out, CBS_data(&inner) + CBS_len(&inner),
81       CBS_data(&outer) + CBS_len(&outer) - CBS_data(&inner) - CBS_len(&inner));
82 }
83 
84 // Skips |tbs_cert|, which must be a TBSCertificate body, to just before the
85 // extensions element.
SkipTBSCertificateToExtensions(CBS * tbs_cert)86 bool SkipTBSCertificateToExtensions(CBS* tbs_cert) {
87   constexpr unsigned kVersionTag =
88       CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0;
89   constexpr unsigned kIssuerUniqueIDTag = CBS_ASN1_CONTEXT_SPECIFIC | 1;
90   constexpr unsigned kSubjectUniqueIDTag = CBS_ASN1_CONTEXT_SPECIFIC | 2;
91   return SkipOptionalElement(tbs_cert, kVersionTag) &&
92          SkipElements(tbs_cert,
93                       6 /* serialNumber through subjectPublicKeyInfo */) &&
94          SkipOptionalElement(tbs_cert, kIssuerUniqueIDTag) &&
95          SkipOptionalElement(tbs_cert, kSubjectUniqueIDTag);
96 }
97 
98 // Looks for the extension with the specified OID in |extensions|, which must
99 // contain the contents of a SEQUENCE of X.509 extension structures. If found,
100 // returns true and sets |*out| to the full extension element.
FindExtensionElement(const CBS & extensions,const uint8_t * oid,size_t oid_len,CBS * out)101 bool FindExtensionElement(const CBS& extensions,
102                           const uint8_t* oid,
103                           size_t oid_len,
104                           CBS* out) {
105   CBS extensions_copy = extensions;
106   CBS result;
107   CBS_init(&result, nullptr, 0);
108   bool found = false;
109   while (CBS_len(&extensions_copy) > 0) {
110     CBS extension_element;
111     if (!CBS_get_asn1_element(&extensions_copy, &extension_element,
112                               CBS_ASN1_SEQUENCE)) {
113       return false;
114     }
115 
116     CBS copy = extension_element;
117     CBS extension, extension_oid;
118     if (!CBS_get_asn1(&copy, &extension, CBS_ASN1_SEQUENCE) ||
119         !CBS_get_asn1(&extension, &extension_oid, CBS_ASN1_OBJECT)) {
120       return false;
121     }
122 
123     if (CBS_mem_equal(&extension_oid, oid, oid_len)) {
124       if (found)
125         return false;
126       found = true;
127       result = extension_element;
128     }
129   }
130   if (!found)
131     return false;
132 
133   *out = result;
134   return true;
135 }
136 
137 // Finds the SignedCertificateTimestampList in an extension with OID |oid| in
138 // |x509_exts|. If found, returns true and sets |*out_sct_list| to the encoded
139 // SCT list.
ParseSCTListFromExtensions(const CBS & extensions,const uint8_t * oid,size_t oid_len,std::string * out_sct_list)140 bool ParseSCTListFromExtensions(const CBS& extensions,
141                                 const uint8_t* oid,
142                                 size_t oid_len,
143                                 std::string* out_sct_list) {
144   CBS extension_element, extension, extension_oid, value, sct_list;
145   if (!FindExtensionElement(extensions, oid, oid_len, &extension_element) ||
146       !CBS_get_asn1(&extension_element, &extension, CBS_ASN1_SEQUENCE) ||
147       !CBS_get_asn1(&extension, &extension_oid, CBS_ASN1_OBJECT) ||
148       // Skip the optional critical element.
149       !SkipOptionalElement(&extension, CBS_ASN1_BOOLEAN) ||
150       // The extension value is stored in an OCTET STRING.
151       !CBS_get_asn1(&extension, &value, CBS_ASN1_OCTETSTRING) ||
152       CBS_len(&extension) != 0 ||
153       // The extension value itself is an OCTET STRING containing the
154       // serialized SCT list.
155       !CBS_get_asn1(&value, &sct_list, CBS_ASN1_OCTETSTRING) ||
156       CBS_len(&value) != 0) {
157     return false;
158   }
159 
160   DCHECK(CBS_mem_equal(&extension_oid, oid, oid_len));
161   *out_sct_list = std::string(
162       reinterpret_cast<const char*>(CBS_data(&sct_list)), CBS_len(&sct_list));
163   return true;
164 }
165 
166 // Finds the SingleResponse in |responses| which matches |issuer| and
167 // |cert_serial_number|. On success, returns true and sets
168 // |*out_single_response| to the body of the SingleResponse starting at the
169 // |certStatus| field.
FindMatchingSingleResponse(CBS * responses,const CRYPTO_BUFFER * issuer,const std::string & cert_serial_number,CBS * out_single_response)170 bool FindMatchingSingleResponse(CBS* responses,
171                                 const CRYPTO_BUFFER* issuer,
172                                 const std::string& cert_serial_number,
173                                 CBS* out_single_response) {
174   std::string_view issuer_spki;
175   if (!asn1::ExtractSPKIFromDERCert(
176           x509_util::CryptoBufferAsStringPiece(issuer), &issuer_spki))
177     return false;
178 
179   // In OCSP, only the key itself is under hash.
180   std::string_view issuer_spk;
181   if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk))
182     return false;
183 
184   // ExtractSubjectPublicKeyFromSPKI does not remove the initial octet encoding
185   // the number of unused bits in the ASN.1 BIT STRING so we do it here. For
186   // public keys, the bitstring is in practice always byte-aligned.
187   if (issuer_spk.empty() || issuer_spk[0] != 0)
188     return false;
189   issuer_spk.remove_prefix(1);
190 
191   // TODO(ekasper): add SHA-384 to crypto/sha2.h and here if it proves
192   // necessary.
193   // TODO(ekasper): only compute the hashes on demand.
194   std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk);
195   std::string issuer_key_sha1_hash =
196       base::SHA1HashString(std::string(issuer_spk));
197 
198   while (CBS_len(responses) > 0) {
199     CBS single_response, cert_id;
200     if (!CBS_get_asn1(responses, &single_response, CBS_ASN1_SEQUENCE) ||
201         !CBS_get_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE)) {
202       return false;
203     }
204 
205     CBS hash_algorithm, hash, serial_number, issuer_name_hash, issuer_key_hash;
206     if (!CBS_get_asn1(&cert_id, &hash_algorithm, CBS_ASN1_SEQUENCE) ||
207         !CBS_get_asn1(&hash_algorithm, &hash, CBS_ASN1_OBJECT) ||
208         !CBS_get_asn1(&cert_id, &issuer_name_hash, CBS_ASN1_OCTETSTRING) ||
209         !CBS_get_asn1(&cert_id, &issuer_key_hash, CBS_ASN1_OCTETSTRING) ||
210         !CBS_get_asn1(&cert_id, &serial_number, CBS_ASN1_INTEGER) ||
211         CBS_len(&cert_id) != 0) {
212       return false;
213     }
214 
215     // Check the serial number matches.
216     if (!StringEqualToCBS(cert_serial_number, &serial_number))
217       continue;
218 
219     // Check if the issuer_key_hash matches.
220     // TODO(ekasper): also use the issuer name hash in matching.
221     if (CBS_mem_equal(&hash, kSHA1Oid, sizeof(kSHA1Oid))) {
222       if (StringEqualToCBS(issuer_key_sha1_hash, &issuer_key_hash)) {
223         *out_single_response = single_response;
224         return true;
225       }
226     } else if (CBS_mem_equal(&hash, kSHA256Oid, sizeof(kSHA256Oid))) {
227       if (StringEqualToCBS(issuer_key_sha256_hash, &issuer_key_hash)) {
228         *out_single_response = single_response;
229         return true;
230       }
231     }
232   }
233 
234   return false;
235 }
236 
237 }  // namespace
238 
ExtractEmbeddedSCTList(const CRYPTO_BUFFER * cert,std::string * sct_list)239 bool ExtractEmbeddedSCTList(const CRYPTO_BUFFER* cert, std::string* sct_list) {
240   CBS cert_cbs;
241   CBS_init(&cert_cbs, CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert));
242   CBS cert_body, tbs_cert, extensions_wrap, extensions;
243   if (!CBS_get_asn1(&cert_cbs, &cert_body, CBS_ASN1_SEQUENCE) ||
244       CBS_len(&cert_cbs) != 0 ||
245       !CBS_get_asn1(&cert_body, &tbs_cert, CBS_ASN1_SEQUENCE) ||
246       !SkipTBSCertificateToExtensions(&tbs_cert) ||
247       // Extract the extensions list.
248       !CBS_get_asn1(&tbs_cert, &extensions_wrap,
249                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 3) ||
250       !CBS_get_asn1(&extensions_wrap, &extensions, CBS_ASN1_SEQUENCE) ||
251       CBS_len(&extensions_wrap) != 0 || CBS_len(&tbs_cert) != 0) {
252     return false;
253   }
254 
255   return ParseSCTListFromExtensions(extensions, kEmbeddedSCTOid,
256                                     sizeof(kEmbeddedSCTOid), sct_list);
257 }
258 
GetPrecertSignedEntry(const CRYPTO_BUFFER * leaf,const CRYPTO_BUFFER * issuer,SignedEntryData * result)259 bool GetPrecertSignedEntry(const CRYPTO_BUFFER* leaf,
260                            const CRYPTO_BUFFER* issuer,
261                            SignedEntryData* result) {
262   result->Reset();
263 
264   // Parse the TBSCertificate.
265   CBS cert_cbs;
266   CBS_init(&cert_cbs, CRYPTO_BUFFER_data(leaf), CRYPTO_BUFFER_len(leaf));
267   CBS cert_body, tbs_cert;
268   if (!CBS_get_asn1(&cert_cbs, &cert_body, CBS_ASN1_SEQUENCE) ||
269       CBS_len(&cert_cbs) != 0 ||
270       !CBS_get_asn1(&cert_body, &tbs_cert, CBS_ASN1_SEQUENCE)) {
271     return false;
272   }
273 
274   CBS tbs_cert_copy = tbs_cert;
275   if (!SkipTBSCertificateToExtensions(&tbs_cert))
276     return false;
277 
278   // Start filling in a new TBSCertificate. Copy everything parsed or skipped
279   // so far to the |new_tbs_cert|.
280   bssl::ScopedCBB cbb;
281   CBB new_tbs_cert;
282   if (!CBB_init(cbb.get(), CBS_len(&tbs_cert_copy)) ||
283       !CBB_add_asn1(cbb.get(), &new_tbs_cert, CBS_ASN1_SEQUENCE) ||
284       !CopyBefore(tbs_cert_copy, tbs_cert, &new_tbs_cert)) {
285     return false;
286   }
287 
288   // Parse the extensions list and find the SCT extension.
289   //
290   // XXX(rsleevi): We could generate precerts for certs without the extension
291   // by leaving the TBSCertificate as-is. The reference implementation does not
292   // do this.
293   constexpr unsigned kExtensionsTag =
294       CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 3;
295   CBS extensions_wrap, extensions, sct_extension;
296   if (!CBS_get_asn1(&tbs_cert, &extensions_wrap, kExtensionsTag) ||
297       !CBS_get_asn1(&extensions_wrap, &extensions, CBS_ASN1_SEQUENCE) ||
298       CBS_len(&extensions_wrap) != 0 || CBS_len(&tbs_cert) != 0 ||
299       !FindExtensionElement(extensions, kEmbeddedSCTOid,
300                             sizeof(kEmbeddedSCTOid), &sct_extension)) {
301     return false;
302   }
303 
304   // Add extensions to the TBSCertificate. Copy all extensions except the
305   // embedded SCT extension.
306   CBB new_extensions_wrap, new_extensions;
307   if (!CBB_add_asn1(&new_tbs_cert, &new_extensions_wrap, kExtensionsTag) ||
308       !CBB_add_asn1(&new_extensions_wrap, &new_extensions, CBS_ASN1_SEQUENCE) ||
309       !CopyBefore(extensions, sct_extension, &new_extensions) ||
310       !CopyAfter(extensions, sct_extension, &new_extensions)) {
311     return false;
312   }
313 
314   uint8_t* new_tbs_cert_der;
315   size_t new_tbs_cert_len;
316   if (!CBB_finish(cbb.get(), &new_tbs_cert_der, &new_tbs_cert_len))
317     return false;
318   bssl::UniquePtr<uint8_t> scoped_new_tbs_cert_der(new_tbs_cert_der);
319 
320   // Extract the issuer's public key.
321   std::string_view issuer_key;
322   if (!asn1::ExtractSPKIFromDERCert(
323           x509_util::CryptoBufferAsStringPiece(issuer), &issuer_key)) {
324     return false;
325   }
326 
327   // Fill in the SignedEntryData.
328   result->type = ct::SignedEntryData::LOG_ENTRY_TYPE_PRECERT;
329   result->tbs_certificate.assign(
330       reinterpret_cast<const char*>(new_tbs_cert_der), new_tbs_cert_len);
331   crypto::SHA256HashString(issuer_key, result->issuer_key_hash.data,
332                            sizeof(result->issuer_key_hash.data));
333 
334   return true;
335 }
336 
GetX509SignedEntry(const CRYPTO_BUFFER * leaf,SignedEntryData * result)337 bool GetX509SignedEntry(const CRYPTO_BUFFER* leaf, SignedEntryData* result) {
338   DCHECK(leaf);
339 
340   result->Reset();
341   result->type = ct::SignedEntryData::LOG_ENTRY_TYPE_X509;
342   result->leaf_certificate =
343       std::string(x509_util::CryptoBufferAsStringPiece(leaf));
344   return true;
345 }
346 
ExtractSCTListFromOCSPResponse(const CRYPTO_BUFFER * issuer,const std::string & cert_serial_number,std::string_view ocsp_response,std::string * sct_list)347 bool ExtractSCTListFromOCSPResponse(const CRYPTO_BUFFER* issuer,
348                                     const std::string& cert_serial_number,
349                                     std::string_view ocsp_response,
350                                     std::string* sct_list) {
351   // The input is an bssl::OCSPResponse. See RFC2560, section 4.2.1. The SCT
352   // list is in the extensions field of the SingleResponse which matches the
353   // input certificate.
354   CBS cbs;
355   CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ocsp_response.data()),
356            ocsp_response.size());
357 
358   // Parse down to the ResponseBytes. The ResponseBytes is optional, but if it's
359   // missing, this can't include an SCT list.
360   CBS sequence, tagged_response_bytes, response_bytes, response_type, response;
361   if (!CBS_get_asn1(&cbs, &sequence, CBS_ASN1_SEQUENCE) || CBS_len(&cbs) != 0 ||
362       !SkipElements(&sequence, 1 /* responseStatus */) ||
363       !CBS_get_asn1(&sequence, &tagged_response_bytes,
364                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
365       CBS_len(&sequence) != 0 ||
366       !CBS_get_asn1(&tagged_response_bytes, &response_bytes,
367                     CBS_ASN1_SEQUENCE) ||
368       CBS_len(&tagged_response_bytes) != 0 ||
369       !CBS_get_asn1(&response_bytes, &response_type, CBS_ASN1_OBJECT) ||
370       !CBS_get_asn1(&response_bytes, &response, CBS_ASN1_OCTETSTRING) ||
371       CBS_len(&response_bytes) != 0) {
372     return false;
373   }
374 
375   // The only relevant ResponseType is id-pkix-ocsp-basic.
376   if (!CBS_mem_equal(&response_type, kOCSPBasicResponseOid,
377                      sizeof(kOCSPBasicResponseOid))) {
378     return false;
379   }
380 
381   // Parse the ResponseData out of the BasicOCSPResponse. Ignore the rest.
382   constexpr unsigned kVersionTag =
383       CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0;
384   CBS basic_response, response_data, responses;
385   if (!CBS_get_asn1(&response, &basic_response, CBS_ASN1_SEQUENCE) ||
386       CBS_len(&response) != 0 ||
387       !CBS_get_asn1(&basic_response, &response_data, CBS_ASN1_SEQUENCE)) {
388     return false;
389   }
390 
391   // Extract the list of SingleResponses from the ResponseData.
392   if (!SkipOptionalElement(&response_data, kVersionTag) ||
393       !SkipElements(&response_data, 2 /* responderID, producedAt */) ||
394       !CBS_get_asn1(&response_data, &responses, CBS_ASN1_SEQUENCE)) {
395     return false;
396   }
397 
398   CBS single_response;
399   if (!FindMatchingSingleResponse(&responses, issuer, cert_serial_number,
400                                   &single_response)) {
401     return false;
402   }
403 
404   // Parse the extensions out of the SingleResponse.
405   constexpr unsigned kNextUpdateTag =
406       CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0;
407   constexpr unsigned kSingleExtensionsTag =
408       CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1;
409   CBS extensions_wrap, extensions;
410   if (!SkipElements(&single_response, 2 /* certStatus, thisUpdate */) ||
411       !SkipOptionalElement(&single_response, kNextUpdateTag) ||
412       !CBS_get_asn1(&single_response, &extensions_wrap, kSingleExtensionsTag) ||
413       !CBS_get_asn1(&extensions_wrap, &extensions, CBS_ASN1_SEQUENCE) ||
414       CBS_len(&extensions_wrap) != 0) {
415     return false;
416   }
417 
418   return ParseSCTListFromExtensions(extensions, kOCSPExtensionOid,
419                                     sizeof(kOCSPExtensionOid), sct_list);
420 }
421 
422 }  // namespace net::ct
423