#include "net/cert/ct_objects_extractor.h"
#include <string.h>
#include <string_view>
#include "base/compiler_specific.h"
#include "base/hash/sha1.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "crypto/hash.h"
#include "crypto/sha2.h"
#include "net/cert/asn1_util.h"
#include "net/cert/signed_certificate_timestamp.h"
#include "net/cert/x509_util.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/mem.h"
namespace net::ct {
namespace {
const uint8_t kOCSPExtensionOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01,
0xD6, 0x79, 0x02, 0x04, 0x05};
const uint8_t kOCSPBasicResponseOid[] = {0x2b, 0x06, 0x01, 0x05, 0x05,
0x07, 0x30, 0x01, 0x01};
const uint8_t kSHA1Oid[] = {0x2b, 0x0e, 0x03, 0x02, 0x1a};
const uint8_t kSHA256Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65,
0x03, 0x04, 0x02, 0x01};
base::span<const uint8_t> SpanFromCBS(const CBS& cbs) {
return bssl::Span<const uint8_t>(cbs);
}
bool StringEqualToCBS(const std::string& value1, const CBS* value2) {
if (CBS_len(value2) != value1.size())
return false;
return UNSAFE_TODO(
memcmp(value1.data(), CBS_data(value2), CBS_len(value2))) == 0;
}
bool SkipElements(CBS* cbs, int count) {
for (int i = 0; i < count; i++) {
if (!CBS_get_any_asn1_element(cbs, nullptr, nullptr, nullptr))
return false;
}
return true;
}
bool SkipOptionalElement(CBS* cbs, unsigned tag) {
CBS unused;
return !CBS_peek_asn1_tag(cbs, tag) || CBS_get_asn1(cbs, &unused, tag);
}
bool CopyBefore(const CBS& outer, const CBS& inner, CBB* out) {
CHECK_LE(CBS_data(&outer), CBS_data(&inner));
CHECK_LE(UNSAFE_TODO(CBS_data(&inner) + CBS_len(&inner)),
UNSAFE_TODO(CBS_data(&outer) + CBS_len(&outer)));
return !!CBB_add_bytes(out, CBS_data(&outer),
UNSAFE_TODO(CBS_data(&inner) - CBS_data(&outer)));
}
bool CopyAfter(const CBS& outer, const CBS& inner, CBB* out) {
CHECK_LE(CBS_data(&outer), CBS_data(&inner));
CHECK_LE(UNSAFE_TODO(CBS_data(&inner) + CBS_len(&inner)),
UNSAFE_TODO(CBS_data(&outer) + CBS_len(&outer)));
return !!CBB_add_bytes(out, UNSAFE_TODO(CBS_data(&inner) + CBS_len(&inner)),
UNSAFE_TODO(CBS_data(&outer) + CBS_len(&outer) -
CBS_data(&inner) - CBS_len(&inner)));
}
bool SkipTBSCertificateToExtensions(CBS* tbs_cert) {
constexpr unsigned kVersionTag =
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0;
constexpr unsigned kIssuerUniqueIDTag = CBS_ASN1_CONTEXT_SPECIFIC | 1;
constexpr unsigned kSubjectUniqueIDTag = CBS_ASN1_CONTEXT_SPECIFIC | 2;
return SkipOptionalElement(tbs_cert, kVersionTag) &&
SkipElements(tbs_cert,
6 ) &&
SkipOptionalElement(tbs_cert, kIssuerUniqueIDTag) &&
SkipOptionalElement(tbs_cert, kSubjectUniqueIDTag);
}
bool FindExtensionElement(const CBS& extensions,
const uint8_t* oid,
size_t oid_len,
CBS* out) {
CBS extensions_copy = extensions;
CBS result;
CBS_init(&result, nullptr, 0);
bool found = false;
while (CBS_len(&extensions_copy) > 0) {
CBS extension_element;
if (!CBS_get_asn1_element(&extensions_copy, &extension_element,
CBS_ASN1_SEQUENCE)) {
return false;
}
CBS copy = extension_element;
CBS extension, extension_oid;
if (!CBS_get_asn1(©, &extension, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&extension, &extension_oid, CBS_ASN1_OBJECT)) {
return false;
}
if (CBS_mem_equal(&extension_oid, oid, oid_len)) {
if (found)
return false;
found = true;
result = extension_element;
}
}
if (!found)
return false;
*out = result;
return true;
}
bool ParseSCTListFromExtensions(const CBS& extensions,
const uint8_t* oid,
size_t oid_len,
std::string* out_sct_list) {
CBS extension_element, extension, extension_oid, value, sct_list;
if (!FindExtensionElement(extensions, oid, oid_len, &extension_element) ||
!CBS_get_asn1(&extension_element, &extension, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&extension, &extension_oid, CBS_ASN1_OBJECT) ||
!SkipOptionalElement(&extension, CBS_ASN1_BOOLEAN) ||
!CBS_get_asn1(&extension, &value, CBS_ASN1_OCTETSTRING) ||
CBS_len(&extension) != 0 ||
!CBS_get_asn1(&value, &sct_list, CBS_ASN1_OCTETSTRING) ||
CBS_len(&value) != 0) {
return false;
}
DCHECK(CBS_mem_equal(&extension_oid, oid, oid_len));
*out_sct_list = std::string(
reinterpret_cast<const char*>(CBS_data(&sct_list)), CBS_len(&sct_list));
return true;
}
bool FindMatchingSingleResponse(CBS* responses,
const CRYPTO_BUFFER* issuer,
base::span<const uint8_t> cert_serial_number,
CBS* out_single_response) {
std::string_view issuer_spki;
if (!asn1::ExtractSPKIFromDERCert(
x509_util::CryptoBufferAsStringPiece(issuer), &issuer_spki))
return false;
std::string_view issuer_spk;
if (!asn1::ExtractSubjectPublicKeyFromSPKI(issuer_spki, &issuer_spk))
return false;
if (issuer_spk.empty() || issuer_spk[0] != 0)
return false;
issuer_spk.remove_prefix(1);
std::string issuer_key_sha256_hash = crypto::SHA256HashString(issuer_spk);
std::string issuer_key_sha1_hash =
base::SHA1HashString(std::string(issuer_spk));
while (CBS_len(responses) > 0) {
CBS single_response, cert_id;
if (!CBS_get_asn1(responses, &single_response, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE)) {
return false;
}
CBS hash_algorithm, hash, serial_number, issuer_name_hash, issuer_key_hash;
if (!CBS_get_asn1(&cert_id, &hash_algorithm, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&hash_algorithm, &hash, CBS_ASN1_OBJECT) ||
!CBS_get_asn1(&cert_id, &issuer_name_hash, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&cert_id, &issuer_key_hash, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&cert_id, &serial_number, CBS_ASN1_INTEGER) ||
CBS_len(&cert_id) != 0) {
return false;
}
if (cert_serial_number != SpanFromCBS(serial_number)) {
continue;
}
if (CBS_mem_equal(&hash, kSHA1Oid, sizeof(kSHA1Oid))) {
if (StringEqualToCBS(issuer_key_sha1_hash, &issuer_key_hash)) {
*out_single_response = single_response;
return true;
}
} else if (CBS_mem_equal(&hash, kSHA256Oid, sizeof(kSHA256Oid))) {
if (StringEqualToCBS(issuer_key_sha256_hash, &issuer_key_hash)) {
*out_single_response = single_response;
return true;
}
}
}
return false;
}
}
bool ExtractEmbeddedSCTList(const CRYPTO_BUFFER* cert, std::string* sct_list) {
CBS cert_cbs;
CBS_init(&cert_cbs, CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert));
CBS cert_body, tbs_cert, extensions_wrap, extensions;
if (!CBS_get_asn1(&cert_cbs, &cert_body, CBS_ASN1_SEQUENCE) ||
CBS_len(&cert_cbs) != 0 ||
!CBS_get_asn1(&cert_body, &tbs_cert, CBS_ASN1_SEQUENCE) ||
!SkipTBSCertificateToExtensions(&tbs_cert) ||
!CBS_get_asn1(&tbs_cert, &extensions_wrap,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 3) ||
!CBS_get_asn1(&extensions_wrap, &extensions, CBS_ASN1_SEQUENCE) ||
CBS_len(&extensions_wrap) != 0 || CBS_len(&tbs_cert) != 0) {
return false;
}
return ParseSCTListFromExtensions(extensions, kEmbeddedSCTOid,
sizeof(kEmbeddedSCTOid), sct_list);
}
bool GetPrecertSignedEntry(const CRYPTO_BUFFER* leaf,
const CRYPTO_BUFFER* issuer,
SignedEntryData* result) {
result->Reset();
CBS cert_cbs;
CBS_init(&cert_cbs, CRYPTO_BUFFER_data(leaf), CRYPTO_BUFFER_len(leaf));
CBS cert_body, tbs_cert;
if (!CBS_get_asn1(&cert_cbs, &cert_body, CBS_ASN1_SEQUENCE) ||
CBS_len(&cert_cbs) != 0 ||
!CBS_get_asn1(&cert_body, &tbs_cert, CBS_ASN1_SEQUENCE)) {
return false;
}
CBS tbs_cert_copy = tbs_cert;
if (!SkipTBSCertificateToExtensions(&tbs_cert))
return false;
bssl::ScopedCBB cbb;
CBB new_tbs_cert;
if (!CBB_init(cbb.get(), CBS_len(&tbs_cert_copy)) ||
!CBB_add_asn1(cbb.get(), &new_tbs_cert, CBS_ASN1_SEQUENCE) ||
!CopyBefore(tbs_cert_copy, tbs_cert, &new_tbs_cert)) {
return false;
}
constexpr unsigned kExtensionsTag =
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 3;
CBS extensions_wrap, extensions, sct_extension;
if (!CBS_get_asn1(&tbs_cert, &extensions_wrap, kExtensionsTag) ||
!CBS_get_asn1(&extensions_wrap, &extensions, CBS_ASN1_SEQUENCE) ||
CBS_len(&extensions_wrap) != 0 || CBS_len(&tbs_cert) != 0 ||
!FindExtensionElement(extensions, kEmbeddedSCTOid,
sizeof(kEmbeddedSCTOid), &sct_extension)) {
return false;
}
CBB new_extensions_wrap, new_extensions;
if (!CBB_add_asn1(&new_tbs_cert, &new_extensions_wrap, kExtensionsTag) ||
!CBB_add_asn1(&new_extensions_wrap, &new_extensions, CBS_ASN1_SEQUENCE) ||
!CopyBefore(extensions, sct_extension, &new_extensions) ||
!CopyAfter(extensions, sct_extension, &new_extensions)) {
return false;
}
uint8_t* new_tbs_cert_der;
size_t new_tbs_cert_len;
if (!CBB_finish(cbb.get(), &new_tbs_cert_der, &new_tbs_cert_len))
return false;
bssl::UniquePtr<uint8_t> scoped_new_tbs_cert_der(new_tbs_cert_der);
std::string_view issuer_key;
if (!asn1::ExtractSPKIFromDERCert(
x509_util::CryptoBufferAsStringPiece(issuer), &issuer_key)) {
return false;
}
result->type = ct::SignedEntryData::LOG_ENTRY_TYPE_PRECERT;
result->tbs_certificate.assign(
reinterpret_cast<const char*>(new_tbs_cert_der), new_tbs_cert_len);
result->issuer_key_hash =
crypto::hash::Sha256(base::as_byte_span(issuer_key));
return true;
}
bool GetX509SignedEntry(const CRYPTO_BUFFER* leaf, SignedEntryData* result) {
DCHECK(leaf);
result->Reset();
result->type = ct::SignedEntryData::LOG_ENTRY_TYPE_X509;
result->leaf_certificate =
std::string(x509_util::CryptoBufferAsStringPiece(leaf));
return true;
}
bool ExtractSCTListFromOCSPResponse(
const CRYPTO_BUFFER* issuer,
base::span<const uint8_t> cert_serial_number,
std::string_view ocsp_response,
std::string* sct_list) {
CBS cbs;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ocsp_response.data()),
ocsp_response.size());
CBS sequence, tagged_response_bytes, response_bytes, response_type, response;
if (!CBS_get_asn1(&cbs, &sequence, CBS_ASN1_SEQUENCE) || CBS_len(&cbs) != 0 ||
!SkipElements(&sequence, 1 ) ||
!CBS_get_asn1(&sequence, &tagged_response_bytes,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
CBS_len(&sequence) != 0 ||
!CBS_get_asn1(&tagged_response_bytes, &response_bytes,
CBS_ASN1_SEQUENCE) ||
CBS_len(&tagged_response_bytes) != 0 ||
!CBS_get_asn1(&response_bytes, &response_type, CBS_ASN1_OBJECT) ||
!CBS_get_asn1(&response_bytes, &response, CBS_ASN1_OCTETSTRING) ||
CBS_len(&response_bytes) != 0) {
return false;
}
if (!CBS_mem_equal(&response_type, kOCSPBasicResponseOid,
sizeof(kOCSPBasicResponseOid))) {
return false;
}
constexpr unsigned kVersionTag =
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0;
CBS basic_response, response_data, responses;
if (!CBS_get_asn1(&response, &basic_response, CBS_ASN1_SEQUENCE) ||
CBS_len(&response) != 0 ||
!CBS_get_asn1(&basic_response, &response_data, CBS_ASN1_SEQUENCE)) {
return false;
}
if (!SkipOptionalElement(&response_data, kVersionTag) ||
!SkipElements(&response_data, 2 ) ||
!CBS_get_asn1(&response_data, &responses, CBS_ASN1_SEQUENCE)) {
return false;
}
CBS single_response;
if (!FindMatchingSingleResponse(&responses, issuer, cert_serial_number,
&single_response)) {
return false;
}
constexpr unsigned kNextUpdateTag =
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0;
constexpr unsigned kSingleExtensionsTag =
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1;
CBS extensions_wrap, extensions;
if (!SkipElements(&single_response, 2 ) ||
!SkipOptionalElement(&single_response, kNextUpdateTag) ||
!CBS_get_asn1(&single_response, &extensions_wrap, kSingleExtensionsTag) ||
!CBS_get_asn1(&extensions_wrap, &extensions, CBS_ASN1_SEQUENCE) ||
CBS_len(&extensions_wrap) != 0) {
return false;
}
return ParseSCTListFromExtensions(extensions, kOCSPExtensionOid,
sizeof(kOCSPExtensionOid), sct_list);
}
}