1 // Copyright 2019 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 "cast/sender/channel/cast_auth_util.h"
6
7 #include <openssl/rand.h>
8
9 #include <algorithm>
10 #include <memory>
11
12 #include "cast/common/certificate/cast_cert_validator.h"
13 #include "cast/common/certificate/cast_cert_validator_internal.h"
14 #include "cast/common/certificate/cast_crl.h"
15 #include "cast/common/channel/proto/cast_channel.pb.h"
16 #include "platform/api/time.h"
17 #include "platform/base/error.h"
18 #include "util/osp_logging.h"
19
20 namespace openscreen {
21 namespace cast {
22
23 using ::cast::channel::AuthResponse;
24 using ::cast::channel::CastMessage;
25 using ::cast::channel::DeviceAuthMessage;
26 using ::cast::channel::HashAlgorithm;
27
28 namespace {
29
30 #define PARSE_ERROR_PREFIX "Failed to parse auth message: "
31
32 // The maximum number of days a cert can live for.
33 constexpr int kMaxSelfSignedCertLifetimeInDays = 4;
34
35 // The size of the nonce challenge in bytes.
36 constexpr int kNonceSizeInBytes = 16;
37
38 // The number of hours after which a nonce is regenerated.
39 constexpr int kNonceExpirationTimeInHours = 24;
40
41 // Extracts an embedded DeviceAuthMessage payload from an auth challenge reply
42 // message.
ParseAuthMessage(const CastMessage & challenge_reply,DeviceAuthMessage * auth_message)43 Error ParseAuthMessage(const CastMessage& challenge_reply,
44 DeviceAuthMessage* auth_message) {
45 if (challenge_reply.payload_type() !=
46 ::cast::channel::CastMessage_PayloadType_BINARY) {
47 return Error(Error::Code::kCastV2WrongPayloadType,
48 PARSE_ERROR_PREFIX "Wrong payload type in challenge reply");
49 }
50 if (!challenge_reply.has_payload_binary()) {
51 return Error(Error::Code::kCastV2NoPayload, PARSE_ERROR_PREFIX
52 "Payload type is binary but payload_binary field not set");
53 }
54 if (!auth_message->ParseFromString(challenge_reply.payload_binary())) {
55 return Error(Error::Code::kCastV2PayloadParsingFailed, PARSE_ERROR_PREFIX
56 "Cannot parse binary payload into DeviceAuthMessage");
57 }
58
59 if (auth_message->has_error()) {
60 std::stringstream ss;
61 ss << PARSE_ERROR_PREFIX "Auth message error: "
62 << auth_message->error().error_type();
63 return Error(Error::Code::kCastV2MessageError, ss.str());
64 }
65 if (!auth_message->has_response()) {
66 return Error(Error::Code::kCastV2NoResponse,
67 PARSE_ERROR_PREFIX "Auth message has no response field");
68 }
69 return Error::None();
70 }
71
72 class CastNonce {
73 public:
GetInstance()74 static CastNonce* GetInstance() {
75 static CastNonce* cast_nonce = new CastNonce();
76 return cast_nonce;
77 }
78
Get()79 static const std::string& Get() {
80 GetInstance()->EnsureNonceTimely();
81 return GetInstance()->nonce_;
82 }
83
84 private:
CastNonce()85 CastNonce() : nonce_(kNonceSizeInBytes, 0) { GenerateNonce(); }
GenerateNonce()86 void GenerateNonce() {
87 OSP_CHECK_EQ(
88 RAND_bytes(reinterpret_cast<uint8_t*>(&nonce_[0]), kNonceSizeInBytes),
89 1);
90 nonce_generation_time_ = GetWallTimeSinceUnixEpoch();
91 }
92
EnsureNonceTimely()93 void EnsureNonceTimely() {
94 if (GetWallTimeSinceUnixEpoch() >
95 (nonce_generation_time_ +
96 std::chrono::hours(kNonceExpirationTimeInHours))) {
97 GenerateNonce();
98 }
99 }
100
101 // The nonce challenge to send to the Cast receiver.
102 // The nonce is updated daily.
103 std::string nonce_;
104 std::chrono::seconds nonce_generation_time_;
105 };
106
107 // Maps Error::Code from certificate verification to Error.
108 // If crl_required is set to false, all revocation related errors are ignored.
MapToOpenscreenError(Error::Code error,bool crl_required)109 Error MapToOpenscreenError(Error::Code error, bool crl_required) {
110 switch (error) {
111 case Error::Code::kErrCertsMissing:
112 return Error(Error::Code::kCastV2PeerCertEmpty,
113 "Failed to locate certificates.");
114 case Error::Code::kErrCertsParse:
115 return Error(Error::Code::kErrCertsParse,
116 "Failed to parse certificates.");
117 case Error::Code::kErrCertsDateInvalid:
118 return Error(Error::Code::kCastV2CertNotSignedByTrustedCa,
119 "Failed date validity check.");
120 case Error::Code::kErrCertsVerifyGeneric:
121 return Error(Error::Code::kCastV2CertNotSignedByTrustedCa,
122 "Failed with a generic certificate verification error.");
123 case Error::Code::kErrCertsRestrictions:
124 return Error(Error::Code::kCastV2CertNotSignedByTrustedCa,
125 "Failed certificate restrictions.");
126 case Error::Code::kErrCertsVerifyUntrustedCert:
127 return Error(Error::Code::kCastV2CertNotSignedByTrustedCa,
128 "Failed with untrusted certificate.");
129 case Error::Code::kErrCrlInvalid:
130 // This error is only encountered if |crl_required| is true.
131 OSP_DCHECK(crl_required);
132 return Error(Error::Code::kErrCrlInvalid,
133 "Failed to provide a valid CRL.");
134 case Error::Code::kErrCertsRevoked:
135 return Error(Error::Code::kErrCertsRevoked,
136 "Failed certificate revocation check.");
137 case Error::Code::kNone:
138 return Error::None();
139 default:
140 return Error(Error::Code::kCastV2CertNotSignedByTrustedCa,
141 "Failed verifying cast device certificate.");
142 }
143 return Error::None();
144 }
145
VerifyAndMapDigestAlgorithm(HashAlgorithm response_digest_algorithm,DigestAlgorithm * digest_algorithm,bool enforce_sha256_checking)146 Error VerifyAndMapDigestAlgorithm(HashAlgorithm response_digest_algorithm,
147 DigestAlgorithm* digest_algorithm,
148 bool enforce_sha256_checking) {
149 switch (response_digest_algorithm) {
150 case ::cast::channel::SHA1:
151 if (enforce_sha256_checking) {
152 return Error(Error::Code::kCastV2DigestUnsupported,
153 "Unsupported digest algorithm.");
154 }
155 *digest_algorithm = DigestAlgorithm::kSha1;
156 break;
157 case ::cast::channel::SHA256:
158 *digest_algorithm = DigestAlgorithm::kSha256;
159 break;
160 default:
161 return Error::Code::kCastV2DigestUnsupported;
162 }
163 return Error::None();
164 }
165
166 } // namespace
167
168 // static
Create()169 AuthContext AuthContext::Create() {
170 return AuthContext(CastNonce::Get());
171 }
172
AuthContext(const std::string & nonce)173 AuthContext::AuthContext(const std::string& nonce) : nonce_(nonce) {}
174
~AuthContext()175 AuthContext::~AuthContext() {}
176
VerifySenderNonce(const std::string & nonce_response,bool enforce_nonce_checking) const177 Error AuthContext::VerifySenderNonce(const std::string& nonce_response,
178 bool enforce_nonce_checking) const {
179 if (nonce_ != nonce_response) {
180 if (enforce_nonce_checking) {
181 return Error(Error::Code::kCastV2SenderNonceMismatch,
182 "Sender nonce mismatched.");
183 }
184 }
185 return Error::None();
186 }
187
VerifyTLSCertificateValidity(X509 * peer_cert,std::chrono::seconds verification_time)188 Error VerifyTLSCertificateValidity(X509* peer_cert,
189 std::chrono::seconds verification_time) {
190 // Ensure the peer cert is valid and doesn't have an excessive remaining
191 // lifetime. Although it is not verified as an X.509 certificate, the entire
192 // structure is signed by the AuthResponse, so the validity field from X.509
193 // is repurposed as this signature's expiration.
194 DateTime not_before;
195 DateTime not_after;
196 if (!GetCertValidTimeRange(peer_cert, ¬_before, ¬_after)) {
197 return Error(Error::Code::kErrCertsParse,
198 PARSE_ERROR_PREFIX "Parsing validity fields failed.");
199 }
200
201 std::chrono::seconds lifetime_limit =
202 verification_time +
203 std::chrono::hours(24 * kMaxSelfSignedCertLifetimeInDays);
204 DateTime verification_time_exploded = {};
205 DateTime lifetime_limit_exploded = {};
206 OSP_CHECK(DateTimeFromSeconds(verification_time.count(),
207 &verification_time_exploded));
208 OSP_CHECK(
209 DateTimeFromSeconds(lifetime_limit.count(), &lifetime_limit_exploded));
210 if (verification_time_exploded < not_before) {
211 return Error(Error::Code::kCastV2TlsCertValidStartDateInFuture,
212 PARSE_ERROR_PREFIX
213 "Certificate's valid start date is in the future.");
214 }
215 if (not_after < verification_time_exploded) {
216 return Error(Error::Code::kCastV2TlsCertExpired,
217 PARSE_ERROR_PREFIX "Certificate has expired.");
218 }
219 if (lifetime_limit_exploded < not_after) {
220 return Error(Error::Code::kCastV2TlsCertValidityPeriodTooLong,
221 PARSE_ERROR_PREFIX "Peer cert lifetime is too long.");
222 }
223 return Error::None();
224 }
225
226 ErrorOr<CastDeviceCertPolicy> VerifyCredentialsImpl(
227 const AuthResponse& response,
228 const std::vector<uint8_t>& signature_input,
229 const CRLPolicy& crl_policy,
230 TrustStore* cast_trust_store,
231 TrustStore* crl_trust_store,
232 const DateTime& verification_time,
233 bool enforce_sha256_checking);
234
AuthenticateChallengeReplyImpl(const CastMessage & challenge_reply,X509 * peer_cert,const AuthContext & auth_context,const CRLPolicy & crl_policy,TrustStore * cast_trust_store,TrustStore * crl_trust_store,const DateTime & verification_time)235 ErrorOr<CastDeviceCertPolicy> AuthenticateChallengeReplyImpl(
236 const CastMessage& challenge_reply,
237 X509* peer_cert,
238 const AuthContext& auth_context,
239 const CRLPolicy& crl_policy,
240 TrustStore* cast_trust_store,
241 TrustStore* crl_trust_store,
242 const DateTime& verification_time) {
243 DeviceAuthMessage auth_message;
244 Error result = ParseAuthMessage(challenge_reply, &auth_message);
245 if (!result.ok()) {
246 return result;
247 }
248
249 result = VerifyTLSCertificateValidity(peer_cert,
250 DateTimeToSeconds(verification_time));
251 if (!result.ok()) {
252 return result;
253 }
254
255 const AuthResponse& response = auth_message.response();
256 const std::string& nonce_response = response.sender_nonce();
257
258 result = auth_context.VerifySenderNonce(nonce_response);
259 if (!result.ok()) {
260 return result;
261 }
262
263 int cert_len = i2d_X509(peer_cert, nullptr);
264 if (cert_len <= 0) {
265 return Error(Error::Code::kErrCertsParse, "Serializing cert failed.");
266 }
267 size_t nonce_response_size = nonce_response.size();
268 std::vector<uint8_t> nonce_plus_peer_cert_der(nonce_response_size + cert_len,
269 0);
270 std::copy(nonce_response.begin(), nonce_response.end(),
271 &nonce_plus_peer_cert_der[0]);
272 uint8_t* data = &nonce_plus_peer_cert_der[nonce_response_size];
273 if (!i2d_X509(peer_cert, &data)) {
274 return Error(Error::Code::kErrCertsParse, "Serializing cert failed.");
275 }
276
277 return VerifyCredentialsImpl(response, nonce_plus_peer_cert_der, crl_policy,
278 cast_trust_store, crl_trust_store,
279 verification_time, false);
280 }
281
AuthenticateChallengeReply(const CastMessage & challenge_reply,X509 * peer_cert,const AuthContext & auth_context)282 ErrorOr<CastDeviceCertPolicy> AuthenticateChallengeReply(
283 const CastMessage& challenge_reply,
284 X509* peer_cert,
285 const AuthContext& auth_context) {
286 DateTime now = {};
287 OSP_CHECK(DateTimeFromSeconds(GetWallTimeSinceUnixEpoch().count(), &now));
288 CRLPolicy policy = CRLPolicy::kCrlOptional;
289 return AuthenticateChallengeReplyImpl(
290 challenge_reply, peer_cert, auth_context, policy,
291 /* cast_trust_store */ nullptr, /* crl_trust_store */ nullptr, now);
292 }
293
AuthenticateChallengeReplyForTest(const CastMessage & challenge_reply,X509 * peer_cert,const AuthContext & auth_context,CRLPolicy crl_policy,TrustStore * cast_trust_store,TrustStore * crl_trust_store,const DateTime & verification_time)294 ErrorOr<CastDeviceCertPolicy> AuthenticateChallengeReplyForTest(
295 const CastMessage& challenge_reply,
296 X509* peer_cert,
297 const AuthContext& auth_context,
298 CRLPolicy crl_policy,
299 TrustStore* cast_trust_store,
300 TrustStore* crl_trust_store,
301 const DateTime& verification_time) {
302 return AuthenticateChallengeReplyImpl(
303 challenge_reply, peer_cert, auth_context, crl_policy, cast_trust_store,
304 crl_trust_store, verification_time);
305 }
306
307 // This function does the following
308 //
309 // * Verifies that the certificate chain |response.client_auth_certificate| +
310 // |response.intermediate_certificate| is valid and chains to a trusted Cast
311 // root. The list of trusted Cast roots can be overrided by providing a
312 // non-nullptr |cast_trust_store|. The certificate is verified at
313 // |verification_time|.
314 //
315 // * Verifies that none of the certificates in the chain are revoked based on
316 // the CRL provided in the response |response.crl|. The CRL is verified to be
317 // valid and its issuer certificate chains to a trusted Cast CRL root. The
318 // list of trusted Cast CRL roots can be overrided by providing a non-nullptr
319 // |crl_trust_store|. If |crl_policy| is kCrlOptional then the result of
320 // revocation checking is ignored. The CRL is verified at |verification_time|.
321 //
322 // * Verifies that |response.signature| matches the signature of
323 // |signature_input| by |response.client_auth_certificate|'s public key.
VerifyCredentialsImpl(const AuthResponse & response,const std::vector<uint8_t> & signature_input,const CRLPolicy & crl_policy,TrustStore * cast_trust_store,TrustStore * crl_trust_store,const DateTime & verification_time,bool enforce_sha256_checking)324 ErrorOr<CastDeviceCertPolicy> VerifyCredentialsImpl(
325 const AuthResponse& response,
326 const std::vector<uint8_t>& signature_input,
327 const CRLPolicy& crl_policy,
328 TrustStore* cast_trust_store,
329 TrustStore* crl_trust_store,
330 const DateTime& verification_time,
331 bool enforce_sha256_checking) {
332 if (response.signature().empty() && !signature_input.empty()) {
333 return Error(Error::Code::kCastV2SignatureEmpty, "Signature is empty.");
334 }
335
336 // Verify the certificate
337 std::unique_ptr<CertVerificationContext> verification_context;
338
339 // Build a single vector containing the certificate chain.
340 std::vector<std::string> cert_chain;
341 cert_chain.push_back(response.client_auth_certificate());
342 cert_chain.insert(cert_chain.end(),
343 response.intermediate_certificate().begin(),
344 response.intermediate_certificate().end());
345
346 // Parse the CRL.
347 std::unique_ptr<CastCRL> crl;
348 if (!response.crl().empty()) {
349 crl = ParseAndVerifyCRL(response.crl(), verification_time, crl_trust_store);
350 }
351
352 // Perform certificate verification.
353 CastDeviceCertPolicy device_policy;
354 Error verify_result =
355 VerifyDeviceCert(cert_chain, verification_time, &verification_context,
356 &device_policy, crl.get(), crl_policy, cast_trust_store);
357
358 // Handle and report errors.
359 Error result = MapToOpenscreenError(verify_result.code(),
360 crl_policy == CRLPolicy::kCrlRequired);
361 if (!result.ok()) {
362 return result;
363 }
364
365 // The certificate is verified at this point.
366 DigestAlgorithm digest_algorithm;
367 Error digest_result = VerifyAndMapDigestAlgorithm(
368 response.hash_algorithm(), &digest_algorithm, enforce_sha256_checking);
369 if (!digest_result.ok()) {
370 return digest_result;
371 }
372
373 ConstDataSpan signature = {
374 reinterpret_cast<const uint8_t*>(response.signature().data()),
375 static_cast<uint32_t>(response.signature().size())};
376 ConstDataSpan siginput = {signature_input.data(),
377 static_cast<uint32_t>(signature_input.size())};
378 if (!verification_context->VerifySignatureOverData(signature, siginput,
379 digest_algorithm)) {
380 return Error(Error::Code::kCastV2SignedBlobsMismatch,
381 "Failed verifying signature over data.");
382 }
383
384 return device_policy;
385 }
386
VerifyCredentials(const AuthResponse & response,const std::vector<uint8_t> & signature_input,bool enforce_revocation_checking,bool enforce_sha256_checking)387 ErrorOr<CastDeviceCertPolicy> VerifyCredentials(
388 const AuthResponse& response,
389 const std::vector<uint8_t>& signature_input,
390 bool enforce_revocation_checking,
391 bool enforce_sha256_checking) {
392 DateTime now = {};
393 OSP_CHECK(DateTimeFromSeconds(GetWallTimeSinceUnixEpoch().count(), &now));
394 CRLPolicy policy = (enforce_revocation_checking) ? CRLPolicy::kCrlRequired
395 : CRLPolicy::kCrlOptional;
396 return VerifyCredentialsImpl(response, signature_input, policy, nullptr,
397 nullptr, now, enforce_sha256_checking);
398 }
399
VerifyCredentialsForTest(const AuthResponse & response,const std::vector<uint8_t> & signature_input,CRLPolicy crl_policy,TrustStore * cast_trust_store,TrustStore * crl_trust_store,const DateTime & verification_time,bool enforce_sha256_checking)400 ErrorOr<CastDeviceCertPolicy> VerifyCredentialsForTest(
401 const AuthResponse& response,
402 const std::vector<uint8_t>& signature_input,
403 CRLPolicy crl_policy,
404 TrustStore* cast_trust_store,
405 TrustStore* crl_trust_store,
406 const DateTime& verification_time,
407 bool enforce_sha256_checking) {
408 return VerifyCredentialsImpl(response, signature_input, crl_policy,
409 cast_trust_store, crl_trust_store,
410 verification_time, enforce_sha256_checking);
411 }
412
413 } // namespace cast
414 } // namespace openscreen
415