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"
15 namespace proximity_auth
{
19 const char kClientSessionPublicKey
[] = "throw away after one use";
20 const char kServerSessionPublicKey
[] = "disposables are not eco-friendly";
21 const char kClientPersistentPublicKey
[] = "saves 50 trees a year";
23 cryptauth::InvocationReason kInvocationReason
=
24 cryptauth::INVOCATION_REASON_MANUAL
;
25 const int kGCMMetadataVersion
= 1;
26 const char kSupportedEnrollmentTypeGcmV1
[] = "gcmV1";
27 const char kResponseStatusOk
[] = "OK";
28 const char kResponseStatusNotOk
[] = "Your key was too bland.";
29 const char kEnrollmentSessionId
[] = "0123456789876543210";
30 const char kFinishEnrollmentError
[] = "A hungry router ate all your packets.";
32 const char kDeviceId
[] = "2015 AD";
33 const cryptauth::DeviceType kDeviceType
= cryptauth::CHROME
;
34 const char kDeviceOsVersion
[] = "41.0.0";
36 // Creates and returns the GcmDeviceInfo message to be uploaded.
37 cryptauth::GcmDeviceInfo
GetDeviceInfo() {
38 cryptauth::GcmDeviceInfo device_info
;
39 device_info
.set_long_device_id(kDeviceId
);
40 device_info
.set_device_type(kDeviceType
);
41 device_info
.set_device_os_version(kDeviceOsVersion
);
42 device_info
.set_user_public_key(kClientPersistentPublicKey
);
43 device_info
.set_key_handle(kClientPersistentPublicKey
);
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(false)),
105 secure_message_delegate_(new FakeSecureMessageDelegate()),
106 enroller_(make_scoped_ptr(client_factory_
),
107 make_scoped_ptr(secure_message_delegate_
)) {
108 client_factory_
->AddObserver(this);
111 // Starts the enroller.
112 void StartEnroller(const cryptauth::GcmDeviceInfo
& device_info
) {
113 secure_message_delegate_
->set_next_public_key(kClientSessionPublicKey
);
114 enroller_result_
.reset();
116 device_info
, kInvocationReason
,
117 base::Bind(&ProximityAuthCryptAuthEnrollerTest::OnEnrollerCompleted
,
118 base::Unretained(this)));
121 // Verifies that |serialized_message| is a valid SecureMessage sent with the
122 // FinishEnrollment API call.
123 void ValidateEnrollmentMessage(const std::string
& serialized_message
) {
124 // Derive the session symmetric key.
125 std::string server_session_private_key
=
126 secure_message_delegate_
->GetPrivateKeyForPublicKey(
127 kServerSessionPublicKey
);
128 std::string symmetric_key
;
129 secure_message_delegate_
->DeriveKey(
130 server_session_private_key
, kClientSessionPublicKey
,
131 base::Bind(&SaveDerivedKey
, &symmetric_key
));
133 std::string inner_message
;
134 std::string inner_payload
;
136 // Unwrap the outer message.
138 securemessage::Header header
;
139 SecureMessageDelegate::UnwrapOptions unwrap_options
;
140 unwrap_options
.encryption_scheme
= securemessage::AES_256_CBC
;
141 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
142 secure_message_delegate_
->UnwrapSecureMessage(
143 serialized_message
, symmetric_key
, unwrap_options
,
144 base::Bind(&SaveUnwrapResults
, &verified
, &inner_message
, &header
));
145 EXPECT_TRUE(verified
);
147 cryptauth::GcmMetadata metadata
;
148 ASSERT_TRUE(metadata
.ParseFromString(header
.public_metadata()));
149 EXPECT_EQ(kGCMMetadataVersion
, metadata
.version());
150 EXPECT_EQ(cryptauth::MessageType::ENROLLMENT
, metadata
.type());
154 // Unwrap inner message.
156 securemessage::Header header
;
157 SecureMessageDelegate::UnwrapOptions unwrap_options
;
158 unwrap_options
.encryption_scheme
= securemessage::NONE
;
159 unwrap_options
.signature_scheme
= securemessage::ECDSA_P256_SHA256
;
160 secure_message_delegate_
->UnwrapSecureMessage(
161 inner_message
, kClientSessionPublicKey
, unwrap_options
,
162 base::Bind(&SaveUnwrapResults
, &verified
, &inner_payload
, &header
));
163 EXPECT_TRUE(verified
);
164 EXPECT_EQ(kClientSessionPublicKey
, header
.verification_key_id());
167 // Check that the decrypted GcmDeviceInfo is correct.
168 cryptauth::GcmDeviceInfo device_info
;
169 ASSERT_TRUE(device_info
.ParseFromString(inner_payload
));
170 EXPECT_EQ(kDeviceId
, device_info
.long_device_id());
171 EXPECT_EQ(kDeviceType
, device_info
.device_type());
172 EXPECT_EQ(kDeviceOsVersion
, device_info
.device_os_version());
173 EXPECT_EQ(kClientPersistentPublicKey
, device_info
.user_public_key());
174 EXPECT_EQ(kClientPersistentPublicKey
, device_info
.key_handle());
175 EXPECT_EQ(kEnrollmentSessionId
, device_info
.enrollment_session_id());
179 // MockCryptAuthClientFactory::Observer:
180 void OnCryptAuthClientCreated(MockCryptAuthClient
* client
) override
{
181 ON_CALL(*client
, SetupEnrollment(_
, _
, _
))
182 .WillByDefault(Invoke(
183 this, &ProximityAuthCryptAuthEnrollerTest::OnSetupEnrollment
));
185 ON_CALL(*client
, FinishEnrollment(_
, _
, _
))
186 .WillByDefault(Invoke(
187 this, &ProximityAuthCryptAuthEnrollerTest::OnFinishEnrollment
));
190 void OnEnrollerCompleted(bool success
) {
191 EXPECT_FALSE(enroller_result_
.get());
192 enroller_result_
.reset(new bool(success
));
195 void OnSetupEnrollment(
196 const cryptauth::SetupEnrollmentRequest
& request
,
197 const CryptAuthClient::SetupEnrollmentCallback
& callback
,
198 const CryptAuthClient::ErrorCallback
& error_callback
) {
199 // Check that SetupEnrollment is called before FinishEnrollment.
200 EXPECT_FALSE(setup_request_
.get());
201 EXPECT_FALSE(finish_request_
.get());
202 EXPECT_TRUE(setup_callback_
.is_null());
203 EXPECT_TRUE(error_callback_
.is_null());
205 setup_request_
.reset(new cryptauth::SetupEnrollmentRequest(request
));
206 setup_callback_
= callback
;
207 error_callback_
= error_callback
;
210 void OnFinishEnrollment(
211 const cryptauth::FinishEnrollmentRequest
& request
,
212 const CryptAuthClient::FinishEnrollmentCallback
& callback
,
213 const CryptAuthClient::ErrorCallback
& error_callback
) {
214 // Check that FinishEnrollment is called after SetupEnrollment.
215 EXPECT_TRUE(setup_request_
.get());
216 EXPECT_FALSE(finish_request_
.get());
217 EXPECT_TRUE(finish_callback_
.is_null());
219 finish_request_
.reset(new cryptauth::FinishEnrollmentRequest(request
));
220 finish_callback_
= callback
;
221 error_callback_
= error_callback
;
224 // Owned by |enroller_|.
225 MockCryptAuthClientFactory
* client_factory_
;
226 // Owned by |enroller_|.
227 FakeSecureMessageDelegate
* secure_message_delegate_
;
228 // The CryptAuthEnroller under test.
229 CryptAuthEnrollerImpl enroller_
;
231 // Stores the result of running |enroller_|.
232 scoped_ptr
<bool> enroller_result_
;
234 // Stored callbacks and requests for SetupEnrollment and FinishEnrollment.
235 scoped_ptr
<cryptauth::SetupEnrollmentRequest
> setup_request_
;
236 scoped_ptr
<cryptauth::FinishEnrollmentRequest
> finish_request_
;
237 CryptAuthClient::SetupEnrollmentCallback setup_callback_
;
238 CryptAuthClient::FinishEnrollmentCallback finish_callback_
;
239 CryptAuthClient::ErrorCallback error_callback_
;
241 DISALLOW_COPY_AND_ASSIGN(ProximityAuthCryptAuthEnrollerTest
);
244 TEST_F(ProximityAuthCryptAuthEnrollerTest
, EnrollmentSucceeds
) {
245 StartEnroller(GetDeviceInfo());
247 // Handle SetupEnrollment request.
248 EXPECT_TRUE(setup_request_
.get());
249 EXPECT_EQ(kInvocationReason
, setup_request_
->invocation_reason());
250 ASSERT_EQ(1, setup_request_
->types_size());
251 EXPECT_EQ(kSupportedEnrollmentTypeGcmV1
, setup_request_
->types(0));
252 ASSERT_FALSE(setup_callback_
.is_null());
253 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
255 // Handle FinishEnrollment request.
256 EXPECT_TRUE(finish_request_
.get());
257 EXPECT_EQ(kEnrollmentSessionId
, finish_request_
->enrollment_session_id());
258 EXPECT_EQ(kClientSessionPublicKey
, finish_request_
->device_ephemeral_key());
259 ValidateEnrollmentMessage(finish_request_
->enrollment_message());
260 EXPECT_EQ(kInvocationReason
, finish_request_
->invocation_reason());
262 ASSERT_FALSE(finish_callback_
.is_null());
263 finish_callback_
.Run(GetFinishEnrollmentResponse(true));
265 ASSERT_TRUE(enroller_result_
.get());
266 EXPECT_TRUE(*enroller_result_
);
269 TEST_F(ProximityAuthCryptAuthEnrollerTest
, SetupEnrollmentApiCallError
) {
270 StartEnroller(GetDeviceInfo());
272 EXPECT_TRUE(setup_request_
.get());
273 ASSERT_FALSE(error_callback_
.is_null());
274 error_callback_
.Run("Setup enrollment failed network");
276 EXPECT_TRUE(finish_callback_
.is_null());
277 ASSERT_TRUE(enroller_result_
.get());
278 EXPECT_FALSE(*enroller_result_
);
281 TEST_F(ProximityAuthCryptAuthEnrollerTest
, SetupEnrollmentBadStatus
) {
282 StartEnroller(GetDeviceInfo());
284 EXPECT_TRUE(setup_request_
.get());
285 setup_callback_
.Run(GetSetupEnrollmentResponse(false));
287 EXPECT_TRUE(finish_callback_
.is_null());
288 ASSERT_TRUE(enroller_result_
.get());
289 EXPECT_FALSE(*enroller_result_
);
292 TEST_F(ProximityAuthCryptAuthEnrollerTest
, SetupEnrollmentNoInfosReturned
) {
293 StartEnroller(GetDeviceInfo());
294 EXPECT_TRUE(setup_request_
.get());
295 cryptauth::SetupEnrollmentResponse response
;
296 response
.set_status(kResponseStatusOk
);
297 setup_callback_
.Run(response
);
299 EXPECT_TRUE(finish_callback_
.is_null());
300 ASSERT_TRUE(enroller_result_
.get());
301 EXPECT_FALSE(*enroller_result_
);
304 TEST_F(ProximityAuthCryptAuthEnrollerTest
, FinishEnrollmentApiCallError
) {
305 StartEnroller(GetDeviceInfo());
306 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
307 ASSERT_FALSE(error_callback_
.is_null());
308 error_callback_
.Run("finish enrollment oauth error");
309 ASSERT_TRUE(enroller_result_
.get());
310 EXPECT_FALSE(*enroller_result_
);
313 TEST_F(ProximityAuthCryptAuthEnrollerTest
, FinishEnrollmentBadStatus
) {
314 StartEnroller(GetDeviceInfo());
315 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
316 ASSERT_FALSE(finish_callback_
.is_null());
317 finish_callback_
.Run(GetFinishEnrollmentResponse(false));
318 ASSERT_TRUE(enroller_result_
.get());
319 EXPECT_FALSE(*enroller_result_
);
322 TEST_F(ProximityAuthCryptAuthEnrollerTest
, ReuseEnroller
) {
323 StartEnroller(GetDeviceInfo());
324 setup_callback_
.Run(GetSetupEnrollmentResponse(true));
325 finish_callback_
.Run(GetFinishEnrollmentResponse(true));
326 EXPECT_TRUE(*enroller_result_
);
328 StartEnroller(GetDeviceInfo());
329 EXPECT_FALSE(*enroller_result_
);
332 TEST_F(ProximityAuthCryptAuthEnrollerTest
, IncompleteDeviceInfo
) {
333 StartEnroller(cryptauth::GcmDeviceInfo());
334 ASSERT_TRUE(enroller_result_
.get());
335 EXPECT_FALSE(*enroller_result_
);
338 } // namespace proximity_auth