1 // Copyright 2019 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.
4
5 #include "chromeos/services/device_sync/cryptauth_device_syncer_impl.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/containers/flat_set.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "chromeos/components/multidevice/logging/logging.h"
14 #include "chromeos/services/device_sync/async_execution_time_metrics_logger.h"
15 #include "chromeos/services/device_sync/cryptauth_client.h"
16 #include "chromeos/services/device_sync/cryptauth_ecies_encryptor_impl.h"
17 #include "chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h"
18 #include "chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl.h"
19 #include "chromeos/services/device_sync/cryptauth_key_registry.h"
20 #include "chromeos/services/device_sync/cryptauth_metadata_syncer_impl.h"
21 #include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
22 #include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
23 #include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
24 #include "chromeos/services/device_sync/synced_bluetooth_address_tracker.h"
25 #include "chromeos/services/device_sync/value_string_encoding.h"
26
27 namespace chromeos {
28
29 namespace device_sync {
30
31 namespace {
32
33 const cryptauthv2::KeyType kGroupKeyType = cryptauthv2::KeyType::P256;
34
35 // Timeout values for asynchronous operations.
36 // TODO(https://crbug.com/933656): Use async execution time metrics to tune
37 // these timeout values. For now, set these timeouts to the max execution time
38 // recorded by the metrics.
39 constexpr base::TimeDelta kWaitingForEncryptedGroupPrivateKeyProcessingTimeout =
40 kMaxAsyncExecutionTime;
41 constexpr base::TimeDelta kWaitingForEncryptedDeviceMetadataProcessingTimeout =
42 kMaxAsyncExecutionTime;
43
RecordGroupPrivateKeyDecryptionMetrics(base::TimeDelta execution_time,CryptAuthAsyncTaskResult result)44 void RecordGroupPrivateKeyDecryptionMetrics(base::TimeDelta execution_time,
45 CryptAuthAsyncTaskResult result) {
46 LogAsyncExecutionTimeMetric(
47 "CryptAuth.DeviceSyncV2.DeviceSyncer.ExecutionTime."
48 "GroupPrivateKeyDecryption",
49 execution_time);
50 LogCryptAuthAsyncTaskSuccessMetric(
51 "CryptAuth.DeviceSyncV2.DeviceSyncer.AsyncTaskResult."
52 "GroupPrivateKeyDecryption",
53 result);
54 }
55
RecordDeviceMetadataDecryptionMetrics(base::TimeDelta execution_time,CryptAuthAsyncTaskResult result)56 void RecordDeviceMetadataDecryptionMetrics(base::TimeDelta execution_time,
57 CryptAuthAsyncTaskResult result) {
58 LogAsyncExecutionTimeMetric(
59 "CryptAuth.DeviceSyncV2.DeviceSyncer.ExecutionTime."
60 "DeviceMetadataDecryption",
61 execution_time);
62 LogCryptAuthAsyncTaskSuccessMetric(
63 "CryptAuth.DeviceSyncV2.DeviceSyncer.AsyncTaskResult."
64 "DeviceMetadataDecryption",
65 result);
66 }
67
68 } // namespace
69
70 // static
71 CryptAuthDeviceSyncerImpl::Factory*
72 CryptAuthDeviceSyncerImpl::Factory::test_factory_ = nullptr;
73
74 // static
75 std::unique_ptr<CryptAuthDeviceSyncer>
Create(CryptAuthDeviceRegistry * device_registry,CryptAuthKeyRegistry * key_registry,CryptAuthClientFactory * client_factory,SyncedBluetoothAddressTracker * synced_bluetooth_address_tracker,PrefService * pref_service,std::unique_ptr<base::OneShotTimer> timer)76 CryptAuthDeviceSyncerImpl::Factory::Create(
77 CryptAuthDeviceRegistry* device_registry,
78 CryptAuthKeyRegistry* key_registry,
79 CryptAuthClientFactory* client_factory,
80 SyncedBluetoothAddressTracker* synced_bluetooth_address_tracker,
81 PrefService* pref_service,
82 std::unique_ptr<base::OneShotTimer> timer) {
83 if (test_factory_) {
84 return test_factory_->CreateInstance(
85 device_registry, key_registry, client_factory,
86 synced_bluetooth_address_tracker, pref_service, std::move(timer));
87 }
88
89 return base::WrapUnique(new CryptAuthDeviceSyncerImpl(
90 device_registry, key_registry, client_factory,
91 synced_bluetooth_address_tracker, pref_service, std::move(timer)));
92 }
93
94 // static
SetFactoryForTesting(Factory * test_factory)95 void CryptAuthDeviceSyncerImpl::Factory::SetFactoryForTesting(
96 Factory* test_factory) {
97 test_factory_ = test_factory;
98 }
99
100 CryptAuthDeviceSyncerImpl::Factory::~Factory() = default;
101
CryptAuthDeviceSyncerImpl(CryptAuthDeviceRegistry * device_registry,CryptAuthKeyRegistry * key_registry,CryptAuthClientFactory * client_factory,SyncedBluetoothAddressTracker * synced_bluetooth_address_tracker,PrefService * pref_service,std::unique_ptr<base::OneShotTimer> timer)102 CryptAuthDeviceSyncerImpl::CryptAuthDeviceSyncerImpl(
103 CryptAuthDeviceRegistry* device_registry,
104 CryptAuthKeyRegistry* key_registry,
105 CryptAuthClientFactory* client_factory,
106 SyncedBluetoothAddressTracker* synced_bluetooth_address_tracker,
107 PrefService* pref_service,
108 std::unique_ptr<base::OneShotTimer> timer)
109 : device_registry_(device_registry),
110 key_registry_(key_registry),
111 client_factory_(client_factory),
112 synced_bluetooth_address_tracker_(synced_bluetooth_address_tracker),
113 pref_service_(pref_service),
114 timer_(std::move(timer)) {
115 DCHECK(device_registry);
116 DCHECK(key_registry);
117 DCHECK(client_factory);
118 }
119
120 CryptAuthDeviceSyncerImpl::~CryptAuthDeviceSyncerImpl() = default;
121
122 // static
GetTimeoutForState(State state)123 base::Optional<base::TimeDelta> CryptAuthDeviceSyncerImpl::GetTimeoutForState(
124 State state) {
125 switch (state) {
126 case State::kWaitingForEncryptedGroupPrivateKeyProcessing:
127 return kWaitingForEncryptedGroupPrivateKeyProcessingTimeout;
128 case State::kWaitingForEncryptedDeviceMetadataProcessing:
129 return kWaitingForEncryptedDeviceMetadataProcessingTimeout;
130 default:
131 // Signifies that there should not be a timeout.
132 // Note: CryptAuthMetadataSyncerImpl, CryptAuthFeatureStatusGetterImpl,
133 // CryptAuthGroupPrivateKeySharerImpl, and BluetoothAdapter guarantee that
134 // the callbacks passed to their public methods are always invoke; in
135 // other words, these implementations handle their relevant timeouts
136 // internally.
137 return base::nullopt;
138 }
139 }
140
141 // static
142 base::Optional<CryptAuthDeviceSyncResult::ResultCode>
ResultCodeErrorFromTimeoutDuringState(State state)143 CryptAuthDeviceSyncerImpl::ResultCodeErrorFromTimeoutDuringState(State state) {
144 switch (state) {
145 case State::kWaitingForEncryptedGroupPrivateKeyProcessing:
146 return CryptAuthDeviceSyncResult::ResultCode::
147 kErrorTimeoutWaitingForGroupPrivateKeyDecryption;
148 case State::kWaitingForEncryptedDeviceMetadataProcessing:
149 return CryptAuthDeviceSyncResult::ResultCode::
150 kErrorTimeoutWaitingForDeviceMetadataDecryption;
151 default:
152 return base::nullopt;
153 }
154 }
155
OnAttemptStarted(const cryptauthv2::ClientMetadata & client_metadata,const cryptauthv2::ClientAppMetadata & client_app_metadata)156 void CryptAuthDeviceSyncerImpl::OnAttemptStarted(
157 const cryptauthv2::ClientMetadata& client_metadata,
158 const cryptauthv2::ClientAppMetadata& client_app_metadata) {
159 DCHECK_EQ(State::kNotStarted, state_);
160
161 request_context_.set_group(CryptAuthKeyBundle::KeyBundleNameEnumToString(
162 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogether));
163 request_context_.mutable_client_metadata()->CopyFrom(client_metadata);
164 request_context_.set_device_id(client_app_metadata.instance_id());
165 request_context_.set_device_id_token(client_app_metadata.instance_id_token());
166
167 const CryptAuthKey* user_key_pair =
168 key_registry_->GetActiveKey(CryptAuthKeyBundle::Name::kUserKeyPair);
169 if (!user_key_pair) {
170 FinishAttempt(
171 CryptAuthDeviceSyncResult::ResultCode::kErrorMissingUserKeyPair);
172 return;
173 }
174
175 local_better_together_device_metadata_.set_public_key(
176 user_key_pair->public_key());
177 local_better_together_device_metadata_.set_no_pii_device_name(
178 client_app_metadata.device_model());
179
180 AttemptNextStep();
181 }
182
SetState(State state)183 void CryptAuthDeviceSyncerImpl::SetState(State state) {
184 timer_->Stop();
185
186 PA_LOG(INFO) << "Transitioning from " << state_ << " to " << state;
187 state_ = state;
188 last_state_change_timestamp_ = base::TimeTicks::Now();
189
190 base::Optional<base::TimeDelta> timeout_for_state = GetTimeoutForState(state);
191 if (!timeout_for_state)
192 return;
193
194 timer_->Start(FROM_HERE, *timeout_for_state,
195 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnTimeout,
196 base::Unretained(this)));
197 }
198
OnTimeout()199 void CryptAuthDeviceSyncerImpl::OnTimeout() {
200 // If there's a timeout specified, there should be a corresponding error code.
201 base::Optional<CryptAuthDeviceSyncResult::ResultCode> error_code =
202 ResultCodeErrorFromTimeoutDuringState(state_);
203 DCHECK(error_code);
204
205 base::TimeDelta execution_time =
206 base::TimeTicks::Now() - last_state_change_timestamp_;
207 switch (state_) {
208 case State::kWaitingForEncryptedGroupPrivateKeyProcessing:
209 RecordGroupPrivateKeyDecryptionMetrics(
210 execution_time, CryptAuthAsyncTaskResult::kTimeout);
211 break;
212 case State::kWaitingForEncryptedDeviceMetadataProcessing:
213 RecordDeviceMetadataDecryptionMetrics(execution_time,
214 CryptAuthAsyncTaskResult::kTimeout);
215 break;
216 default:
217 NOTREACHED();
218 }
219
220 FinishAttempt(*error_code);
221 }
222
AttemptNextStep()223 void CryptAuthDeviceSyncerImpl::AttemptNextStep() {
224 switch (state_) {
225 case State::kNotStarted:
226 GetBluetoothAddress();
227 return;
228 case State::kWaitingForBluetoothAddress:
229 SyncMetadata();
230 return;
231 case State::kWaitingForMetadataSync:
232 GetFeatureStatuses();
233 return;
234 case State::kWaitingForFeatureStatuses:
235 ProcessEncryptedGroupPrivateKey();
236 return;
237 case State::kWaitingForEncryptedGroupPrivateKeyProcessing:
238 ProcessEncryptedDeviceMetadata();
239 return;
240 case State::kWaitingForEncryptedDeviceMetadataProcessing:
241 ShareGroupPrivateKey();
242 return;
243 case State::kWaitingForGroupPrivateKeySharing: {
244 CryptAuthDeviceSyncResult::ResultCode result_code =
245 did_non_fatal_error_occur_
246 ? CryptAuthDeviceSyncResult::ResultCode::
247 kFinishedWithNonFatalErrors
248 : CryptAuthDeviceSyncResult::ResultCode::kSuccess;
249 FinishAttempt(result_code);
250 return;
251 }
252 case State::kFinished:
253 NOTREACHED();
254 return;
255 }
256 }
257
GetBluetoothAddress()258 void CryptAuthDeviceSyncerImpl::GetBluetoothAddress() {
259 DCHECK_EQ(State::kNotStarted, state_);
260 SetState(State::kWaitingForBluetoothAddress);
261 synced_bluetooth_address_tracker_->GetBluetoothAddress(
262 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnBluetoothAddress,
263 weak_ptr_factory_.GetWeakPtr()));
264 }
265
OnBluetoothAddress(const std::string & bluetooth_address)266 void CryptAuthDeviceSyncerImpl::OnBluetoothAddress(
267 const std::string& bluetooth_address) {
268 DCHECK_EQ(State::kWaitingForBluetoothAddress, state_);
269
270 if (!bluetooth_address.empty()) {
271 local_better_together_device_metadata_.set_bluetooth_public_address(
272 bluetooth_address);
273 }
274
275 AttemptNextStep();
276 }
277
SyncMetadata()278 void CryptAuthDeviceSyncerImpl::SyncMetadata() {
279 SetState(State::kWaitingForMetadataSync);
280
281 metadata_syncer_ = CryptAuthMetadataSyncerImpl::Factory::Create(
282 client_factory_, pref_service_);
283 metadata_syncer_->SyncMetadata(
284 request_context_, local_better_together_device_metadata_,
285 key_registry_->GetActiveKey(
286 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey),
287 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnSyncMetadataFinished,
288 base::Unretained(this)));
289 }
290
OnSyncMetadataFinished(const CryptAuthMetadataSyncer::IdToDeviceMetadataPacketMap & id_to_device_metadata_packet_map,std::unique_ptr<CryptAuthKey> new_group_key,const base::Optional<cryptauthv2::EncryptedGroupPrivateKey> & encrypted_group_private_key,const base::Optional<cryptauthv2::ClientDirective> & new_client_directive,CryptAuthDeviceSyncResult::ResultCode device_sync_result_code)291 void CryptAuthDeviceSyncerImpl::OnSyncMetadataFinished(
292 const CryptAuthMetadataSyncer::IdToDeviceMetadataPacketMap&
293 id_to_device_metadata_packet_map,
294 std::unique_ptr<CryptAuthKey> new_group_key,
295 const base::Optional<cryptauthv2::EncryptedGroupPrivateKey>&
296 encrypted_group_private_key,
297 const base::Optional<cryptauthv2::ClientDirective>& new_client_directive,
298 CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
299 DCHECK_EQ(State::kWaitingForMetadataSync, state_);
300
301 id_to_device_metadata_packet_map_ = id_to_device_metadata_packet_map;
302 encrypted_group_private_key_ = encrypted_group_private_key;
303 new_client_directive_ = new_client_directive;
304
305 // If a new group key pair was created or if CryptAuth returned a new group
306 // public key during the metadata sync, add the new group key to the key
307 // registry.
308 if (new_group_key)
309 SetGroupKey(*new_group_key);
310
311 switch (CryptAuthDeviceSyncResult::GetResultType(device_sync_result_code)) {
312 case CryptAuthDeviceSyncResult::ResultType::kNonFatalError:
313 did_non_fatal_error_occur_ = true;
314 FALLTHROUGH;
315 case CryptAuthDeviceSyncResult::ResultType::kSuccess:
316 // At a minimum, the local device metadata should be returned if no fatal
317 // error occurred.
318 DCHECK(base::Contains(id_to_device_metadata_packet_map_,
319 request_context_.device_id()));
320
321 // A group key should be established by now if no fatal error occurred.
322 DCHECK(key_registry_->GetActiveKey(
323 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey));
324
325 AttemptNextStep();
326 return;
327 case CryptAuthDeviceSyncResult::ResultType::kFatalError:
328 FinishAttempt(device_sync_result_code);
329 return;
330 }
331 }
332
SetGroupKey(const CryptAuthKey & new_group_key)333 void CryptAuthDeviceSyncerImpl::SetGroupKey(const CryptAuthKey& new_group_key) {
334 DCHECK_EQ(kGroupKeyType, new_group_key.type());
335
336 const CryptAuthKey* current_group_key = key_registry_->GetActiveKey(
337 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey);
338 if (current_group_key) {
339 if (*current_group_key == new_group_key)
340 return;
341
342 PA_LOG(VERBOSE) << "Deleting old DeviceSync BetterTogether group key:\n"
343 << "public = "
344 << util::EncodeAsString(current_group_key->public_key())
345 << ",\nprivate = "
346 << (current_group_key->private_key().empty()
347 ? "[empty]"
348 : "[not empty]");
349 key_registry_->DeleteKey(
350 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey,
351 current_group_key->handle());
352 }
353
354 PA_LOG(VERBOSE) << "New DeviceSync BetterTogether group key:\n"
355 << "public = "
356 << util::EncodeAsString(new_group_key.public_key())
357 << ",\nprivate = "
358 << (new_group_key.private_key().empty() ? "[empty]"
359 : "[not empty]");
360
361 key_registry_->AddKey(
362 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey,
363 new_group_key);
364 }
365
GetFeatureStatuses()366 void CryptAuthDeviceSyncerImpl::GetFeatureStatuses() {
367 SetState(State::kWaitingForFeatureStatuses);
368
369 base::flat_set<std::string> device_ids;
370 for (const auto& id_packet_pair : id_to_device_metadata_packet_map_)
371 device_ids.insert(id_packet_pair.first);
372
373 feature_status_getter_ =
374 CryptAuthFeatureStatusGetterImpl::Factory::Create(client_factory_);
375 feature_status_getter_->GetFeatureStatuses(
376 request_context_, device_ids,
377 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnGetFeatureStatusesFinished,
378 base::Unretained(this)));
379 }
380
OnGetFeatureStatusesFinished(const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap & id_to_device_software_feature_info_map,CryptAuthDeviceSyncResult::ResultCode device_sync_result_code)381 void CryptAuthDeviceSyncerImpl::OnGetFeatureStatusesFinished(
382 const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
383 id_to_device_software_feature_info_map,
384 CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
385 DCHECK_EQ(State::kWaitingForFeatureStatuses, state_);
386
387 switch (CryptAuthDeviceSyncResult::GetResultType(device_sync_result_code)) {
388 case CryptAuthDeviceSyncResult::ResultType::kNonFatalError:
389 did_non_fatal_error_occur_ = true;
390 FALLTHROUGH;
391 case CryptAuthDeviceSyncResult::ResultType::kSuccess:
392 // We require that the local device feature statuses are returned; the
393 // local device is needed in the registry.
394 if (!base::Contains(id_to_device_software_feature_info_map,
395 request_context_.device_id())) {
396 FinishAttempt(CryptAuthDeviceSyncResult::ResultCode::
397 kErrorMissingLocalDeviceFeatureStatuses);
398 return;
399 }
400
401 BuildNewDeviceRegistry(id_to_device_software_feature_info_map);
402 AttemptNextStep();
403 return;
404
405 case CryptAuthDeviceSyncResult::ResultType::kFatalError:
406 FinishAttempt(device_sync_result_code);
407 return;
408 }
409 }
410
BuildNewDeviceRegistry(const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap & id_to_device_software_feature_info_map)411 void CryptAuthDeviceSyncerImpl::BuildNewDeviceRegistry(
412 const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
413 id_to_device_software_feature_info_map) {
414 // Add all device information to the new registry except the new remote device
415 // BetterTogether metadata that will be decrypted and added later if possible.
416 // In the interim, use the existing BetterTogether metadata for the device
417 // from the current registry, if available.
418 new_device_registry_map_ = CryptAuthDeviceRegistry::InstanceIdToDeviceMap();
419 for (const auto& id_device_software_feature_info_pair :
420 id_to_device_software_feature_info_map) {
421 const std::string& id = id_device_software_feature_info_pair.first;
422
423 // The IDs in |id_to_device_software_feature_info_map| should be a subset of
424 // those in |id_to_device_metadata_packet_map|.
425 const auto packet_it = id_to_device_metadata_packet_map_.find(id);
426 DCHECK(packet_it != id_to_device_metadata_packet_map_.end());
427 const cryptauthv2::DeviceMetadataPacket& packet = packet_it->second;
428
429 // Add BetterTogetherDeviceMetadata for the local device and all devices
430 // with BetterTogetherDeviceMetadata in the existing device registry.
431 base::Optional<cryptauthv2::BetterTogetherDeviceMetadata> beto_metadata;
432 if (id == request_context_.device_id()) {
433 beto_metadata = local_better_together_device_metadata_;
434 } else {
435 const CryptAuthDevice* existing_device = device_registry_->GetDevice(id);
436 if (existing_device)
437 beto_metadata = existing_device->better_together_device_metadata;
438 }
439
440 new_device_registry_map_->try_emplace(
441 id, id, packet.device_name(), packet.device_public_key(),
442 id_device_software_feature_info_pair.second.last_modified_time,
443 beto_metadata,
444 id_device_software_feature_info_pair.second.feature_state_map);
445 }
446 }
447
ProcessEncryptedGroupPrivateKey()448 void CryptAuthDeviceSyncerImpl::ProcessEncryptedGroupPrivateKey() {
449 SetState(State::kWaitingForEncryptedGroupPrivateKeyProcessing);
450
451 // CryptAuth will not return the group private key in the SyncMetadata
452 // response if the key has not been uploaded by another user device or
453 // possibly if we already own the group private key.
454 if (!encrypted_group_private_key_) {
455 AttemptNextStep();
456 return;
457 }
458
459 if (encrypted_group_private_key_->encrypted_private_key().empty()) {
460 // TODO(https://crbug.com/936273): Log metrics for empty private key.
461 PA_LOG(ERROR) << "Group private key from CryptAuth unexpectedly empty.";
462 did_non_fatal_error_occur_ = true;
463 AttemptNextStep();
464 return;
465 }
466
467 const CryptAuthKey* device_sync_better_together_key =
468 key_registry_->GetActiveKey(
469 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogether);
470 if (!device_sync_better_together_key ||
471 device_sync_better_together_key->private_key().empty()) {
472 FinishAttempt(CryptAuthDeviceSyncResult::ResultCode::
473 kErrorMissingLocalDeviceSyncBetterTogetherKey);
474 return;
475 }
476
477 encryptor_ = CryptAuthEciesEncryptorImpl::Factory::Create();
478 encryptor_->Decrypt(
479 encrypted_group_private_key_->encrypted_private_key(),
480 device_sync_better_together_key->private_key(),
481 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnGroupPrivateKeyDecrypted,
482 base::Unretained(this)));
483 }
484
OnGroupPrivateKeyDecrypted(const base::Optional<std::string> & group_private_key_from_cryptauth)485 void CryptAuthDeviceSyncerImpl::OnGroupPrivateKeyDecrypted(
486 const base::Optional<std::string>& group_private_key_from_cryptauth) {
487 DCHECK_EQ(State::kWaitingForEncryptedGroupPrivateKeyProcessing, state_);
488
489 bool success = group_private_key_from_cryptauth.has_value();
490 RecordGroupPrivateKeyDecryptionMetrics(
491 base::TimeTicks::Now() - last_state_change_timestamp_,
492 success ? CryptAuthAsyncTaskResult::kSuccess
493 : CryptAuthAsyncTaskResult::kError);
494
495 if (!success) {
496 FinishAttempt(
497 CryptAuthDeviceSyncResult::ResultCode::kErrorDecryptingGroupPrivateKey);
498 return;
499 }
500
501 const CryptAuthKey* group_key = key_registry_->GetActiveKey(
502 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey);
503 DCHECK(group_key);
504
505 // If there is no group private key in the key registry, add the newly
506 // decrypted group private key. If a group private key already exists in the
507 // key registry, verify it against the newly decrypted group private key.
508 if (group_key->private_key().empty()) {
509 SetGroupKey(CryptAuthKey(group_key->public_key(),
510 *group_private_key_from_cryptauth,
511 CryptAuthKey::Status::kActive, kGroupKeyType));
512 } else {
513 bool is_group_private_key_consistent =
514 group_key->private_key() == group_private_key_from_cryptauth;
515 base::UmaHistogramBoolean(
516 "CryptAuth.DeviceSyncV2.DeviceSyncer.IsGroupPrivateKeyConsistent",
517 is_group_private_key_consistent);
518 if (!is_group_private_key_consistent) {
519 PA_LOG(ERROR) << "Group private key from CryptAuth unexpectedly "
520 << "disagrees with the one in local storage. Using "
521 << "group private key from local key registry.";
522 did_non_fatal_error_occur_ = true;
523 }
524 }
525
526 AttemptNextStep();
527 }
528
ProcessEncryptedDeviceMetadata()529 void CryptAuthDeviceSyncerImpl::ProcessEncryptedDeviceMetadata() {
530 SetState(State::kWaitingForEncryptedDeviceMetadataProcessing);
531
532 // If we still do not have a group private key, we cannot decrypt device
533 // metadata nor share the group private key. Finish the DeviceSync attempt and
534 // wait for a GCM notification alerting us that the group private key is
535 // available.
536 const CryptAuthKey* group_key = key_registry_->GetActiveKey(
537 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey);
538 DCHECK(group_key);
539 if (group_key->private_key().empty()) {
540 CryptAuthDeviceSyncResult::ResultCode result_code =
541 did_non_fatal_error_occur_
542 ? CryptAuthDeviceSyncResult::ResultCode::kFinishedWithNonFatalErrors
543 : CryptAuthDeviceSyncResult::ResultCode::kSuccess;
544 FinishAttempt(result_code);
545 return;
546 }
547
548 DCHECK(new_device_registry_map_);
549 CryptAuthEciesEncryptor::IdToInputMap id_to_encrypted_metadata_map;
550 for (const auto& id_device_pair : *new_device_registry_map_) {
551 const auto it =
552 id_to_device_metadata_packet_map_.find(id_device_pair.first);
553 DCHECK(it != id_to_device_metadata_packet_map_.end());
554
555 // Do not try to decrypt metadata that is not sent. This can happen if a
556 // device has not uploaded metadata encrypted with the correct group public
557 // key.
558 if (it->second.encrypted_metadata().empty())
559 continue;
560
561 id_to_encrypted_metadata_map[id_device_pair.first] =
562 CryptAuthEciesEncryptor::PayloadAndKey(it->second.encrypted_metadata(),
563 group_key->private_key());
564 }
565
566 if (id_to_encrypted_metadata_map.empty()) {
567 PA_LOG(ERROR) << "No encrypted metadata sent by CryptAuth. We expect the "
568 << "local device's encrypted metadata, at a minimum.";
569 did_non_fatal_error_occur_ = true;
570 AttemptNextStep();
571 return;
572 }
573
574 encryptor_ = CryptAuthEciesEncryptorImpl::Factory::Create();
575 encryptor_->BatchDecrypt(
576 id_to_encrypted_metadata_map,
577 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnDeviceMetadataDecrypted,
578 base::Unretained(this)));
579 }
580
OnDeviceMetadataDecrypted(const CryptAuthEciesEncryptor::IdToOutputMap & id_to_decrypted_metadata_map)581 void CryptAuthDeviceSyncerImpl::OnDeviceMetadataDecrypted(
582 const CryptAuthEciesEncryptor::IdToOutputMap&
583 id_to_decrypted_metadata_map) {
584 DCHECK_EQ(State::kWaitingForEncryptedDeviceMetadataProcessing, state_);
585
586 // Record a success because the operation did not timeout. A separate metric
587 // tracks individual decryption failures.
588 RecordDeviceMetadataDecryptionMetrics(
589 base::TimeTicks::Now() - last_state_change_timestamp_,
590 CryptAuthAsyncTaskResult::kSuccess);
591
592 AddDecryptedMetadataToNewDeviceRegistry(id_to_decrypted_metadata_map);
593
594 AttemptNextStep();
595 }
596
AddDecryptedMetadataToNewDeviceRegistry(const CryptAuthEciesEncryptor::IdToOutputMap & id_to_decrypted_metadata_map)597 void CryptAuthDeviceSyncerImpl::AddDecryptedMetadataToNewDeviceRegistry(
598 const CryptAuthEciesEncryptor::IdToOutputMap&
599 id_to_decrypted_metadata_map) {
600 DCHECK(new_device_registry_map_);
601
602 // Update the new device registry with BetterTogether device metadata.
603 for (const auto& id_metadata_pair : id_to_decrypted_metadata_map) {
604 bool was_metadata_decrypted = id_metadata_pair.second.has_value();
605 base::UmaHistogramBoolean(
606 "CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataDecryptionSuccess",
607 was_metadata_decrypted);
608 if (!was_metadata_decrypted) {
609 PA_LOG(ERROR) << "Metadata for device with Instance ID "
610 << id_metadata_pair.first
611 << " was not able to be decrypted.";
612 did_non_fatal_error_occur_ = true;
613 continue;
614 }
615
616 cryptauthv2::BetterTogetherDeviceMetadata decrypted_metadata;
617 bool was_metadata_parsed =
618 decrypted_metadata.ParseFromString(*id_metadata_pair.second);
619 base::UmaHistogramBoolean(
620 "CryptAuth.DeviceSyncV2.DeviceSyncer.MetadataParsingSuccess",
621 was_metadata_parsed);
622 if (!was_metadata_parsed) {
623 PA_LOG(ERROR) << "Metadata for device with Instance ID "
624 << id_metadata_pair.first << " was not able to be parsed.";
625 did_non_fatal_error_occur_ = true;
626 continue;
627 }
628
629 auto it = new_device_registry_map_->find(id_metadata_pair.first);
630 DCHECK(it != new_device_registry_map_->end());
631
632 // The local device should already have its metadata set. Verify consistency
633 // with data from CryptAuth.
634 if (id_metadata_pair.first == request_context_.device_id()) {
635 DCHECK(it->second.better_together_device_metadata);
636 bool is_local_device_metadata_consistent =
637 id_metadata_pair.second ==
638 it->second.better_together_device_metadata->SerializeAsString();
639 base::UmaHistogramBoolean(
640 "CryptAuth.DeviceSyncV2.DeviceSyncer.IsLocalDeviceMetadataConsistent",
641 is_local_device_metadata_consistent);
642 if (!is_local_device_metadata_consistent) {
643 PA_LOG(ERROR) << "Local device (Instance ID: "
644 << request_context_.device_id()
645 << ") metadata disagrees with that sent in SyncMetadata "
646 << "response.";
647 did_non_fatal_error_occur_ = true;
648 }
649 continue;
650 }
651
652 it->second.better_together_device_metadata = decrypted_metadata;
653 }
654 }
655
ShareGroupPrivateKey()656 void CryptAuthDeviceSyncerImpl::ShareGroupPrivateKey() {
657 SetState(State::kWaitingForGroupPrivateKeySharing);
658
659 CryptAuthGroupPrivateKeySharer::IdToEncryptingKeyMap id_to_encrypting_key_map;
660 for (const auto& id_packet_pair : id_to_device_metadata_packet_map_) {
661 if (!id_packet_pair.second.need_group_private_key())
662 continue;
663
664 id_to_encrypting_key_map.insert_or_assign(
665 id_packet_pair.first, id_packet_pair.second.device_public_key());
666 }
667
668 // No device needs the group private key.
669 if (id_to_encrypting_key_map.empty()) {
670 OnShareGroupPrivateKeyFinished(
671 CryptAuthDeviceSyncResult::ResultCode::kSuccess);
672 return;
673 }
674
675 const CryptAuthKey* group_key = key_registry_->GetActiveKey(
676 CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey);
677 DCHECK(group_key);
678
679 group_private_key_sharer_ =
680 CryptAuthGroupPrivateKeySharerImpl::Factory::Create(client_factory_);
681 group_private_key_sharer_->ShareGroupPrivateKey(
682 request_context_, *group_key, id_to_encrypting_key_map,
683 base::BindOnce(&CryptAuthDeviceSyncerImpl::OnShareGroupPrivateKeyFinished,
684 base::Unretained(this)));
685 }
686
OnShareGroupPrivateKeyFinished(CryptAuthDeviceSyncResult::ResultCode device_sync_result_code)687 void CryptAuthDeviceSyncerImpl::OnShareGroupPrivateKeyFinished(
688 CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
689 DCHECK_EQ(State::kWaitingForGroupPrivateKeySharing, state_);
690
691 switch (CryptAuthDeviceSyncResult::GetResultType(device_sync_result_code)) {
692 case CryptAuthDeviceSyncResult::ResultType::kNonFatalError:
693 did_non_fatal_error_occur_ = true;
694 FALLTHROUGH;
695 case CryptAuthDeviceSyncResult::ResultType::kSuccess:
696 AttemptNextStep();
697 return;
698 case CryptAuthDeviceSyncResult::ResultType::kFatalError:
699 FinishAttempt(device_sync_result_code);
700 return;
701 }
702 }
703
FinishAttempt(CryptAuthDeviceSyncResult::ResultCode result_code)704 void CryptAuthDeviceSyncerImpl::FinishAttempt(
705 CryptAuthDeviceSyncResult::ResultCode result_code) {
706 SetState(State::kFinished);
707
708 metadata_syncer_.reset();
709 feature_status_getter_.reset();
710 encryptor_.reset();
711 group_private_key_sharer_.reset();
712
713 CryptAuthDeviceSyncResult::ResultType result_type =
714 CryptAuthDeviceSyncResult::GetResultType(result_code);
715 if (result_type == CryptAuthDeviceSyncResult::ResultType::kSuccess) {
716 synced_bluetooth_address_tracker_->SetLastSyncedBluetoothAddress(
717 local_better_together_device_metadata_.bluetooth_public_address());
718 }
719
720 bool did_device_registry_change =
721 new_device_registry_map_ &&
722 device_registry_->SetRegistry(*new_device_registry_map_);
723
724 OnAttemptFinished(CryptAuthDeviceSyncResult(
725 result_code, did_device_registry_change, new_client_directive_));
726 }
727
operator <<(std::ostream & stream,const CryptAuthDeviceSyncerImpl::State & state)728 std::ostream& operator<<(std::ostream& stream,
729 const CryptAuthDeviceSyncerImpl::State& state) {
730 switch (state) {
731 case CryptAuthDeviceSyncerImpl::State::kNotStarted:
732 stream << "[DeviceSyncer state: Not started]";
733 break;
734 case CryptAuthDeviceSyncerImpl::State::kWaitingForBluetoothAddress:
735 stream << "[DeviceSyncer state: Waiting for Bluetooth address]";
736 break;
737 case CryptAuthDeviceSyncerImpl::State::kWaitingForMetadataSync:
738 stream << "[DeviceSyncer state: Waiting for metadata sync]";
739 break;
740 case CryptAuthDeviceSyncerImpl::State::kWaitingForFeatureStatuses:
741 stream << "[DeviceSyncer state: Waiting for feature statuses]";
742 break;
743 case CryptAuthDeviceSyncerImpl::State::
744 kWaitingForEncryptedGroupPrivateKeyProcessing:
745 stream << "[DeviceSyncer state: Waiting for encrypted group private key "
746 << "processing]";
747 break;
748 case CryptAuthDeviceSyncerImpl::State::
749 kWaitingForEncryptedDeviceMetadataProcessing:
750 stream << "[DeviceSyncer state: Waiting for encrypted device metadata "
751 "processing]";
752 break;
753 case CryptAuthDeviceSyncerImpl::State::kWaitingForGroupPrivateKeySharing:
754 stream << "[DeviceSyncer state: Waiting for group private key "
755 << "to be shared]";
756 break;
757 case CryptAuthDeviceSyncerImpl::State::kFinished:
758 stream << "[DeviceSyncer state: Finished]";
759 break;
760 }
761
762 return stream;
763 }
764
765 } // namespace device_sync
766
767 } // namespace chromeos
768