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

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

Issue 2709523008: [Cast Channel] Add support for nonce challenge to Cast channel authentication. (Closed)
Patch Set: Addresses comments Created 3 years, 9 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/feature_list.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 20 matching lines...) Expand all
31 31
32 // Enforce certificate revocation when enabled. 32 // Enforce certificate revocation when enabled.
33 // If disabled, any revocation failures are ignored. 33 // If disabled, any revocation failures are ignored.
34 // 34 //
35 // This flags only controls the enforcement. Revocation is checked regardless. 35 // This flags only controls the enforcement. Revocation is checked regardless.
36 // 36 //
37 // This flag tracks the changes necessary to fully enforce revocation. 37 // This flag tracks the changes necessary to fully enforce revocation.
38 const base::Feature kEnforceRevocationChecking{ 38 const base::Feature kEnforceRevocationChecking{
39 "CastCertificateRevocation", base::FEATURE_DISABLED_BY_DEFAULT}; 39 "CastCertificateRevocation", base::FEATURE_DISABLED_BY_DEFAULT};
40 40
41 // Enforce nonce checking when enabled.
42 // If disabled, the nonce value returned from the device is not checked against
43 // the one sent to the device. As a result, the nonce can be empty and omitted
44 // from the signature. This allows backwards compatibility with legacy Cast
45 // receivers.
46
47 const base::Feature kEnforceNonceChecking{"CastNonceEnforced",
48 base::FEATURE_DISABLED_BY_DEFAULT};
49
41 namespace cast_crypto = ::cast_certificate; 50 namespace cast_crypto = ::cast_certificate;
42 51
43 // Extracts an embedded DeviceAuthMessage payload from an auth challenge reply 52 // Extracts an embedded DeviceAuthMessage payload from an auth challenge reply
44 // message. 53 // message.
45 AuthResult ParseAuthMessage(const CastMessage& challenge_reply, 54 AuthResult ParseAuthMessage(const CastMessage& challenge_reply,
46 DeviceAuthMessage* auth_message) { 55 DeviceAuthMessage* auth_message) {
47 if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) { 56 if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) {
48 return AuthResult::CreateWithParseError( 57 return AuthResult::CreateWithParseError(
49 "Wrong payload type in challenge reply", 58 "Wrong payload type in challenge reply",
50 AuthResult::ERROR_WRONG_PAYLOAD_TYPE); 59 AuthResult::ERROR_WRONG_PAYLOAD_TYPE);
(...skipping 27 matching lines...) Expand all
78 // Must match with histogram enum CastCertificateStatus. 87 // Must match with histogram enum CastCertificateStatus.
79 // This should never be reordered. 88 // This should never be reordered.
80 enum CertVerificationStatus { 89 enum CertVerificationStatus {
81 CERT_STATUS_OK, 90 CERT_STATUS_OK,
82 CERT_STATUS_INVALID_CRL, 91 CERT_STATUS_INVALID_CRL,
83 CERT_STATUS_VERIFICATION_FAILED, 92 CERT_STATUS_VERIFICATION_FAILED,
84 CERT_STATUS_REVOKED, 93 CERT_STATUS_REVOKED,
85 CERT_STATUS_COUNT, 94 CERT_STATUS_COUNT,
86 }; 95 };
87 96
97 enum NonceVerificationStatus {
98 NONCE_MATCH,
99 NONCE_MISMATCH,
100 NONCE_MISSING,
101 NONCE_COUNT,
102 };
103
88 } // namespace 104 } // namespace
89 105
90 AuthResult::AuthResult() 106 AuthResult::AuthResult()
91 : error_type(ERROR_NONE), channel_policies(POLICY_NONE) {} 107 : error_type(ERROR_NONE), channel_policies(POLICY_NONE) {}
92 108
93 AuthResult::AuthResult(const std::string& error_message, ErrorType error_type) 109 AuthResult::AuthResult(const std::string& error_message, ErrorType error_type)
94 : error_message(error_message), error_type(error_type) {} 110 : error_message(error_message), error_type(error_type) {}
95 111
96 AuthResult::~AuthResult() { 112 AuthResult::~AuthResult() {
97 } 113 }
98 114
99 // static 115 // static
100 AuthResult AuthResult::CreateWithParseError(const std::string& error_message, 116 AuthResult AuthResult::CreateWithParseError(const std::string& error_message,
101 ErrorType error_type) { 117 ErrorType error_type) {
102 return AuthResult(kParseErrorPrefix + error_message, error_type); 118 return AuthResult(kParseErrorPrefix + error_message, error_type);
103 } 119 }
104 120
105 AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply, 121 // Verifies the peer certificate and populates |peer_cert_der| with the DER
106 const net::X509Certificate& peer_cert) { 122 // encoded certificate.
107 DeviceAuthMessage auth_message; 123 AuthResult VerifyTLSCertificate(const net::X509Certificate& peer_cert,
108 AuthResult result = ParseAuthMessage(challenge_reply, &auth_message); 124 std::string* peer_cert_der,
109 if (!result.success()) { 125 const base::Time& verification_time) {
110 return result;
111 }
112
113 // Get the DER-encoded form of the certificate. 126 // Get the DER-encoded form of the certificate.
114 std::string peer_cert_der;
115 if (!net::X509Certificate::GetDEREncoded(peer_cert.os_cert_handle(), 127 if (!net::X509Certificate::GetDEREncoded(peer_cert.os_cert_handle(),
116 &peer_cert_der) || 128 peer_cert_der) ||
117 peer_cert_der.empty()) { 129 peer_cert_der->empty()) {
118 return AuthResult::CreateWithParseError( 130 return AuthResult::CreateWithParseError(
119 "Could not create DER-encoded peer cert.", 131 "Could not create DER-encoded peer cert.",
120 AuthResult::ERROR_CERT_PARSING_FAILED); 132 AuthResult::ERROR_CERT_PARSING_FAILED);
121 } 133 }
122 134
123 // Ensure the peer cert is valid and doesn't have an excessive remaining 135 // Ensure the peer cert is valid and doesn't have an excessive remaining
124 // lifetime. Although it is not verified as an X.509 certificate, the entire 136 // lifetime. Although it is not verified as an X.509 certificate, the entire
125 // structure is signed by the AuthResponse, so the validity field from X.509 137 // structure is signed by the AuthResponse, so the validity field from X.509
126 // is repurposed as this signature's expiration. 138 // is repurposed as this signature's expiration.
127 base::Time expiry = peer_cert.valid_expiry(); 139 base::Time expiry = peer_cert.valid_expiry();
128 base::Time lifetime_limit = 140 base::Time lifetime_limit =
129 base::Time::Now() + 141 verification_time +
130 base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays); 142 base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays);
131 if (peer_cert.valid_start().is_null() || 143 if (peer_cert.valid_start().is_null() ||
132 peer_cert.valid_start() > base::Time::Now()) { 144 peer_cert.valid_start() > verification_time) {
133 return AuthResult::CreateWithParseError( 145 return AuthResult::CreateWithParseError(
134 "Certificate's valid start date is in the future.", 146 "Certificate's valid start date is in the future.",
135 AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE); 147 AuthResult::ERROR_TLS_CERT_VALID_START_DATE_IN_FUTURE);
136 } 148 }
137 if (expiry.is_null() || peer_cert.HasExpired()) { 149 if (expiry.is_null() || peer_cert.valid_expiry() < verification_time) {
138 return AuthResult::CreateWithParseError("Certificate has expired.", 150 return AuthResult::CreateWithParseError("Certificate has expired.",
139 AuthResult::ERROR_TLS_CERT_EXPIRED); 151 AuthResult::ERROR_TLS_CERT_EXPIRED);
140 } 152 }
141 if (expiry > lifetime_limit) { 153 if (expiry > lifetime_limit) {
142 return AuthResult::CreateWithParseError( 154 return AuthResult::CreateWithParseError(
143 "Peer cert lifetime is too long.", 155 "Peer cert lifetime is too long.",
144 AuthResult::ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG); 156 AuthResult::ERROR_TLS_CERT_VALIDITY_PERIOD_TOO_LONG);
145 } 157 }
158 return AuthResult();
159 }
160
161 // Verifies the nonce received in the response is equivalent to the one sent.
162 AuthResult VerifySenderNonce(const std::string& nonce,
163 const std::string& nonce_response) {
164 if (nonce != nonce_response) {
165 if (nonce_response.empty()) {
166 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Nonce", NONCE_MISSING,
167 NONCE_COUNT);
168 } else {
169 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Nonce", NONCE_MISMATCH,
170 NONCE_COUNT);
171 }
172 if (base::FeatureList::IsEnabled(kEnforceNonceChecking)) {
173 return AuthResult("Sender nonce mismatched.",
174 AuthResult::ERROR_SENDER_NONCE_MISMATCH);
175 }
176 } else {
177 UMA_HISTOGRAM_ENUMERATION("Cast.Channel.Nonce", NONCE_MATCH, NONCE_COUNT);
178 }
179 return AuthResult();
180 }
181
182 AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply,
183 const net::X509Certificate& peer_cert,
184 const std::string& nonce) {
185 DeviceAuthMessage auth_message;
186 AuthResult result = ParseAuthMessage(challenge_reply, &auth_message);
187 if (!result.success()) {
188 return result;
189 }
190
191 std::string peer_cert_der;
192 result = VerifyTLSCertificate(peer_cert, &peer_cert_der, base::Time::Now());
193 if (!result.success()) {
194 return result;
195 }
146 196
147 const AuthResponse& response = auth_message.response(); 197 const AuthResponse& response = auth_message.response();
148 return VerifyCredentials(response, peer_cert_der); 198 const std::string& nonce_response = response.sender_nonce();
199
200 result = VerifySenderNonce(nonce, nonce_response);
201 if (!result.success()) {
202 return result;
203 }
204
205 return VerifyCredentials(response, nonce_response + peer_cert_der);
149 } 206 }
150 207
151 // This function does the following 208 // This function does the following
152 // 209 //
153 // * Verifies that the certificate chain |response.client_auth_certificate| + 210 // * Verifies that the certificate chain |response.client_auth_certificate| +
154 // |response.intermediate_certificate| is valid and chains to a trusted 211 // |response.intermediate_certificate| is valid and chains to a trusted
155 // Cast root. The list of trusted Cast roots can be overrided by providing a 212 // 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 213 // non-nullptr |cast_trust_store|. The certificate is verified at
157 // |verification_time|. 214 // |verification_time|.
158 // 215 //
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 net::TrustStore* crl_trust_store, 329 net::TrustStore* crl_trust_store,
273 const base::Time& verification_time) { 330 const base::Time& verification_time) {
274 return VerifyCredentialsImpl(response, signature_input, crl_policy, 331 return VerifyCredentialsImpl(response, signature_input, crl_policy,
275 cast_trust_store, crl_trust_store, 332 cast_trust_store, crl_trust_store,
276 verification_time); 333 verification_time);
277 } 334 }
278 335
279 } // namespace cast_channel 336 } // namespace cast_channel
280 } // namespace api 337 } // namespace api
281 } // namespace extensions 338 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698