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