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

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

Powered by Google App Engine
This is Rietveld 408576698