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, &not_before, &not_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