// Copyright (c) 2012 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 "remoting/protocol/v2_authenticator.h" #include #include "base/base64.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "remoting/base/constants.h" #include "remoting/base/rsa_key_pair.h" #include "remoting/protocol/ssl_hmac_channel_authenticator.h" #include "third_party/libjingle_xmpp/xmllite/xmlelement.h" using crypto::P224EncryptedKeyExchange; namespace remoting { namespace protocol { namespace { const jingle_xmpp::StaticQName kEkeTag = { kChromotingXmlNamespace, "eke-message" }; const jingle_xmpp::StaticQName kCertificateTag = { kChromotingXmlNamespace, "certificate" }; } // namespace // static bool V2Authenticator::IsEkeMessage(const jingle_xmpp::XmlElement* message) { return message->FirstNamed(kEkeTag) != nullptr; } // static std::unique_ptr V2Authenticator::CreateForClient( const std::string& shared_secret, Authenticator::State initial_state) { return base::WrapUnique(new V2Authenticator( P224EncryptedKeyExchange::kPeerTypeClient, shared_secret, initial_state)); } // static std::unique_ptr V2Authenticator::CreateForHost( const std::string& local_cert, scoped_refptr key_pair, const std::string& shared_secret, Authenticator::State initial_state) { std::unique_ptr result(new V2Authenticator( P224EncryptedKeyExchange::kPeerTypeServer, shared_secret, initial_state)); result->local_cert_ = local_cert; result->local_key_pair_ = key_pair; return std::move(result); } V2Authenticator::V2Authenticator( crypto::P224EncryptedKeyExchange::PeerType type, const std::string& shared_secret, Authenticator::State initial_state) : certificate_sent_(false), key_exchange_impl_(type, shared_secret), state_(initial_state), started_(false), rejection_reason_(RejectionReason::INVALID_CREDENTIALS) { pending_messages_.push(key_exchange_impl_.GetNextMessage()); } V2Authenticator::~V2Authenticator() = default; Authenticator::State V2Authenticator::state() const { if (state_ == ACCEPTED && !pending_messages_.empty()) return MESSAGE_READY; return state_; } bool V2Authenticator::started() const { return started_; } Authenticator::RejectionReason V2Authenticator::rejection_reason() const { DCHECK_EQ(state(), REJECTED); return rejection_reason_; } void V2Authenticator::ProcessMessage(const jingle_xmpp::XmlElement* message, base::OnceClosure resume_callback) { ProcessMessageInternal(message); std::move(resume_callback).Run(); } void V2Authenticator::ProcessMessageInternal(const jingle_xmpp::XmlElement* message) { DCHECK_EQ(state(), WAITING_MESSAGE); // Parse the certificate. std::string base64_cert = message->TextNamed(kCertificateTag); if (!base64_cert.empty()) { if (!base::Base64Decode(base64_cert, &remote_cert_)) { LOG(WARNING) << "Failed to decode certificate received from the peer."; remote_cert_.clear(); } } // Client always expect certificate in the first message. if (!is_host_side() && remote_cert_.empty()) { LOG(WARNING) << "No valid host certificate."; state_ = REJECTED; rejection_reason_ = RejectionReason::PROTOCOL_ERROR; return; } const jingle_xmpp::XmlElement* eke_element = message->FirstNamed(kEkeTag); if (!eke_element) { LOG(WARNING) << "No eke-message found."; state_ = REJECTED; rejection_reason_ = RejectionReason::PROTOCOL_ERROR; return; } for (; eke_element; eke_element = eke_element->NextNamed(kEkeTag)) { std::string base64_message = eke_element->BodyText(); std::string spake_message; if (base64_message.empty() || !base::Base64Decode(base64_message, &spake_message)) { LOG(WARNING) << "Failed to decode auth message received from the peer."; state_ = REJECTED; rejection_reason_ = RejectionReason::PROTOCOL_ERROR; return; } P224EncryptedKeyExchange::Result result = key_exchange_impl_.ProcessMessage(spake_message); started_ = true; switch (result) { case P224EncryptedKeyExchange::kResultPending: pending_messages_.push(key_exchange_impl_.GetNextMessage()); break; case P224EncryptedKeyExchange::kResultFailed: state_ = REJECTED; rejection_reason_ = RejectionReason::INVALID_CREDENTIALS; return; case P224EncryptedKeyExchange::kResultSuccess: auth_key_ = key_exchange_impl_.GetKey(); state_ = ACCEPTED; return; } } state_ = MESSAGE_READY; } std::unique_ptr V2Authenticator::GetNextMessage() { DCHECK_EQ(state(), MESSAGE_READY); std::unique_ptr message = CreateEmptyAuthenticatorMessage(); DCHECK(!pending_messages_.empty()); while (!pending_messages_.empty()) { const std::string& spake_message = pending_messages_.front(); std::string base64_message; base::Base64Encode(spake_message, &base64_message); jingle_xmpp::XmlElement* eke_tag = new jingle_xmpp::XmlElement(kEkeTag); eke_tag->SetBodyText(base64_message); message->AddElement(eke_tag); pending_messages_.pop(); } if (!local_cert_.empty() && !certificate_sent_) { jingle_xmpp::XmlElement* certificate_tag = new jingle_xmpp::XmlElement(kCertificateTag); std::string base64_cert; base::Base64Encode(local_cert_, &base64_cert); certificate_tag->SetBodyText(base64_cert); message->AddElement(certificate_tag); certificate_sent_ = true; } if (state_ != ACCEPTED) { state_ = WAITING_MESSAGE; } return message; } const std::string& V2Authenticator::GetAuthKey() const { return auth_key_; } std::unique_ptr V2Authenticator::CreateChannelAuthenticator() const { DCHECK_EQ(state(), ACCEPTED); CHECK(!auth_key_.empty()); if (is_host_side()) { return SslHmacChannelAuthenticator::CreateForHost( local_cert_, local_key_pair_, auth_key_); } else { return SslHmacChannelAuthenticator::CreateForClient( remote_cert_, auth_key_); } } bool V2Authenticator::is_host_side() const { return local_key_pair_.get() != nullptr; } } // namespace protocol } // namespace remoting