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
14using crypto::P224EncryptedKeyExchange;
15
16#if defined(_WIN32) && defined(GetMessage)
17#undef GetMessage
18#endif
19
20namespace remoting {
21namespace protocol {
22
23namespace {
24
25const buzz::StaticQName kEkeTag = { kChromotingXmlNamespace,
26                                    "eke-message" };
27const buzz::StaticQName kCertificateTag = { kChromotingXmlNamespace,
28                                            "certificate" };
29
30}  // namespace
31
32// static
33bool V2Authenticator::IsEkeMessage(const buzz::XmlElement* message) {
34  return message->FirstNamed(kEkeTag) != NULL;
35}
36
37// static
38scoped_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
46scoped_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
58V2Authenticator::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      rejection_reason_(INVALID_CREDENTIALS) {
66  pending_messages_.push(key_exchange_impl_.GetMessage());
67}
68
69V2Authenticator::~V2Authenticator() {
70}
71
72Authenticator::State V2Authenticator::state() const {
73  if (state_ == ACCEPTED && !pending_messages_.empty())
74    return MESSAGE_READY;
75  return state_;
76}
77
78Authenticator::RejectionReason V2Authenticator::rejection_reason() const {
79  DCHECK_EQ(state(), REJECTED);
80  return rejection_reason_;
81}
82
83void V2Authenticator::ProcessMessage(const buzz::XmlElement* message,
84                                     const base::Closure& resume_callback) {
85  ProcessMessageInternal(message);
86  resume_callback.Run();
87}
88
89void V2Authenticator::ProcessMessageInternal(const buzz::XmlElement* message) {
90  DCHECK_EQ(state(), WAITING_MESSAGE);
91
92  // Parse the certificate.
93  std::string base64_cert = message->TextNamed(kCertificateTag);
94  if (!base64_cert.empty()) {
95    if (!base::Base64Decode(base64_cert, &remote_cert_)) {
96      LOG(WARNING) << "Failed to decode certificate received from the peer.";
97      remote_cert_.clear();
98    }
99  }
100
101  // Client always expect certificate in the first message.
102  if (!is_host_side() && remote_cert_.empty()) {
103    LOG(WARNING) << "No valid host certificate.";
104    state_ = REJECTED;
105    rejection_reason_ = PROTOCOL_ERROR;
106    return;
107  }
108
109  const buzz::XmlElement* eke_element = message->FirstNamed(kEkeTag);
110  if (!eke_element) {
111    LOG(WARNING) << "No eke-message found.";
112    state_ = REJECTED;
113    rejection_reason_ = PROTOCOL_ERROR;
114    return;
115  }
116
117  for (; eke_element; eke_element = eke_element->NextNamed(kEkeTag)) {
118    std::string base64_message = eke_element->BodyText();
119    std::string spake_message;
120    if (base64_message.empty() ||
121        !base::Base64Decode(base64_message, &spake_message)) {
122      LOG(WARNING) << "Failed to decode auth message received from the peer.";
123      state_ = REJECTED;
124      rejection_reason_ = PROTOCOL_ERROR;
125      return;
126    }
127
128    P224EncryptedKeyExchange::Result result =
129        key_exchange_impl_.ProcessMessage(spake_message);
130    switch (result) {
131      case P224EncryptedKeyExchange::kResultPending:
132        pending_messages_.push(key_exchange_impl_.GetMessage());
133        break;
134
135      case P224EncryptedKeyExchange::kResultFailed:
136        state_ = REJECTED;
137        rejection_reason_ = INVALID_CREDENTIALS;
138        return;
139
140      case P224EncryptedKeyExchange::kResultSuccess:
141        auth_key_ = key_exchange_impl_.GetKey();
142        state_ = ACCEPTED;
143        return;
144    }
145  }
146
147  state_ = MESSAGE_READY;
148}
149
150scoped_ptr<buzz::XmlElement> V2Authenticator::GetNextMessage() {
151  DCHECK_EQ(state(), MESSAGE_READY);
152
153  scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage();
154
155  DCHECK(!pending_messages_.empty());
156  while (!pending_messages_.empty()) {
157    const std::string& spake_message = pending_messages_.front();
158    std::string base64_message;
159    if (!base::Base64Encode(spake_message, &base64_message)) {
160      LOG(DFATAL) << "Cannot perform base64 encode on certificate";
161      continue;
162    }
163
164    buzz::XmlElement* eke_tag = new buzz::XmlElement(kEkeTag);
165    eke_tag->SetBodyText(base64_message);
166    message->AddElement(eke_tag);
167
168    pending_messages_.pop();
169  }
170
171  if (!local_cert_.empty() && !certificate_sent_) {
172    buzz::XmlElement* certificate_tag = new buzz::XmlElement(kCertificateTag);
173    std::string base64_cert;
174    if (!base::Base64Encode(local_cert_, &base64_cert)) {
175      LOG(DFATAL) << "Cannot perform base64 encode on certificate";
176    }
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
188scoped_ptr<ChannelAuthenticator>
189V2Authenticator::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
204bool V2Authenticator::is_host_side() const {
205  return local_key_pair_.get() != NULL;
206}
207
208}  // namespace protocol
209}  // namespace remoting
210