Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(503)

Side by Side Diff: remoting/protocol/spake2_authenticator.cc

Issue 1778223003: Revert of Implement authenticator based on SPAKE2 implementation in boringssl. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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/spake2_authenticator.h"
6
7 #include <utility>
8
9 #include "base/base64.h"
10 #include "base/logging.h"
11 #include "base/sys_byteorder.h"
12 #include "crypto/hmac.h"
13 #include "crypto/secure_util.h"
14 #include "remoting/base/constants.h"
15 #include "remoting/base/rsa_key_pair.h"
16 #include "remoting/protocol/ssl_hmac_channel_authenticator.h"
17 #include "third_party/boringssl/src/include/openssl/curve25519.h"
18 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
19
20 namespace remoting {
21 namespace protocol {
22
23 namespace {
24
25 // Each peer sends 2 messages: <spake-message> and <verification-hash>. The
26 // content of <spake-message> is the output of SPAKE2_generate_msg() and must
27 // be passed to SPAKE2_process_msg() on the other end. This is enough to
28 // generate authentication key. <verification-hash> is sent to confirm that both
29 // ends get the same authentication key (which means they both know the
30 // password). This verification hash is calculated in
31 // CalculateVerificationHash() as follows:
32 // HMAC_SHA256(auth_key, ("host"|"client") + local_jid.length() + local_jid +
33 // remote_jid.length() + remote_jid)
34 // where auth_key is the key produced by SPAKE2.
35
36 const buzz::StaticQName kSpakeMessageTag = {kChromotingXmlNamespace,
37 "spake-message"};
38 const buzz::StaticQName kVerificationHashTag = {kChromotingXmlNamespace,
39 "verification-hash"};
40 const buzz::StaticQName kCertificateTag = {kChromotingXmlNamespace,
41 "certificate"};
42
43 scoped_ptr<buzz::XmlElement> EncodeBinaryValueToXml(
44 const buzz::StaticQName& qname,
45 const std::string& content) {
46 std::string content_base64;
47 base::Base64Encode(content, &content_base64);
48
49 scoped_ptr<buzz::XmlElement> result(new buzz::XmlElement(qname));
50 result->SetBodyText(content_base64);
51 return result;
52 }
53
54 // Finds tag named |qname| in base_message and decodes it from base64 and stores
55 // in |data|. If the element is not present then found is set to false otherwise
56 // it's set to true. If the element is there and it's content cound't be decoded
57 // then false is returned.
58 bool DecodeBinaryValueFromXml(const buzz::XmlElement* message,
59 const buzz::QName& qname,
60 bool* found,
61 std::string* data) {
62 const buzz::XmlElement* element = message->FirstNamed(qname);
63 *found = element != nullptr;
64 if (!*found)
65 return true;
66
67 if (!base::Base64Decode(element->BodyText(), data)) {
68 LOG(WARNING) << "Failed to parse " << qname.LocalPart();
69 return false;
70 }
71
72 return !data->empty();
73 }
74
75 std::string PrefixWithLength(const std::string& str) {
76 uint32_t length = base::HostToNet32(str.size());
77 return std::string(reinterpret_cast<char*>(&length), sizeof(length)) + str;
78 }
79
80 } // namespace
81
82 // static
83 scoped_ptr<Authenticator> Spake2Authenticator::CreateForClient(
84 const std::string& local_id,
85 const std::string& remote_id,
86 const std::string& shared_secret,
87 Authenticator::State initial_state) {
88 return make_scoped_ptr(new Spake2Authenticator(
89 local_id, remote_id, shared_secret, false, initial_state));
90 }
91
92 // static
93 scoped_ptr<Authenticator> Spake2Authenticator::CreateForHost(
94 const std::string& local_id,
95 const std::string& remote_id,
96 const std::string& shared_secret,
97 const std::string& local_cert,
98 scoped_refptr<RsaKeyPair> key_pair,
99 Authenticator::State initial_state) {
100 scoped_ptr<Spake2Authenticator> result(new Spake2Authenticator(
101 local_id, remote_id, shared_secret, true, initial_state));
102 result->local_cert_ = local_cert;
103 result->local_key_pair_ = key_pair;
104 return std::move(result);
105 }
106
107 Spake2Authenticator::Spake2Authenticator(const std::string& local_id,
108 const std::string& remote_id,
109 const std::string& shared_secret,
110 bool is_host,
111 Authenticator::State initial_state)
112 : local_id_(local_id),
113 remote_id_(remote_id),
114 shared_secret_(shared_secret),
115 is_host_(is_host),
116 state_(initial_state) {
117 spake2_context_ = SPAKE2_CTX_new(
118 is_host ? spake2_role_bob : spake2_role_alice,
119 reinterpret_cast<const uint8_t*>(local_id_.data()), local_id_.size(),
120 reinterpret_cast<const uint8_t*>(remote_id_.data()), remote_id_.size());
121
122 // Generate first message and push it to |pending_messages_|.
123 uint8_t message[SPAKE2_MAX_MSG_SIZE];
124 size_t message_size;
125 int result = SPAKE2_generate_msg(
126 spake2_context_, message, &message_size, sizeof(message),
127 reinterpret_cast<const uint8_t*>(shared_secret_.data()),
128 shared_secret_.size());
129 CHECK(result);
130 local_spake_message_.assign(reinterpret_cast<char*>(message), message_size);
131 }
132
133 Spake2Authenticator::~Spake2Authenticator() {
134 SPAKE2_CTX_free(spake2_context_);
135 }
136
137 Authenticator::State Spake2Authenticator::state() const {
138 if (state_ == ACCEPTED && !outgoing_verification_hash_.empty())
139 return MESSAGE_READY;
140 return state_;
141 }
142
143 bool Spake2Authenticator::started() const {
144 return started_;
145 }
146
147 Authenticator::RejectionReason Spake2Authenticator::rejection_reason() const {
148 DCHECK_EQ(state(), REJECTED);
149 return rejection_reason_;
150 }
151
152 void Spake2Authenticator::ProcessMessage(const buzz::XmlElement* message,
153 const base::Closure& resume_callback) {
154 ProcessMessageInternal(message);
155 resume_callback.Run();
156 }
157
158 void Spake2Authenticator::ProcessMessageInternal(
159 const buzz::XmlElement* message) {
160 DCHECK_EQ(state(), WAITING_MESSAGE);
161
162 // Parse the certificate.
163 bool cert_present;
164 if (!DecodeBinaryValueFromXml(message, kCertificateTag, &cert_present,
165 &remote_cert_)) {
166 state_ = REJECTED;
167 rejection_reason_ = PROTOCOL_ERROR;
168 return;
169 }
170
171 // Client always expects certificate in the first message.
172 if (!is_host_ && remote_cert_.empty()) {
173 LOG(WARNING) << "No valid host certificate.";
174 state_ = REJECTED;
175 rejection_reason_ = PROTOCOL_ERROR;
176 return;
177 }
178
179 bool spake_message_present = false;
180 std::string spake_message;
181 bool verification_hash_present = false;
182 std::string verification_hash;
183 if (!DecodeBinaryValueFromXml(message, kSpakeMessageTag,
184 &spake_message_present, &spake_message) ||
185 !DecodeBinaryValueFromXml(message, kVerificationHashTag,
186 &verification_hash_present,
187 &verification_hash)) {
188 state_ = REJECTED;
189 rejection_reason_ = PROTOCOL_ERROR;
190 return;
191 }
192
193 // |auth_key_| is generated when <spake-message> is received.
194 if (auth_key_.empty()) {
195 if (!spake_message_present) {
196 LOG(WARNING) << "<spake-message> not found.";
197 state_ = REJECTED;
198 rejection_reason_ = PROTOCOL_ERROR;
199 return;
200 }
201 uint8_t key[SPAKE2_MAX_KEY_SIZE];
202 size_t key_size;
203 started_ = true;
204 int result = SPAKE2_process_msg(
205 spake2_context_, key, &key_size, sizeof(key),
206 reinterpret_cast<const uint8_t*>(spake_message.data()),
207 spake_message.size());
208 if (!result) {
209 state_ = REJECTED;
210 rejection_reason_ = INVALID_CREDENTIALS;
211 return;
212 }
213 CHECK(key_size);
214 auth_key_.assign(reinterpret_cast<char*>(key), key_size);
215
216 outgoing_verification_hash_ =
217 CalculateVerificationHash(is_host_, local_id_, remote_id_);
218 expected_verification_hash_ =
219 CalculateVerificationHash(!is_host_, remote_id_, local_id_);
220 } else if (spake_message_present) {
221 LOG(WARNING) << "Received duplicate <spake-message>.";
222 state_ = REJECTED;
223 rejection_reason_ = PROTOCOL_ERROR;
224 return;
225 }
226
227 if (spake_message_sent_ && !verification_hash_present) {
228 LOG(WARNING) << "Didn't receive <verification-hash> when expected.";
229 state_ = REJECTED;
230 rejection_reason_ = PROTOCOL_ERROR;
231 return;
232 }
233
234 if (verification_hash_present) {
235 if (verification_hash.size() != expected_verification_hash_.size() ||
236 !crypto::SecureMemEqual(verification_hash.data(),
237 expected_verification_hash_.data(),
238 verification_hash.size())) {
239 state_ = REJECTED;
240 rejection_reason_ = INVALID_CREDENTIALS;
241 return;
242 }
243 state_ = ACCEPTED;
244 return;
245 }
246
247 state_ = MESSAGE_READY;
248 }
249
250 scoped_ptr<buzz::XmlElement> Spake2Authenticator::GetNextMessage() {
251 DCHECK_EQ(state(), MESSAGE_READY);
252
253 scoped_ptr<buzz::XmlElement> message = CreateEmptyAuthenticatorMessage();
254
255 if (!spake_message_sent_) {
256 if (!local_cert_.empty()) {
257 message->AddElement(
258 EncodeBinaryValueToXml(kCertificateTag, local_cert_).release());
259 }
260
261 message->AddElement(
262 EncodeBinaryValueToXml(kSpakeMessageTag, local_spake_message_)
263 .release());
264
265 spake_message_sent_ = true;
266 }
267
268 if (!outgoing_verification_hash_.empty()) {
269 message->AddElement(EncodeBinaryValueToXml(kVerificationHashTag,
270 outgoing_verification_hash_)
271 .release());
272 outgoing_verification_hash_.clear();
273 }
274
275 if (state_ != ACCEPTED) {
276 state_ = WAITING_MESSAGE;
277 }
278 return message;
279 }
280
281 const std::string& Spake2Authenticator::GetAuthKey() const {
282 return auth_key_;
283 }
284
285 scoped_ptr<ChannelAuthenticator>
286 Spake2Authenticator::CreateChannelAuthenticator() const {
287 DCHECK_EQ(state(), ACCEPTED);
288 CHECK(!auth_key_.empty());
289
290 if (is_host_) {
291 return SslHmacChannelAuthenticator::CreateForHost(
292 local_cert_, local_key_pair_, auth_key_);
293 } else {
294 return SslHmacChannelAuthenticator::CreateForClient(remote_cert_,
295 auth_key_);
296 }
297 }
298
299 std::string Spake2Authenticator::CalculateVerificationHash(
300 bool from_host,
301 const std::string& local_id,
302 const std::string& remote_id) {
303 std::string message = (from_host ? "host" : "client") +
304 PrefixWithLength(local_id) +
305 PrefixWithLength(remote_id);
306 crypto::HMAC hmac(crypto::HMAC::SHA256);
307 std::string result(hmac.DigestLength(), '\0');
308 if (!hmac.Init(auth_key_) ||
309 !hmac.Sign(message, reinterpret_cast<uint8_t*>(&result[0]),
310 result.length())) {
311 LOG(FATAL) << "Failed to calculate HMAC.";
312 }
313 return result;
314 }
315
316 } // namespace protocol
317 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/protocol/spake2_authenticator.h ('k') | remoting/protocol/spake2_authenticator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698