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