159 lines
5.6 KiB
C++
159 lines
5.6 KiB
C++
// 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 <openssl/evp.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#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<const uint8_t> 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<AuthResponse> 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<uint8_t> 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<std::string> 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
|