OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |