• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/receiver/channel/device_auth_namespace_handler.h"
6 
7 #include <openssl/evp.h>
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "cast/common/certificate/cast_cert_validator.h"
13 #include "cast/common/channel/message_util.h"
14 #include "cast/common/channel/proto/cast_channel.pb.h"
15 #include "cast/common/channel/virtual_connection.h"
16 #include "cast/common/channel/virtual_connection_router.h"
17 #include "platform/base/tls_credentials.h"
18 #include "util/crypto/digest_sign.h"
19 
20 using ::cast::channel::AuthChallenge;
21 using ::cast::channel::AuthError;
22 using ::cast::channel::AuthResponse;
23 using ::cast::channel::CastMessage;
24 using ::cast::channel::DeviceAuthMessage;
25 using ::cast::channel::HashAlgorithm;
26 using ::cast::channel::SignatureAlgorithm;
27 
28 namespace openscreen {
29 namespace cast {
30 
31 namespace {
32 
GenerateErrorMessage(AuthError::ErrorType error_type)33 CastMessage GenerateErrorMessage(AuthError::ErrorType error_type) {
34   DeviceAuthMessage message;
35   AuthError* error = message.mutable_error();
36   error->set_error_type(error_type);
37   std::string payload;
38   message.SerializeToString(&payload);
39 
40   CastMessage response;
41   response.set_protocol_version(
42       ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0);
43   response.set_namespace_(kAuthNamespace);
44   response.set_payload_type(::cast::channel::CastMessage_PayloadType_BINARY);
45   response.set_payload_binary(std::move(payload));
46   return response;
47 }
48 
49 }  // namespace
50 
DeviceAuthNamespaceHandler(CredentialsProvider * creds_provider)51 DeviceAuthNamespaceHandler::DeviceAuthNamespaceHandler(
52     CredentialsProvider* creds_provider)
53     : creds_provider_(creds_provider) {}
54 
55 DeviceAuthNamespaceHandler::~DeviceAuthNamespaceHandler() = default;
56 
OnMessage(VirtualConnectionRouter * router,CastSocket * socket,CastMessage message)57 void DeviceAuthNamespaceHandler::OnMessage(VirtualConnectionRouter* router,
58                                            CastSocket* socket,
59                                            CastMessage message) {
60   if (!socket) {
61     return;  // Don't handle auth messages from local senders. That's nonsense.
62   }
63   if (message.payload_type() !=
64       ::cast::channel::CastMessage_PayloadType_BINARY) {
65     return;
66   }
67   const std::string& payload = message.payload_binary();
68   DeviceAuthMessage device_auth_message;
69   if (!device_auth_message.ParseFromArray(payload.data(), payload.length())) {
70     // TODO(btolsch): Consider all of these cases for future error reporting
71     // mechanism.
72     return;
73   }
74 
75   if (!device_auth_message.has_challenge()) {
76     return;
77   }
78 
79   if (device_auth_message.has_response() || device_auth_message.has_error()) {
80     return;
81   }
82 
83   const VirtualConnection virtual_conn{
84       message.destination_id(), message.source_id(), socket->socket_id()};
85   const AuthChallenge& challenge = device_auth_message.challenge();
86   const SignatureAlgorithm sig_alg = challenge.signature_algorithm();
87   HashAlgorithm hash_alg = challenge.hash_algorithm();
88   // TODO(btolsch): Reconsider supporting SHA1 after further metrics
89   // investigation.
90   if ((sig_alg != ::cast::channel::UNSPECIFIED &&
91        sig_alg != ::cast::channel::RSASSA_PKCS1v15) ||
92       (hash_alg != ::cast::channel::SHA1 &&
93        hash_alg != ::cast::channel::SHA256)) {
94     router->Send(virtual_conn, GenerateErrorMessage(
95                                    AuthError::SIGNATURE_ALGORITHM_UNAVAILABLE));
96     return;
97   }
98   const EVP_MD* digest =
99       hash_alg == ::cast::channel::SHA256 ? EVP_sha256() : EVP_sha1();
100 
101   const absl::Span<const uint8_t> tls_cert_der =
102       creds_provider_->GetCurrentTlsCertAsDer();
103   const DeviceCredentials& device_creds =
104       creds_provider_->GetCurrentDeviceCredentials();
105   if (tls_cert_der.empty() || device_creds.certs.empty() ||
106       !device_creds.private_key) {
107     // TODO(btolsch): Add this to future error reporting.
108     router->Send(virtual_conn, GenerateErrorMessage(AuthError::INTERNAL_ERROR));
109     return;
110   }
111 
112   std::unique_ptr<AuthResponse> auth_response(new AuthResponse());
113   auth_response->set_client_auth_certificate(device_creds.certs[0]);
114   for (auto it = device_creds.certs.begin() + 1; it != device_creds.certs.end();
115        ++it) {
116     auth_response->add_intermediate_certificate(*it);
117   }
118   auth_response->set_signature_algorithm(::cast::channel::RSASSA_PKCS1v15);
119   auth_response->set_hash_algorithm(hash_alg);
120   std::string sender_nonce;
121   if (challenge.has_sender_nonce()) {
122     sender_nonce = challenge.sender_nonce();
123     auth_response->set_sender_nonce(sender_nonce);
124   }
125 
126   auth_response->set_crl(device_creds.serialized_crl);
127 
128   std::vector<uint8_t> to_be_signed;
129   to_be_signed.reserve(sender_nonce.size() + tls_cert_der.size());
130   to_be_signed.insert(to_be_signed.end(), sender_nonce.begin(),
131                       sender_nonce.end());
132   to_be_signed.insert(to_be_signed.end(), tls_cert_der.begin(),
133                       tls_cert_der.end());
134 
135   ErrorOr<std::string> signature =
136       SignData(digest, device_creds.private_key.get(), to_be_signed);
137   if (!signature) {
138     router->Send(virtual_conn, GenerateErrorMessage(AuthError::INTERNAL_ERROR));
139     return;
140   }
141   auth_response->set_signature(std::move(signature.value()));
142 
143   DeviceAuthMessage response_auth_message;
144   response_auth_message.set_allocated_response(auth_response.release());
145 
146   std::string response_string;
147   response_auth_message.SerializeToString(&response_string);
148   CastMessage response;
149   response.set_protocol_version(
150       ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0);
151   response.set_namespace_(kAuthNamespace);
152   response.set_payload_type(::cast::channel::CastMessage_PayloadType_BINARY);
153   response.set_payload_binary(std::move(response_string));
154   router->Send(virtual_conn, std::move(response));
155 }
156 
157 }  // namespace cast
158 }  // namespace openscreen
159