[email protected] | 130686a | 2012-11-06 18:22:09 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 6e7845ae | 2013-03-29 21:48:11 | [diff] [blame] | 5 | #include "net/cert/x509_util.h" |
[email protected] | 130686a | 2012-11-06 18:22:09 | [diff] [blame] | 6 | |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 7 | #include <string.h> |
David Benjamin | 03c28a4 | 2018-05-11 23:12:07 | [diff] [blame] | 8 | #include <map> |
danakj | 3a4770d | 2016-04-16 03:09:29 | [diff] [blame] | 9 | #include <memory> |
| 10 | |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 11 | #include "base/lazy_instance.h" |
Hans Wennborg | a65658a | 2020-04-21 08:09:46 | [diff] [blame] | 12 | #include "base/logging.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 13 | #include "base/memory/raw_ptr.h" |
Hans Wennborg | 725d043 | 2020-06-18 13:54:16 | [diff] [blame] | 14 | #include "base/notreached.h" |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 15 | #include "base/strings/string_split.h" |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 16 | #include "base/strings/string_util.h" |
[email protected] | 9da992db | 2013-06-28 05:40:47 | [diff] [blame] | 17 | #include "base/time/time.h" |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 18 | #include "build/build_config.h" |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 19 | #include "crypto/openssl_util.h" |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 20 | #include "crypto/rsa_private_key.h" |
Ryan Sleevi | 3dabe0b | 2018-04-05 03:59:01 | [diff] [blame] | 21 | #include "crypto/sha2.h" |
asanka | 5ffd5d7 | 2016-03-23 16:20:49 | [diff] [blame] | 22 | #include "net/base/hash_value.h" |
Ryan Sleevi | 3dabe0b | 2018-04-05 03:59:01 | [diff] [blame] | 23 | #include "net/cert/asn1_util.h" |
Eric Roman | 7b45a27 | 2017-08-02 03:21:44 | [diff] [blame] | 24 | #include "net/cert/internal/cert_errors.h" |
jam | 8b3813b7 | 2016-09-06 20:25:47 | [diff] [blame] | 25 | #include "net/cert/internal/name_constraints.h" |
asanka | 5ffd5d7 | 2016-03-23 16:20:49 | [diff] [blame] | 26 | #include "net/cert/internal/parse_certificate.h" |
jam | 8b3813b7 | 2016-09-06 20:25:47 | [diff] [blame] | 27 | #include "net/cert/internal/parse_name.h" |
asanka | 5ffd5d7 | 2016-03-23 16:20:49 | [diff] [blame] | 28 | #include "net/cert/internal/signature_algorithm.h" |
[email protected] | 6e7845ae | 2013-03-29 21:48:11 | [diff] [blame] | 29 | #include "net/cert/x509_certificate.h" |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 30 | #include "net/der/encode_values.h" |
jam | 8b3813b7 | 2016-09-06 20:25:47 | [diff] [blame] | 31 | #include "net/der/input.h" |
| 32 | #include "net/der/parse_values.h" |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 33 | #include "third_party/boringssl/src/include/openssl/bytestring.h" |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 34 | #include "third_party/boringssl/src/include/openssl/digest.h" |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 35 | #include "third_party/boringssl/src/include/openssl/evp.h" |
| 36 | #include "third_party/boringssl/src/include/openssl/mem.h" |
Matt Mueller | c458af1 | 2021-11-11 19:33:16 | [diff] [blame] | 37 | #include "third_party/boringssl/src/include/openssl/pkcs7.h" |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 38 | #include "third_party/boringssl/src/include/openssl/pool.h" |
| 39 | #include "third_party/boringssl/src/include/openssl/stack.h" |
[email protected] | 130686a | 2012-11-06 18:22:09 | [diff] [blame] | 40 | |
Tsuyoshi Horo | 4f516be | 2022-06-14 11:53:13 | [diff] [blame^] | 41 | namespace net::x509_util { |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 42 | |
jam | 8b3813b7 | 2016-09-06 20:25:47 | [diff] [blame] | 43 | namespace { |
| 44 | |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 45 | bool AddRSASignatureAlgorithm(CBB* cbb, DigestAlgorithm algorithm) { |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 46 | // See RFC 4055. |
| 47 | static const uint8_t kSHA256WithRSAEncryption[] = { |
| 48 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b}; |
| 49 | |
| 50 | // An AlgorithmIdentifier is described in RFC 5280, 4.1.1.2. |
| 51 | CBB sequence, oid, params; |
| 52 | if (!CBB_add_asn1(cbb, &sequence, CBS_ASN1_SEQUENCE) || |
| 53 | !CBB_add_asn1(&sequence, &oid, CBS_ASN1_OBJECT)) { |
| 54 | return false; |
| 55 | } |
| 56 | |
| 57 | switch (algorithm) { |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 58 | case DIGEST_SHA256: |
| 59 | if (!CBB_add_bytes(&oid, kSHA256WithRSAEncryption, |
| 60 | sizeof(kSHA256WithRSAEncryption))) |
| 61 | return false; |
| 62 | break; |
| 63 | } |
| 64 | |
| 65 | // All supported algorithms use null parameters. |
| 66 | if (!CBB_add_asn1(&sequence, ¶ms, CBS_ASN1_NULL) || !CBB_flush(cbb)) { |
| 67 | return false; |
| 68 | } |
| 69 | |
| 70 | return true; |
| 71 | } |
| 72 | |
| 73 | const EVP_MD* ToEVP(DigestAlgorithm alg) { |
| 74 | switch (alg) { |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 75 | case DIGEST_SHA256: |
| 76 | return EVP_sha256(); |
| 77 | } |
| 78 | return nullptr; |
| 79 | } |
| 80 | |
Christine Franks | f4373ad | 2022-02-17 19:21:32 | [diff] [blame] | 81 | class BufferPoolSingleton { |
| 82 | public: |
| 83 | BufferPoolSingleton() : pool_(CRYPTO_BUFFER_POOL_new()) {} |
| 84 | CRYPTO_BUFFER_POOL* pool() { return pool_; } |
| 85 | |
| 86 | private: |
| 87 | // The singleton is leaky, so there is no need to use a smart pointer. |
| 88 | raw_ptr<CRYPTO_BUFFER_POOL> pool_; |
| 89 | }; |
| 90 | |
| 91 | base::LazyInstance<BufferPoolSingleton>::Leaky g_buffer_pool_singleton = |
| 92 | LAZY_INSTANCE_INITIALIZER; |
| 93 | |
| 94 | } // namespace |
| 95 | |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 96 | // Adds an X.509 Name with the specified distinguished name to |cbb|. |
| 97 | bool AddName(CBB* cbb, base::StringPiece name) { |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 98 | // See RFC 4519. |
| 99 | static const uint8_t kCommonName[] = {0x55, 0x04, 0x03}; |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 100 | static const uint8_t kCountryName[] = {0x55, 0x04, 0x06}; |
| 101 | static const uint8_t kOrganizationName[] = {0x55, 0x04, 0x0a}; |
| 102 | static const uint8_t kOrganizationalUnitName[] = {0x55, 0x04, 0x0b}; |
| 103 | |
| 104 | std::vector<std::string> attributes = SplitString( |
| 105 | name, /*separators=*/",", base::WhitespaceHandling::TRIM_WHITESPACE, |
| 106 | base::SplitResult::SPLIT_WANT_NONEMPTY); |
| 107 | |
| 108 | if (attributes.size() == 0) { |
| 109 | LOG(ERROR) << "Missing DN or wrong format"; |
| 110 | return false; |
| 111 | } |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 112 | |
| 113 | // See RFC 5280, section 4.1.2.4. |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 114 | CBB rdns; |
| 115 | if (!CBB_add_asn1(cbb, &rdns, CBS_ASN1_SEQUENCE)) { |
| 116 | return false; |
| 117 | } |
| 118 | |
| 119 | for (const std::string& attribute : attributes) { |
| 120 | std::vector<std::string> parts = |
| 121 | SplitString(attribute, /*separators=*/"=", |
| 122 | base::WhitespaceHandling::KEEP_WHITESPACE, |
| 123 | base::SplitResult::SPLIT_WANT_ALL); |
| 124 | if (parts.size() != 2) { |
| 125 | LOG(ERROR) << "Wrong DN format at " + attribute; |
| 126 | return false; |
| 127 | } |
| 128 | |
| 129 | const std::string& type_string = parts[0]; |
| 130 | const std::string& value_string = parts[1]; |
| 131 | base::span<const uint8_t> type_bytes; |
| 132 | if (type_string == "CN") { |
| 133 | type_bytes = kCommonName; |
| 134 | } else if (type_string == "C") { |
| 135 | type_bytes = kCountryName; |
| 136 | } else if (type_string == "O") { |
| 137 | type_bytes = kOrganizationName; |
| 138 | } else if (type_string == "OU") { |
| 139 | type_bytes = kOrganizationalUnitName; |
| 140 | } else { |
| 141 | LOG(ERROR) << "Unrecognized type " + type_string; |
| 142 | return false; |
| 143 | } |
| 144 | |
| 145 | CBB rdn, attr, type, value; |
| 146 | if (!CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET) || |
| 147 | !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) || |
| 148 | !CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT) || |
| 149 | !CBB_add_bytes(&type, type_bytes.data(), type_bytes.size()) || |
Jeongwoo Park | 7f55908 | 2020-08-10 21:50:16 | [diff] [blame] | 150 | !CBB_add_asn1(&attr, &value, type_string == "C" ? |
| 151 | CBS_ASN1_PRINTABLESTRING : CBS_ASN1_UTF8STRING) || |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 152 | !CBB_add_bytes(&value, |
| 153 | reinterpret_cast<const uint8_t*>(value_string.data()), |
| 154 | value_string.size()) || |
| 155 | !CBB_flush(&rdns)) { |
| 156 | return false; |
| 157 | } |
| 158 | } |
| 159 | if (!CBB_flush(cbb)) { |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 160 | return false; |
| 161 | } |
| 162 | return true; |
| 163 | } |
| 164 | |
Matt Mueller | 0a8ada3 | 2020-05-08 19:24:14 | [diff] [blame] | 165 | bool CBBAddTime(CBB* cbb, base::Time time) { |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 166 | der::GeneralizedTime generalized_time; |
| 167 | if (!der::EncodeTimeAsGeneralizedTime(time, &generalized_time)) |
| 168 | return false; |
| 169 | |
| 170 | // Per RFC 5280, 4.1.2.5, times which fit in UTCTime must be encoded as |
| 171 | // UTCTime rather than GeneralizedTime. |
| 172 | CBB child; |
| 173 | uint8_t* out; |
| 174 | if (generalized_time.InUTCTimeRange()) { |
| 175 | return CBB_add_asn1(cbb, &child, CBS_ASN1_UTCTIME) && |
| 176 | CBB_add_space(&child, &out, der::kUTCTimeLength) && |
| 177 | der::EncodeUTCTime(generalized_time, out) && CBB_flush(cbb); |
| 178 | } |
| 179 | |
| 180 | return CBB_add_asn1(cbb, &child, CBS_ASN1_GENERALIZEDTIME) && |
| 181 | CBB_add_space(&child, &out, der::kGeneralizedTimeLength) && |
| 182 | der::EncodeGeneralizedTime(generalized_time, out) && CBB_flush(cbb); |
| 183 | } |
| 184 | |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 185 | bool GetTLSServerEndPointChannelBinding(const X509Certificate& certificate, |
| 186 | std::string* token) { |
| 187 | static const char kChannelBindingPrefix[] = "tls-server-end-point:"; |
| 188 | |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 189 | base::StringPiece der_encoded_certificate = |
| 190 | x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer()); |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 191 | |
| 192 | der::Input tbs_certificate_tlv; |
| 193 | der::Input signature_algorithm_tlv; |
| 194 | der::BitString signature_value; |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 195 | if (!ParseCertificate(der::Input(der_encoded_certificate), |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 196 | &tbs_certificate_tlv, &signature_algorithm_tlv, |
| 197 | &signature_value, nullptr)) |
| 198 | return false; |
| 199 | |
| 200 | std::unique_ptr<SignatureAlgorithm> signature_algorithm = |
| 201 | SignatureAlgorithm::Create(signature_algorithm_tlv, nullptr); |
| 202 | if (!signature_algorithm) |
| 203 | return false; |
| 204 | |
| 205 | const EVP_MD* digest_evp_md = nullptr; |
| 206 | switch (signature_algorithm->digest()) { |
| 207 | case net::DigestAlgorithm::Md2: |
| 208 | case net::DigestAlgorithm::Md4: |
| 209 | // Shouldn't be reachable. |
| 210 | digest_evp_md = nullptr; |
| 211 | break; |
| 212 | |
| 213 | // Per RFC 5929 section 4.1, MD5 and SHA1 map to SHA256. |
| 214 | case net::DigestAlgorithm::Md5: |
| 215 | case net::DigestAlgorithm::Sha1: |
| 216 | case net::DigestAlgorithm::Sha256: |
| 217 | digest_evp_md = EVP_sha256(); |
| 218 | break; |
| 219 | |
| 220 | case net::DigestAlgorithm::Sha384: |
| 221 | digest_evp_md = EVP_sha384(); |
| 222 | break; |
| 223 | |
| 224 | case net::DigestAlgorithm::Sha512: |
| 225 | digest_evp_md = EVP_sha512(); |
| 226 | break; |
| 227 | } |
| 228 | if (!digest_evp_md) |
| 229 | return false; |
| 230 | |
| 231 | uint8_t digest[EVP_MAX_MD_SIZE]; |
| 232 | unsigned int out_size; |
| 233 | if (!EVP_Digest(der_encoded_certificate.data(), |
| 234 | der_encoded_certificate.size(), digest, &out_size, |
| 235 | digest_evp_md, nullptr)) |
| 236 | return false; |
| 237 | |
| 238 | token->assign(kChannelBindingPrefix); |
| 239 | token->append(digest, digest + out_size); |
| 240 | return true; |
| 241 | } |
[email protected] | 130686a | 2012-11-06 18:22:09 | [diff] [blame] | 242 | |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 243 | // RSA keys created by CreateKeyAndSelfSignedCert will be of this length. |
wtc | 69f8ea8 | 2015-06-04 00:08:13 | [diff] [blame] | 244 | static const uint16_t kRSAKeyLength = 1024; |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 245 | |
David Benjamin | ccfdc64 | 2017-09-15 16:38:20 | [diff] [blame] | 246 | // Certificates made by CreateKeyAndSelfSignedCert will be signed using this |
| 247 | // digest algorithm. |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 248 | static const DigestAlgorithm kSignatureDigestAlgorithm = DIGEST_SHA256; |
| 249 | |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 250 | bool CreateKeyAndSelfSignedCert(const std::string& subject, |
wtc | 69f8ea8 | 2015-06-04 00:08:13 | [diff] [blame] | 251 | uint32_t serial_number, |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 252 | base::Time not_valid_before, |
| 253 | base::Time not_valid_after, |
danakj | 3a4770d | 2016-04-16 03:09:29 | [diff] [blame] | 254 | std::unique_ptr<crypto::RSAPrivateKey>* key, |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 255 | std::string* der_cert) { |
danakj | 3a4770d | 2016-04-16 03:09:29 | [diff] [blame] | 256 | std::unique_ptr<crypto::RSAPrivateKey> new_key( |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 257 | crypto::RSAPrivateKey::Create(kRSAKeyLength)); |
Adam Langley | 0a4e4db | 2018-03-20 21:49:52 | [diff] [blame] | 258 | if (!new_key) |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 259 | return false; |
| 260 | |
Adam Langley | 09d08579 | 2018-03-27 17:51:10 | [diff] [blame] | 261 | bool success = CreateSelfSignedCert(new_key->key(), kSignatureDigestAlgorithm, |
| 262 | subject, serial_number, not_valid_before, |
Adam Langley | 28254a4 | 2018-09-07 18:33:27 | [diff] [blame] | 263 | not_valid_after, {}, der_cert); |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 264 | if (success) |
inlinechan | 894515af | 2016-12-09 02:40:10 | [diff] [blame] | 265 | *key = std::move(new_key); |
[email protected] | d99b2fb4 | 2013-11-01 05:14:29 | [diff] [blame] | 266 | |
| 267 | return success; |
| 268 | } |
| 269 | |
Adam Langley | 28254a4 | 2018-09-07 18:33:27 | [diff] [blame] | 270 | Extension::Extension(base::span<const uint8_t> in_oid, |
| 271 | bool in_critical, |
| 272 | base::span<const uint8_t> in_contents) |
| 273 | : oid(in_oid), critical(in_critical), contents(in_contents) {} |
| 274 | Extension::~Extension() {} |
| 275 | Extension::Extension(const Extension&) = default; |
| 276 | |
Adam Langley | 0a4e4db | 2018-03-20 21:49:52 | [diff] [blame] | 277 | bool CreateSelfSignedCert(EVP_PKEY* key, |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 278 | DigestAlgorithm alg, |
| 279 | const std::string& subject, |
| 280 | uint32_t serial_number, |
| 281 | base::Time not_valid_before, |
| 282 | base::Time not_valid_after, |
Adam Langley | 28254a4 | 2018-09-07 18:33:27 | [diff] [blame] | 283 | const std::vector<Extension>& extension_specs, |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 284 | std::string* der_encoded) { |
| 285 | crypto::EnsureOpenSSLInit(); |
| 286 | crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 287 | |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 288 | // See RFC 5280, section 4.1. First, construct the TBSCertificate. |
| 289 | bssl::ScopedCBB cbb; |
| 290 | CBB tbs_cert, version, validity; |
| 291 | uint8_t* tbs_cert_bytes; |
| 292 | size_t tbs_cert_len; |
| 293 | if (!CBB_init(cbb.get(), 64) || |
| 294 | !CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE) || |
| 295 | !CBB_add_asn1(&tbs_cert, &version, |
| 296 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || |
| 297 | !CBB_add_asn1_uint64(&version, 2) || |
| 298 | !CBB_add_asn1_uint64(&tbs_cert, serial_number) || |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 299 | !AddRSASignatureAlgorithm(&tbs_cert, alg) || // signature |
| 300 | !AddName(&tbs_cert, subject) || // issuer |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 301 | !CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE) || |
Matt Mueller | 0a8ada3 | 2020-05-08 19:24:14 | [diff] [blame] | 302 | !CBBAddTime(&validity, not_valid_before) || |
| 303 | !CBBAddTime(&validity, not_valid_after) || |
Nina Satragno | 4cca61729 | 2019-12-19 17:00:50 | [diff] [blame] | 304 | !AddName(&tbs_cert, subject) || // subject |
| 305 | !EVP_marshal_public_key(&tbs_cert, key)) { // subjectPublicKeyInfo |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 306 | return false; |
| 307 | } |
Adam Langley | 28254a4 | 2018-09-07 18:33:27 | [diff] [blame] | 308 | |
| 309 | if (!extension_specs.empty()) { |
| 310 | CBB outer_extensions, extensions; |
| 311 | if (!CBB_add_asn1(&tbs_cert, &outer_extensions, |
| 312 | 3 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED) || |
| 313 | !CBB_add_asn1(&outer_extensions, &extensions, CBS_ASN1_SEQUENCE)) { |
| 314 | return false; |
| 315 | } |
| 316 | |
| 317 | for (const auto& extension_spec : extension_specs) { |
| 318 | CBB extension, oid, value; |
| 319 | if (!CBB_add_asn1(&extensions, &extension, CBS_ASN1_SEQUENCE) || |
| 320 | !CBB_add_asn1(&extension, &oid, CBS_ASN1_OBJECT) || |
| 321 | !CBB_add_bytes(&oid, extension_spec.oid.data(), |
| 322 | extension_spec.oid.size()) || |
| 323 | (extension_spec.critical && !CBB_add_asn1_bool(&extension, 1)) || |
| 324 | !CBB_add_asn1(&extension, &value, CBS_ASN1_OCTETSTRING) || |
| 325 | !CBB_add_bytes(&value, extension_spec.contents.data(), |
| 326 | extension_spec.contents.size()) || |
| 327 | !CBB_flush(&extensions)) { |
| 328 | return false; |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | if (!CBB_flush(&tbs_cert)) { |
| 333 | return false; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | if (!CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) |
| 338 | return false; |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 339 | bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(tbs_cert_bytes); |
| 340 | |
| 341 | // Sign the TBSCertificate and write the entire certificate. |
| 342 | CBB cert, signature; |
| 343 | bssl::ScopedEVP_MD_CTX ctx; |
| 344 | uint8_t* sig_out; |
| 345 | size_t sig_len; |
| 346 | uint8_t* cert_bytes; |
| 347 | size_t cert_len; |
| 348 | if (!CBB_init(cbb.get(), tbs_cert_len) || |
| 349 | !CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE) || |
| 350 | !CBB_add_bytes(&cert, tbs_cert_bytes, tbs_cert_len) || |
| 351 | !AddRSASignatureAlgorithm(&cert, alg) || |
| 352 | !CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) || |
| 353 | !CBB_add_u8(&signature, 0 /* no unused bits */) || |
Adam Langley | 0a4e4db | 2018-03-20 21:49:52 | [diff] [blame] | 354 | !EVP_DigestSignInit(ctx.get(), nullptr, ToEVP(alg), nullptr, key) || |
David Benjamin | bdc94d7 | 2017-08-07 23:25:21 | [diff] [blame] | 355 | // Compute the maximum signature length. |
| 356 | !EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes, |
| 357 | tbs_cert_len) || |
| 358 | !CBB_reserve(&signature, &sig_out, sig_len) || |
| 359 | // Actually sign the TBSCertificate. |
| 360 | !EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes, |
| 361 | tbs_cert_len) || |
| 362 | !CBB_did_write(&signature, sig_len) || |
| 363 | !CBB_finish(cbb.get(), &cert_bytes, &cert_len)) { |
| 364 | return false; |
| 365 | } |
| 366 | bssl::UniquePtr<uint8_t> delete_cert_bytes(cert_bytes); |
| 367 | der_encoded->assign(reinterpret_cast<char*>(cert_bytes), cert_len); |
| 368 | return true; |
| 369 | } |
| 370 | |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 371 | CRYPTO_BUFFER_POOL* GetBufferPool() { |
| 372 | return g_buffer_pool_singleton.Get().pool(); |
| 373 | } |
| 374 | |
Matt Mueller | e6dac72 | 2021-11-18 15:33:44 | [diff] [blame] | 375 | bssl::UniquePtr<CRYPTO_BUFFER> CreateCryptoBuffer( |
| 376 | base::span<const uint8_t> data) { |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 377 | return bssl::UniquePtr<CRYPTO_BUFFER>( |
Matt Mueller | e6dac72 | 2021-11-18 15:33:44 | [diff] [blame] | 378 | CRYPTO_BUFFER_new(data.data(), data.size(), GetBufferPool())); |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 379 | } |
| 380 | |
| 381 | bssl::UniquePtr<CRYPTO_BUFFER> CreateCryptoBuffer( |
| 382 | const base::StringPiece& data) { |
| 383 | return bssl::UniquePtr<CRYPTO_BUFFER>( |
| 384 | CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(data.data()), |
| 385 | data.size(), GetBufferPool())); |
| 386 | } |
| 387 | |
Matt Mueller | f50e116 | 2021-11-01 18:29:53 | [diff] [blame] | 388 | bssl::UniquePtr<CRYPTO_BUFFER> CreateCryptoBufferFromStaticDataUnsafe( |
| 389 | base::span<const uint8_t> data) { |
| 390 | return bssl::UniquePtr<CRYPTO_BUFFER>( |
| 391 | CRYPTO_BUFFER_new_from_static_data_unsafe(data.data(), data.size(), |
| 392 | GetBufferPool())); |
| 393 | } |
| 394 | |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 395 | bool CryptoBufferEqual(const CRYPTO_BUFFER* a, const CRYPTO_BUFFER* b) { |
| 396 | DCHECK(a && b); |
| 397 | if (a == b) |
| 398 | return true; |
| 399 | return CRYPTO_BUFFER_len(a) == CRYPTO_BUFFER_len(b) && |
| 400 | memcmp(CRYPTO_BUFFER_data(a), CRYPTO_BUFFER_data(b), |
| 401 | CRYPTO_BUFFER_len(a)) == 0; |
| 402 | } |
| 403 | |
Matt Mueller | 0dc37d6 | 2017-11-16 04:29:46 | [diff] [blame] | 404 | base::StringPiece CryptoBufferAsStringPiece(const CRYPTO_BUFFER* buffer) { |
| 405 | return base::StringPiece( |
| 406 | reinterpret_cast<const char*>(CRYPTO_BUFFER_data(buffer)), |
| 407 | CRYPTO_BUFFER_len(buffer)); |
| 408 | } |
| 409 | |
David Benjamin | 316373d9 | 2021-11-04 20:43:25 | [diff] [blame] | 410 | base::span<const uint8_t> CryptoBufferAsSpan(const CRYPTO_BUFFER* buffer) { |
| 411 | return base::make_span(CRYPTO_BUFFER_data(buffer), CRYPTO_BUFFER_len(buffer)); |
| 412 | } |
| 413 | |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 414 | scoped_refptr<X509Certificate> CreateX509CertificateFromBuffers( |
David Benjamin | c792c696 | 2018-04-26 14:31:14 | [diff] [blame] | 415 | const STACK_OF(CRYPTO_BUFFER) * buffers) { |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 416 | if (sk_CRYPTO_BUFFER_num(buffers) == 0) { |
| 417 | NOTREACHED(); |
| 418 | return nullptr; |
| 419 | } |
| 420 | |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 421 | std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediate_chain; |
| 422 | for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(buffers); ++i) { |
| 423 | intermediate_chain.push_back( |
David Benjamin | 4db85cf | 2018-07-10 16:10:04 | [diff] [blame] | 424 | bssl::UpRef(sk_CRYPTO_BUFFER_value(buffers, i))); |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 425 | } |
| 426 | return X509Certificate::CreateFromBuffer( |
David Benjamin | 4db85cf | 2018-07-10 16:10:04 | [diff] [blame] | 427 | bssl::UpRef(sk_CRYPTO_BUFFER_value(buffers, 0)), |
Matt Mueller | a419327 | 2017-12-07 00:23:34 | [diff] [blame] | 428 | std::move(intermediate_chain)); |
David Benjamin | f349dc4b | 2017-08-04 19:17:00 | [diff] [blame] | 429 | } |
| 430 | |
Matt Mueller | c458af1 | 2021-11-11 19:33:16 | [diff] [blame] | 431 | bool CreateCertBuffersFromPKCS7Bytes( |
| 432 | base::span<const uint8_t> data, |
| 433 | std::vector<bssl::UniquePtr<CRYPTO_BUFFER>>* handles) { |
| 434 | crypto::EnsureOpenSSLInit(); |
| 435 | crypto::OpenSSLErrStackTracer err_cleaner(FROM_HERE); |
| 436 | |
| 437 | CBS der_data; |
| 438 | CBS_init(&der_data, data.data(), data.size()); |
| 439 | STACK_OF(CRYPTO_BUFFER)* certs = sk_CRYPTO_BUFFER_new_null(); |
| 440 | bool success = |
| 441 | PKCS7_get_raw_certificates(certs, &der_data, x509_util::GetBufferPool()); |
| 442 | if (success) { |
| 443 | for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(certs); ++i) { |
| 444 | handles->push_back( |
| 445 | bssl::UniquePtr<CRYPTO_BUFFER>(sk_CRYPTO_BUFFER_value(certs, i))); |
| 446 | } |
| 447 | } |
| 448 | // |handles| took ownership of the individual buffers, so only free the list |
| 449 | // itself. |
| 450 | sk_CRYPTO_BUFFER_free(certs); |
| 451 | |
| 452 | return success; |
| 453 | } |
| 454 | |
Matt Mueller | 31f64a7 | 2017-09-07 23:55:55 | [diff] [blame] | 455 | ParseCertificateOptions DefaultParseCertificateOptions() { |
| 456 | ParseCertificateOptions options; |
| 457 | options.allow_invalid_serial_numbers = true; |
| 458 | return options; |
| 459 | } |
| 460 | |
Ryan Sleevi | 3dabe0b | 2018-04-05 03:59:01 | [diff] [blame] | 461 | bool CalculateSha256SpkiHash(const CRYPTO_BUFFER* buffer, HashValue* hash) { |
| 462 | base::StringPiece spki; |
| 463 | if (!asn1::ExtractSPKIFromDERCert(CryptoBufferAsStringPiece(buffer), &spki)) { |
| 464 | return false; |
| 465 | } |
| 466 | *hash = HashValue(HASH_VALUE_SHA256); |
| 467 | crypto::SHA256HashString(spki, hash->data(), hash->size()); |
| 468 | return true; |
| 469 | } |
| 470 | |
David Benjamin | 03c28a4 | 2018-05-11 23:12:07 | [diff] [blame] | 471 | bool SignatureVerifierInitWithCertificate( |
| 472 | crypto::SignatureVerifier* verifier, |
| 473 | crypto::SignatureVerifier::SignatureAlgorithm signature_algorithm, |
| 474 | base::span<const uint8_t> signature, |
| 475 | const CRYPTO_BUFFER* certificate) { |
| 476 | base::StringPiece cert_der = |
| 477 | x509_util::CryptoBufferAsStringPiece(certificate); |
| 478 | |
| 479 | der::Input tbs_certificate_tlv; |
| 480 | der::Input signature_algorithm_tlv; |
| 481 | der::BitString signature_value; |
| 482 | ParsedTbsCertificate tbs; |
| 483 | if (!ParseCertificate(der::Input(cert_der), &tbs_certificate_tlv, |
| 484 | &signature_algorithm_tlv, &signature_value, nullptr) || |
| 485 | !ParseTbsCertificate(tbs_certificate_tlv, |
| 486 | DefaultParseCertificateOptions(), &tbs, nullptr)) { |
| 487 | return false; |
| 488 | } |
| 489 | |
| 490 | // The key usage extension, if present, must assert the digitalSignature bit. |
Hubert Chao | 0718996 | 2022-05-06 15:44:52 | [diff] [blame] | 491 | if (tbs.extensions_tlv) { |
David Benjamin | 03c28a4 | 2018-05-11 23:12:07 | [diff] [blame] | 492 | std::map<der::Input, ParsedExtension> extensions; |
Hubert Chao | 0718996 | 2022-05-06 15:44:52 | [diff] [blame] | 493 | if (!ParseExtensions(tbs.extensions_tlv.value(), &extensions)) { |
David Benjamin | 03c28a4 | 2018-05-11 23:12:07 | [diff] [blame] | 494 | return false; |
| 495 | } |
| 496 | ParsedExtension key_usage_ext; |
Matt Mueller | c16093c | 2022-02-11 19:45:27 | [diff] [blame] | 497 | if (ConsumeExtension(der::Input(kKeyUsageOid), &extensions, |
| 498 | &key_usage_ext)) { |
David Benjamin | 03c28a4 | 2018-05-11 23:12:07 | [diff] [blame] | 499 | der::BitString key_usage; |
| 500 | if (!ParseKeyUsage(key_usage_ext.value, &key_usage) || |
| 501 | !key_usage.AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE)) { |
| 502 | return false; |
| 503 | } |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | return verifier->VerifyInit( |
| 508 | signature_algorithm, signature, |
| 509 | base::make_span(tbs.spki_tlv.UnsafeData(), tbs.spki_tlv.Length())); |
| 510 | } |
| 511 | |
David Benjamin | 07a07d65 | 2020-02-26 22:26:59 | [diff] [blame] | 512 | bool HasSHA1Signature(const CRYPTO_BUFFER* cert_buffer) { |
| 513 | der::Input tbs_certificate_tlv; |
| 514 | der::Input signature_algorithm_tlv; |
| 515 | der::BitString signature_value; |
| 516 | if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_buffer), |
| 517 | CRYPTO_BUFFER_len(cert_buffer)), |
| 518 | &tbs_certificate_tlv, &signature_algorithm_tlv, |
| 519 | &signature_value, /*out_errors=*/nullptr)) { |
| 520 | return false; |
| 521 | } |
| 522 | |
| 523 | std::unique_ptr<SignatureAlgorithm> signature_algorithm = |
| 524 | SignatureAlgorithm::Create(signature_algorithm_tlv, /*errors=*/nullptr); |
| 525 | if (!signature_algorithm) |
| 526 | return false; |
| 527 | |
| 528 | return signature_algorithm->digest() == net::DigestAlgorithm::Sha1; |
| 529 | } |
| 530 | |
Tsuyoshi Horo | 4f516be | 2022-06-14 11:53:13 | [diff] [blame^] | 531 | } // namespace net::x509_util |