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

Side by Side Diff: chrome/browser/local_discovery/privetv3_session.cc

Issue 877613002: Added pairing with Spake SHA224 key exchange. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@brillo1
Patch Set: Sun 01/25/2015 23:51:07.99 Created 5 years, 11 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #if defined(_WIN32) && defined(GetMessage)
6 // Method in p224_spake conflicts with macro on Windows.
7 #undef GetMessage
8 #endif
9
5 #include "chrome/browser/local_discovery/privetv3_session.h" 10 #include "chrome/browser/local_discovery/privetv3_session.h"
6 11
12 #include "base/base64.h"
7 #include "base/json/json_writer.h" 13 #include "base/json/json_writer.h"
8 #include "base/logging.h" 14 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h" 15 #include "base/message_loop/message_loop.h"
10 #include "chrome/browser/local_discovery/privet_constants.h" 16 #include "chrome/browser/local_discovery/privet_constants.h"
11 #include "chrome/browser/local_discovery/privet_http.h" 17 #include "chrome/browser/local_discovery/privet_http.h"
12 #include "chrome/browser/local_discovery/privet_url_fetcher.h" 18 #include "chrome/browser/local_discovery/privet_url_fetcher.h"
13 #include "chrome/common/cloud_print/cloud_print_constants.h" 19 #include "chrome/common/cloud_print/cloud_print_constants.h"
20 #include "crypto/hmac.h"
21 #include "crypto/p224_spake.h"
14 #include "url/gurl.h" 22 #include "url/gurl.h"
15 23
16 namespace local_discovery { 24 namespace local_discovery {
17 25
18 namespace { 26 namespace {
19 27
20 const char kUrlPlaceHolder[] = "http://host/"; 28 const char kUrlPlaceHolder[] = "http://host/";
21 29
22 const char kStubPrivetCode[] = "1234"; 30 const char kStubPrivetCode[] = "1234";
23 31
24 char kPrivetV3AuthAnonymous[] = "Privet anonymous"; 32 char kPrivetV3AuthAnonymous[] = "Privet anonymous";
33 char kPrivetV3CryptoP224Spake2[] = "p224_spake2";
34
35 char kPrivetV3InfoKeyAuth[] = "authentication";
36 char kPrivetV3InfoKeyPairing[] = "authentication";
37 char kPrivetV3InfoKeyHttpsPort[] = "endpoints.httpsPort";
38
39 char kPrivetV3KeyPairing[] = "pairing";
40 char kPrivetV3KeyCrypto[] = "crypto";
41 char kPrivetV3KeySessionId[] = "sessionId";
42 char kPrivetV3KeyDeviceCommitment[] = "deviceCommitment";
43 char kPrivetV3KeyClientCommitment[] = "clientCommitment";
44 char kPrivetV3KeyCertFingerprint[] = "certFingerprint";
45 char kPrivetV3KeyCertSignature[] = "certSignature";
46
47 char kPrivetV3PairingStartPath[] = "/privet/v3/pairing/start";
48 char kPrivetV3PairingConfirmPath[] = "/privet/v3/pairing/confirm";
Aleksey Shlyapnikov 2015/01/26 21:47:45 Make them all const char.
Vitaly Buka (NO REVIEWS) 2015/01/27 00:16:33 Done.
49
25 GURL CreatePrivetURL(const std::string& path) { 50 GURL CreatePrivetURL(const std::string& path) {
26 GURL url(kUrlPlaceHolder); 51 GURL url(kUrlPlaceHolder);
27 GURL::Replacements replacements; 52 GURL::Replacements replacements;
28 replacements.SetPathStr(path); 53 replacements.SetPathStr(path);
29 return url.ReplaceComponents(replacements); 54 return url.ReplaceComponents(replacements);
30 } 55 }
31 56
57 template <typename T>
58 class EnumToStringMap {
59 public:
60 static std::string FindNameById(T id) {
61 for (const Map& m : kMap) {
62 if (m.id == id) {
63 DCHECK(m.name);
64 return m.name;
65 }
66 }
67 NOTREACHED();
68 return std::string();
69 }
70
71 static bool FindIdByName(const std::string& name, T* id) {
72 for (const Map& m : kMap) {
73 if (m.name && m.name == name) {
74 *id = m.id;
75 return true;
76 }
77 }
78 return false;
79 }
80
81 private:
82 struct Map {
Aleksey Shlyapnikov 2015/01/26 21:47:45 Element
Vitaly Buka (NO REVIEWS) 2015/01/27 00:16:33 Done.
83 const T id;
84 const char* const name;
85 };
86 static const Map kMap[];
87 };
88
89 using PairingType = PrivetV3Session::PairingType;
90
91 template <>
92 const EnumToStringMap<PrivetV3Session::PairingType>::Map
93 EnumToStringMap<PrivetV3Session::PairingType>::kMap[] = {
94 {PairingType::PAIRING_TYPE_PINCODE, "pinCode"},
95 {PairingType::PAIRING_TYPE_EMBEDDEDCODE, "embeddedCode"},
96 {PairingType::PAIRING_TYPE_ULTRASOUNDDSSSBROADCASTER,
97 "ultrasoundDsssBroadcaster"},
98 {PairingType::PAIRING_TYPE_AUDIBLEDTMFBROADCASTER,
99 "audibleDtmfBroadcaster"},
100 };
101
102 template <typename T>
103 std::string EnumToString(T id) {
104 return EnumToStringMap<T>::FindNameById(id);
105 }
106
107 template <typename T>
108 bool StringToEnum(const std::string& name, T* id) {
109 return EnumToStringMap<T>::FindIdByName(name, id);
110 }
111
112 bool GetDecodedString(const base::DictionaryValue& response,
113 const std::string& key,
114 std::string* value) {
115 std::string base64;
116 return response.GetString(key, &base64) && base::Base64Decode(base64, value);
117 }
118
32 } // namespace 119 } // namespace
33 120
34 class PrivetV3Session::FetcherDelegate : public PrivetURLFetcher::Delegate { 121 class PrivetV3Session::FetcherDelegate : public PrivetURLFetcher::Delegate {
35 public: 122 public:
36 FetcherDelegate(const base::WeakPtr<PrivetV3Session>& session, 123 FetcherDelegate(const base::WeakPtr<PrivetV3Session>& session,
37 const std::string& auth_token, 124 const std::string& auth_token,
38 const PrivetV3Session::MessageCallback& callback); 125 const PrivetV3Session::MessageCallback& callback);
39 ~FetcherDelegate() override; 126 ~FetcherDelegate() override;
40 127
41 // PrivetURLFetcher::Delegate methods. 128 // PrivetURLFetcher::Delegate methods.
42 std::string GetAuthToken() override; 129 std::string GetAuthToken() override;
43 void OnNeedPrivetToken( 130 void OnNeedPrivetToken(
44 PrivetURLFetcher* fetcher, 131 PrivetURLFetcher* fetcher,
45 const PrivetURLFetcher::TokenCallback& callback) override; 132 const PrivetURLFetcher::TokenCallback& callback) override;
46 void OnError(PrivetURLFetcher* fetcher, 133 void OnError(PrivetURLFetcher* fetcher,
47 PrivetURLFetcher::ErrorType error) override; 134 PrivetURLFetcher::ErrorType error) override;
48 void OnParsedJson(PrivetURLFetcher* fetcher, 135 void OnParsedJson(PrivetURLFetcher* fetcher,
49 const base::DictionaryValue& value, 136 const base::DictionaryValue& value,
50 bool has_error) override; 137 bool has_error) override;
51 138
139 PrivetURLFetcher* CreateURLFetcher(const GURL& url,
140 net::URLFetcher::RequestType request_type);
141
52 private: 142 private:
53 friend class PrivetV3Session;
54 void DeleteThis(); 143 void DeleteThis();
55 144
56 scoped_ptr<PrivetURLFetcher> url_fetcher_; 145 scoped_ptr<PrivetURLFetcher> url_fetcher_;
146
57 base::WeakPtr<PrivetV3Session> session_; 147 base::WeakPtr<PrivetV3Session> session_;
58 std::string auth_token_; 148 std::string auth_token_;
59 MessageCallback callback_; 149 MessageCallback callback_;
60 }; 150 };
61 151
62 PrivetV3Session::FetcherDelegate::FetcherDelegate( 152 PrivetV3Session::FetcherDelegate::FetcherDelegate(
63 const base::WeakPtr<PrivetV3Session>& session, 153 const base::WeakPtr<PrivetV3Session>& session,
64 const std::string& auth_token, 154 const std::string& auth_token,
65 const PrivetV3Session::MessageCallback& callback) 155 const PrivetV3Session::MessageCallback& callback)
66 : session_(session), auth_token_(auth_token), callback_(callback) { 156 : session_(session), auth_token_(auth_token), callback_(callback) {
(...skipping 26 matching lines...) Expand all
93 PrivetURLFetcher* fetcher, 183 PrivetURLFetcher* fetcher,
94 const base::DictionaryValue& value, 184 const base::DictionaryValue& value,
95 bool has_error) { 185 bool has_error) {
96 if (session_) { 186 if (session_) {
97 DeleteThis(); 187 DeleteThis();
98 callback_.Run( 188 callback_.Run(
99 has_error ? Result::STATUS_DEVICEERROR : Result::STATUS_SUCCESS, value); 189 has_error ? Result::STATUS_DEVICEERROR : Result::STATUS_SUCCESS, value);
100 } 190 }
101 } 191 }
102 192
193 PrivetURLFetcher* PrivetV3Session::FetcherDelegate::CreateURLFetcher(
194 const GURL& url,
195 net::URLFetcher::RequestType request_type) {
196 DCHECK(!url_fetcher_);
197 url_fetcher_ =
198 session_->client_->CreateURLFetcher(url, request_type, this).Pass();
199 url_fetcher_->V3Mode();
200 return url_fetcher_.get();
201 }
202
103 void PrivetV3Session::FetcherDelegate::DeleteThis() { 203 void PrivetV3Session::FetcherDelegate::DeleteThis() {
104 base::MessageLoop::current()->PostTask( 204 base::MessageLoop::current()->PostTask(
105 FROM_HERE, base::Bind(&PrivetV3Session::DeleteFetcher, session_, 205 FROM_HERE, base::Bind(&PrivetV3Session::DeleteFetcher, session_,
106 base::Unretained(this))); 206 base::Unretained(this)));
107 } 207 }
108 208
109 PrivetV3Session::PrivetV3Session(scoped_ptr<PrivetHTTPClient> client) 209 PrivetV3Session::PrivetV3Session(scoped_ptr<PrivetHTTPClient> client)
110 : client_(client.Pass()), code_confirmed_(false), weak_ptr_factory_(this) { 210 : client_(client.Pass()), weak_ptr_factory_(this) {
111 } 211 }
112 212
113 PrivetV3Session::~PrivetV3Session() { 213 PrivetV3Session::~PrivetV3Session() {
114 } 214 }
115 215
116 void PrivetV3Session::Init(const InitCallback& callback) { 216 void PrivetV3Session::Init(const InitCallback& callback) {
117 privet_auth_token_ = kPrivetV3AuthAnonymous; 217 privet_auth_token_ = kPrivetV3AuthAnonymous;
118 218
119 // TODO: call /info. 219 DCHECK(fetchers_.empty());
120 base::MessageLoop::current()->PostDelayedTask( 220
121 FROM_HERE, 221 privet_auth_token_ = kPrivetV3AuthAnonymous;
122 base::Bind(&PrivetV3Session::RunCallback, weak_ptr_factory_.GetWeakPtr(), 222
123 base::Bind(callback, Result::STATUS_SUCCESS, 223 StartGetRequest(kPrivetInfoPath,
124 std::vector<PairingType>( 224 base::Bind(&PrivetV3Session::OnInfoDone,
125 1, PairingType::PAIRING_TYPE_EMBEDDEDCODE))), 225 weak_ptr_factory_.GetWeakPtr(), callback));
126 base::TimeDelta::FromSeconds(1)); 226 }
227
228 void PrivetV3Session::OnInfoDone(const InitCallback& callback,
229 Result result,
230 const base::DictionaryValue& response) {
231 std::vector<PairingType> pairing_types;
232 if (result != Result::STATUS_SUCCESS)
233 return callback.Run(result, pairing_types);
234
235 const base::DictionaryValue* authentication = nullptr;
236 const base::ListValue* pairing = nullptr;
237 const base::ListValue* crypto = nullptr;
238 if (!response.GetDictionary(kPrivetV3InfoKeyAuth, &authentication) ||
239 !authentication->GetList(kPrivetV3KeyPairing, &pairing) ||
240 !authentication->GetList(kPrivetV3KeyCrypto, &crypto)) {
241 return callback.Run(Result::STATUS_SESSIONERROR, pairing_types);
242 }
243
244 bool has_compartible_crypto = false;
Aleksey Shlyapnikov 2015/01/26 21:47:45 has_compatible_crypto
Vitaly Buka (NO REVIEWS) 2015/01/27 00:16:33 Done.
245 for (const base::Value* value : *crypto) {
246 std::string crypto_string;
247 // The only supported crypto.
248 if (value->GetAsString(&crypto_string) &&
249 crypto_string == kPrivetV3CryptoP224Spake2) {
250 has_compartible_crypto = true;
251 break;
252 }
253 }
254
255 if (!has_compartible_crypto)
256 return callback.Run(Result::STATUS_SESSIONERROR, pairing_types);
257
258 for (const base::Value* value : *pairing) {
259 std::string pairing_string;
260 PairingType pairing_type;
261 if (!value->GetAsString(&pairing_string) ||
262 !StringToEnum(pairing_string, &pairing_type)) {
263 continue; // Skip unknown pairing.
264 }
265 pairing_types.push_back(pairing_type);
266 }
267
268 callback.Run(Result::STATUS_SUCCESS, pairing_types);
127 } 269 }
128 270
129 void PrivetV3Session::StartPairing(PairingType pairing_type, 271 void PrivetV3Session::StartPairing(PairingType pairing_type,
130 const ResultCallback& callback) { 272 const ResultCallback& callback) {
131 // TODO: call /privet/v3/pairing/start. 273 base::DictionaryValue input;
132 base::MessageLoop::current()->PostDelayedTask( 274 input.SetString(kPrivetV3KeyPairing, EnumToString(pairing_type));
133 FROM_HERE, 275 input.SetString(kPrivetV3KeyCrypto, kPrivetV3CryptoP224Spake2);
134 base::Bind(&PrivetV3Session::RunCallback, weak_ptr_factory_.GetWeakPtr(), 276
135 base::Bind(callback, Result::STATUS_SUCCESS)), 277 StartPostRequest(kPrivetV3PairingStartPath, input,
136 base::TimeDelta::FromSeconds(1)); 278 base::Bind(&PrivetV3Session::OnPairingStartDone,
279 weak_ptr_factory_.GetWeakPtr(), callback));
280 }
281
282 void PrivetV3Session::OnPairingStartDone(
283 const ResultCallback& callback,
284 Result result,
285 const base::DictionaryValue& response) {
286 if (result != Result::STATUS_SUCCESS)
287 return callback.Run(result);
288
289 if (!response.GetString(kPrivetV3KeySessionId, &session_id_) ||
290 !GetDecodedString(response, kPrivetV3KeyDeviceCommitment, &commitment_)) {
291 return callback.Run(Result::STATUS_SESSIONERROR);
292 }
293
294 return callback.Run(Result::STATUS_SUCCESS);
137 } 295 }
138 296
139 void PrivetV3Session::ConfirmCode(const std::string& code, 297 void PrivetV3Session::ConfirmCode(const std::string& code,
140 const ResultCallback& callback) { 298 const ResultCallback& callback) {
141 // TODO: call /privet/v3/pairing/confirm. 299 spake_.reset(new crypto::P224EncryptedKeyExchange(
142 if (code == kStubPrivetCode) { 300 crypto::P224EncryptedKeyExchange::kPeerTypeClient, code));
143 code_confirmed_ = true; 301
144 callback.Run(Result::STATUS_SUCCESS); 302 base::DictionaryValue input;
145 } else { 303 input.SetString(kPrivetV3KeySessionId, session_id_);
146 callback.Run(Result::STATUS_BADPAIRINGCODEERROR); 304
305 std::string client_commitment;
306 base::Base64Encode(spake_->GetMessage(), &client_commitment);
307 input.SetString(kPrivetV3KeyClientCommitment, client_commitment);
308
309 // Call ProcessMessage after GetMessage().
310 crypto::P224EncryptedKeyExchange::Result result =
311 spake_->ProcessMessage(commitment_);
312 DCHECK_EQ(result, crypto::P224EncryptedKeyExchange::kResultPending);
313
314 StartPostRequest(kPrivetV3PairingConfirmPath, input,
315 base::Bind(&PrivetV3Session::OnPairingConfirmDone,
316 weak_ptr_factory_.GetWeakPtr(), callback));
317 }
318
319 void PrivetV3Session::OnPairingConfirmDone(
320 const ResultCallback& callback,
321 Result result,
322 const base::DictionaryValue& response) {
323 if (result != Result::STATUS_SUCCESS)
324 return callback.Run(result);
325
326 std::string fingerprint;
327 std::string signature;
328 if (!GetDecodedString(response, kPrivetV3KeyCertFingerprint, &fingerprint) ||
329 !GetDecodedString(response, kPrivetV3KeyCertSignature, &signature)) {
330 return callback.Run(Result::STATUS_SESSIONERROR);
147 } 331 }
332
333 crypto::HMAC hmac(crypto::HMAC::SHA256);
334 // Key will be verified below, using HMAC.
335 const std::string& key = spake_->GetUnverifiedKey();
336 if (!hmac.Init(reinterpret_cast<const unsigned char*>(key.c_str()),
337 key.size()) ||
338 !hmac.Verify(fingerprint, signature)) {
339 return callback.Run(Result::STATUS_SESSIONERROR);
340 }
341
342 return callback.Run(Result::STATUS_SUCCESS);
148 } 343 }
149 344
150 void PrivetV3Session::SendMessage(const std::string& api, 345 void PrivetV3Session::SendMessage(const std::string& api,
151 const base::DictionaryValue& input, 346 const base::DictionaryValue& input,
152 const MessageCallback& callback) { 347 const MessageCallback& callback) {
153 if (!code_confirmed_) 348 // TODO(vitalybuka): Implement validating HTTPS certificate using
349 // fingerprint_.
350 if (fingerprint_.empty())
154 return callback.Run(Result::STATUS_SESSIONERROR, base::DictionaryValue()); 351 return callback.Run(Result::STATUS_SESSIONERROR, base::DictionaryValue());
155 352
156 FetcherDelegate* fetcher_delegate(new FetcherDelegate( 353 StartPostRequest(api, input, callback);
157 weak_ptr_factory_.GetWeakPtr(), privet_auth_token_, callback));
158 fetchers_.push_back(fetcher_delegate);
159
160 scoped_ptr<PrivetURLFetcher> url_fetcher(client_->CreateURLFetcher(
161 CreatePrivetURL(api), net::URLFetcher::POST, fetcher_delegate));
162
163 std::string json;
164 base::JSONWriter::WriteWithOptions(
165 &input, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
166 url_fetcher->SetUploadData(cloud_print::kContentTypeJSON, json);
167
168 fetcher_delegate->url_fetcher_ = url_fetcher.Pass();
169 fetcher_delegate->url_fetcher_->V3Mode();
170 fetcher_delegate->url_fetcher_->Start();
171 } 354 }
172 355
173 void PrivetV3Session::RunCallback(const base::Closure& callback) { 356 void PrivetV3Session::RunCallback(const base::Closure& callback) {
174 callback.Run(); 357 callback.Run();
175 } 358 }
176 359
360 void PrivetV3Session::StartGetRequest(const std::string& api,
361 const MessageCallback& callback) {
362 CreateFetcher(api, net::URLFetcher::RequestType::GET, callback)->Start();
363 }
364
365 void PrivetV3Session::StartPostRequest(const std::string& api,
366 const base::DictionaryValue& input,
367 const MessageCallback& callback) {
368 PrivetURLFetcher* fetcher =
369 CreateFetcher(api, net::URLFetcher::RequestType::POST, callback);
370
371 std::string json;
372 base::JSONWriter::WriteWithOptions(
373 &input, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
374 fetcher->SetUploadData(cloud_print::kContentTypeJSON, json);
375 fetcher->Start();
376 }
377
378 PrivetURLFetcher* PrivetV3Session::CreateFetcher(
379 const std::string& api,
380 net::URLFetcher::RequestType request_type,
381 const MessageCallback& callback) {
382 fetchers_.push_back(new FetcherDelegate(weak_ptr_factory_.GetWeakPtr(),
383 privet_auth_token_, callback));
384 return fetchers_.back()->CreateURLFetcher(CreatePrivetURL(api), request_type);
385 }
386
177 void PrivetV3Session::DeleteFetcher(const FetcherDelegate* fetcher) { 387 void PrivetV3Session::DeleteFetcher(const FetcherDelegate* fetcher) {
178 fetchers_.erase(std::find(fetchers_.begin(), fetchers_.end(), fetcher)); 388 fetchers_.erase(std::find(fetchers_.begin(), fetchers_.end(), fetcher));
179 } 389 }
180 390
181 } // namespace local_discovery 391 } // namespace local_discovery
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698