Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / local_discovery / privetv3_session_unittest.cc
blobe2b80e7b080b54d541b29d46bce27903f4332724
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
3 // found in the LICENSE file.
5 #include "chrome/browser/local_discovery/privetv3_session.h"
7 #include "base/base64.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "chrome/browser/local_discovery/privet_http.h"
11 #include "content/public/test/test_utils.h"
12 #include "crypto/hmac.h"
13 #include "crypto/p224_spake.h"
14 #include "net/url_request/test_url_fetcher_factory.h"
15 #include "net/url_request/url_request_test_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 namespace local_discovery {
21 namespace {
23 using testing::InSequence;
24 using testing::Invoke;
25 using testing::SaveArg;
26 using testing::StrictMock;
27 using testing::_;
29 using PairingType = PrivetV3Session::PairingType;
30 using Result = PrivetV3Session::Result;
32 const char kInfoResponse[] =
33 "{\"version\":\"3.0\","
34 "\"endpoints\":{\"httpsPort\": 443},"
35 "\"authentication\":{"
36 " \"mode\":[\"anonymous\",\"pairing\",\"cloud\"],"
37 " \"pairing\":[\"pinCode\",\"embeddedCode\"],"
38 " \"crypto\":[\"p224_spake2\"]"
39 "}}";
41 class MockPrivetHTTPClient : public PrivetHTTPClient {
42 public:
43 MockPrivetHTTPClient() {
44 request_context_ = new net::TestURLRequestContextGetter(
45 base::ThreadTaskRunnerHandle::Get());
48 MOCK_METHOD0(GetName, const std::string&());
49 MOCK_METHOD1(
50 CreateInfoOperationPtr,
51 PrivetJSONOperation*(const PrivetJSONOperation::ResultCallback&));
53 void RefreshPrivetToken(
54 const PrivetURLFetcher::TokenCallback& callback) override {
55 FAIL();
58 scoped_ptr<PrivetJSONOperation> CreateInfoOperation(
59 const PrivetJSONOperation::ResultCallback& callback) override {
60 return make_scoped_ptr(CreateInfoOperationPtr(callback));
63 scoped_ptr<PrivetURLFetcher> CreateURLFetcher(
64 const GURL& url,
65 net::URLFetcher::RequestType request_type,
66 PrivetURLFetcher::Delegate* delegate) override {
67 return make_scoped_ptr(new PrivetURLFetcher(
68 url, request_type, request_context_.get(), delegate));
71 scoped_refptr<net::TestURLRequestContextGetter> request_context_;
74 } // namespace
76 class PrivetV3SessionTest : public testing::Test {
77 public:
78 PrivetV3SessionTest()
79 : fetcher_factory_(nullptr),
80 session_(make_scoped_ptr(new MockPrivetHTTPClient())) {}
82 ~PrivetV3SessionTest() override {}
84 void OnInitialized(Result result, const base::DictionaryValue& info) {
85 info_.MergeDictionary(&info);
86 OnInitializedMock(result, info);
89 MOCK_METHOD2(OnInitializedMock, void(Result, const base::DictionaryValue&));
90 MOCK_METHOD1(OnPairingStarted, void(Result));
91 MOCK_METHOD1(OnCodeConfirmed, void(Result));
92 MOCK_METHOD2(OnMessageSend, void(Result, const base::DictionaryValue&));
93 MOCK_METHOD1(OnPostData, void(const base::DictionaryValue&));
95 protected:
96 void SetUp() override {
97 EXPECT_CALL(*this, OnInitializedMock(_, _)).Times(0);
98 EXPECT_CALL(*this, OnPairingStarted(_)).Times(0);
99 EXPECT_CALL(*this, OnCodeConfirmed(_)).Times(0);
100 EXPECT_CALL(*this, OnMessageSend(_, _)).Times(0);
101 EXPECT_CALL(*this, OnPostData(_)).Times(0);
102 session_.on_post_data_ =
103 base::Bind(&PrivetV3SessionTest::OnPostData, base::Unretained(this));
106 base::DictionaryValue info_;
107 base::MessageLoop loop_;
108 base::Closure quit_closure_;
109 net::FakeURLFetcherFactory fetcher_factory_;
110 PrivetV3Session session_;
113 TEST_F(PrivetV3SessionTest, InitError) {
114 EXPECT_CALL(*this, OnInitializedMock(Result::STATUS_CONNECTIONERROR, _))
115 .Times(1);
116 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"), "",
117 net::HTTP_OK, net::URLRequestStatus::FAILED);
118 session_.Init(
119 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
120 base::RunLoop().RunUntilIdle();
123 TEST_F(PrivetV3SessionTest, VersionError) {
124 std::string response(kInfoResponse);
125 base::ReplaceFirstSubstringAfterOffset(&response, 0, "3.0", "4.1");
127 EXPECT_CALL(*this, OnInitializedMock(Result::STATUS_SESSIONERROR, _))
128 .Times(1);
129 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"), response,
130 net::HTTP_OK,
131 net::URLRequestStatus::SUCCESS);
132 session_.Init(
133 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
134 base::RunLoop().RunUntilIdle();
137 TEST_F(PrivetV3SessionTest, ModeError) {
138 std::string response(kInfoResponse);
139 base::ReplaceFirstSubstringAfterOffset(&response, 0, "mode", "mode_");
141 EXPECT_CALL(*this, OnInitializedMock(Result::STATUS_SESSIONERROR, _))
142 .Times(1);
143 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"), response,
144 net::HTTP_OK,
145 net::URLRequestStatus::SUCCESS);
146 session_.Init(
147 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
148 base::RunLoop().RunUntilIdle();
151 TEST_F(PrivetV3SessionTest, Pairing) {
152 EXPECT_CALL(*this, OnInitializedMock(Result::STATUS_SUCCESS, _))
153 .Times(1);
154 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"),
155 kInfoResponse, net::HTTP_OK,
156 net::URLRequestStatus::SUCCESS);
158 session_.Init(
159 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
160 base::RunLoop().RunUntilIdle();
162 const base::ListValue* pairing = nullptr;
163 ASSERT_TRUE(info_.GetList("authentication.pairing", &pairing));
165 std::string pairing_string;
166 ASSERT_TRUE(pairing->GetString(0, &pairing_string));
167 EXPECT_EQ("pinCode", pairing_string);
169 ASSERT_TRUE(pairing->GetString(1, &pairing_string));
170 EXPECT_EQ("embeddedCode", pairing_string);
172 crypto::P224EncryptedKeyExchange spake(
173 crypto::P224EncryptedKeyExchange::kPeerTypeServer, "testPin");
175 EXPECT_CALL(*this, OnPairingStarted(Result::STATUS_SUCCESS)).Times(1);
176 EXPECT_CALL(*this, OnPostData(_))
177 .WillOnce(
178 testing::Invoke([this, &spake](const base::DictionaryValue& data) {
179 std::string pairing_type;
180 EXPECT_TRUE(data.GetString("pairing", &pairing_type));
181 EXPECT_EQ("embeddedCode", pairing_type);
183 std::string crypto_type;
184 EXPECT_TRUE(data.GetString("crypto", &crypto_type));
185 EXPECT_EQ("p224_spake2", crypto_type);
187 std::string device_commitment;
188 base::Base64Encode(spake.GetNextMessage(), &device_commitment);
189 fetcher_factory_.SetFakeResponse(
190 GURL("http://host/privet/v3/pairing/start"),
191 base::StringPrintf(
192 "{\"deviceCommitment\":\"%s\",\"sessionId\":\"testId\"}",
193 device_commitment.c_str()),
194 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
195 }));
196 session_.StartPairing(PairingType::PAIRING_TYPE_EMBEDDEDCODE,
197 base::Bind(&PrivetV3SessionTest::OnPairingStarted,
198 base::Unretained(this)));
199 base::RunLoop().RunUntilIdle();
201 EXPECT_TRUE(session_.fingerprint_.empty());
202 EXPECT_EQ("Privet anonymous", session_.privet_auth_token_);
204 EXPECT_CALL(*this, OnCodeConfirmed(Result::STATUS_SUCCESS)).Times(1);
205 InSequence in_sequence;
206 EXPECT_CALL(*this, OnPostData(_))
207 .WillOnce(
208 testing::Invoke([this, &spake](const base::DictionaryValue& data) {
209 std::string commitment_base64;
210 EXPECT_TRUE(data.GetString("clientCommitment", &commitment_base64));
211 std::string commitment;
212 EXPECT_TRUE(base::Base64Decode(commitment_base64, &commitment));
214 std::string session_id;
215 EXPECT_TRUE(data.GetString("sessionId", &session_id));
216 EXPECT_EQ("testId", session_id);
218 EXPECT_EQ(spake.ProcessMessage(commitment),
219 crypto::P224EncryptedKeyExchange::kResultPending);
221 std::string fingerprint("testFinterprint");
222 std::string fingerprint_base64;
223 base::Base64Encode(fingerprint, &fingerprint_base64);
225 crypto::HMAC hmac(crypto::HMAC::SHA256);
226 const std::string& key = spake.GetUnverifiedKey();
227 EXPECT_TRUE(hmac.Init(key));
228 std::string signature(hmac.DigestLength(), ' ');
229 EXPECT_TRUE(hmac.Sign(fingerprint, reinterpret_cast<unsigned char*>(
230 string_as_array(&signature)),
231 signature.size()));
233 std::string signature_base64;
234 base::Base64Encode(signature, &signature_base64);
236 fetcher_factory_.SetFakeResponse(
237 GURL("http://host/privet/v3/pairing/confirm"),
238 base::StringPrintf(
239 "{\"certFingerprint\":\"%s\",\"certSignature\":\"%s\"}",
240 fingerprint_base64.c_str(), signature_base64.c_str()),
241 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
242 }));
243 EXPECT_CALL(*this, OnPostData(_))
244 .WillOnce(
245 testing::Invoke([this, &spake](const base::DictionaryValue& data) {
246 std::string access_token_base64;
247 EXPECT_TRUE(data.GetString("authCode", &access_token_base64));
248 std::string access_token;
249 EXPECT_TRUE(base::Base64Decode(access_token_base64, &access_token));
251 crypto::HMAC hmac(crypto::HMAC::SHA256);
252 const std::string& key = spake.GetUnverifiedKey();
253 EXPECT_TRUE(hmac.Init(key));
254 EXPECT_TRUE(hmac.Verify("testId", access_token));
256 fetcher_factory_.SetFakeResponse(
257 GURL("http://host/privet/v3/auth"),
258 "{\"accessToken\":\"567\",\"tokenType\":\"testType\","
259 "\"scope\":\"owner\"}",
260 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
261 }));
262 session_.ConfirmCode("testPin",
263 base::Bind(&PrivetV3SessionTest::OnCodeConfirmed,
264 base::Unretained(this)));
265 base::RunLoop().RunUntilIdle();
267 EXPECT_FALSE(session_.fingerprint_.empty());
268 EXPECT_EQ("testType 567", session_.privet_auth_token_);
271 TEST_F(PrivetV3SessionTest, Cancel) {
272 EXPECT_CALL(*this, OnInitializedMock(Result::STATUS_SUCCESS, _)).Times(1);;
273 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"),
274 kInfoResponse, net::HTTP_OK,
275 net::URLRequestStatus::SUCCESS);
277 session_.Init(
278 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
279 base::RunLoop().RunUntilIdle();
281 EXPECT_CALL(*this, OnPairingStarted(Result::STATUS_SUCCESS)).Times(1);
282 EXPECT_CALL(*this, OnPostData(_))
283 .WillOnce(testing::Invoke([this](const base::DictionaryValue& data) {
284 std::string device_commitment;
285 base::Base64Encode("1234", &device_commitment);
286 fetcher_factory_.SetFakeResponse(
287 GURL("http://host/privet/v3/pairing/start"),
288 base::StringPrintf(
289 "{\"deviceCommitment\":\"%s\",\"sessionId\":\"testId\"}",
290 device_commitment.c_str()),
291 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
292 }));
293 session_.StartPairing(PairingType::PAIRING_TYPE_EMBEDDEDCODE,
294 base::Bind(&PrivetV3SessionTest::OnPairingStarted,
295 base::Unretained(this)));
296 base::RunLoop().RunUntilIdle();
298 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/v3/pairing/cancel"),
299 kInfoResponse, net::HTTP_OK,
300 net::URLRequestStatus::SUCCESS);
301 EXPECT_CALL(*this, OnPostData(_))
302 .WillOnce(testing::Invoke([this](const base::DictionaryValue& data) {
303 std::string session_id;
304 EXPECT_TRUE(data.GetString("sessionId", &session_id));
305 }));
308 // TODO(vitalybuka): replace PrivetHTTPClient with regular URL fetcher and
309 // implement SendMessage test.
311 } // namespace local_discovery