Home | History | Annotate | Download | only in protocol
      1 // Copyright (c) 2012 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 "remoting/protocol/v2_authenticator.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/logging.h"
      9 #include "remoting/base/constants.h"
     10 #include "remoting/base/rsa_key_pair.h"
     11 #include "remoting/protocol/ssl_hmac_channel_authenticator.h"
     12 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
     13 
     14 using crypto::P224EncryptedKeyExchange;
     15 
     16 #if defined(_WIN32) && defined(GetMessage)
     17 #undef GetMessage
     18 #endif
     19 
     20 namespace remoting {
     21 namespace protocol {
     22 
     23 namespace {
     24 
     25 const buzz::StaticQName kEkeTag = { kChromotingXmlNamespace,
     26                                     "eke-message" };
     27 const buzz::StaticQName kCertificateTag = { kChromotingXmlNamespace,
     28                                             "certificate" };
     29 
     30 }  // namespace
     31 
     32 // static
     33 bool V2Authenticator::IsEkeMessage(const buzz::XmlElement* message) {
     34   return message->FirstNamed(kEkeTag) != NULL;
     35 }
     36 
     37 // static
     38 scoped_ptr<Authenticator> V2Authenticator::CreateForClient(
     39     const std::string& shared_secret,
     40     Authenticator::State initial_state) {
     41   return scoped_ptr<Authenticator>(new V2Authenticator(
     42       P224EncryptedKeyExchange::kPeerTypeClient, shared_secret, initial_state));
     43 }
     44 
     45 // static
     46 scoped_ptr<Authenticator> V2Authenticator::CreateForHost(
     47     const std::string& local_cert,
     48     scoped_refptr<RsaKeyPair> key_pair,
     49     const std::string& shared_secret,
     50     Authenticator::State initial_state) {
     51   scoped_ptr<V2Authenticator> result(new V2Authenticator(
     52       P224EncryptedKeyExchange::kPeerTypeServer, shared_secret, initial_state));
     53   result->local_cert_ = local_cert;
     54   result->local_key_pair_ = key_pair;
     55   return scoped_ptr<Authenticator>(result.Pass());
     56 }
     57 
     58 V2Authenticator::V2Authenticator(
     59     crypto::P224EncryptedKeyExchange::PeerType type,
     60     const std::string& shared_secret,
     61     Authenticator::State initial_state)
     62     : certificate_sent_(false),
     63       key_exchange_impl_(type, shared_secret),
     64       state_(initial_state),
     65       started_(false),
     66       rejection_reason_(INVALID_CREDENTIALS) {
     67   pending_messages_.push(key_exchange_impl_.GetMessage());
     68 }
     69 
     70 V2Authenticator::~V2Authenticator() {
     71 }
     72 
     73 Authenticator::State V2Authenticator::state() const {
     74   if (state_ == ACCEPTED && !pending_messages_.empty())
     75     return MESSAGE_READY;
     76   return state_;
     77 }
     78 
     79 bool V2Authenticator::started() const {
     80   return started_;
     81 }
     82 
     83 Authenticator::RejectionReason V2Authenticator::rejection_reason() const {
     84   DCHECK_EQ(state(), REJECTED);
     85   return rejection_reason_;
     86 }
     87 
     88 void V2Authenticator::ProcessMessage(const buzz::XmlElement* message,
     89                                      const base::Closure& resume_callback) {
     90   ProcessMessageInternal(message);
     91   resume_callback.Run();
     92 }
     93 
     94 void V2Authenticator::ProcessMessageInternal(const buzz::XmlElement* message) {
     95   DCHECK_EQ(state(), WAITING_MESSAGE);
     96 
     97   // Parse the certificate.
     98   std::string base64_cert = message->TextNamed(kCertificateTag);
     99   if (!base64_cert.empty()) {
    100     if (!base::Base64Decode(base64_cert, &remote_cert_)) {
    101       LOG(WARNING) << "Failed to decode certificate received from the peer.";
    102       remote_cert_.clear();
    103     }
    104   }
    105 
    106   // Client always expect certificate in the first message.
    107   if (!is_host_side() && remote_cert_.empty()) {
    108     LOG(WARNING) << "No valid host certificate.";
    109     state_ = REJECTED;
    110     rejection_reason_ = PROTOCOL_ERROR;
    111     return;
    112   }
    113 
    114   const buzz::XmlElement* eke_element = message->FirstNamed(kEkeTag);
    115   if (!eke_element) {
    116     LOG(WARNING) << "No eke-message found.";
    117     state_ = REJECTED;
    118     rejection_reason_ = PROTOCOL_ERROR;
    119     return;
    120   }
    121 
    122   for (; eke_element; eke_element = eke_element->NextNamed(kEkeTag)) {
    123     std::string base64_message = eke_element->BodyText();
    124     std::string spake_message;
    125     if (base64_message.empty() ||
    126         !base::Base64Decode(base64_message, &spake_message)) {
    127       LOG(WARNING) << "Failed to decode auth message received from the peer.";
    128       state_ = REJECTED;
    129       rejection_reason_ = PROTOCOL_ERROR;
    130       return;
    131     }
    132 
    133     P224EncryptedKeyExchange::Result result =
    134         key_exchange_impl_.ProcessMessage(spake_message);
    135     started_ = true;
    136     switch (result) {
    137       case P224EncryptedKeyExchange::kResultPending:
    138         pending_messages_.push(key_exchange_impl_.GetMessage());
    139         break;
    140 
    141       case P224EncryptedKeyExchange::kResultFailed:
    142         state_ = REJECTED;
    143         rejection_reason_ = INVALID_CREDENTIALS;
    144         return;
    145 
    146       case P224EncryptedKeyExchange::kResultSuccess:
    147         auth_key_ = key_exchange_impl_.GetKey();
    148         state_ = ACCEPTED;
    149         return;
    150     }
    151   }
    152   state_ = MESSAGE_READY;
    153 }
    154 
    155 scoped_ptr<buzz::XmlElement> V2Authenticator::GetNextMessage() {
    156   DCHECK_EQ(state(), MESSAGE_READY);
    157 
    158   scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage();
    159 
    160   DCHECK(!pending_messages_.empty());
    161   while (!pending_messages_.empty()) {
    162     const std::string& spake_message = pending_messages_.front();
    163     std::string base64_message;
    164     base::Base64Encode(spake_message, &base64_message);
    165 
    166     buzz::XmlElement* eke_tag = new buzz::XmlElement(kEkeTag);
    167     eke_tag->SetBodyText(base64_message);
    168     message->AddElement(eke_tag);
    169 
    170     pending_messages_.pop();
    171   }
    172 
    173   if (!local_cert_.empty() && !certificate_sent_) {
    174     buzz::XmlElement* certificate_tag = new buzz::XmlElement(kCertificateTag);
    175     std::string base64_cert;
    176     base::Base64Encode(local_cert_, &base64_cert);
    177     certificate_tag->SetBodyText(base64_cert);
    178     message->AddElement(certificate_tag);
    179     certificate_sent_ = true;
    180   }
    181 
    182   if (state_ != ACCEPTED) {
    183     state_ = WAITING_MESSAGE;
    184   }
    185   return message.Pass();
    186 }
    187 
    188 scoped_ptr<ChannelAuthenticator>
    189 V2Authenticator::CreateChannelAuthenticator() const {
    190   DCHECK_EQ(state(), ACCEPTED);
    191   CHECK(!auth_key_.empty());
    192 
    193   if (is_host_side()) {
    194     return scoped_ptr<ChannelAuthenticator>(
    195         SslHmacChannelAuthenticator::CreateForHost(
    196             local_cert_, local_key_pair_, auth_key_).Pass());
    197   } else {
    198     return scoped_ptr<ChannelAuthenticator>(
    199         SslHmacChannelAuthenticator::CreateForClient(
    200             remote_cert_, auth_key_).Pass());
    201   }
    202 }
    203 
    204 bool V2Authenticator::is_host_side() const {
    205   return local_key_pair_.get() != NULL;
    206 }
    207 
    208 }  // namespace protocol
    209 }  // namespace remoting
    210