Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: extensions/browser/api/cast_channel/cast_auth_util.cc

Issue 2303673004: Hook up Chrome Cast sender to Cast CRL. (Closed)
Patch Set: Fixed nit Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/browser/api/cast_channel/cast_auth_util.h" 5 #include "extensions/browser/api/cast_channel/cast_auth_util.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/feature_list.h"
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/macros.h" 11 #include "base/macros.h"
12 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h" 14 #include "base/strings/stringprintf.h"
13 #include "components/cast_certificate/cast_cert_validator.h" 15 #include "components/cast_certificate/cast_cert_validator.h"
16 #include "components/cast_certificate/cast_crl.h"
14 #include "extensions/browser/api/cast_channel/cast_message_util.h" 17 #include "extensions/browser/api/cast_channel/cast_message_util.h"
15 #include "extensions/common/api/cast_channel/cast_channel.pb.h" 18 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
16 #include "net/cert/x509_certificate.h" 19 #include "net/cert/x509_certificate.h"
17 #include "net/der/parse_values.h" 20 #include "net/der/parse_values.h"
18 21
19 namespace extensions { 22 namespace extensions {
20 namespace api { 23 namespace api {
21 namespace cast_channel { 24 namespace cast_channel {
22 namespace { 25 namespace {
23 26
24 const char* const kParseErrorPrefix = "Failed to parse auth message: "; 27 const char* const kParseErrorPrefix = "Failed to parse auth message: ";
25 28
26 // The maximum number of days a cert can live for. 29 // The maximum number of days a cert can live for.
27 const int kMaxSelfSignedCertLifetimeInDays = 4; 30 const int kMaxSelfSignedCertLifetimeInDays = 4;
28 31
32 // Enforce certificate revocation when enabled.
33 // If disabled, any revocation failures are ignored.
34 //
35 // This flags only controls the enforcement. Revocation is checked regardless.
36 //
37 // This flag tracks the changes necessary to fully enforce revocation.
38 const base::Feature kEnforceRevocationChecking{
39 "CastCertificateRevocation", base::FEATURE_DISABLED_BY_DEFAULT};
40
29 namespace cast_crypto = ::cast_certificate; 41 namespace cast_crypto = ::cast_certificate;
30 42
31 // Extracts an embedded DeviceAuthMessage payload from an auth challenge reply 43 // Extracts an embedded DeviceAuthMessage payload from an auth challenge reply
32 // message. 44 // message.
33 AuthResult ParseAuthMessage(const CastMessage& challenge_reply, 45 AuthResult ParseAuthMessage(const CastMessage& challenge_reply,
34 DeviceAuthMessage* auth_message) { 46 DeviceAuthMessage* auth_message) {
35 if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) { 47 if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) {
36 return AuthResult::CreateWithParseError( 48 return AuthResult::CreateWithParseError(
37 "Wrong payload type in challenge reply", 49 "Wrong payload type in challenge reply",
38 AuthResult::ERROR_WRONG_PAYLOAD_TYPE); 50 AuthResult::ERROR_WRONG_PAYLOAD_TYPE);
(...skipping 17 matching lines...) Expand all
56 base::IntToString(auth_message->error().error_type()), 68 base::IntToString(auth_message->error().error_type()),
57 AuthResult::ERROR_MESSAGE_ERROR); 69 AuthResult::ERROR_MESSAGE_ERROR);
58 } 70 }
59 if (!auth_message->has_response()) { 71 if (!auth_message->has_response()) {
60 return AuthResult::CreateWithParseError( 72 return AuthResult::CreateWithParseError(
61 "Auth message has no response field", AuthResult::ERROR_NO_RESPONSE); 73 "Auth message has no response field", AuthResult::ERROR_NO_RESPONSE);
62 } 74 }
63 return AuthResult(); 75 return AuthResult();
64 } 76 }
65 77
78 // Must match with histogram enum CastCertificateStatus.
79 // This should never be reordered.
80 enum CertVerificationStatus {
81 CERT_STATUS_OK,
82 CERT_STATUS_INVALID_CRL,
83 CERT_STATUS_VERIFICATION_FAILED,
84 CERT_STATUS_REVOKED,
85 CERT_STATUS_COUNT,
86 };
87
66 } // namespace 88 } // namespace
67 89
68 AuthResult::AuthResult() 90 AuthResult::AuthResult()
69 : error_type(ERROR_NONE), channel_policies(POLICY_NONE) {} 91 : error_type(ERROR_NONE), channel_policies(POLICY_NONE) {}
70 92
71 AuthResult::AuthResult(const std::string& error_message, ErrorType error_type) 93 AuthResult::AuthResult(const std::string& error_message, ErrorType error_type)
72 : error_message(error_message), error_type(error_type) {} 94 : error_message(error_message), error_type(error_type) {}
73 95
74 AuthResult::~AuthResult() { 96 AuthResult::~AuthResult() {
75 } 97 }
(...skipping 27 matching lines...) Expand all
103 // structure is signed by the AuthResponse, so the validity field from X.509 125 // structure is signed by the AuthResponse, so the validity field from X.509
104 // is repurposed as this signature's expiration. 126 // is repurposed as this signature's expiration.
105 base::Time expiry = peer_cert.valid_expiry(); 127 base::Time expiry = peer_cert.valid_expiry();
106 base::Time lifetime_limit = 128 base::Time lifetime_limit =
107 base::Time::Now() + 129 base::Time::Now() +
108 base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays); 130 base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays);
109 if (peer_cert.valid_start().is_null() || 131 if (peer_cert.valid_start().is_null() ||
110 peer_cert.valid_start() > base::Time::Now()) { 132 peer_cert.valid_start() > base::Time::Now()) {
111 return AuthResult::CreateWithParseError( 133 return AuthResult::CreateWithParseError(
112 "Certificate's valid start date is in the future.", 134 "Certificate's valid start date is in the future.",
113 AuthResult::ERROR_VALID_START_DATE_IN_FUTURE); 135 AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE);
114 } 136 }
115 if (expiry.is_null() || peer_cert.HasExpired()) { 137 if (expiry.is_null() || peer_cert.HasExpired()) {
116 return AuthResult::CreateWithParseError("Certificate has expired.", 138 return AuthResult::CreateWithParseError("Certificate has expired.",
117 AuthResult::ERROR_CERT_EXPIRED); 139 AuthResult::ERROR_TLS_CERT_EXPIRED);
118 } 140 }
119 if (expiry > lifetime_limit) { 141 if (expiry > lifetime_limit) {
120 return AuthResult::CreateWithParseError( 142 return AuthResult::CreateWithParseError(
121 "Peer cert lifetime is too long.", 143 "Peer cert lifetime is too long.",
122 AuthResult::ERROR_VALIDITY_PERIOD_TOO_LONG); 144 AuthResult::ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG);
123 } 145 }
124 146
125 const AuthResponse& response = auth_message.response(); 147 const AuthResponse& response = auth_message.response();
126 return VerifyCredentials(response, peer_cert_der); 148 return VerifyCredentials(response, peer_cert_der);
127 } 149 }
128 150
129 // This function does the following 151 // This function does the following
130 // 152 //
131 // * Verifies that the certificate chain |response.client_auth_certificate| + 153 // * Verifies that the certificate chain |response.client_auth_certificate| +
132 // |response.intermediate_certificate| is valid and chains to a trusted 154 // |response.intermediate_certificate| is valid and chains to a trusted
133 // Cast root. 155 // Cast root. The list of trusted Cast roots can be overrided by providing a
156 // non-nullptr |cast_trust_store|. The certificate is verified at
157 // |verification_time|.
158 //
159 // * Verifies that none of the certificates in the chain are revoked based on
160 // the CRL provided in the response |response.crl|. The CRL is verified to be
161 // valid and its issuer certificate chains to a trusted Cast CRL root. The
162 // list of trusted Cast CRL roots can be overrided by providing a non-nullptr
163 // |crl_trust_store|. If |crl_policy| is CRL_OPTIONAL then the result of
164 // revocation checking is ignored. The CRL is verified at
165 // |verification_time|.
134 // 166 //
135 // * Verifies that |response.signature| matches the signature 167 // * Verifies that |response.signature| matches the signature
136 // of |signature_input| by |response.client_auth_certificate|'s public 168 // of |signature_input| by |response.client_auth_certificate|'s public
137 // key. 169 // key.
138 AuthResult VerifyCredentials(const AuthResponse& response, 170 AuthResult VerifyCredentialsImpl(const AuthResponse& response,
139 const std::string& signature_input) { 171 const std::string& signature_input,
172 const cast_crypto::CRLPolicy& crl_policy,
173 net::TrustStore* cast_trust_store,
174 net::TrustStore* crl_trust_store,
175 const base::Time& verification_time) {
140 // Verify the certificate 176 // Verify the certificate
141 std::unique_ptr<cast_crypto::CertVerificationContext> verification_context; 177 std::unique_ptr<cast_crypto::CertVerificationContext> verification_context;
142 178
143 // Build a single vector containing the certificate chain. 179 // Build a single vector containing the certificate chain.
144 std::vector<std::string> cert_chain; 180 std::vector<std::string> cert_chain;
145 cert_chain.push_back(response.client_auth_certificate()); 181 cert_chain.push_back(response.client_auth_certificate());
146 cert_chain.insert(cert_chain.end(), 182 cert_chain.insert(cert_chain.end(),
147 response.intermediate_certificate().begin(), 183 response.intermediate_certificate().begin(),
148 response.intermediate_certificate().end()); 184 response.intermediate_certificate().end());
149 185
150 // Use the current time when checking certificate validity. 186 // Parse the CRL.
151 base::Time now = base::Time::Now(); 187 std::unique_ptr<cast_crypto::CastCRL> crl =
152 188 cast_crypto::ParseAndVerifyCRLUsingCustomTrustStore(
153 // CRL should not be enforced until it is served. 189 response.crl(), verification_time, crl_trust_store);
154 cast_crypto::CastDeviceCertPolicy device_policy; 190 if (!crl) {
155 if (!cast_crypto::VerifyDeviceCert( 191 // CRL is invalid.
156 cert_chain, now, &verification_context, &device_policy, nullptr, 192 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Certificate",
157 cast_certificate::CRLPolicy::CRL_OPTIONAL)) { 193 CERT_STATUS_INVALID_CRL, CERT_STATUS_COUNT);
158 // TODO(eroman): The error information was lost; this error is ambiguous. 194 if (crl_policy == cast_crypto::CRLPolicy::CRL_REQUIRED) {
159 return AuthResult("Failed verifying cast device certificate", 195 return AuthResult("Failed verifying Cast CRL.",
160 AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA); 196 AuthResult::ERROR_CRL_INVALID);
197 }
161 } 198 }
162 199
200 cast_crypto::CastDeviceCertPolicy device_policy;
201 bool verification_success =
202 cast_crypto::VerifyDeviceCertUsingCustomTrustStore(
203 cert_chain, verification_time, &verification_context, &device_policy,
204 crl.get(), crl_policy, cast_trust_store);
205 if (!verification_success) {
206 // TODO(ryanchung): Once this feature is completely rolled-out, remove the
207 // reverification step and use error reporting to get verification errors
208 // for metrics.
209 bool verification_no_crl_success =
210 cast_crypto::VerifyDeviceCertUsingCustomTrustStore(
211 cert_chain, verification_time, &verification_context,
212 &device_policy, nullptr, cast_crypto::CRLPolicy::CRL_OPTIONAL,
213 cast_trust_store);
214 if (!verification_no_crl_success) {
215 // TODO(eroman): The error information was lost; this error is ambiguous.
216 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Certificate",
217 CERT_STATUS_VERIFICATION_FAILED,
218 CERT_STATUS_COUNT);
219 return AuthResult("Failed verifying cast device certificate",
220 AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA);
221 }
222 if (crl) {
223 // If CRL was not present, it should've been recorded as such.
224 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Certificate", CERT_STATUS_REVOKED,
225 CERT_STATUS_COUNT);
226 }
227 if (crl_policy == cast_crypto::CRLPolicy::CRL_REQUIRED) {
228 // Device is revoked.
229 return AuthResult("Failed certificate revocation check.",
230 AuthResult::ERROR_CERT_REVOKED);
231 }
232 }
233 // The certificate is verified at this point.
234 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Certificate", CERT_STATUS_OK,
235 CERT_STATUS_COUNT);
163 if (!verification_context->VerifySignatureOverData(response.signature(), 236 if (!verification_context->VerifySignatureOverData(response.signature(),
164 signature_input)) { 237 signature_input)) {
165 return AuthResult("Failed verifying signature over data", 238 return AuthResult("Failed verifying signature over data",
166 AuthResult::ERROR_SIGNED_BLOBS_MISMATCH); 239 AuthResult::ERROR_SIGNED_BLOBS_MISMATCH);
167 } 240 }
168 241
169 AuthResult success; 242 AuthResult success;
170 243
171 // Set the policy into the result. 244 // Set the policy into the result.
172 switch (device_policy) { 245 switch (device_policy) {
173 case cast_crypto::CastDeviceCertPolicy::AUDIO_ONLY: 246 case cast_crypto::CastDeviceCertPolicy::AUDIO_ONLY:
174 success.channel_policies = AuthResult::POLICY_AUDIO_ONLY; 247 success.channel_policies = AuthResult::POLICY_AUDIO_ONLY;
175 break; 248 break;
176 case cast_crypto::CastDeviceCertPolicy::NONE: 249 case cast_crypto::CastDeviceCertPolicy::NONE:
177 success.channel_policies = AuthResult::POLICY_NONE; 250 success.channel_policies = AuthResult::POLICY_NONE;
178 break; 251 break;
179 } 252 }
180 253
181 return success; 254 return success;
182 } 255 }
183 256
257 AuthResult VerifyCredentials(const AuthResponse& response,
258 const std::string& signature_input) {
259 base::Time now = base::Time::Now();
260 cast_crypto::CRLPolicy policy = cast_crypto::CRLPolicy::CRL_REQUIRED;
261 if (!base::FeatureList::IsEnabled(kEnforceRevocationChecking)) {
262 policy = cast_crypto::CRLPolicy::CRL_OPTIONAL;
263 }
264 return VerifyCredentialsImpl(response, signature_input, policy, nullptr,
265 nullptr, now);
266 }
267
268 AuthResult VerifyCredentialsForTest(const AuthResponse& response,
269 const std::string& signature_input,
270 const cast_crypto::CRLPolicy& crl_policy,
271 net::TrustStore* cast_trust_store,
272 net::TrustStore* crl_trust_store,
273 const base::Time& verification_time) {
274 return VerifyCredentialsImpl(response, signature_input, crl_policy,
275 cast_trust_store, crl_trust_store,
276 verification_time);
277 }
278
184 } // namespace cast_channel 279 } // namespace cast_channel
185 } // namespace api 280 } // namespace api
186 } // namespace extensions 281 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/browser/api/cast_channel/cast_auth_util.h ('k') | extensions/browser/api/cast_channel/cast_auth_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698