Index: extensions/browser/api/cast_channel/cast_auth_util.cc |
diff --git a/extensions/browser/api/cast_channel/cast_auth_util.cc b/extensions/browser/api/cast_channel/cast_auth_util.cc |
deleted file mode 100644 |
index 9406fde850cddb3f35b59cb565618beed129e60b..0000000000000000000000000000000000000000 |
--- a/extensions/browser/api/cast_channel/cast_auth_util.cc |
+++ /dev/null |
@@ -1,399 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "extensions/browser/api/cast_channel/cast_auth_util.h" |
- |
-#include <vector> |
- |
-#include "base/feature_list.h" |
-#include "base/logging.h" |
-#include "base/macros.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/memory/singleton.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/stringprintf.h" |
-#include "components/cast_certificate/cast_cert_validator.h" |
-#include "components/cast_certificate/cast_crl.h" |
-#include "crypto/random.h" |
-#include "extensions/browser/api/cast_channel/cast_message_util.h" |
-#include "extensions/common/api/cast_channel/cast_channel.pb.h" |
-#include "net/cert/x509_certificate.h" |
-#include "net/der/parse_values.h" |
- |
-namespace extensions { |
-namespace api { |
-namespace cast_channel { |
-namespace { |
- |
-const char kParseErrorPrefix[] = "Failed to parse auth message: "; |
- |
-// The maximum number of days a cert can live for. |
-const int kMaxSelfSignedCertLifetimeInDays = 4; |
- |
-// The size of the nonce challenge in bytes. |
-const int kNonceSizeInBytes = 16; |
- |
-// The number of hours after which a nonce is regenerated. |
-long kNonceExpirationTimeInHours = 24; |
- |
-// Enforce certificate revocation when enabled. |
-// If disabled, any revocation failures are ignored. |
-// |
-// This flags only controls the enforcement. Revocation is checked regardless. |
-// |
-// This flag tracks the changes necessary to fully enforce revocation. |
-const base::Feature kEnforceRevocationChecking{ |
- "CastCertificateRevocation", base::FEATURE_DISABLED_BY_DEFAULT}; |
- |
-// Enforce nonce checking when enabled. |
-// If disabled, the nonce value returned from the device is not checked against |
-// the one sent to the device. As a result, the nonce can be empty and omitted |
-// from the signature. This allows backwards compatibility with legacy Cast |
-// receivers. |
- |
-const base::Feature kEnforceNonceChecking{"CastNonceEnforced", |
- base::FEATURE_DISABLED_BY_DEFAULT}; |
- |
-namespace cast_crypto = ::cast_certificate; |
- |
-// Extracts an embedded DeviceAuthMessage payload from an auth challenge reply |
-// message. |
-AuthResult ParseAuthMessage(const CastMessage& challenge_reply, |
- DeviceAuthMessage* auth_message) { |
- if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) { |
- return AuthResult::CreateWithParseError( |
- "Wrong payload type in challenge reply", |
- AuthResult::ERROR_WRONG_PAYLOAD_TYPE); |
- } |
- if (!challenge_reply.has_payload_binary()) { |
- return AuthResult::CreateWithParseError( |
- "Payload type is binary but payload_binary field not set", |
- AuthResult::ERROR_NO_PAYLOAD); |
- } |
- if (!auth_message->ParseFromString(challenge_reply.payload_binary())) { |
- return AuthResult::CreateWithParseError( |
- "Cannot parse binary payload into DeviceAuthMessage", |
- AuthResult::ERROR_PAYLOAD_PARSING_FAILED); |
- } |
- |
- VLOG(1) << "Auth message: " << AuthMessageToString(*auth_message); |
- |
- if (auth_message->has_error()) { |
- return AuthResult::CreateWithParseError( |
- "Auth message error: " + |
- base::IntToString(auth_message->error().error_type()), |
- AuthResult::ERROR_MESSAGE_ERROR); |
- } |
- if (!auth_message->has_response()) { |
- return AuthResult::CreateWithParseError( |
- "Auth message has no response field", AuthResult::ERROR_NO_RESPONSE); |
- } |
- return AuthResult(); |
-} |
- |
-class CastNonce { |
- public: |
- static CastNonce* GetInstance() { |
- return base::Singleton<CastNonce, |
- base::LeakySingletonTraits<CastNonce>>::get(); |
- } |
- |
- static const std::string& Get() { |
- GetInstance()->EnsureNonceTimely(); |
- return GetInstance()->nonce_; |
- } |
- |
- private: |
- friend struct base::DefaultSingletonTraits<CastNonce>; |
- |
- CastNonce() { GenerateNonce(); } |
- void GenerateNonce() { |
- // Create a cryptographically secure nonce. |
- crypto::RandBytes(base::WriteInto(&nonce_, kNonceSizeInBytes + 1), |
- kNonceSizeInBytes); |
- nonce_generation_time_ = base::Time::Now(); |
- } |
- |
- void EnsureNonceTimely() { |
- if (base::Time::Now() > |
- (nonce_generation_time_ + |
- base::TimeDelta::FromHours(kNonceExpirationTimeInHours))) { |
- GenerateNonce(); |
- } |
- } |
- |
- // The nonce challenge to send to the Cast receiver. |
- // The nonce is updated daily. |
- std::string nonce_; |
- base::Time nonce_generation_time_; |
-}; |
- |
-// Must match with histogram enum CastCertificateStatus. |
-// This should never be reordered. |
-enum CertVerificationStatus { |
- CERT_STATUS_OK, |
- CERT_STATUS_INVALID_CRL, |
- CERT_STATUS_VERIFICATION_FAILED, |
- CERT_STATUS_REVOKED, |
- CERT_STATUS_COUNT, |
-}; |
- |
-// Must match with histogram enum CastNonce. |
-// This should never be reordered. |
-enum NonceVerificationStatus { |
- NONCE_MATCH, |
- NONCE_MISMATCH, |
- NONCE_MISSING, |
- NONCE_COUNT, |
-}; |
- |
-// Record certificate verification histogram events. |
-void RecordCertificateEvent(CertVerificationStatus event) { |
- UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Certificate", event, |
- CERT_STATUS_COUNT); |
-} |
- |
-// Record nonce verification histogram events. |
-void RecordNonceEvent(NonceVerificationStatus event) { |
- UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Nonce", event, NONCE_COUNT); |
-} |
- |
-} // namespace |
- |
-AuthResult::AuthResult() |
- : error_type(ERROR_NONE), channel_policies(POLICY_NONE) {} |
- |
-AuthResult::AuthResult(const std::string& error_message, ErrorType error_type) |
- : error_message(error_message), error_type(error_type) {} |
- |
-AuthResult::~AuthResult() { |
-} |
- |
-// static |
-AuthResult AuthResult::CreateWithParseError(const std::string& error_message, |
- ErrorType error_type) { |
- return AuthResult(kParseErrorPrefix + error_message, error_type); |
-} |
- |
-// static |
-AuthContext AuthContext::Create() { |
- return AuthContext(CastNonce::Get()); |
-} |
- |
-AuthContext::AuthContext(const std::string& nonce) : nonce_(nonce) {} |
- |
-AuthContext::~AuthContext() {} |
- |
-AuthResult AuthContext::VerifySenderNonce( |
- const std::string& nonce_response) const { |
- if (nonce_ != nonce_response) { |
- if (nonce_response.empty()) { |
- RecordNonceEvent(NONCE_MISSING); |
- } else { |
- RecordNonceEvent(NONCE_MISMATCH); |
- } |
- if (base::FeatureList::IsEnabled(kEnforceNonceChecking)) { |
- return AuthResult("Sender nonce mismatched.", |
- AuthResult::ERROR_SENDER_NONCE_MISMATCH); |
- } |
- } else { |
- RecordNonceEvent(NONCE_MATCH); |
- } |
- return AuthResult(); |
-} |
- |
-// Verifies the peer certificate and populates |peer_cert_der| with the DER |
-// encoded certificate. |
-AuthResult VerifyTLSCertificate(const net::X509Certificate& peer_cert, |
- std::string* peer_cert_der, |
- const base::Time& verification_time) { |
- // Get the DER-encoded form of the certificate. |
- if (!net::X509Certificate::GetDEREncoded(peer_cert.os_cert_handle(), |
- peer_cert_der) || |
- peer_cert_der->empty()) { |
- return AuthResult::CreateWithParseError( |
- "Could not create DER-encoded peer cert.", |
- AuthResult::ERROR_CERT_PARSING_FAILED); |
- } |
- |
- // Ensure the peer cert is valid and doesn't have an excessive remaining |
- // lifetime. Although it is not verified as an X.509 certificate, the entire |
- // structure is signed by the AuthResponse, so the validity field from X.509 |
- // is repurposed as this signature's expiration. |
- base::Time expiry = peer_cert.valid_expiry(); |
- base::Time lifetime_limit = |
- verification_time + |
- base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays); |
- if (peer_cert.valid_start().is_null() || |
- peer_cert.valid_start() > verification_time) { |
- return AuthResult::CreateWithParseError( |
- "Certificate's valid start date is in the future.", |
- AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE); |
- } |
- if (expiry.is_null() || peer_cert.valid_expiry() < verification_time) { |
- return AuthResult::CreateWithParseError("Certificate has expired.", |
- AuthResult::ERROR_TLS_CERT_EXPIRED); |
- } |
- if (expiry > lifetime_limit) { |
- return AuthResult::CreateWithParseError( |
- "Peer cert lifetime is too long.", |
- AuthResult::ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG); |
- } |
- return AuthResult(); |
-} |
- |
-AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply, |
- const net::X509Certificate& peer_cert, |
- const AuthContext& auth_context) { |
- DeviceAuthMessage auth_message; |
- AuthResult result = ParseAuthMessage(challenge_reply, &auth_message); |
- if (!result.success()) { |
- return result; |
- } |
- |
- std::string peer_cert_der; |
- result = VerifyTLSCertificate(peer_cert, &peer_cert_der, base::Time::Now()); |
- if (!result.success()) { |
- return result; |
- } |
- |
- const AuthResponse& response = auth_message.response(); |
- const std::string& nonce_response = response.sender_nonce(); |
- |
- result = auth_context.VerifySenderNonce(nonce_response); |
- if (!result.success()) { |
- return result; |
- } |
- |
- return VerifyCredentials(response, nonce_response + peer_cert_der); |
-} |
- |
-// This function does the following |
-// |
-// * Verifies that the certificate chain |response.client_auth_certificate| + |
-// |response.intermediate_certificate| is valid and chains to a trusted |
-// Cast root. The list of trusted Cast roots can be overrided by providing a |
-// non-nullptr |cast_trust_store|. The certificate is verified at |
-// |verification_time|. |
-// |
-// * Verifies that none of the certificates in the chain are revoked based on |
-// the CRL provided in the response |response.crl|. The CRL is verified to be |
-// valid and its issuer certificate chains to a trusted Cast CRL root. The |
-// list of trusted Cast CRL roots can be overrided by providing a non-nullptr |
-// |crl_trust_store|. If |crl_policy| is CRL_OPTIONAL then the result of |
-// revocation checking is ignored. The CRL is verified at |
-// |verification_time|. |
-// |
-// * Verifies that |response.signature| matches the signature |
-// of |signature_input| by |response.client_auth_certificate|'s public |
-// key. |
-AuthResult VerifyCredentialsImpl(const AuthResponse& response, |
- const std::string& signature_input, |
- const cast_crypto::CRLPolicy& crl_policy, |
- net::TrustStore* cast_trust_store, |
- net::TrustStore* crl_trust_store, |
- const base::Time& verification_time) { |
- // Verify the certificate |
- std::unique_ptr<cast_crypto::CertVerificationContext> verification_context; |
- |
- // Build a single vector containing the certificate chain. |
- std::vector<std::string> cert_chain; |
- cert_chain.push_back(response.client_auth_certificate()); |
- cert_chain.insert(cert_chain.end(), |
- response.intermediate_certificate().begin(), |
- response.intermediate_certificate().end()); |
- |
- // Parse the CRL. |
- std::unique_ptr<cast_crypto::CastCRL> crl = |
- cast_crypto::ParseAndVerifyCRLUsingCustomTrustStore( |
- response.crl(), verification_time, crl_trust_store); |
- if (!crl) { |
- // CRL is invalid. |
- RecordCertificateEvent(CERT_STATUS_INVALID_CRL); |
- if (crl_policy == cast_crypto::CRLPolicy::CRL_REQUIRED) { |
- return AuthResult("Failed verifying Cast CRL.", |
- AuthResult::ERROR_CRL_INVALID); |
- } |
- } |
- |
- cast_crypto::CastDeviceCertPolicy device_policy; |
- bool verification_success = |
- cast_crypto::VerifyDeviceCertUsingCustomTrustStore( |
- cert_chain, verification_time, &verification_context, &device_policy, |
- crl.get(), crl_policy, cast_trust_store); |
- if (!verification_success) { |
- // TODO(ryanchung): Once this feature is completely rolled-out, remove the |
- // reverification step and use error reporting to get verification errors |
- // for metrics. |
- bool verification_no_crl_success = |
- cast_crypto::VerifyDeviceCertUsingCustomTrustStore( |
- cert_chain, verification_time, &verification_context, |
- &device_policy, nullptr, cast_crypto::CRLPolicy::CRL_OPTIONAL, |
- cast_trust_store); |
- if (!verification_no_crl_success) { |
- // TODO(eroman): The error information was lost; this error is ambiguous. |
- RecordCertificateEvent(CERT_STATUS_VERIFICATION_FAILED); |
- return AuthResult("Failed verifying cast device certificate", |
- AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA); |
- } |
- if (crl) { |
- // If CRL was not present, it should've been recorded as such. |
- RecordCertificateEvent(CERT_STATUS_REVOKED); |
- } |
- if (crl_policy == cast_crypto::CRLPolicy::CRL_REQUIRED) { |
- // Device is revoked. |
- return AuthResult("Failed certificate revocation check.", |
- AuthResult::ERROR_CERT_REVOKED); |
- } |
- } |
- // The certificate is verified at this point. |
- RecordCertificateEvent(CERT_STATUS_OK); |
- if (!verification_context->VerifySignatureOverData(response.signature(), |
- signature_input)) { |
- return AuthResult("Failed verifying signature over data", |
- AuthResult::ERROR_SIGNED_BLOBS_MISMATCH); |
- } |
- |
- AuthResult success; |
- |
- // Set the policy into the result. |
- switch (device_policy) { |
- case cast_crypto::CastDeviceCertPolicy::AUDIO_ONLY: |
- success.channel_policies = AuthResult::POLICY_AUDIO_ONLY; |
- break; |
- case cast_crypto::CastDeviceCertPolicy::NONE: |
- success.channel_policies = AuthResult::POLICY_NONE; |
- break; |
- } |
- |
- return success; |
-} |
- |
-AuthResult VerifyCredentials(const AuthResponse& response, |
- const std::string& signature_input) { |
- base::Time now = base::Time::Now(); |
- cast_crypto::CRLPolicy policy = cast_crypto::CRLPolicy::CRL_REQUIRED; |
- if (!base::FeatureList::IsEnabled(kEnforceRevocationChecking)) { |
- policy = cast_crypto::CRLPolicy::CRL_OPTIONAL; |
- } |
- return VerifyCredentialsImpl(response, signature_input, policy, nullptr, |
- nullptr, now); |
-} |
- |
-AuthResult VerifyCredentialsForTest(const AuthResponse& response, |
- const std::string& signature_input, |
- const cast_crypto::CRLPolicy& crl_policy, |
- net::TrustStore* cast_trust_store, |
- net::TrustStore* crl_trust_store, |
- const base::Time& verification_time) { |
- return VerifyCredentialsImpl(response, signature_input, crl_policy, |
- cast_trust_store, crl_trust_store, |
- verification_time); |
-} |
- |
-} // namespace cast_channel |
-} // namespace api |
-} // namespace extensions |