Roll libvpx 861f35:1fff3e
[chromium-blink-merge.git] / components / proximity_auth / client_unittest.cc
blob49c0b1ace197a66b3e3d06de8903756ea277aaf4
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 "components/proximity_auth/client.h"
7 #include "base/macros.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "components/proximity_auth/client_observer.h"
10 #include "components/proximity_auth/connection.h"
11 #include "components/proximity_auth/remote_device.h"
12 #include "components/proximity_auth/remote_status_update.h"
13 #include "components/proximity_auth/secure_context.h"
14 #include "components/proximity_auth/wire_message.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 using testing::_;
19 using testing::AllOf;
20 using testing::EndsWith;
21 using testing::Eq;
22 using testing::Field;
23 using testing::NiceMock;
24 using testing::Pointee;
25 using testing::Return;
26 using testing::StrictMock;
28 namespace proximity_auth {
29 namespace {
31 const char kChallenge[] = "a most difficult challenge";
32 const char kFakeEncodingSuffix[] = ", but encoded";
34 class MockSecureContext : public SecureContext {
35 public:
36 MockSecureContext() {
37 // By default, mock a secure context that uses the 3.1 protocol. Individual
38 // tests override this as needed.
39 ON_CALL(*this, GetProtocolVersion())
40 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ONE));
42 virtual ~MockSecureContext() {}
44 MOCK_CONST_METHOD0(GetReceivedAuthMessage, std::string());
45 MOCK_CONST_METHOD0(GetProtocolVersion, ProtocolVersion());
47 virtual std::string Encode(const std::string& message) override {
48 return message + kFakeEncodingSuffix;
51 virtual std::string Decode(const std::string& encoded_message) override {
52 EXPECT_THAT(encoded_message, EndsWith(kFakeEncodingSuffix));
53 std::string decoded_message = encoded_message;
54 decoded_message.erase(decoded_message.rfind(kFakeEncodingSuffix));
55 return decoded_message;
58 private:
59 DISALLOW_COPY_AND_ASSIGN(MockSecureContext);
62 class FakeConnection : public Connection {
63 public:
64 FakeConnection() : Connection(RemoteDevice()) { Connect(); }
65 ~FakeConnection() override { Disconnect(); }
67 void Connect() override { SetStatus(CONNECTED); }
69 void Disconnect() override { SetStatus(DISCONNECTED); }
71 void SendMessageImpl(scoped_ptr<WireMessage> message) override {
72 ASSERT_FALSE(current_message_);
73 current_message_ = message.Pass();
76 // Completes the current send operation with success |success|.
77 void FinishSendingMessageWithSuccess(bool success) {
78 ASSERT_TRUE(current_message_);
79 // Capture a copy of the message, as OnDidSendMessage() might reentrantly
80 // call SendMessage().
81 scoped_ptr<WireMessage> sent_message = current_message_.Pass();
82 OnDidSendMessage(*sent_message, success);
85 // Simulates receiving a wire message with the given |payload|.
86 void ReceiveMessageWithPayload(const std::string& payload) {
87 pending_payload_ = payload;
88 OnBytesReceived(std::string());
89 pending_payload_.clear();
92 // Returns a message containing the payload set via
93 // ReceiveMessageWithPayload().
94 scoped_ptr<WireMessage> DeserializeWireMessage(
95 bool* is_incomplete_message) override {
96 *is_incomplete_message = false;
97 return make_scoped_ptr(new WireMessage(std::string(), pending_payload_));
100 WireMessage* current_message() { return current_message_.get(); }
102 private:
103 // The message currently being sent. Only set between a call to
104 // SendMessageImpl() and FinishSendingMessageWithSuccess().
105 scoped_ptr<WireMessage> current_message_;
107 // The payload that should be returned when DeserializeWireMessage() is
108 // called.
109 std::string pending_payload_;
111 DISALLOW_COPY_AND_ASSIGN(FakeConnection);
114 class MockClientObserver : public ClientObserver {
115 public:
116 explicit MockClientObserver(Client* client) : client_(client) {
117 client_->AddObserver(this);
119 virtual ~MockClientObserver() { client_->RemoveObserver(this); }
121 MOCK_METHOD1(OnUnlockEventSent, void(bool success));
122 MOCK_METHOD1(OnRemoteStatusUpdate,
123 void(const RemoteStatusUpdate& status_update));
124 MOCK_METHOD1(OnDecryptResponseProxy,
125 void(const std::string* decrypted_bytes));
126 MOCK_METHOD1(OnUnlockResponse, void(bool success));
127 MOCK_METHOD0(OnDisconnected, void());
129 virtual void OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
130 OnDecryptResponseProxy(decrypted_bytes.get());
133 private:
134 // The client that |this| instance observes.
135 Client* const client_;
137 DISALLOW_COPY_AND_ASSIGN(MockClientObserver);
140 class TestClient : public Client {
141 public:
142 TestClient()
143 : Client(make_scoped_ptr(new NiceMock<FakeConnection>()),
144 make_scoped_ptr(new NiceMock<MockSecureContext>())) {}
145 ~TestClient() override {}
147 // Simple getters for the mock objects owned by |this| client.
148 FakeConnection* GetFakeConnection() {
149 return static_cast<FakeConnection*>(connection());
151 MockSecureContext* GetMockSecureContext() {
152 return static_cast<MockSecureContext*>(secure_context());
155 private:
156 DISALLOW_COPY_AND_ASSIGN(TestClient);
159 } // namespace
161 TEST(ProximityAuthClientTest, SupportsSignIn_ProtocolVersionThreeZero) {
162 TestClient client;
163 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
164 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
165 EXPECT_FALSE(client.SupportsSignIn());
168 TEST(ProximityAuthClientTest, SupportsSignIn_ProtocolVersionThreeOne) {
169 TestClient client;
170 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
171 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ONE));
172 EXPECT_TRUE(client.SupportsSignIn());
175 TEST(ProximityAuthClientTest, OnConnectionStatusChanged_ConnectionDisconnects) {
176 TestClient client;
177 MockClientObserver observer(&client);
179 EXPECT_CALL(observer, OnDisconnected());
180 client.GetFakeConnection()->Disconnect();
183 TEST(ProximityAuthClientTest, DispatchUnlockEvent_SendsExpectedMessage) {
184 TestClient client;
185 client.DispatchUnlockEvent();
187 WireMessage* message = client.GetFakeConnection()->current_message();
188 ASSERT_TRUE(message);
189 EXPECT_EQ(std::string(), message->permit_id());
190 EXPECT_EQ(
192 "\"name\":\"easy_unlock\","
193 "\"type\":\"event\""
194 "}, but encoded",
195 message->payload());
198 TEST(ProximityAuthClientTest, DispatchUnlockEvent_SendMessageFails) {
199 TestClient client;
200 MockClientObserver observer(&client);
201 client.DispatchUnlockEvent();
203 EXPECT_CALL(observer, OnUnlockEventSent(false));
204 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
207 TEST(ProximityAuthClientTest, DispatchUnlockEvent_SendMessageSucceeds) {
208 TestClient client;
209 MockClientObserver observer(&client);
210 client.DispatchUnlockEvent();
212 EXPECT_CALL(observer, OnUnlockEventSent(true));
213 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
216 TEST(ProximityAuthClientTest,
217 RequestDecryption_SignInUnsupported_DoesntSendMessage) {
218 TestClient client;
219 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
220 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
221 client.RequestDecryption(kChallenge);
222 EXPECT_FALSE(client.GetFakeConnection()->current_message());
225 TEST(ProximityAuthClientTest, RequestDecryption_SendsExpectedMessage) {
226 TestClient client;
227 client.RequestDecryption(kChallenge);
229 WireMessage* message = client.GetFakeConnection()->current_message();
230 ASSERT_TRUE(message);
231 EXPECT_EQ(std::string(), message->permit_id());
232 EXPECT_EQ(
234 "\"encrypted_data\":\"YSBtb3N0IGRpZmZpY3VsdCBjaGFsbGVuZ2U=\","
235 "\"type\":\"decrypt_request\""
236 "}, but encoded",
237 message->payload());
240 TEST(ProximityAuthClientTest,
241 RequestDecryption_SendsExpectedMessage_UsingBase64UrlEncoding) {
242 TestClient client;
243 client.RequestDecryption("\xFF\xE6");
245 WireMessage* message = client.GetFakeConnection()->current_message();
246 ASSERT_TRUE(message);
247 EXPECT_EQ(std::string(), message->permit_id());
248 EXPECT_EQ(
250 "\"encrypted_data\":\"_-Y=\","
251 "\"type\":\"decrypt_request\""
252 "}, but encoded",
253 message->payload());
256 TEST(ProximityAuthClientTest, RequestDecryption_SendMessageFails) {
257 TestClient client;
258 MockClientObserver observer(&client);
259 client.RequestDecryption(kChallenge);
261 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
262 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
265 TEST(ProximityAuthClientTest, RequestDecryption_SendSucceeds_WaitsForReply) {
266 TestClient client;
267 MockClientObserver observer(&client);
268 client.RequestDecryption(kChallenge);
270 EXPECT_CALL(observer, OnDecryptResponseProxy(_)).Times(0);
271 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
274 TEST(ProximityAuthClientTest,
275 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_NoData) {
276 TestClient client;
277 MockClientObserver observer(&client);
278 client.RequestDecryption(kChallenge);
279 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
281 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
282 client.GetFakeConnection()->ReceiveMessageWithPayload(
283 "{\"type\":\"decrypt_response\"}, but encoded");
286 TEST(ProximityAuthClientTest,
287 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_InvalidData) {
288 TestClient client;
289 MockClientObserver observer(&client);
290 client.RequestDecryption(kChallenge);
291 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
293 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
294 client.GetFakeConnection()->ReceiveMessageWithPayload(
296 "\"type\":\"decrypt_response\","
297 "\"data\":\"not a base64-encoded string\""
298 "}, but encoded");
301 TEST(ProximityAuthClientTest,
302 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_ValidData) {
303 TestClient client;
304 MockClientObserver observer(&client);
305 client.RequestDecryption(kChallenge);
306 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
308 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("a winner is you"))));
309 client.GetFakeConnection()->ReceiveMessageWithPayload(
311 "\"type\":\"decrypt_response\","
312 "\"data\":\"YSB3aW5uZXIgaXMgeW91\"" // "a winner is you", base64-encoded
313 "}, but encoded");
316 // Verify that the client correctly parses base64url encoded data.
317 TEST(ProximityAuthClientTest,
318 RequestDecryption_SendSucceeds_ParsesBase64UrlEncodingInReply) {
319 TestClient client;
320 MockClientObserver observer(&client);
321 client.RequestDecryption(kChallenge);
322 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
324 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("\xFF\xE6"))));
325 client.GetFakeConnection()->ReceiveMessageWithPayload(
327 "\"type\":\"decrypt_response\","
328 "\"data\":\"_-Y=\"" // "\0xFF\0xE6", base64url-encoded.
329 "}, but encoded");
332 TEST(ProximityAuthClientTest,
333 RequestUnlock_SignInUnsupported_DoesntSendMessage) {
334 TestClient client;
335 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
336 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
337 client.RequestUnlock();
338 EXPECT_FALSE(client.GetFakeConnection()->current_message());
341 TEST(ProximityAuthClientTest, RequestUnlock_SendsExpectedMessage) {
342 TestClient client;
343 client.RequestUnlock();
345 WireMessage* message = client.GetFakeConnection()->current_message();
346 ASSERT_TRUE(message);
347 EXPECT_EQ(std::string(), message->permit_id());
348 EXPECT_EQ("{\"type\":\"unlock_request\"}, but encoded", message->payload());
351 TEST(ProximityAuthClientTest, RequestUnlock_SendMessageFails) {
352 TestClient client;
353 MockClientObserver observer(&client);
354 client.RequestUnlock();
356 EXPECT_CALL(observer, OnUnlockResponse(false));
357 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
360 TEST(ProximityAuthClientTest, RequestUnlock_SendSucceeds_WaitsForReply) {
361 TestClient client;
362 MockClientObserver observer(&client);
363 client.RequestUnlock();
365 EXPECT_CALL(observer, OnUnlockResponse(_)).Times(0);
366 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
369 TEST(ProximityAuthClientTest,
370 RequestUnlock_SendSucceeds_NotifiesObserversOnReply) {
371 TestClient client;
372 MockClientObserver observer(&client);
373 client.RequestUnlock();
374 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
376 EXPECT_CALL(observer, OnUnlockResponse(true));
377 client.GetFakeConnection()->ReceiveMessageWithPayload(
378 "{\"type\":\"unlock_response\"}, but encoded");
381 TEST(ProximityAuthClientTest, OnMessageReceived_RemoteStatusUpdate_Invalid) {
382 TestClient client;
383 MockClientObserver observer(&client);
385 // Receive a status update message that's missing all the data.
386 EXPECT_CALL(observer, OnRemoteStatusUpdate(_)).Times(0);
387 client.GetFakeConnection()->ReceiveMessageWithPayload(
388 "{\"type\":\"status_update\"}, but encoded");
391 TEST(ProximityAuthClientTest, OnMessageReceived_RemoteStatusUpdate_Valid) {
392 TestClient client;
393 MockClientObserver observer(&client);
395 EXPECT_CALL(observer,
396 OnRemoteStatusUpdate(
397 AllOf(Field(&RemoteStatusUpdate::user_presence, USER_PRESENT),
398 Field(&RemoteStatusUpdate::secure_screen_lock_state,
399 SECURE_SCREEN_LOCK_ENABLED),
400 Field(&RemoteStatusUpdate::trust_agent_state,
401 TRUST_AGENT_UNSUPPORTED))));
402 client.GetFakeConnection()->ReceiveMessageWithPayload(
404 "\"type\":\"status_update\","
405 "\"user_presence\":\"present\","
406 "\"secure_screen_lock\":\"enabled\","
407 "\"trust_agent\":\"unsupported\""
408 "}, but encoded");
411 TEST(ProximityAuthClientTest, OnMessageReceived_InvalidJSON) {
412 TestClient client;
413 StrictMock<MockClientObserver> observer(&client);
414 client.RequestUnlock();
415 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
417 // The StrictMock will verify that no observer methods are called.
418 client.GetFakeConnection()->ReceiveMessageWithPayload(
419 "Not JSON, but encoded");
422 TEST(ProximityAuthClientTest, OnMessageReceived_MissingTypeField) {
423 TestClient client;
424 StrictMock<MockClientObserver> observer(&client);
425 client.RequestUnlock();
426 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
428 // The StrictMock will verify that no observer methods are called.
429 client.GetFakeConnection()->ReceiveMessageWithPayload(
430 "{\"some key that's not 'type'\":\"some value\"}, but encoded");
433 TEST(ProximityAuthClientTest, OnMessageReceived_UnexpectedReply) {
434 TestClient client;
435 StrictMock<MockClientObserver> observer(&client);
437 // The StrictMock will verify that no observer methods are called.
438 client.GetFakeConnection()->ReceiveMessageWithPayload(
439 "{\"type\":\"unlock_response\"}, but encoded");
442 TEST(ProximityAuthClientTest,
443 OnMessageReceived_MismatchedReply_UnlockInReplyToDecrypt) {
444 TestClient client;
445 StrictMock<MockClientObserver> observer(&client);
447 client.RequestDecryption(kChallenge);
448 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
450 // The StrictMock will verify that no observer methods are called.
451 client.GetFakeConnection()->ReceiveMessageWithPayload(
452 "{\"type\":\"unlock_response\"}, but encoded");
455 TEST(ProximityAuthClientTest,
456 OnMessageReceived_MismatchedReply_DecryptInReplyToUnlock) {
457 TestClient client;
458 StrictMock<MockClientObserver> observer(&client);
460 client.RequestUnlock();
461 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
463 // The StrictMock will verify that no observer methods are called.
464 client.GetFakeConnection()->ReceiveMessageWithPayload(
466 "\"type\":\"decrypt_response\","
467 "\"data\":\"YSB3aW5uZXIgaXMgeW91\""
468 "}, but encoded");
471 TEST(ProximityAuthClientTest, BuffersMessages_WhileSending) {
472 TestClient client;
473 MockClientObserver observer(&client);
475 // Initiate a decryption request, and then initiate an unlock request before
476 // the decryption request is even finished sending.
477 client.RequestDecryption(kChallenge);
478 client.RequestUnlock();
480 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
481 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
483 EXPECT_CALL(observer, OnUnlockResponse(false));
484 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
487 TEST(ProximityAuthClientTest, BuffersMessages_WhileAwaitingReply) {
488 TestClient client;
489 MockClientObserver observer(&client);
491 // Initiate a decryption request, and allow the message to be sent.
492 client.RequestDecryption(kChallenge);
493 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
495 // At this point, the client is awaiting a reply to the decryption message.
496 // While it's waiting, initiate an unlock request.
497 client.RequestUnlock();
499 // Now simulate a response arriving for the original decryption request.
500 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("a winner is you"))));
501 client.GetFakeConnection()->ReceiveMessageWithPayload(
503 "\"type\":\"decrypt_response\","
504 "\"data\":\"YSB3aW5uZXIgaXMgeW91\""
505 "}, but encoded");
507 // The unlock request should have remained buffered, and should only now be
508 // sent.
509 EXPECT_CALL(observer, OnUnlockResponse(false));
510 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
513 } // namespace proximity_auth