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.
5 #include "remoting/protocol/v2_authenticator.h"
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/webrtc/libjingle/xmllite/xmlelement.h"
14 using crypto::P224EncryptedKeyExchange
;
16 #if defined(_WIN32) && defined(GetMessage)
25 const buzz::StaticQName kEkeTag
= { kChromotingXmlNamespace
,
27 const buzz::StaticQName kCertificateTag
= { kChromotingXmlNamespace
,
33 bool V2Authenticator::IsEkeMessage(const buzz::XmlElement
* message
) {
34 return message
->FirstNamed(kEkeTag
) != NULL
;
38 scoped_ptr
<Authenticator
> V2Authenticator::CreateForClient(
39 const std::string
& shared_secret
,
40 Authenticator::State initial_state
) {
41 return make_scoped_ptr(new V2Authenticator(
42 P224EncryptedKeyExchange::kPeerTypeClient
, shared_secret
, initial_state
));
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
;
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
),
66 rejection_reason_(INVALID_CREDENTIALS
) {
67 pending_messages_
.push(key_exchange_impl_
.GetMessage());
70 V2Authenticator::~V2Authenticator() {
73 Authenticator::State
V2Authenticator::state() const {
74 if (state_
== ACCEPTED
&& !pending_messages_
.empty())
79 bool V2Authenticator::started() const {
83 Authenticator::RejectionReason
V2Authenticator::rejection_reason() const {
84 DCHECK_EQ(state(), REJECTED
);
85 return rejection_reason_
;
88 void V2Authenticator::ProcessMessage(const buzz::XmlElement
* message
,
89 const base::Closure
& resume_callback
) {
90 ProcessMessageInternal(message
);
91 resume_callback
.Run();
94 void V2Authenticator::ProcessMessageInternal(const buzz::XmlElement
* message
) {
95 DCHECK_EQ(state(), WAITING_MESSAGE
);
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();
106 // Client always expect certificate in the first message.
107 if (!is_host_side() && remote_cert_
.empty()) {
108 LOG(WARNING
) << "No valid host certificate.";
110 rejection_reason_
= PROTOCOL_ERROR
;
114 const buzz::XmlElement
* eke_element
= message
->FirstNamed(kEkeTag
);
116 LOG(WARNING
) << "No eke-message found.";
118 rejection_reason_
= PROTOCOL_ERROR
;
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.";
129 rejection_reason_
= PROTOCOL_ERROR
;
133 P224EncryptedKeyExchange::Result result
=
134 key_exchange_impl_
.ProcessMessage(spake_message
);
137 case P224EncryptedKeyExchange::kResultPending
:
138 pending_messages_
.push(key_exchange_impl_
.GetMessage());
141 case P224EncryptedKeyExchange::kResultFailed
:
143 rejection_reason_
= INVALID_CREDENTIALS
;
146 case P224EncryptedKeyExchange::kResultSuccess
:
147 auth_key_
= key_exchange_impl_
.GetKey();
152 state_
= MESSAGE_READY
;
155 scoped_ptr
<buzz::XmlElement
> V2Authenticator::GetNextMessage() {
156 DCHECK_EQ(state(), MESSAGE_READY
);
158 scoped_ptr
<buzz::XmlElement
> message
= CreateEmptyAuthenticatorMessage();
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
);
166 buzz::XmlElement
* eke_tag
= new buzz::XmlElement(kEkeTag
);
167 eke_tag
->SetBodyText(base64_message
);
168 message
->AddElement(eke_tag
);
170 pending_messages_
.pop();
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;
182 if (state_
!= ACCEPTED
) {
183 state_
= WAITING_MESSAGE
;
185 return message
.Pass();
188 scoped_ptr
<ChannelAuthenticator
>
189 V2Authenticator::CreateChannelAuthenticator() const {
190 DCHECK_EQ(state(), ACCEPTED
);
191 CHECK(!auth_key_
.empty());
193 if (is_host_side()) {
194 return SslHmacChannelAuthenticator::CreateForHost(
195 local_cert_
, local_key_pair_
, auth_key_
);
197 return SslHmacChannelAuthenticator::CreateForClient(
198 remote_cert_
, auth_key_
);
202 bool V2Authenticator::is_host_side() const {
203 return local_key_pair_
.get() != NULL
;
206 } // namespace protocol
207 } // namespace remoting