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
;
21 const buzz::StaticQName kEkeTag
= { kChromotingXmlNamespace
,
23 const buzz::StaticQName kCertificateTag
= { kChromotingXmlNamespace
,
29 bool V2Authenticator::IsEkeMessage(const buzz::XmlElement
* message
) {
30 return message
->FirstNamed(kEkeTag
) != nullptr;
34 scoped_ptr
<Authenticator
> V2Authenticator::CreateForClient(
35 const std::string
& shared_secret
,
36 Authenticator::State initial_state
) {
37 return make_scoped_ptr(new V2Authenticator(
38 P224EncryptedKeyExchange::kPeerTypeClient
, shared_secret
, initial_state
));
42 scoped_ptr
<Authenticator
> V2Authenticator::CreateForHost(
43 const std::string
& local_cert
,
44 scoped_refptr
<RsaKeyPair
> key_pair
,
45 const std::string
& shared_secret
,
46 Authenticator::State initial_state
) {
47 scoped_ptr
<V2Authenticator
> result(new V2Authenticator(
48 P224EncryptedKeyExchange::kPeerTypeServer
, shared_secret
, initial_state
));
49 result
->local_cert_
= local_cert
;
50 result
->local_key_pair_
= key_pair
;
54 V2Authenticator::V2Authenticator(
55 crypto::P224EncryptedKeyExchange::PeerType type
,
56 const std::string
& shared_secret
,
57 Authenticator::State initial_state
)
58 : certificate_sent_(false),
59 key_exchange_impl_(type
, shared_secret
),
60 state_(initial_state
),
62 rejection_reason_(INVALID_CREDENTIALS
) {
63 pending_messages_
.push(key_exchange_impl_
.GetNextMessage());
66 V2Authenticator::~V2Authenticator() {
69 Authenticator::State
V2Authenticator::state() const {
70 if (state_
== ACCEPTED
&& !pending_messages_
.empty())
75 bool V2Authenticator::started() const {
79 Authenticator::RejectionReason
V2Authenticator::rejection_reason() const {
80 DCHECK_EQ(state(), REJECTED
);
81 return rejection_reason_
;
84 void V2Authenticator::ProcessMessage(const buzz::XmlElement
* message
,
85 const base::Closure
& resume_callback
) {
86 ProcessMessageInternal(message
);
87 resume_callback
.Run();
90 void V2Authenticator::ProcessMessageInternal(const buzz::XmlElement
* message
) {
91 DCHECK_EQ(state(), WAITING_MESSAGE
);
93 // Parse the certificate.
94 std::string base64_cert
= message
->TextNamed(kCertificateTag
);
95 if (!base64_cert
.empty()) {
96 if (!base::Base64Decode(base64_cert
, &remote_cert_
)) {
97 LOG(WARNING
) << "Failed to decode certificate received from the peer.";
102 // Client always expect certificate in the first message.
103 if (!is_host_side() && remote_cert_
.empty()) {
104 LOG(WARNING
) << "No valid host certificate.";
106 rejection_reason_
= PROTOCOL_ERROR
;
110 const buzz::XmlElement
* eke_element
= message
->FirstNamed(kEkeTag
);
112 LOG(WARNING
) << "No eke-message found.";
114 rejection_reason_
= PROTOCOL_ERROR
;
118 for (; eke_element
; eke_element
= eke_element
->NextNamed(kEkeTag
)) {
119 std::string base64_message
= eke_element
->BodyText();
120 std::string spake_message
;
121 if (base64_message
.empty() ||
122 !base::Base64Decode(base64_message
, &spake_message
)) {
123 LOG(WARNING
) << "Failed to decode auth message received from the peer.";
125 rejection_reason_
= PROTOCOL_ERROR
;
129 P224EncryptedKeyExchange::Result result
=
130 key_exchange_impl_
.ProcessMessage(spake_message
);
133 case P224EncryptedKeyExchange::kResultPending
:
134 pending_messages_
.push(key_exchange_impl_
.GetNextMessage());
137 case P224EncryptedKeyExchange::kResultFailed
:
139 rejection_reason_
= INVALID_CREDENTIALS
;
142 case P224EncryptedKeyExchange::kResultSuccess
:
143 auth_key_
= key_exchange_impl_
.GetKey();
148 state_
= MESSAGE_READY
;
151 scoped_ptr
<buzz::XmlElement
> V2Authenticator::GetNextMessage() {
152 DCHECK_EQ(state(), MESSAGE_READY
);
154 scoped_ptr
<buzz::XmlElement
> message
= CreateEmptyAuthenticatorMessage();
156 DCHECK(!pending_messages_
.empty());
157 while (!pending_messages_
.empty()) {
158 const std::string
& spake_message
= pending_messages_
.front();
159 std::string base64_message
;
160 base::Base64Encode(spake_message
, &base64_message
);
162 buzz::XmlElement
* eke_tag
= new buzz::XmlElement(kEkeTag
);
163 eke_tag
->SetBodyText(base64_message
);
164 message
->AddElement(eke_tag
);
166 pending_messages_
.pop();
169 if (!local_cert_
.empty() && !certificate_sent_
) {
170 buzz::XmlElement
* certificate_tag
= new buzz::XmlElement(kCertificateTag
);
171 std::string base64_cert
;
172 base::Base64Encode(local_cert_
, &base64_cert
);
173 certificate_tag
->SetBodyText(base64_cert
);
174 message
->AddElement(certificate_tag
);
175 certificate_sent_
= true;
178 if (state_
!= ACCEPTED
) {
179 state_
= WAITING_MESSAGE
;
181 return message
.Pass();
184 scoped_ptr
<ChannelAuthenticator
>
185 V2Authenticator::CreateChannelAuthenticator() const {
186 DCHECK_EQ(state(), ACCEPTED
);
187 CHECK(!auth_key_
.empty());
189 if (is_host_side()) {
190 return SslHmacChannelAuthenticator::CreateForHost(
191 local_cert_
, local_key_pair_
, auth_key_
);
193 return SslHmacChannelAuthenticator::CreateForClient(
194 remote_cert_
, auth_key_
);
198 bool V2Authenticator::is_host_side() const {
199 return local_key_pair_
.get() != nullptr;
202 } // namespace protocol
203 } // namespace remoting