// 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 "base/base64.h" #include "base/logging.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/source/talk/xmllite/xmlelement.h" using crypto::P224EncryptedKeyExchange; #if defined(_WIN32) && defined(GetMessage) #undef GetMessage #endif namespace remoting { namespace protocol { namespace { const buzz::StaticQName kEkeTag = { kChromotingXmlNamespace, "eke-message" }; const buzz::StaticQName kCertificateTag = { kChromotingXmlNamespace, "certificate" }; } // namespace // static bool V2Authenticator::IsEkeMessage(const buzz::XmlElement* message) { return message->FirstNamed(kEkeTag) != NULL; } // static scoped_ptr<Authenticator> V2Authenticator::CreateForClient( const std::string& shared_secret, Authenticator::State initial_state) { return scoped_ptr<Authenticator>(new V2Authenticator( P224EncryptedKeyExchange::kPeerTypeClient, shared_secret, initial_state)); } // static scoped_ptr<Authenticator> V2Authenticator::CreateForHost( const std::string& local_cert, scoped_refptr<RsaKeyPair> key_pair, const std::string& shared_secret, Authenticator::State initial_state) { scoped_ptr<V2Authenticator> result(new V2Authenticator( P224EncryptedKeyExchange::kPeerTypeServer, shared_secret, initial_state)); result->local_cert_ = local_cert; result->local_key_pair_ = key_pair; return scoped_ptr<Authenticator>(result.Pass()); } 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), rejection_reason_(INVALID_CREDENTIALS) { pending_messages_.push(key_exchange_impl_.GetMessage()); } V2Authenticator::~V2Authenticator() { } Authenticator::State V2Authenticator::state() const { if (state_ == ACCEPTED && !pending_messages_.empty()) return MESSAGE_READY; return state_; } Authenticator::RejectionReason V2Authenticator::rejection_reason() const { DCHECK_EQ(state(), REJECTED); return rejection_reason_; } void V2Authenticator::ProcessMessage(const buzz::XmlElement* message, const base::Closure& resume_callback) { ProcessMessageInternal(message); resume_callback.Run(); } void V2Authenticator::ProcessMessageInternal(const buzz::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_ = PROTOCOL_ERROR; return; } const buzz::XmlElement* eke_element = message->FirstNamed(kEkeTag); if (!eke_element) { LOG(WARNING) << "No eke-message found."; state_ = REJECTED; rejection_reason_ = 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_ = PROTOCOL_ERROR; return; } P224EncryptedKeyExchange::Result result = key_exchange_impl_.ProcessMessage(spake_message); switch (result) { case P224EncryptedKeyExchange::kResultPending: pending_messages_.push(key_exchange_impl_.GetMessage()); break; case P224EncryptedKeyExchange::kResultFailed: state_ = REJECTED; rejection_reason_ = INVALID_CREDENTIALS; return; case P224EncryptedKeyExchange::kResultSuccess: auth_key_ = key_exchange_impl_.GetKey(); state_ = ACCEPTED; return; } } state_ = MESSAGE_READY; } scoped_ptr<buzz::XmlElement> V2Authenticator::GetNextMessage() { DCHECK_EQ(state(), MESSAGE_READY); scoped_ptr<buzz::XmlElement> 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); buzz::XmlElement* eke_tag = new buzz::XmlElement(kEkeTag); eke_tag->SetBodyText(base64_message); message->AddElement(eke_tag); pending_messages_.pop(); } if (!local_cert_.empty() && !certificate_sent_) { buzz::XmlElement* certificate_tag = new buzz::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.Pass(); } scoped_ptr<ChannelAuthenticator> V2Authenticator::CreateChannelAuthenticator() const { DCHECK_EQ(state(), ACCEPTED); CHECK(!auth_key_.empty()); if (is_host_side()) { return scoped_ptr<ChannelAuthenticator>( SslHmacChannelAuthenticator::CreateForHost( local_cert_, local_key_pair_, auth_key_).Pass()); } else { return scoped_ptr<ChannelAuthenticator>( SslHmacChannelAuthenticator::CreateForClient( remote_cert_, auth_key_).Pass()); } } bool V2Authenticator::is_host_side() const { return local_key_pair_.get() != NULL; } } // namespace protocol } // namespace remoting