Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 | |
| 5 #include "components/cast_certificate/cast_crl.h" | |
| 6 | |
| 7 #include "base/base64.h" | |
| 8 #include "base/memory/ptr_util.h" | |
| 9 #include "base/memory/singleton.h" | |
| 10 #include "components/cast_certificate/cast_cert_validator.h" | |
| 11 #include "components/cast_certificate/proto/revocation.pb.h" | |
| 12 #include "crypto/sha2.h" | |
| 13 #include "net/cert/internal/parse_certificate.h" | |
| 14 #include "net/cert/internal/parsed_certificate.h" | |
| 15 #include "net/cert/internal/signature_algorithm.h" | |
| 16 #include "net/cert/internal/signature_policy.h" | |
| 17 #include "net/cert/internal/trust_store.h" | |
| 18 #include "net/cert/internal/verify_certificate_chain.h" | |
| 19 #include "net/cert/internal/verify_signed_data.h" | |
| 20 #include "net/cert/x509_certificate.h" | |
| 21 #include "net/der/input.h" | |
| 22 #include "net/der/parser.h" | |
| 23 #include "net/der/parse_values.h" | |
| 24 | |
| 25 namespace cast_certificate { | |
| 26 namespace { | |
| 27 // ------------------------------------------------------------------------- | |
| 28 // Cast CRL trust anchors. | |
| 29 // ------------------------------------------------------------------------- | |
| 30 | |
| 31 // There is one trusted root for Cast CRL certificate chains: | |
| 32 // | |
| 33 // (1) CN=Cast CRL Root CA (kCastCRLRootCaDer) | |
| 34 // | |
| 35 // These constants are defined by the file included next: | |
| 36 | |
| 37 #include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h" | |
| 38 | |
| 39 // Singleton for the Cast CRL trust store. | |
| 40 class CastCRLTrustStore { | |
| 41 public: | |
| 42 static CastCRLTrustStore* GetInstance() { | |
| 43 return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits< | |
| 44 CastCRLTrustStore>>::get(); | |
| 45 } | |
| 46 | |
| 47 static net::TrustStore& Get() { return GetInstance()->store_; } | |
| 48 | |
| 49 private: | |
| 50 friend struct base::DefaultSingletonTraits<CastCRLTrustStore>; | |
| 51 | |
| 52 CastCRLTrustStore() { | |
| 53 // Initialize the trust store with the root certificate. | |
| 54 // TODO (ryanchung): Add official Cast CRL Root here | |
| 55 // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate:: | |
| 56 // net::ParsedCertificate::CreateFromCertificateData( | |
| 57 // kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer), | |
| 58 // net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE); | |
| 59 // CHECK(root); | |
| 60 // store_.AddTrustedCertificate(std::move(root)); | |
| 61 } | |
| 62 | |
| 63 net::TrustStore store_; | |
| 64 DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore); | |
| 65 }; | |
| 66 | |
| 67 // The parsed certificate information. | |
| 68 struct CastCertificateInfo { | |
| 69 uint64_t serial_number; | |
| 70 std::string issuer_name; | |
| 71 net::der::Input spki_tlv; | |
| 72 std::string key_hash; | |
| 73 }; | |
| 74 | |
| 75 // Parses a der encoded certificate for the information for CastCertificateInfo. | |
| 76 bool ParseCertificateInfo(scoped_refptr<net::ParsedCertificate>& cert, | |
| 77 CastCertificateInfo* parsed_cert) { | |
| 78 // Parse the serial number. | |
| 79 if (!net::der::ParseUint64(cert->tbs().serial_number, | |
| 80 &parsed_cert->serial_number)) { | |
| 81 return false; | |
| 82 } | |
| 83 | |
| 84 // Parse the public key and calculate its hash. | |
|
sheretov
2016/06/13 19:18:35
This is wrong. We should hash the SPKI directly -
ryanchung
2016/06/21 21:45:06
OK, Done.
Originally, the hash of the subject publ
| |
| 85 net::der::Parser outer_parser(cert->tbs().spki_tlv); | |
| 86 net::der::Parser spki_parser; | |
| 87 net::der::BitString key_bits; | |
| 88 if (!outer_parser.ReadSequence(&spki_parser)) | |
| 89 return false; | |
| 90 if (outer_parser.HasMore()) | |
| 91 return false; | |
| 92 if (!spki_parser.SkipTag(net::der::kSequence)) | |
| 93 return false; | |
| 94 if (!spki_parser.ReadBitString(&key_bits)) | |
| 95 return false; | |
| 96 std::string public_key = key_bits.bytes().AsString(); | |
| 97 parsed_cert->key_hash = crypto::SHA256HashString(public_key); | |
| 98 parsed_cert->spki_tlv = cert->tbs().spki_tlv; | |
| 99 | |
| 100 // The issuer's name. | |
| 101 parsed_cert->issuer_name = cert->normalized_issuer().AsString(); | |
| 102 return true; | |
| 103 } | |
| 104 | |
| 105 // Converts a base::Time::Exploded to a net::der::GeneralizedTime. | |
| 106 net::der::GeneralizedTime ConvertExplodedTime( | |
| 107 const base::Time::Exploded& exploded) { | |
| 108 net::der::GeneralizedTime result; | |
| 109 result.year = exploded.year; | |
| 110 result.month = exploded.month; | |
| 111 result.day = exploded.day_of_month; | |
| 112 result.hours = exploded.hour; | |
| 113 result.minutes = exploded.minute; | |
| 114 result.seconds = exploded.second; | |
| 115 return result; | |
| 116 } | |
| 117 | |
| 118 // Specifies the signature verification policy. | |
| 119 std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() { | |
| 120 return base::WrapUnique(new net::SimpleSignaturePolicy(2048)); | |
| 121 } | |
| 122 | |
| 123 class ScopedCheckUnreferencedCert { | |
| 124 public: | |
| 125 explicit ScopedCheckUnreferencedCert(net::ParsedCertificate* cert) | |
| 126 : cert_(cert) {} | |
| 127 ~ScopedCheckUnreferencedCert() { DCHECK(cert_->HasOneRef()); } | |
| 128 | |
| 129 private: | |
| 130 net::ParsedCertificate* cert_; | |
| 131 }; | |
| 132 | |
| 133 // Verifies the CRL is signed by a trusted CRL authority at the time the CRL was | |
|
sheretov
2016/06/13 19:18:35
The CRL, including all the certs in the path shoul
ryanchung
2016/06/21 21:45:05
Added the current time here.
As discussed offline,
| |
| 134 // issued. | |
| 135 bool VerifyCRL(const Crl& crl, const TbsCrl& tbs_crl) { | |
| 136 // Verify the trust of the CRL authority. | |
| 137 base::Time signing_time = | |
| 138 base::Time::UnixEpoch() + | |
| 139 base::TimeDelta::FromMilliseconds(tbs_crl.issuance_time_millis()); | |
| 140 base::Time::Exploded signing_time_exploded; | |
| 141 signing_time.UTCExplode(&signing_time_exploded); | |
| 142 net::der::GeneralizedTime signing_time_generalized = | |
| 143 ConvertExplodedTime(signing_time_exploded); | |
| 144 std::vector<scoped_refptr<net::ParsedCertificate>> input_chain; | |
| 145 scoped_refptr<net::ParsedCertificate> parsed_cert = | |
| 146 net::ParsedCertificate::CreateFromCertificateData( | |
| 147 reinterpret_cast<const uint8_t*>(crl.signer_cert().data()), | |
| 148 crl.signer_cert().size(), | |
| 149 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE); | |
| 150 if (parsed_cert == nullptr) | |
| 151 return false; | |
| 152 ScopedCheckUnreferencedCert ref_checker(parsed_cert.get()); | |
| 153 input_chain.push_back(std::move(parsed_cert)); | |
| 154 | |
| 155 auto signature_policy = CreateCastSignaturePolicy(); | |
| 156 if (!net::VerifyCertificateChain(input_chain, CastCRLTrustStore::Get(), | |
| 157 signature_policy.get(), | |
| 158 signing_time_generalized)) { | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 // Parse the signer certificate | |
| 163 if (input_chain.size() == 0) | |
| 164 return false; | |
| 165 CastCertificateInfo cert_info; | |
| 166 if (!ParseCertificateInfo(input_chain[0], &cert_info)) | |
| 167 return false; | |
| 168 | |
| 169 // Parse the signature. | |
| 170 std::string signature; | |
| 171 signature = crl.signature(); | |
| 172 net::der::BitString signature_value_bit_string; | |
| 173 signature_value_bit_string = | |
| 174 net::der::BitString(net::der::Input(&signature), 0); | |
| 175 | |
| 176 // Verify the signature | |
| 177 std::unique_ptr<net::SignaturePolicy> policy = | |
| 178 base::WrapUnique(new net::SimpleSignaturePolicy(2048)); | |
| 179 std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type = | |
| 180 net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256); | |
| 181 if (!VerifySignedData( | |
| 182 *signature_algorithm_type, net::der::Input(&crl.tbs_crl()), | |
| 183 signature_value_bit_string, cert_info.spki_tlv, policy.get())) { | |
| 184 VLOG(2) << "CRL - Signature verification failed."; | |
| 185 return false; | |
| 186 } | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 class CastCRLImpl : public CastCRL { | |
| 191 public: | |
| 192 CastCRLImpl(const TbsCrl& tbs_crl); | |
| 193 ~CastCRLImpl() override; | |
| 194 | |
| 195 bool VerifyDeviceCertRevocation(const std::vector<std::string>& certs, | |
| 196 const base::Time::Exploded& time) override; | |
| 197 | |
| 198 private: | |
| 199 uint64_t issuance_time_millis_; | |
| 200 uint64_t validity_period_millis_; | |
| 201 | |
| 202 // Hash of all revoked public key. | |
| 203 std::unordered_set<std::string> revoked_hashes_; | |
| 204 | |
| 205 // Revoked serial number ranges indexed by issuer public key hash. | |
| 206 std::unordered_map<std::string, std::pair<uint64_t, uint64_t>> | |
| 207 revoked_serial_numbers_; | |
| 208 DISALLOW_COPY_AND_ASSIGN(CastCRLImpl); | |
| 209 }; | |
| 210 | |
| 211 CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl) { | |
| 212 // Parse the validity information. | |
| 213 issuance_time_millis_ = tbs_crl.issuance_time_millis(); | |
| 214 validity_period_millis_ = tbs_crl.validity_period_millis(); | |
| 215 | |
| 216 // Parse the revoked hashes. | |
| 217 for (const auto& hash : tbs_crl.revoked_public_key_hashes()) { | |
| 218 revoked_hashes_.insert(hash); | |
| 219 } | |
| 220 | |
| 221 // Parse the revoked serial ranges. | |
| 222 for (const auto& range : tbs_crl.revoked_serial_number_ranges()) { | |
| 223 std::string hash = range.issuer_public_key_hash(); | |
| 224 uint64_t first_serial_number = range.first_serial_number(); | |
| 225 uint64_t last_serial_number = range.last_serial_number(); | |
| 226 std::pair<uint64_t, uint64_t> revocation_range(first_serial_number, | |
| 227 last_serial_number); | |
| 228 std::pair<std::string, std::pair<uint64_t, uint64_t>> revocation_entry( | |
| 229 hash, revocation_range); | |
| 230 revoked_serial_numbers_.insert(revocation_entry); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 CastCRLImpl::~CastCRLImpl() {} | |
| 235 | |
| 236 // Verifies the revocation status of the certificate chain, at the specified | |
| 237 // time. | |
| 238 bool CastCRLImpl::VerifyDeviceCertRevocation( | |
|
sheretov
2016/06/13 19:18:35
I would call this checkRevocation
ryanchung
2016/06/21 21:45:05
Done.
| |
| 239 const std::vector<std::string>& certs, | |
| 240 const base::Time::Exploded& time) { | |
| 241 if (certs.empty()) | |
| 242 return false; | |
| 243 | |
| 244 // Check the validity of the CRl at the specified time. | |
| 245 base::Time utc_time = base::Time::FromUTCExploded(time); | |
| 246 base::Time validity_start = | |
| 247 base::Time::UnixEpoch() + | |
| 248 base::TimeDelta::FromMilliseconds(issuance_time_millis_); | |
| 249 base::Time validity_end = | |
| 250 base::Time::UnixEpoch() + | |
| 251 base::TimeDelta::FromMilliseconds(issuance_time_millis_ + | |
| 252 validity_period_millis_); | |
| 253 if (utc_time < validity_start || utc_time > validity_end) { | |
|
sheretov
2016/06/13 19:18:35
nit: utc_time >= validity_end
ryanchung
2016/06/21 21:45:06
Done.
| |
| 254 VLOG(2) << "CRL expired. Perform hard fail."; | |
| 255 return false; | |
| 256 } | |
| 257 | |
| 258 // Assume the certs form a valid path. | |
| 259 std::string issuer_key_hash; | |
| 260 for (int i = certs.size() - 1; i >= 0; --i) { | |
| 261 // Check public key revocation. | |
| 262 scoped_refptr<net::ParsedCertificate> parsed_cert = | |
| 263 net::ParsedCertificate::CreateFromCertificateData( | |
| 264 reinterpret_cast<const uint8_t*>(certs[i].data()), certs[i].size(), | |
| 265 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE); | |
| 266 if (parsed_cert == nullptr) | |
| 267 return false; | |
| 268 ScopedCheckUnreferencedCert ref_checker(parsed_cert.get()); | |
| 269 CastCertificateInfo cert_info; | |
| 270 if (!ParseCertificateInfo(parsed_cert, &cert_info)) | |
| 271 return false; | |
| 272 if (revoked_hashes_.find(cert_info.key_hash) != revoked_hashes_.end()) { | |
| 273 VLOG(2) << "Public key is revoked."; | |
| 274 return false; | |
| 275 } | |
| 276 | |
| 277 // Check serial range revocation. | |
| 278 if (issuer_key_hash.empty()) { | |
| 279 std::string issuer_cert = | |
| 280 FindCastTrustAnchorByName(cert_info.issuer_name); | |
|
sheretov
2016/06/13 19:18:35
This seems awkward here. Can we pass in the root's
ryanchung
2016/06/21 21:45:05
I've updated this function to take in the verified
| |
| 281 if (issuer_cert.empty()) | |
| 282 return false; | |
| 283 scoped_refptr<net::ParsedCertificate> parsed_issuer_cert = | |
| 284 net::ParsedCertificate::CreateFromCertificateData( | |
| 285 reinterpret_cast<const uint8_t*>(issuer_cert.data()), | |
| 286 issuer_cert.size(), | |
| 287 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE); | |
| 288 if (parsed_cert == nullptr) | |
| 289 return false; | |
| 290 ScopedCheckUnreferencedCert ref_checker(parsed_issuer_cert.get()); | |
| 291 CastCertificateInfo issuer_cert_info; | |
| 292 if (!ParseCertificateInfo(parsed_issuer_cert, &issuer_cert_info)) | |
| 293 return false; | |
| 294 if (revoked_hashes_.find(issuer_cert_info.key_hash) != | |
|
sheretov
2016/06/13 19:18:35
Why are we checking revocation on the trust anchor
ryanchung
2016/06/21 21:45:06
Removed.
| |
| 295 revoked_hashes_.end()) { | |
| 296 VLOG(2) << "Issuer public key is revoked."; | |
| 297 return false; | |
| 298 } | |
| 299 issuer_key_hash = issuer_cert_info.key_hash; | |
| 300 } | |
| 301 DCHECK(!issuer_key_hash.empty()); | |
| 302 auto issuer_revoked_serials = revoked_serial_numbers_.find(issuer_key_hash); | |
| 303 if (issuer_revoked_serials != revoked_serial_numbers_.end()) { | |
| 304 if (issuer_revoked_serials->second.first <= cert_info.serial_number && | |
| 305 issuer_revoked_serials->second.second >= cert_info.serial_number) { | |
| 306 VLOG(2) << "Serial number is revoked"; | |
| 307 return false; | |
| 308 } | |
| 309 } | |
| 310 issuer_key_hash = cert_info.key_hash; | |
| 311 } | |
| 312 return true; | |
| 313 } | |
| 314 | |
| 315 } // namespace | |
| 316 | |
| 317 std::unique_ptr<CastCRL> ParseCRL(const std::string& crl_proto) { | |
| 318 CrlBundle cast_crl; | |
| 319 if (!cast_crl.ParseFromString(crl_proto)) { | |
| 320 LOG(ERROR) << "CRL - Binary could not be parsed."; | |
| 321 return nullptr; | |
| 322 } | |
| 323 TbsCrl tbs_crl; | |
| 324 Crl supported_crl; | |
| 325 bool supported_supported_crl_found = false; | |
|
sheretov
2016/06/13 19:18:35
supported_supported?
ryanchung
2016/06/21 21:45:05
Removed this due to comment below.
| |
| 326 for (auto const& crl : cast_crl.crls()) { | |
| 327 if (!tbs_crl.ParseFromString(crl.tbs_crl())) { | |
| 328 LOG(WARNING) << "Binary TBS CRL could not be parsed."; | |
| 329 continue; | |
| 330 } | |
| 331 // The supported version of the CRL is version 0 | |
| 332 // version 0: Spki Hash Algorithm = SHA-256 | |
| 333 // Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256 | |
| 334 if (tbs_crl.version() == 0) { | |
|
sheretov
2016/06/13 19:18:35
Please create a constant for the CRL version, so w
ryanchung
2016/06/21 21:45:06
Done.
| |
| 335 supported_crl = crl; | |
|
sheretov
2016/06/13 19:18:35
Is this assignment making a copy? Why have the tb
ryanchung
2016/06/21 21:45:05
Changed this to return in the loop.
| |
| 336 supported_supported_crl_found = true; | |
| 337 break; | |
| 338 } | |
| 339 } | |
| 340 if (!supported_supported_crl_found) { | |
| 341 LOG(ERROR) << "No supported version of revocation data."; | |
| 342 return nullptr; | |
| 343 } | |
| 344 if (!VerifyCRL(supported_crl, tbs_crl)) { | |
| 345 LOG(ERROR) << "CRL - Verification failed."; | |
| 346 return nullptr; | |
| 347 } | |
| 348 | |
| 349 return base::WrapUnique(new CastCRLImpl(tbs_crl)); | |
| 350 } | |
| 351 | |
| 352 bool AddCRLTrustAnchorForTest(const uint8_t* data, size_t length) { | |
| 353 scoped_refptr<net::ParsedCertificate> anchor( | |
| 354 net::ParsedCertificate::CreateFromCertificateData( | |
| 355 data, length, | |
| 356 net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE)); | |
| 357 if (!anchor) | |
| 358 return false; | |
| 359 CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor)); | |
| 360 return true; | |
| 361 } | |
| 362 | |
| 363 void ClearCRLTrustAnchorForTest() { | |
| 364 CastCRLTrustStore::Get().Clear(); | |
| 365 } | |
| 366 | |
| 367 } // namespace cast_certificate | |
| OLD | NEW |