// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_METADATA_SYNCER_H_
#define CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_METADATA_SYNCER_H_
#include <memory>
#include <optional>
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "chromeos/ash/services/device_sync/cryptauth_device_sync_result.h"
#include "chromeos/ash/services/device_sync/cryptauth_key.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_directive.pb.h"
namespace cryptauthv2 {
class BetterTogetherDeviceMetadata;
} // namespace cryptauthv2
namespace ash {
namespace device_sync {
// Handles the SyncMetadata portion of the CryptAuth v2 DeviceSync protocol,
// which consists of one or two SyncMetadata request/response interactions with
// the CryptAuth servers:
//
// 1a) First SyncMetadataRequest: Contains what this device believes to be the
// group public key. This could be a newly generated key or one from local
// storage. The device's metadata is encrypted with this key and included in
// the request.
//
// 1b) First SyncMetadataResponse: The response from CryptAuth possibly includes
// the correct group key pair, where the private key is encrypted with this
// device's CryptAuthKeyBunde::kDeviceSyncBetterTogether public key. If the
// group public key is not set, the server is indicating that a new group
// key pair must be created by this device. In this case or if the group
// public key in the response differs from the one sent in the request,
// another SyncMetadataRequest is made. Otherwise, the second
// SyncMetadataRequest is skipped and the encrypted remote device metadata
// is decrypted using the group private key if available. Note: The remote
// devices are only those registered in the DeviceSync:BetterTogether group,
// in other words, those using v2 DeviceSync.
//
// 2a) (Possible) Second SyncMetadataRequest: Not invoked if the group public
// key from the first SyncMetadataResponse (1b) agrees with the one sent in
// the first SyncMetadataRequest (1a). The client has the correct group
// public key at this point.
//
// 2b) (Possible) Second SyncMetadataResponse: The included remote device
// metadata can be decrypted if a group private key is sent. If no group
// private key is returned, the client must wait for a GCM message
// requesting a DeviceSync when the key becomes available.
//
// The output of a successful flow is:
// - A map from Instance ID to DeviceMetadataPacket, which contains device
// metadata encrypted with the group public key.
// - (Optional) A new group public and private key created by the client or a
// new group public key provided by CryptAuth. Null if no new group key was
// created.
// - (Optional) A group private key, encrypted with this device's
// CryptAuthKeyBunde::kDeviceSyncBetterTogether public key.
// - (Optional) A new ClientDirective sent from the CryptAuth server.
//
// A CryptAuthMetadataSyncer object is designed to be used for only one
// SyncMetadata() call. For a new DeviceSync attempt, a new object should be
// created.
class CryptAuthMetadataSyncer {
public:
using IdToDeviceMetadataPacketMap =
base::flat_map<std::string, cryptauthv2::DeviceMetadataPacket>;
using SyncMetadataAttemptFinishedCallback = base::OnceCallback<void(
const IdToDeviceMetadataPacketMap&,
std::unique_ptr<CryptAuthKey>,
const std::optional<cryptauthv2::EncryptedGroupPrivateKey>&,
const std::optional<cryptauthv2::ClientDirective>&,
CryptAuthDeviceSyncResult::ResultCode)>;
CryptAuthMetadataSyncer(const CryptAuthMetadataSyncer&) = delete;
CryptAuthMetadataSyncer& operator=(const CryptAuthMetadataSyncer&) = delete;
virtual ~CryptAuthMetadataSyncer();
// Starts the SyncMetadata portion of the CryptAuth v2 DeviceSync flow.
void SyncMetadata(
const cryptauthv2::RequestContext& request_context,
const cryptauthv2::BetterTogetherDeviceMetadata& local_device_metadata,
const CryptAuthKey* initial_group_key,
SyncMetadataAttemptFinishedCallback callback);
protected:
CryptAuthMetadataSyncer();
virtual void OnAttemptStarted(
const cryptauthv2::RequestContext& request_context,
const cryptauthv2::BetterTogetherDeviceMetadata& local_device_metadata,
const CryptAuthKey* initial_group_key) = 0;
void OnAttemptFinished(
const IdToDeviceMetadataPacketMap& id_to_device_metadata_packet_map,
std::unique_ptr<CryptAuthKey> new_group_key,
const std::optional<cryptauthv2::EncryptedGroupPrivateKey>&
encrypted_group_private_key,
const std::optional<cryptauthv2::ClientDirective>& new_client_directive,
CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);
private:
SyncMetadataAttemptFinishedCallback callback_;
bool was_sync_metadata_called_ = false;
};
} // namespace device_sync
} // namespace ash
#endif // CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_METADATA_SYNCER_H_