Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / local_discovery / privetv3_session_unittest.cc
blob3e9c1a42e19c702629ebdee51f7c00f6a9664711
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 "chrome/browser/local_discovery/privet_http.h"
10 #include "content/public/test/test_utils.h"
11 #include "crypto/hmac.h"
12 #include "crypto/p224_spake.h"
13 #include "net/url_request/test_url_fetcher_factory.h"
14 #include "net/url_request/url_request_test_util.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 namespace local_discovery {
20 namespace {
22 using testing::InSequence;
23 using testing::Invoke;
24 using testing::SaveArg;
25 using testing::StrictMock;
26 using testing::_;
28 using PairingType = PrivetV3Session::PairingType;
29 using Result = PrivetV3Session::Result;
31 const char kInfoResponse[] =
32 "{\"version\":\"3.0\","
33 "\"endpoints\":{\"httpsPort\": 443},"
34 "\"authentication\":{"
35 " \"mode\":[\"anonymous\",\"pairing\",\"cloud\"],"
36 " \"pairing\":[\"pinCode\",\"embeddedCode\"],"
37 " \"crypto\":[\"p224_spake2\"]"
38 "}}";
40 class MockPrivetHTTPClient : public PrivetHTTPClient {
41 public:
42 MockPrivetHTTPClient() {
43 request_context_ =
44 new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
47 MOCK_METHOD0(GetName, const std::string&());
48 MOCK_METHOD1(
49 CreateInfoOperationPtr,
50 PrivetJSONOperation*(const PrivetJSONOperation::ResultCallback&));
52 virtual void RefreshPrivetToken(
53 const PrivetURLFetcher::TokenCallback& callback) override {
54 FAIL();
57 virtual scoped_ptr<PrivetJSONOperation> CreateInfoOperation(
58 const PrivetJSONOperation::ResultCallback& callback) override {
59 return make_scoped_ptr(CreateInfoOperationPtr(callback));
62 virtual scoped_ptr<PrivetURLFetcher> CreateURLFetcher(
63 const GURL& url,
64 net::URLFetcher::RequestType request_type,
65 PrivetURLFetcher::Delegate* delegate) override {
66 return make_scoped_ptr(new PrivetURLFetcher(
67 url, request_type, request_context_.get(), delegate));
70 scoped_refptr<net::TestURLRequestContextGetter> request_context_;
73 } // namespace
75 class PrivetV3SessionTest : public testing::Test {
76 public:
77 PrivetV3SessionTest()
78 : fetcher_factory_(nullptr),
79 session_(make_scoped_ptr(new MockPrivetHTTPClient())) {}
81 virtual ~PrivetV3SessionTest() {}
83 MOCK_METHOD2(OnInitialized, void(Result, const std::vector<PairingType>&));
84 MOCK_METHOD1(OnPairingStarted, void(Result));
85 MOCK_METHOD1(OnCodeConfirmed, void(Result));
86 MOCK_METHOD2(OnMessageSend, void(Result, const base::DictionaryValue& value));
87 MOCK_METHOD1(OnPostData, void(const base::DictionaryValue& data));
89 protected:
90 virtual void SetUp() override {
91 EXPECT_CALL(*this, OnInitialized(_, _)).Times(0);
92 EXPECT_CALL(*this, OnPairingStarted(_)).Times(0);
93 EXPECT_CALL(*this, OnCodeConfirmed(_)).Times(0);
94 EXPECT_CALL(*this, OnMessageSend(_, _)).Times(0);
95 EXPECT_CALL(*this, OnPostData(_)).Times(0);
96 session_.on_post_data_ =
97 base::Bind(&PrivetV3SessionTest::OnPostData, base::Unretained(this));
100 base::MessageLoop loop_;
101 base::Closure quit_closure_;
102 net::FakeURLFetcherFactory fetcher_factory_;
103 PrivetV3Session session_;
106 TEST_F(PrivetV3SessionTest, InitError) {
107 EXPECT_CALL(*this, OnInitialized(Result::STATUS_CONNECTIONERROR, _)).Times(1);
108 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"), "",
109 net::HTTP_OK, net::URLRequestStatus::FAILED);
110 session_.Init(
111 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
112 base::RunLoop().RunUntilIdle();
115 TEST_F(PrivetV3SessionTest, VersionError) {
116 std::string response(kInfoResponse);
117 ReplaceFirstSubstringAfterOffset(&response, 0, "3.0", "4.1");
119 EXPECT_CALL(*this, OnInitialized(Result::STATUS_SESSIONERROR, _)).Times(1);
120 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"), response,
121 net::HTTP_OK,
122 net::URLRequestStatus::SUCCESS);
123 session_.Init(
124 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
125 base::RunLoop().RunUntilIdle();
128 TEST_F(PrivetV3SessionTest, ModeError) {
129 std::string response(kInfoResponse);
130 ReplaceFirstSubstringAfterOffset(&response, 0, "mode", "mode_");
132 EXPECT_CALL(*this, OnInitialized(Result::STATUS_SESSIONERROR, _)).Times(1);
133 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"), response,
134 net::HTTP_OK,
135 net::URLRequestStatus::SUCCESS);
136 session_.Init(
137 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
138 base::RunLoop().RunUntilIdle();
141 TEST_F(PrivetV3SessionTest, Pairing) {
142 std::vector<PairingType> pairings;
143 EXPECT_CALL(*this, OnInitialized(Result::STATUS_SUCCESS, _))
144 .WillOnce(SaveArg<1>(&pairings));
145 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"),
146 kInfoResponse, net::HTTP_OK,
147 net::URLRequestStatus::SUCCESS);
149 session_.Init(
150 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
151 base::RunLoop().RunUntilIdle();
153 EXPECT_EQ(2u, pairings.size());
154 EXPECT_EQ(PairingType::PAIRING_TYPE_PINCODE, pairings[0]);
155 EXPECT_EQ(PairingType::PAIRING_TYPE_EMBEDDEDCODE, pairings[1]);
157 crypto::P224EncryptedKeyExchange spake(
158 crypto::P224EncryptedKeyExchange::kPeerTypeServer, "testPin");
160 EXPECT_CALL(*this, OnPairingStarted(Result::STATUS_SUCCESS)).Times(1);
161 EXPECT_CALL(*this, OnPostData(_))
162 .WillOnce(
163 testing::Invoke([this, &spake](const base::DictionaryValue& data) {
164 std::string pairing_type;
165 EXPECT_TRUE(data.GetString("pairing", &pairing_type));
166 EXPECT_EQ("embeddedCode", pairing_type);
168 std::string crypto_type;
169 EXPECT_TRUE(data.GetString("crypto", &crypto_type));
170 EXPECT_EQ("p224_spake2", crypto_type);
172 std::string device_commitment;
173 base::Base64Encode(spake.GetNextMessage(), &device_commitment);
174 fetcher_factory_.SetFakeResponse(
175 GURL("http://host/privet/v3/pairing/start"),
176 base::StringPrintf(
177 "{\"deviceCommitment\":\"%s\",\"sessionId\":\"testId\"}",
178 device_commitment.c_str()),
179 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
180 }));
181 session_.StartPairing(PairingType::PAIRING_TYPE_EMBEDDEDCODE,
182 base::Bind(&PrivetV3SessionTest::OnPairingStarted,
183 base::Unretained(this)));
184 base::RunLoop().RunUntilIdle();
186 EXPECT_TRUE(session_.fingerprint_.empty());
187 EXPECT_EQ("Privet anonymous", session_.privet_auth_token_);
189 EXPECT_CALL(*this, OnCodeConfirmed(Result::STATUS_SUCCESS)).Times(1);
190 InSequence in_sequence;
191 EXPECT_CALL(*this, OnPostData(_))
192 .WillOnce(
193 testing::Invoke([this, &spake](const base::DictionaryValue& data) {
194 std::string commitment_base64;
195 EXPECT_TRUE(data.GetString("clientCommitment", &commitment_base64));
196 std::string commitment;
197 EXPECT_TRUE(base::Base64Decode(commitment_base64, &commitment));
199 std::string session_id;
200 EXPECT_TRUE(data.GetString("sessionId", &session_id));
201 EXPECT_EQ("testId", session_id);
203 EXPECT_EQ(spake.ProcessMessage(commitment),
204 crypto::P224EncryptedKeyExchange::kResultPending);
206 std::string fingerprint("testFinterprint");
207 std::string fingerprint_base64;
208 base::Base64Encode(fingerprint, &fingerprint_base64);
210 crypto::HMAC hmac(crypto::HMAC::SHA256);
211 const std::string& key = spake.GetUnverifiedKey();
212 EXPECT_TRUE(hmac.Init(key));
213 std::string signature(hmac.DigestLength(), ' ');
214 EXPECT_TRUE(hmac.Sign(fingerprint, reinterpret_cast<unsigned char*>(
215 string_as_array(&signature)),
216 signature.size()));
218 std::string signature_base64;
219 base::Base64Encode(signature, &signature_base64);
221 fetcher_factory_.SetFakeResponse(
222 GURL("http://host/privet/v3/pairing/confirm"),
223 base::StringPrintf(
224 "{\"certFingerprint\":\"%s\",\"certSignature\":\"%s\"}",
225 fingerprint_base64.c_str(), signature_base64.c_str()),
226 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
227 }));
228 EXPECT_CALL(*this, OnPostData(_))
229 .WillOnce(
230 testing::Invoke([this, &spake](const base::DictionaryValue& data) {
231 std::string access_token_base64;
232 EXPECT_TRUE(data.GetString("authCode", &access_token_base64));
233 std::string access_token;
234 EXPECT_TRUE(base::Base64Decode(access_token_base64, &access_token));
236 crypto::HMAC hmac(crypto::HMAC::SHA256);
237 const std::string& key = spake.GetUnverifiedKey();
238 EXPECT_TRUE(hmac.Init(key));
239 EXPECT_TRUE(hmac.Verify("testId", access_token));
241 fetcher_factory_.SetFakeResponse(
242 GURL("http://host/privet/v3/auth"),
243 "{\"accessToken\":\"567\",\"tokenType\":\"testType\","
244 "\"scope\":\"owner\"}",
245 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
246 }));
247 session_.ConfirmCode("testPin",
248 base::Bind(&PrivetV3SessionTest::OnCodeConfirmed,
249 base::Unretained(this)));
250 base::RunLoop().RunUntilIdle();
252 EXPECT_FALSE(session_.fingerprint_.empty());
253 EXPECT_EQ("testType 567", session_.privet_auth_token_);
256 TEST_F(PrivetV3SessionTest, Cancel) {
257 EXPECT_CALL(*this, OnInitialized(Result::STATUS_SUCCESS, _));
258 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/info"),
259 kInfoResponse, net::HTTP_OK,
260 net::URLRequestStatus::SUCCESS);
262 session_.Init(
263 base::Bind(&PrivetV3SessionTest::OnInitialized, base::Unretained(this)));
264 base::RunLoop().RunUntilIdle();
266 EXPECT_CALL(*this, OnPairingStarted(Result::STATUS_SUCCESS)).Times(1);
267 EXPECT_CALL(*this, OnPostData(_))
268 .WillOnce(testing::Invoke([this](const base::DictionaryValue& data) {
269 std::string device_commitment;
270 base::Base64Encode("1234", &device_commitment);
271 fetcher_factory_.SetFakeResponse(
272 GURL("http://host/privet/v3/pairing/start"),
273 base::StringPrintf(
274 "{\"deviceCommitment\":\"%s\",\"sessionId\":\"testId\"}",
275 device_commitment.c_str()),
276 net::HTTP_OK, net::URLRequestStatus::SUCCESS);
277 }));
278 session_.StartPairing(PairingType::PAIRING_TYPE_EMBEDDEDCODE,
279 base::Bind(&PrivetV3SessionTest::OnPairingStarted,
280 base::Unretained(this)));
281 base::RunLoop().RunUntilIdle();
283 fetcher_factory_.SetFakeResponse(GURL("http://host/privet/v3/pairing/cancel"),
284 kInfoResponse, net::HTTP_OK,
285 net::URLRequestStatus::SUCCESS);
286 EXPECT_CALL(*this, OnPostData(_))
287 .WillOnce(testing::Invoke([this](const base::DictionaryValue& data) {
288 std::string session_id;
289 EXPECT_TRUE(data.GetString("sessionId", &session_id));
290 }));
293 // TODO(vitalybuka): replace PrivetHTTPClient with regular URL fetcher and
294 // implement SendMessage test.
296 } // namespace local_discovery