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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/local_discovery/privetv3_session.cc
diff --git a/chrome/browser/local_discovery/privetv3_session.cc b/chrome/browser/local_discovery/privetv3_session.cc
index bd5f4ca678b97c43dd9d128de3c5543ec2125f98..10e0c38dec79fb47fa2d26ac3445d0407dbfc0dd 100644
--- a/chrome/browser/local_discovery/privetv3_session.cc
+++ b/chrome/browser/local_discovery/privetv3_session.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/local_discovery/privetv3_session.h"
+#include "base/base64.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
@@ -11,17 +12,34 @@
#include "chrome/browser/local_discovery/privet_http.h"
#include "chrome/browser/local_discovery/privet_url_fetcher.h"
#include "chrome/common/cloud_print/cloud_print_constants.h"
+#include "crypto/hmac.h"
+#include "crypto/p224_spake.h"
#include "url/gurl.h"
namespace local_discovery {
namespace {
-const char kUrlPlaceHolder[] = "http://host/";
+const char kPrivetV3AuthAnonymous[] = "Privet anonymous";
+const char kPrivetV3CryptoP224Spake2[] = "p224_spake2";
-const char kStubPrivetCode[] = "1234";
+const char kPrivetV3InfoKeyAuth[] = "authentication";
+const char kPrivetV3InfoKeyVersion[] = "version";
+const char kPrivetV3InfoVersion[] = "3.0";
-const char kPrivetV3AuthAnonymous[] = "Privet anonymous";
+const char kPrivetV3KeyCertFingerprint[] = "certFingerprint";
+const char kPrivetV3KeyCertSignature[] = "certSignature";
+const char kPrivetV3KeyClientCommitment[] = "clientCommitment";
+const char kPrivetV3KeyCrypto[] = "crypto";
+const char kPrivetV3KeyDeviceCommitment[] = "deviceCommitment";
+const char kPrivetV3KeyMode[] = "mode";
+const char kPrivetV3KeyPairing[] = "pairing";
+const char kPrivetV3KeySessionId[] = "sessionId";
+
+const char kPrivetV3PairingConfirmPath[] = "/privet/v3/pairing/confirm";
+const char kPrivetV3PairingStartPath[] = "/privet/v3/pairing/start";
+
+const char kUrlPlaceHolder[] = "http://host/";
GURL CreatePrivetURL(const std::string& path) {
GURL url(kUrlPlaceHolder);
@@ -30,6 +48,79 @@ GURL CreatePrivetURL(const std::string& path) {
return url.ReplaceComponents(replacements);
}
+template <typename T>
+class EnumToStringMap {
+ public:
+ static std::string FindNameById(T id) {
+ for (const Element& m : kMap) {
+ if (m.id == id) {
+ DCHECK(m.name);
+ return m.name;
+ }
+ }
+ NOTREACHED();
+ return std::string();
+ }
+
+ static bool FindIdByName(const std::string& name, T* id) {
+ for (const Element& m : kMap) {
+ if (m.name && m.name == name) {
+ *id = m.id;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ struct Element {
+ const T id;
+ const char* const name;
+ };
+ static const Element kMap[];
+};
+
+using PairingType = PrivetV3Session::PairingType;
+
+template <>
+const EnumToStringMap<PrivetV3Session::PairingType>::Element
+ EnumToStringMap<PrivetV3Session::PairingType>::kMap[] = {
+ {PairingType::PAIRING_TYPE_PINCODE, "pinCode"},
+ {PairingType::PAIRING_TYPE_EMBEDDEDCODE, "embeddedCode"},
+};
+
+template <typename T>
+std::string EnumToString(T id) {
+ return EnumToStringMap<T>::FindNameById(id);
+}
+
+template <typename T>
+bool StringToEnum(const std::string& name, T* id) {
+ return EnumToStringMap<T>::FindIdByName(name, id);
+}
+
+bool GetDecodedString(const base::DictionaryValue& response,
+ const std::string& key,
+ std::string* value) {
+ std::string base64;
+ return response.GetString(key, &base64) && base::Base64Decode(base64, value);
+}
+
+bool ContainsString(const base::DictionaryValue& dictionary,
+ const std::string& key,
+ const std::string& expected_value) {
+ const base::ListValue* list_of_string = nullptr;
+ if (!dictionary.GetList(key, &list_of_string))
+ return false;
+
+ for (const base::Value* value : *list_of_string) {
+ std::string string_value;
+ if (value->GetAsString(&string_value) && string_value == expected_value)
+ return true;
+ }
+ return false;
+}
+
} // namespace
class PrivetV3Session::FetcherDelegate : public PrivetURLFetcher::Delegate {
@@ -50,11 +141,14 @@ class PrivetV3Session::FetcherDelegate : public PrivetURLFetcher::Delegate {
const base::DictionaryValue& value,
bool has_error) override;
+ PrivetURLFetcher* CreateURLFetcher(const GURL& url,
+ net::URLFetcher::RequestType request_type);
+
private:
- friend class PrivetV3Session;
void DeleteThis();
scoped_ptr<PrivetURLFetcher> url_fetcher_;
+
base::WeakPtr<PrivetV3Session> session_;
std::string auth_token_;
MessageCallback callback_;
@@ -101,6 +195,16 @@ void PrivetV3Session::FetcherDelegate::OnParsedJson(
}
}
+PrivetURLFetcher* PrivetV3Session::FetcherDelegate::CreateURLFetcher(
+ const GURL& url,
+ net::URLFetcher::RequestType request_type) {
+ DCHECK(!url_fetcher_);
+ url_fetcher_ =
+ session_->client_->CreateURLFetcher(url, request_type, this).Pass();
+ url_fetcher_->V3Mode();
+ return url_fetcher_.get();
+}
+
void PrivetV3Session::FetcherDelegate::DeleteThis() {
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&PrivetV3Session::DeleteFetcher, session_,
@@ -108,71 +212,183 @@ void PrivetV3Session::FetcherDelegate::DeleteThis() {
}
PrivetV3Session::PrivetV3Session(scoped_ptr<PrivetHTTPClient> client)
- : client_(client.Pass()), code_confirmed_(false), weak_ptr_factory_(this) {
+ : client_(client.Pass()), weak_ptr_factory_(this) {
}
PrivetV3Session::~PrivetV3Session() {
}
void PrivetV3Session::Init(const InitCallback& callback) {
+ DCHECK(fetchers_.empty());
+ DCHECK(fingerprint_.empty());
+ DCHECK(session_id_.empty());
+ DCHECK(privet_auth_token_.empty());
+
privet_auth_token_ = kPrivetV3AuthAnonymous;
- // TODO: call /info.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&PrivetV3Session::RunCallback, weak_ptr_factory_.GetWeakPtr(),
- base::Bind(callback, Result::STATUS_SUCCESS,
- std::vector<PairingType>(
- 1, PairingType::PAIRING_TYPE_EMBEDDEDCODE))),
- base::TimeDelta::FromSeconds(1));
+ StartGetRequest(kPrivetInfoPath,
+ base::Bind(&PrivetV3Session::OnInfoDone,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void PrivetV3Session::OnInfoDone(const InitCallback& callback,
+ Result result,
+ const base::DictionaryValue& response) {
+ std::vector<PairingType> pairing_types;
+ if (result != Result::STATUS_SUCCESS)
+ return callback.Run(result, pairing_types);
+
+ std::string version;
+ if (!response.GetString(kPrivetV3InfoKeyVersion, &version) ||
+ version != kPrivetV3InfoVersion) {
+ return callback.Run(Result::STATUS_SESSIONERROR, pairing_types);
+ }
+
+ const base::DictionaryValue* authentication = nullptr;
+ const base::ListValue* pairing = nullptr;
+ if (!response.GetDictionary(kPrivetV3InfoKeyAuth, &authentication) ||
+ !authentication->GetList(kPrivetV3KeyPairing, &pairing)) {
+ return callback.Run(Result::STATUS_SESSIONERROR, pairing_types);
+ }
+
+ // The only supported crypto.
+ if (!ContainsString(*authentication, kPrivetV3KeyCrypto,
+ kPrivetV3CryptoP224Spake2) ||
+ !ContainsString(*authentication, kPrivetV3KeyMode, kPrivetV3KeyPairing)) {
+ return callback.Run(Result::STATUS_SESSIONERROR, pairing_types);
+ }
+
+ for (const base::Value* value : *pairing) {
+ std::string pairing_string;
+ PairingType pairing_type;
+ if (!value->GetAsString(&pairing_string) ||
+ !StringToEnum(pairing_string, &pairing_type)) {
+ continue; // Skip unknown pairing.
+ }
+ pairing_types.push_back(pairing_type);
+ }
+
+ callback.Run(Result::STATUS_SUCCESS, pairing_types);
}
void PrivetV3Session::StartPairing(PairingType pairing_type,
const ResultCallback& callback) {
- // TODO: call /privet/v3/pairing/start.
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&PrivetV3Session::RunCallback, weak_ptr_factory_.GetWeakPtr(),
- base::Bind(callback, Result::STATUS_SUCCESS)),
- base::TimeDelta::FromSeconds(1));
+ base::DictionaryValue input;
+ input.SetString(kPrivetV3KeyPairing, EnumToString(pairing_type));
+ input.SetString(kPrivetV3KeyCrypto, kPrivetV3CryptoP224Spake2);
+
+ StartPostRequest(kPrivetV3PairingStartPath, input,
+ base::Bind(&PrivetV3Session::OnPairingStartDone,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void PrivetV3Session::OnPairingStartDone(
+ const ResultCallback& callback,
+ Result result,
+ const base::DictionaryValue& response) {
+ if (result != Result::STATUS_SUCCESS)
+ return callback.Run(result);
+
+ if (!response.GetString(kPrivetV3KeySessionId, &session_id_) ||
+ !GetDecodedString(response, kPrivetV3KeyDeviceCommitment, &commitment_)) {
+ return callback.Run(Result::STATUS_SESSIONERROR);
+ }
+
+ return callback.Run(Result::STATUS_SUCCESS);
}
void PrivetV3Session::ConfirmCode(const std::string& code,
const ResultCallback& callback) {
- // TODO: call /privet/v3/pairing/confirm.
- if (code == kStubPrivetCode) {
- code_confirmed_ = true;
- callback.Run(Result::STATUS_SUCCESS);
- } else {
- callback.Run(Result::STATUS_BADPAIRINGCODEERROR);
+ if (session_id_.empty())
+ return callback.Run(Result::STATUS_SESSIONERROR);
+
+ spake_.reset(new crypto::P224EncryptedKeyExchange(
+ crypto::P224EncryptedKeyExchange::kPeerTypeClient, code));
+
+ base::DictionaryValue input;
+ input.SetString(kPrivetV3KeySessionId, session_id_);
+
+ std::string client_commitment;
+ base::Base64Encode(spake_->GetNextMessage(), &client_commitment);
+ input.SetString(kPrivetV3KeyClientCommitment, client_commitment);
+
+ // Call ProcessMessage after GetNextMessage().
+ crypto::P224EncryptedKeyExchange::Result result =
+ spake_->ProcessMessage(commitment_);
+ DCHECK_EQ(result, crypto::P224EncryptedKeyExchange::kResultPending);
+
+ StartPostRequest(kPrivetV3PairingConfirmPath, input,
+ base::Bind(&PrivetV3Session::OnPairingConfirmDone,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void PrivetV3Session::OnPairingConfirmDone(
+ const ResultCallback& callback,
+ Result result,
+ const base::DictionaryValue& response) {
+ if (result != Result::STATUS_SUCCESS)
+ return callback.Run(result);
+
+ std::string fingerprint;
+ std::string signature;
+ if (!GetDecodedString(response, kPrivetV3KeyCertFingerprint, &fingerprint) ||
+ !GetDecodedString(response, kPrivetV3KeyCertSignature, &signature)) {
+ return callback.Run(Result::STATUS_SESSIONERROR);
}
+
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ // Key will be verified below, using HMAC.
+ const std::string& key = spake_->GetUnverifiedKey();
+ if (!hmac.Init(reinterpret_cast<const unsigned char*>(key.c_str()),
+ key.size()) ||
+ !hmac.Verify(fingerprint, signature)) {
+ return callback.Run(Result::STATUS_SESSIONERROR);
+ }
+
+ return callback.Run(Result::STATUS_SUCCESS);
}
void PrivetV3Session::SendMessage(const std::string& api,
const base::DictionaryValue& input,
const MessageCallback& callback) {
- if (!code_confirmed_)
+ // TODO(vitalybuka): Implement validating HTTPS certificate using
+ // fingerprint_.
+ if (fingerprint_.empty())
return callback.Run(Result::STATUS_SESSIONERROR, base::DictionaryValue());
- FetcherDelegate* fetcher_delegate(new FetcherDelegate(
- weak_ptr_factory_.GetWeakPtr(), privet_auth_token_, callback));
- fetchers_.push_back(fetcher_delegate);
+ StartPostRequest(api, input, callback);
+}
+
+void PrivetV3Session::RunCallback(const base::Closure& callback) {
+ callback.Run();
+}
- scoped_ptr<PrivetURLFetcher> url_fetcher(client_->CreateURLFetcher(
- CreatePrivetURL(api), net::URLFetcher::POST, fetcher_delegate));
+void PrivetV3Session::StartGetRequest(const std::string& api,
+ const MessageCallback& callback) {
+ CreateFetcher(api, net::URLFetcher::RequestType::GET, callback)->Start();
+}
+void PrivetV3Session::StartPostRequest(const std::string& api,
+ const base::DictionaryValue& input,
+ const MessageCallback& callback) {
+ if (!on_post_data_.is_null())
+ on_post_data_.Run(input);
std::string json;
base::JSONWriter::WriteWithOptions(
&input, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
- url_fetcher->SetUploadData(cloud_print::kContentTypeJSON, json);
-
- fetcher_delegate->url_fetcher_ = url_fetcher.Pass();
- fetcher_delegate->url_fetcher_->V3Mode();
- fetcher_delegate->url_fetcher_->Start();
+ PrivetURLFetcher* fetcher =
+ CreateFetcher(api, net::URLFetcher::RequestType::POST, callback);
+ fetcher->SetUploadData(cloud_print::kContentTypeJSON, json);
+ fetcher->Start();
}
-void PrivetV3Session::RunCallback(const base::Closure& callback) {
- callback.Run();
+PrivetURLFetcher* PrivetV3Session::CreateFetcher(
+ const std::string& api,
+ net::URLFetcher::RequestType request_type,
+ const MessageCallback& callback) {
+ fetchers_.push_back(new FetcherDelegate(weak_ptr_factory_.GetWeakPtr(),
+ privet_auth_token_, callback));
+ return fetchers_.back()->CreateURLFetcher(CreatePrivetURL(api), request_type);
}
void PrivetV3Session::DeleteFetcher(const FetcherDelegate* fetcher) {
« 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