1 // Copyright 2015 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/cryptauth/cryptauth_enroller_impl.h"
8 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_utils.h"
9 #include "components/proximity_auth/cryptauth/fake_secure_message_delegate.h"
10 #include "components/proximity_auth/cryptauth/mock_cryptauth_client.h"
11 #include "testing/gtest/include/gtest/gtest.h"
14 using ::testing::Return
;
16 namespace proximity_auth
{
20 const char kAccessTokenUsed
[] = "access token used by CryptAuthClient";
22 const char kClientSessionPublicKey
[] = "throw away after one use";
23 const char kServerSessionPublicKey
[] = "disposables are not eco-friendly";
25 cryptauth::InvocationReason kInvocationReason
=
26 cryptauth::INVOCATION_REASON_MANUAL
;
27 const int kGCMMetadataVersion
= 1;
28 const char kSupportedEnrollmentTypeGcmV1
[] = "gcmV1";
29 const char kResponseStatusOk
[] = "ok";
30 const char kResponseStatusNotOk
[] = "Your key was too bland.";
31 const char kEnrollmentSessionId
[] = "0123456789876543210";
32 const char kFinishEnrollmentError
[] = "A hungry router ate all your packets.";
34 const char kDeviceId
[] = "2015 AD";
35 const cryptauth::DeviceType kDeviceType
= cryptauth::CHROME
;
36 const char kDeviceOsVersion
[] = "41.0.0";
38 // Creates and returns the GcmDeviceInfo message to be uploaded.
39 cryptauth::GcmDeviceInfo
GetDeviceInfo() {
40 cryptauth::GcmDeviceInfo device_info
;
41 device_info
.set_long_device_id(kDeviceId
);
42 device_info
.set_device_type(kDeviceType
);
43 device_info
.set_device_os_version(kDeviceOsVersion
);
47 // Creates and returns the SetupEnrollmentResponse message to be returned to the
48 // enroller with the session_. If |success| is false, then a bad response will
50 cryptauth::SetupEnrollmentResponse
GetSetupEnrollmentResponse(bool success
) {
51 cryptauth::SetupEnrollmentResponse response
;
53 response
.set_status(kResponseStatusNotOk
);
57 response
.set_status(kResponseStatusOk
);
58 cryptauth::SetupEnrollmentInfo
* info
= response
.add_infos();
59 info
->set_type(kSupportedEnrollmentTypeGcmV1
);
60 info
->set_enrollment_session_id(kEnrollmentSessionId
);
61 info
->set_server_ephemeral_key(kServerSessionPublicKey
);
65 // Creates and returns the FinishEnrollmentResponse message to be returned to
66 // the enroller with the session_. If |success| is false, then a bad response
68 cryptauth::FinishEnrollmentResponse
GetFinishEnrollmentResponse(bool success
) {
69 cryptauth::FinishEnrollmentResponse response
;
71 response
.set_status(kResponseStatusOk
);
73 response
.set_status(kResponseStatusNotOk
);
74 response
.set_error_message(kFinishEnrollmentError
);
79 // Callback that saves the key returned by SecureMessageDelegate::DeriveKey().
80 void SaveDerivedKey(std::string
* value_out
, const std::string
& value
) {
84 // Callback that saves the results returned by
85 // SecureMessageDelegate::UnwrapSecureMessage().
86 void SaveUnwrapResults(bool* verified_out
,
87 std::string
* payload_out
,
88 securemessage::Header
* header_out
,
90 const std::string
& payload
,
91 const securemessage::Header
& header
) {
92 *verified_out
= verified
;
93 *payload_out
= payload
;
99 class ProximityAuthCryptAuthEnrollerTest
100 : public testing::Test
,
101 public MockCryptAuthClientFactory::Observer
{
103 ProximityAuthCryptAuthEnrollerTest()
104 : client_factory_(new MockCryptAuthClientFactory(
105 MockCryptAuthClientFactory::MockType::MAKE_NICE_MOCKS
)),
106 secure_message_delegate_(new FakeSecureMessageDelegate()),
107 enroller_(make_scoped_ptr(client_factory_
),
108 make_scoped_ptr(secure_message_delegate_
)) {
109 client_factory_
->AddObserver(this);
111 // This call is actually synchronous.
112 secure_message_delegate_
->GenerateKeyPair(
113 base::Bind(&ProximityAuthCryptAuthEnrollerTest::OnKeyPairGenerated
,
114 base::Unretained(this)));
117 // Starts the enroller.
118 void StartEnroller(const cryptauth::GcmDeviceInfo
& device_info
) {
119 secure_message_delegate_
->set_next_public_key(kClientSessionPublicKey
);
120 enroller_result_
.reset();
122 user_public_key_
, user_private_key_
, device_info
, kInvocationReason
,
123 base::Bind(&ProximityAuthCryptAuthEnrollerTest::OnEnrollerCompleted
,
124 base::Unretained(this)));
127 // Verifies that |serialized_message| is a valid SecureMessage sent with the
128 // FinishEnrollment API call.
129 void ValidateEnrollmentMessage(const std::string
& serialized_message
) {
130 // Derive the session symmetric key.
131 std::string server_session_private_key
=
132 secure_message_delegate_
->GetPrivateKeyForPublicKey(
133 kServerSessionPublicKey
);
134 std::string symmetric_key
;
135 secure_message_delegate_
->DeriveKey(
136 server_session_private_key
, kClientSessionPublicKey
,
137 base::Bind(&SaveDerivedKey
, &symmetric_key
));
139 std::string inner_message
;
140 std::string inner_payload
;
142 // Unwrap the outer message.
144 securemessage::Header header
;
145 SecureMessageDelegate::UnwrapOptions unwrap_options
;
146 unwrap_options
.encryption_scheme
= securemessage::AES_256_CBC
;
147 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
148 secure_message_delegate_
->UnwrapSecureMessage(
149 serialized_message
, symmetric_key
, unwrap_options
,
150 base::Bind(&SaveUnwrapResults
, &verified
, &inner_message
, &header
));
151 EXPECT_TRUE(verified
);
153 cryptauth::GcmMetadata metadata
;
154 ASSERT_TRUE(metadata
.ParseFromString(header
.public_metadata()));
155 EXPECT_EQ(kGCMMetadataVersion
, metadata
.version());
156 EXPECT_EQ(cryptauth::MessageType::ENROLLMENT
, metadata
.type());
160 // Unwrap inner message.
162 securemessage::Header header
;
163 SecureMessageDelegate::UnwrapOptions unwrap_options
;
164 unwrap_options
.encryption_scheme
= securemessage::NONE
;
165 unwrap_options
.signature_scheme
= securemessage::ECDSA_P256_SHA256
;
166 secure_message_delegate_
->UnwrapSecureMessage(
167 inner_message
, user_public_key_
, unwrap_options
,
168 base::Bind(&SaveUnwrapResults
, &verified
, &inner_payload
, &header
));
169 EXPECT_TRUE(verified
);
170 EXPECT_EQ(user_public_key_
, header
.verification_key_id());
173 // Check that the decrypted GcmDeviceInfo is correct.
174 cryptauth::GcmDeviceInfo device_info
;
175 ASSERT_TRUE(device_info
.ParseFromString(inner_payload
));
176 EXPECT_EQ(kDeviceId
, device_info
.long_device_id());
177 EXPECT_EQ(kDeviceType
, device_info
.device_type());
178 EXPECT_EQ(kDeviceOsVersion
, device_info
.device_os_version());
179 EXPECT_EQ(user_public_key_
, device_info
.user_public_key());
180 EXPECT_EQ(user_public_key_
, device_info
.key_handle());
181 EXPECT_EQ(kEnrollmentSessionId
, device_info
.enrollment_session_id());
185 // MockCryptAuthClientFactory::Observer:
186 void OnCryptAuthClientCreated(MockCryptAuthClient
* client
) override
{
187 ON_CALL(*client
, SetupEnrollment(_
, _
, _
))
188 .WillByDefault(Invoke(
189 this, &ProximityAuthCryptAuthEnrollerTest::OnSetupEnrollment
));
191 ON_CALL(*client
, FinishEnrollment(_
, _
, _
))
192 .WillByDefault(Invoke(
193 this, &ProximityAuthCryptAuthEnrollerTest::OnFinishEnrollment
));
195 ON_CALL(*client
, GetAccessTokenUsed())
196 .WillByDefault(Return(kAccessTokenUsed
));
199 void OnKeyPairGenerated(const std::string
& public_key
,
200 const std::string
& private_key
) {
201 user_public_key_
= public_key
;
202 user_private_key_
= private_key
;
205 void OnEnrollerCompleted(bool success
) {
206 EXPECT_FALSE(enroller_result_
.get());
207 enroller_result_
.reset(new bool(success
));
210 void OnSetupEnrollment(
211 const cryptauth::SetupEnrollmentRequest
& request
,
212 const CryptAuthClient::SetupEnrollmentCallback
& callback
,
213 const CryptAuthClient::ErrorCallback
& error_callback
) {
214 // Check that SetupEnrollment is called before FinishEnrollment.
215 EXPECT_FALSE(setup_request_
.get());
216 EXPECT_FALSE(finish_request_
.get());
217 EXPECT_TRUE(setup_callback_
.is_null());
218 EXPECT_TRUE(error_callback_
.is_null());
220 setup_request_
.reset(new cryptauth::SetupEnrollmentRequest(request
));
221 setup_callback_
= callback
;
222 error_callback_
= error_callback
;
225 void OnFinishEnrollment(
226 const cryptauth::FinishEnrollmentRequest
& request
,
227 const CryptAuthClient::FinishEnrollmentCallback
& callback
,
228 const CryptAuthClient::ErrorCallback
& error_callback
) {
229 // Check that FinishEnrollment is called after SetupEnrollment.
230 EXPECT_TRUE(setup_request_
.get());
231 EXPECT_FALSE(finish_request_
.get());
232 EXPECT_TRUE(finish_callback_
.is_null());
234 finish_request_
.reset(new cryptauth::FinishEnrollmentRequest(request
));
235 finish_callback_
= callback
;
236 error_callback_
= error_callback
;
239 // The persistent user key-pair.
240 std::string user_public_key_
;
241 std::string user_private_key_
;
243 // Owned by |enroller_|.
244 MockCryptAuthClientFactory
* client_factory_
;
245 // Owned by |enroller_|.
246 FakeSecureMessageDelegate
* secure_message_delegate_
;
247 // The CryptAuthEnroller under test.
248 CryptAuthEnrollerImpl enroller_
;
250 // Stores the result of running |enroller_|.
251 scoped_ptr
<bool> enroller_result_
;
253 // Stored callbacks and requests for SetupEnrollment and FinishEnrollment.
254 scoped_ptr
<cryptauth::SetupEnrollmentRequest
> setup_request_
;
255 scoped_ptr
<cryptauth::FinishEnrollmentRequest
> finish_request_
;
256 CryptAuthClient::SetupEnrollmentCallback setup_callback_
;
257 CryptAuthClient::FinishEnrollmentCallback finish_callback_
;
258 CryptAuthClient::ErrorCallback error_callback_
;
260 DISALLOW_COPY_AND_ASSIGN(ProximityAuthCryptAuthEnrollerTest
);
263 TEST_F(ProximityAuthCryptAuthEnrollerTest
, EnrollmentSucceeds
) {
264 StartEnroller(GetDeviceInfo());
266 // Handle SetupEnrollment request.
267 EXPECT_TRUE(setup_request_
.get());
268 EXPECT_EQ(kInvocationReason
, setup_request_
->invocation_reason());
269 ASSERT_EQ(1, setup_request_
->types_size());
270 EXPECT_EQ(kSupportedEnrollmentTypeGcmV1
, setup_request_
->types(0));
271 ASSERT_FALSE(setup_callback_
.is_null());
272 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
274 // Handle FinishEnrollment request.
275 EXPECT_TRUE(finish_request_
.get());
276 EXPECT_EQ(kEnrollmentSessionId
, finish_request_
->enrollment_session_id());
277 EXPECT_EQ(kClientSessionPublicKey
, finish_request_
->device_ephemeral_key());
278 ValidateEnrollmentMessage(finish_request_
->enrollment_message());
279 EXPECT_EQ(kInvocationReason
, finish_request_
->invocation_reason());
281 ASSERT_FALSE(finish_callback_
.is_null());
282 finish_callback_
.Run(GetFinishEnrollmentResponse(true));
284 ASSERT_TRUE(enroller_result_
.get());
285 EXPECT_TRUE(*enroller_result_
);
288 TEST_F(ProximityAuthCryptAuthEnrollerTest
, SetupEnrollmentApiCallError
) {
289 StartEnroller(GetDeviceInfo());
291 EXPECT_TRUE(setup_request_
.get());
292 ASSERT_FALSE(error_callback_
.is_null());
293 error_callback_
.Run("Setup enrollment failed network");
295 EXPECT_TRUE(finish_callback_
.is_null());
296 ASSERT_TRUE(enroller_result_
.get());
297 EXPECT_FALSE(*enroller_result_
);
300 TEST_F(ProximityAuthCryptAuthEnrollerTest
, SetupEnrollmentBadStatus
) {
301 StartEnroller(GetDeviceInfo());
303 EXPECT_TRUE(setup_request_
.get());
304 setup_callback_
.Run(GetSetupEnrollmentResponse(false));
306 EXPECT_TRUE(finish_callback_
.is_null());
307 ASSERT_TRUE(enroller_result_
.get());
308 EXPECT_FALSE(*enroller_result_
);
311 TEST_F(ProximityAuthCryptAuthEnrollerTest
, SetupEnrollmentNoInfosReturned
) {
312 StartEnroller(GetDeviceInfo());
313 EXPECT_TRUE(setup_request_
.get());
314 cryptauth::SetupEnrollmentResponse response
;
315 response
.set_status(kResponseStatusOk
);
316 setup_callback_
.Run(response
);
318 EXPECT_TRUE(finish_callback_
.is_null());
319 ASSERT_TRUE(enroller_result_
.get());
320 EXPECT_FALSE(*enroller_result_
);
323 TEST_F(ProximityAuthCryptAuthEnrollerTest
, FinishEnrollmentApiCallError
) {
324 StartEnroller(GetDeviceInfo());
325 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
326 ASSERT_FALSE(error_callback_
.is_null());
327 error_callback_
.Run("finish enrollment oauth error");
328 ASSERT_TRUE(enroller_result_
.get());
329 EXPECT_FALSE(*enroller_result_
);
332 TEST_F(ProximityAuthCryptAuthEnrollerTest
, FinishEnrollmentBadStatus
) {
333 StartEnroller(GetDeviceInfo());
334 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
335 ASSERT_FALSE(finish_callback_
.is_null());
336 finish_callback_
.Run(GetFinishEnrollmentResponse(false));
337 ASSERT_TRUE(enroller_result_
.get());
338 EXPECT_FALSE(*enroller_result_
);
341 TEST_F(ProximityAuthCryptAuthEnrollerTest
, ReuseEnroller
) {
342 StartEnroller(GetDeviceInfo());
343 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
344 finish_callback_
.Run(GetFinishEnrollmentResponse(true));
345 EXPECT_TRUE(*enroller_result_
);
347 StartEnroller(GetDeviceInfo());
348 EXPECT_FALSE(*enroller_result_
);
351 TEST_F(ProximityAuthCryptAuthEnrollerTest
, IncompleteDeviceInfo
) {
352 StartEnroller(cryptauth::GcmDeviceInfo());
353 ASSERT_TRUE(enroller_result_
.get());
354 EXPECT_FALSE(*enroller_result_
);
357 } // namespace proximity_auth