| 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 #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/base64.h" |
| 8 #include "base/json/json_writer.h" | 8 #include "base/json/json_writer.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 const char kPrivetV3KeyClientCommitment[] = "clientCommitment"; | 37 const char kPrivetV3KeyClientCommitment[] = "clientCommitment"; |
| 38 const char kPrivetV3KeyCrypto[] = "crypto"; | 38 const char kPrivetV3KeyCrypto[] = "crypto"; |
| 39 const char kPrivetV3KeyDeviceCommitment[] = "deviceCommitment"; | 39 const char kPrivetV3KeyDeviceCommitment[] = "deviceCommitment"; |
| 40 const char kPrivetV3KeyMode[] = "mode"; | 40 const char kPrivetV3KeyMode[] = "mode"; |
| 41 const char kPrivetV3KeyPairing[] = "pairing"; | 41 const char kPrivetV3KeyPairing[] = "pairing"; |
| 42 const char kPrivetV3KeyRequestedScope[] = "requestedScope"; | 42 const char kPrivetV3KeyRequestedScope[] = "requestedScope"; |
| 43 const char kPrivetV3KeyScope[] = "scope"; | 43 const char kPrivetV3KeyScope[] = "scope"; |
| 44 const char kPrivetV3KeySessionId[] = "sessionId"; | 44 const char kPrivetV3KeySessionId[] = "sessionId"; |
| 45 const char kPrivetV3KeyTokenType[] = "tokenType"; | 45 const char kPrivetV3KeyTokenType[] = "tokenType"; |
| 46 | 46 |
| 47 const char kPrivetV3InfoHttpsPort[] = "endpoints.httpsPort"; |
| 48 |
| 47 const char kPrivetV3PairingStartPath[] = "/privet/v3/pairing/start"; | 49 const char kPrivetV3PairingStartPath[] = "/privet/v3/pairing/start"; |
| 48 const char kPrivetV3PairingConfirmPath[] = "/privet/v3/pairing/confirm"; | 50 const char kPrivetV3PairingConfirmPath[] = "/privet/v3/pairing/confirm"; |
| 49 const char kPrivetV3PairingCancelPath[] = "/privet/v3/pairing/cancel"; | 51 const char kPrivetV3PairingCancelPath[] = "/privet/v3/pairing/cancel"; |
| 50 const char kPrivetV3AuthPath[] = "/privet/v3/auth"; | 52 const char kPrivetV3AuthPath[] = "/privet/v3/auth"; |
| 51 | 53 |
| 52 const char kUrlPlaceHolder[] = "http://host/"; | 54 const char kUrlPlaceHolder[] = "http://host/"; |
| 53 | 55 |
| 54 const int kUrlFetcherTimeoutSec = 30; | 56 const int kUrlFetcherTimeoutSec = 60; |
| 55 | 57 |
| 56 GURL CreatePrivetURL(const std::string& path) { | 58 GURL CreatePrivetURL(const std::string& path) { |
| 57 GURL url(kUrlPlaceHolder); | 59 GURL url(kUrlPlaceHolder); |
| 58 GURL::Replacements replacements; | 60 GURL::Replacements replacements; |
| 59 replacements.SetPathStr(path); | 61 replacements.SetPathStr(path); |
| 60 return url.ReplaceComponents(replacements); | 62 return url.ReplaceComponents(replacements); |
| 61 } | 63 } |
| 62 | 64 |
| 63 using PairingType = PrivetV3Session::PairingType; | 65 using PairingType = PrivetV3Session::PairingType; |
| 64 | 66 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 } | 137 } |
| 136 | 138 |
| 137 std::string PrivetV3Session::FetcherDelegate::GetAuthToken() { | 139 std::string PrivetV3Session::FetcherDelegate::GetAuthToken() { |
| 138 return auth_token_; | 140 return auth_token_; |
| 139 } | 141 } |
| 140 | 142 |
| 141 void PrivetV3Session::FetcherDelegate::OnNeedPrivetToken( | 143 void PrivetV3Session::FetcherDelegate::OnNeedPrivetToken( |
| 142 PrivetURLFetcher* fetcher, | 144 PrivetURLFetcher* fetcher, |
| 143 const PrivetURLFetcher::TokenCallback& callback) { | 145 const PrivetURLFetcher::TokenCallback& callback) { |
| 144 NOTREACHED(); | 146 NOTREACHED(); |
| 145 OnError(fetcher, PrivetURLFetcher::URL_FETCH_ERROR); | 147 OnError(fetcher, PrivetURLFetcher::UNKNOWN_ERROR); |
| 146 } | 148 } |
| 147 | 149 |
| 148 void PrivetV3Session::FetcherDelegate::OnError( | 150 void PrivetV3Session::FetcherDelegate::OnError( |
| 149 PrivetURLFetcher* fetcher, | 151 PrivetURLFetcher* fetcher, |
| 150 PrivetURLFetcher::ErrorType error) { | 152 PrivetURLFetcher::ErrorType error) { |
| 151 LOG(ERROR) << "PrivetURLFetcher url: " << fetcher->url() | 153 LOG(ERROR) << "PrivetURLFetcher url: " << fetcher->url() |
| 152 << ", error: " << error | 154 << ", error: " << error |
| 153 << ", response code: " << fetcher->response_code(); | 155 << ", response code: " << fetcher->response_code(); |
| 154 ReplyAndDestroyItself(Result::STATUS_CONNECTIONERROR, | 156 ReplyAndDestroyItself(Result::STATUS_CONNECTIONERROR, |
| 155 base::DictionaryValue()); | 157 base::DictionaryValue()); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 PrivetV3Session::PrivetV3Session(scoped_ptr<PrivetHTTPClient> client) | 209 PrivetV3Session::PrivetV3Session(scoped_ptr<PrivetHTTPClient> client) |
| 208 : client_(client.Pass()), weak_ptr_factory_(this) { | 210 : client_(client.Pass()), weak_ptr_factory_(this) { |
| 209 } | 211 } |
| 210 | 212 |
| 211 PrivetV3Session::~PrivetV3Session() { | 213 PrivetV3Session::~PrivetV3Session() { |
| 212 Cancel(); | 214 Cancel(); |
| 213 } | 215 } |
| 214 | 216 |
| 215 void PrivetV3Session::Init(const InitCallback& callback) { | 217 void PrivetV3Session::Init(const InitCallback& callback) { |
| 216 DCHECK(fetchers_.empty()); | 218 DCHECK(fetchers_.empty()); |
| 217 DCHECK(fingerprint_.empty()); | 219 DCHECK(!client_->IsInHttpsMode()); |
| 218 DCHECK(session_id_.empty()); | 220 DCHECK(session_id_.empty()); |
| 219 DCHECK(privet_auth_token_.empty()); | 221 DCHECK(privet_auth_token_.empty()); |
| 220 | 222 |
| 221 privet_auth_token_ = kPrivetV3AuthAnonymous; | 223 privet_auth_token_ = kPrivetV3AuthAnonymous; |
| 222 | |
| 223 StartGetRequest(kPrivetInfoPath, | 224 StartGetRequest(kPrivetInfoPath, |
| 224 base::Bind(&PrivetV3Session::OnInfoDone, | 225 base::Bind(&PrivetV3Session::OnInfoDone, |
| 225 weak_ptr_factory_.GetWeakPtr(), callback)); | 226 weak_ptr_factory_.GetWeakPtr(), callback)); |
| 226 } | 227 } |
| 227 | 228 |
| 228 void PrivetV3Session::OnInfoDone(const InitCallback& callback, | 229 void PrivetV3Session::OnInfoDone(const InitCallback& callback, |
| 229 Result result, | 230 Result result, |
| 230 const base::DictionaryValue& response) { | 231 const base::DictionaryValue& response) { |
| 231 if (result != Result::STATUS_SUCCESS) | 232 if (result != Result::STATUS_SUCCESS) |
| 232 return callback.Run(result, response); | 233 return callback.Run(result, response); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 247 } | 248 } |
| 248 | 249 |
| 249 // The only supported crypto. | 250 // The only supported crypto. |
| 250 if (!ContainsString(*authentication, kPrivetV3KeyCrypto, | 251 if (!ContainsString(*authentication, kPrivetV3KeyCrypto, |
| 251 kPrivetV3CryptoP224Spake2) || | 252 kPrivetV3CryptoP224Spake2) || |
| 252 !ContainsString(*authentication, kPrivetV3KeyMode, kPrivetV3KeyPairing)) { | 253 !ContainsString(*authentication, kPrivetV3KeyMode, kPrivetV3KeyPairing)) { |
| 253 LOG(ERROR) << "Response: " << response; | 254 LOG(ERROR) << "Response: " << response; |
| 254 return callback.Run(Result::STATUS_SESSIONERROR, response); | 255 return callback.Run(Result::STATUS_SESSIONERROR, response); |
| 255 } | 256 } |
| 256 | 257 |
| 258 int port = 0; |
| 259 if (!response.GetInteger(kPrivetV3InfoHttpsPort, &port) || port <= 0 || |
| 260 port > std::numeric_limits<uint16_t>::max()) { |
| 261 LOG(ERROR) << "Response: " << response; |
| 262 return callback.Run(Result::STATUS_SESSIONERROR, response); |
| 263 } |
| 264 https_port_ = port; |
| 265 |
| 257 callback.Run(Result::STATUS_SUCCESS, response); | 266 callback.Run(Result::STATUS_SUCCESS, response); |
| 258 } | 267 } |
| 259 | 268 |
| 260 void PrivetV3Session::StartPairing(PairingType pairing_type, | 269 void PrivetV3Session::StartPairing(PairingType pairing_type, |
| 261 const ResultCallback& callback) { | 270 const ResultCallback& callback) { |
| 262 base::DictionaryValue input; | 271 base::DictionaryValue input; |
| 263 input.SetString(kPrivetV3KeyPairing, | 272 input.SetString(kPrivetV3KeyPairing, |
| 264 extensions::api::gcd_private::ToString(pairing_type)); | 273 extensions::api::gcd_private::ToString(pairing_type)); |
| 265 input.SetString(kPrivetV3KeyCrypto, kPrivetV3CryptoP224Spake2); | 274 input.SetString(kPrivetV3KeyCrypto, kPrivetV3CryptoP224Spake2); |
| 266 | 275 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 316 const ResultCallback& callback, | 325 const ResultCallback& callback, |
| 317 Result result, | 326 Result result, |
| 318 const base::DictionaryValue& response) { | 327 const base::DictionaryValue& response) { |
| 319 if (result != Result::STATUS_SUCCESS) | 328 if (result != Result::STATUS_SUCCESS) |
| 320 return callback.Run(result); | 329 return callback.Run(result); |
| 321 | 330 |
| 322 std::string fingerprint; | 331 std::string fingerprint; |
| 323 std::string signature; | 332 std::string signature; |
| 324 if (!GetDecodedString(response, kPrivetV3KeyCertFingerprint, &fingerprint) || | 333 if (!GetDecodedString(response, kPrivetV3KeyCertFingerprint, &fingerprint) || |
| 325 !GetDecodedString(response, kPrivetV3KeyCertSignature, &signature)) { | 334 !GetDecodedString(response, kPrivetV3KeyCertSignature, &signature)) { |
| 326 LOG(ERROR) << "Response: " << response; | 335 LOG(ERROR) << "Invalid response: " << response; |
| 327 return callback.Run(Result::STATUS_SESSIONERROR); | 336 return callback.Run(Result::STATUS_SESSIONERROR); |
| 328 } | 337 } |
| 329 | 338 |
| 339 net::SHA256HashValue hash; |
| 340 if (fingerprint.size() != sizeof(hash.data)) { |
| 341 LOG(ERROR) << "Invalid fingerprint size: " << response; |
| 342 return callback.Run(Result::STATUS_SESSIONERROR); |
| 343 } |
| 344 memcpy(hash.data, fingerprint.data(), sizeof(hash.data)); |
| 345 |
| 330 crypto::HMAC hmac(crypto::HMAC::SHA256); | 346 crypto::HMAC hmac(crypto::HMAC::SHA256); |
| 331 // Key will be verified below, using HMAC. | 347 // Key will be verified below, using HMAC. |
| 332 const std::string& key = spake_->GetUnverifiedKey(); | 348 const std::string& key = spake_->GetUnverifiedKey(); |
| 333 if (!hmac.Init(reinterpret_cast<const unsigned char*>(key.c_str()), | 349 if (!hmac.Init(reinterpret_cast<const unsigned char*>(key.c_str()), |
| 334 key.size()) || | 350 key.size()) || |
| 335 !hmac.Verify(fingerprint, signature)) { | 351 !hmac.Verify(fingerprint, signature)) { |
| 336 LOG(ERROR) << "Response: " << response; | 352 LOG(ERROR) << "Verification failed: " << response; |
| 337 return callback.Run(Result::STATUS_SESSIONERROR); | 353 return callback.Run(Result::STATUS_BADPAIRINGCODEERROR); |
| 338 } | 354 } |
| 339 | 355 |
| 340 std::string auth_code(hmac.DigestLength(), ' '); | 356 std::string auth_code(hmac.DigestLength(), ' '); |
| 341 if (!hmac.Sign(session_id_, | 357 if (!hmac.Sign(session_id_, |
| 342 reinterpret_cast<unsigned char*>(string_as_array(&auth_code)), | 358 reinterpret_cast<unsigned char*>(string_as_array(&auth_code)), |
| 343 auth_code.size())) { | 359 auth_code.size())) { |
| 344 LOG(FATAL) << "Signing failed"; | 360 LOG(FATAL) << "Signing failed"; |
| 345 return callback.Run(Result::STATUS_SESSIONERROR); | 361 return callback.Run(Result::STATUS_SESSIONERROR); |
| 346 } | 362 } |
| 347 // From now this is expected certificate. | 363 |
| 348 fingerprint_ = fingerprint; | 364 // From now use only https with fixed certificate. |
| 365 VLOG(1) << "Expected certificate: " << fingerprint; |
| 366 client_->SwitchToHttps(https_port_, hash); |
| 349 | 367 |
| 350 std::string auth_code_base64; | 368 std::string auth_code_base64; |
| 351 base::Base64Encode(auth_code, &auth_code_base64); | 369 base::Base64Encode(auth_code, &auth_code_base64); |
| 352 | 370 |
| 353 base::DictionaryValue input; | 371 base::DictionaryValue input; |
| 354 input.SetString(kPrivetV3KeyAuthCode, auth_code_base64); | 372 input.SetString(kPrivetV3KeyAuthCode, auth_code_base64); |
| 355 input.SetString(kPrivetV3KeyMode, kPrivetV3KeyPairing); | 373 input.SetString(kPrivetV3KeyMode, kPrivetV3KeyPairing); |
| 356 input.SetString(kPrivetV3KeyRequestedScope, kPrivetV3Auto); | 374 input.SetString(kPrivetV3KeyRequestedScope, kPrivetV3Auto); |
| 357 | 375 |
| 358 // Now we can use SendMessage with certificate validation. | 376 // Now we can use SendMessage with certificate validation. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 379 } | 397 } |
| 380 | 398 |
| 381 privet_auth_token_ = token_type + " " + access_token; | 399 privet_auth_token_ = token_type + " " + access_token; |
| 382 | 400 |
| 383 return callback.Run(Result::STATUS_SUCCESS); | 401 return callback.Run(Result::STATUS_SUCCESS); |
| 384 } | 402 } |
| 385 | 403 |
| 386 void PrivetV3Session::SendMessage(const std::string& api, | 404 void PrivetV3Session::SendMessage(const std::string& api, |
| 387 const base::DictionaryValue& input, | 405 const base::DictionaryValue& input, |
| 388 const MessageCallback& callback) { | 406 const MessageCallback& callback) { |
| 389 // TODO(vitalybuka): Implement validating HTTPS certificate using | 407 if (!client_->IsInHttpsMode()) { |
| 390 // fingerprint_. | |
| 391 if (fingerprint_.empty()) { | |
| 392 LOG(ERROR) << "Session is not paired"; | 408 LOG(ERROR) << "Session is not paired"; |
| 393 return callback.Run(Result::STATUS_SESSIONERROR, base::DictionaryValue()); | 409 return callback.Run(Result::STATUS_SESSIONERROR, base::DictionaryValue()); |
| 394 } | 410 } |
| 395 | 411 |
| 396 StartPostRequest(api, input, callback); | 412 StartPostRequest(api, input, callback); |
| 397 } | 413 } |
| 398 | 414 |
| 399 void PrivetV3Session::RunCallback(const base::Closure& callback) { | 415 void PrivetV3Session::RunCallback(const base::Closure& callback) { |
| 400 callback.Run(); | 416 callback.Run(); |
| 401 } | 417 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 432 return fetcher->CreateURLFetcher(CreatePrivetURL(api), request_type, | 448 return fetcher->CreateURLFetcher(CreatePrivetURL(api), request_type, |
| 433 orphaned); | 449 orphaned); |
| 434 } | 450 } |
| 435 | 451 |
| 436 void PrivetV3Session::DeleteFetcher(const FetcherDelegate* fetcher) { | 452 void PrivetV3Session::DeleteFetcher(const FetcherDelegate* fetcher) { |
| 437 fetchers_.erase(std::find(fetchers_.begin(), fetchers_.end(), fetcher)); | 453 fetchers_.erase(std::find(fetchers_.begin(), fetchers_.end(), fetcher)); |
| 438 } | 454 } |
| 439 | 455 |
| 440 void PrivetV3Session::Cancel() { | 456 void PrivetV3Session::Cancel() { |
| 441 // Cancel started unconfirmed sessions. | 457 // Cancel started unconfirmed sessions. |
| 442 if (session_id_.empty() || !fingerprint_.empty()) | 458 if (session_id_.empty() || client_->IsInHttpsMode()) |
| 443 return; | 459 return; |
| 444 base::DictionaryValue input; | 460 base::DictionaryValue input; |
| 445 input.SetString(kPrivetV3KeySessionId, session_id_); | 461 input.SetString(kPrivetV3KeySessionId, session_id_); |
| 446 StartPostRequest(kPrivetV3PairingCancelPath, input, MessageCallback()); | 462 StartPostRequest(kPrivetV3PairingCancelPath, input, MessageCallback()); |
| 447 } | 463 } |
| 448 | 464 |
| 449 } // namespace local_discovery | 465 } // namespace local_discovery |
| OLD | NEW |