// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cast/receiver/channel/device_auth_namespace_handler.h" #include #include #include #include "cast/common/certificate/cast_cert_validator.h" #include "cast/common/channel/message_util.h" #include "cast/common/channel/proto/cast_channel.pb.h" #include "cast/common/channel/virtual_connection.h" #include "cast/common/channel/virtual_connection_router.h" #include "platform/base/tls_credentials.h" #include "util/crypto/digest_sign.h" using ::cast::channel::AuthChallenge; using ::cast::channel::AuthError; using ::cast::channel::AuthResponse; using ::cast::channel::CastMessage; using ::cast::channel::DeviceAuthMessage; using ::cast::channel::HashAlgorithm; using ::cast::channel::SignatureAlgorithm; namespace openscreen { namespace cast { namespace { CastMessage GenerateErrorMessage(AuthError::ErrorType error_type) { DeviceAuthMessage message; AuthError* error = message.mutable_error(); error->set_error_type(error_type); std::string payload; message.SerializeToString(&payload); CastMessage response; response.set_protocol_version( ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0); response.set_namespace_(kAuthNamespace); response.set_payload_type(::cast::channel::CastMessage_PayloadType_BINARY); response.set_payload_binary(std::move(payload)); return response; } } // namespace DeviceAuthNamespaceHandler::DeviceAuthNamespaceHandler( CredentialsProvider* creds_provider) : creds_provider_(creds_provider) {} DeviceAuthNamespaceHandler::~DeviceAuthNamespaceHandler() = default; void DeviceAuthNamespaceHandler::OnMessage(VirtualConnectionRouter* router, CastSocket* socket, CastMessage message) { if (!socket) { return; // Don't handle auth messages from local senders. That's nonsense. } if (message.payload_type() != ::cast::channel::CastMessage_PayloadType_BINARY) { return; } const std::string& payload = message.payload_binary(); DeviceAuthMessage device_auth_message; if (!device_auth_message.ParseFromArray(payload.data(), payload.length())) { // TODO(btolsch): Consider all of these cases for future error reporting // mechanism. return; } if (!device_auth_message.has_challenge()) { return; } if (device_auth_message.has_response() || device_auth_message.has_error()) { return; } const VirtualConnection virtual_conn{ message.destination_id(), message.source_id(), socket->socket_id()}; const AuthChallenge& challenge = device_auth_message.challenge(); const SignatureAlgorithm sig_alg = challenge.signature_algorithm(); HashAlgorithm hash_alg = challenge.hash_algorithm(); // TODO(btolsch): Reconsider supporting SHA1 after further metrics // investigation. if ((sig_alg != ::cast::channel::UNSPECIFIED && sig_alg != ::cast::channel::RSASSA_PKCS1v15) || (hash_alg != ::cast::channel::SHA1 && hash_alg != ::cast::channel::SHA256)) { router->Send(virtual_conn, GenerateErrorMessage( AuthError::SIGNATURE_ALGORITHM_UNAVAILABLE)); return; } const EVP_MD* digest = hash_alg == ::cast::channel::SHA256 ? EVP_sha256() : EVP_sha1(); const absl::Span tls_cert_der = creds_provider_->GetCurrentTlsCertAsDer(); const DeviceCredentials& device_creds = creds_provider_->GetCurrentDeviceCredentials(); if (tls_cert_der.empty() || device_creds.certs.empty() || !device_creds.private_key) { // TODO(btolsch): Add this to future error reporting. router->Send(virtual_conn, GenerateErrorMessage(AuthError::INTERNAL_ERROR)); return; } std::unique_ptr auth_response(new AuthResponse()); auth_response->set_client_auth_certificate(device_creds.certs[0]); for (auto it = device_creds.certs.begin() + 1; it != device_creds.certs.end(); ++it) { auth_response->add_intermediate_certificate(*it); } auth_response->set_signature_algorithm(::cast::channel::RSASSA_PKCS1v15); auth_response->set_hash_algorithm(hash_alg); std::string sender_nonce; if (challenge.has_sender_nonce()) { sender_nonce = challenge.sender_nonce(); auth_response->set_sender_nonce(sender_nonce); } auth_response->set_crl(device_creds.serialized_crl); std::vector to_be_signed; to_be_signed.reserve(sender_nonce.size() + tls_cert_der.size()); to_be_signed.insert(to_be_signed.end(), sender_nonce.begin(), sender_nonce.end()); to_be_signed.insert(to_be_signed.end(), tls_cert_der.begin(), tls_cert_der.end()); ErrorOr signature = SignData(digest, device_creds.private_key.get(), to_be_signed); if (!signature) { router->Send(virtual_conn, GenerateErrorMessage(AuthError::INTERNAL_ERROR)); return; } auth_response->set_signature(std::move(signature.value())); DeviceAuthMessage response_auth_message; response_auth_message.set_allocated_response(auth_response.release()); std::string response_string; response_auth_message.SerializeToString(&response_string); CastMessage response; response.set_protocol_version( ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0); response.set_namespace_(kAuthNamespace); response.set_payload_type(::cast::channel::CastMessage_PayloadType_BINARY); response.set_payload_binary(std::move(response_string)); router->Send(virtual_conn, std::move(response)); } } // namespace cast } // namespace openscreen