chromium/chromeos/ash/services/device_sync/cryptauth_key_bundle.h

// 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_KEY_BUNDLE_H_
#define CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_

#include <optional>

#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/values.h"
#include "chromeos/ash/services/device_sync/cryptauth_key.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_directive.pb.h"

namespace ash::device_sync {

// A group of related CryptAuthKeys, uniquely identified by their handles.
//
// No more than one key in the bundle can be active at a time, and only the
// active key should be used for encryption, signing, etc. Inactive keys are
// retained and can be activated in the future, for example, due to a
// SyncSingleKeyResponse::KeyAction sent by CryptAuth.
//
// All key bundles used in Chrome OS are enumerated in the Name enum class. The
// name string corresponding to each enum value can be retrieved via
// KeyBundleNameEnumToString(). For key bundles that enroll with CryptAuth, this
// string is used to populate the SyncSingleKeysRequest::key_name protobuf
// field.
class CryptAuthKeyBundle {
 public:
  // Names that uniquely define a CryptAuthKeyBundle.
  enum class Name {
    // A non-rotated asymmetric key associated with a user on the device. It is
    // used for encrypting device-to-device communications, for example, and it
    // has historically been used as a device identifier.
    kUserKeyPair,
    // Currently unused but required for CryptAuth v2 Enrollment.
    kLegacyAuthzenKey,
    // Enrolling this asymmetric key adds the device to the user's DeviceSync v2
    // "DeviceSync:BetterTogether" group. This key is not to be confused with
    // the unenrolled kDeviceSyncBetterTogetherGroupKey.
    kDeviceSyncBetterTogether,
    // A key pair that does *not* enroll with CryptAuth, used to encrypt and
    // decrypt the metadata of all devices in the user's
    // "DeviceSync:BetterTogether" group. This metadata is passed in an
    // end-to-end encrypted fashion via DeviceSync v2 SyncMetadata calls .
    kDeviceSyncBetterTogetherGroupKey
  };

  // Returns all Name enum values as a set.
  static const base::flat_set<CryptAuthKeyBundle::Name>& AllNames();

  // Returns the Name enum value of all key bundles that enroll with CryptAuth.
  static const base::flat_set<CryptAuthKeyBundle::Name>& AllEnrollableNames();

  static std::string KeyBundleNameEnumToString(CryptAuthKeyBundle::Name name);
  static std::optional<CryptAuthKeyBundle::Name> KeyBundleNameStringToEnum(
      const std::string& name);

  static std::optional<CryptAuthKeyBundle> FromDictionary(
      const base::Value::Dict& dict);

  explicit CryptAuthKeyBundle(Name name);

  CryptAuthKeyBundle(const CryptAuthKeyBundle&);

  ~CryptAuthKeyBundle();

  Name name() const { return name_; }

  const base::flat_map<std::string, CryptAuthKey>& handle_to_key_map() const {
    return handle_to_key_map_;
  }

  const std::optional<cryptauthv2::KeyDirective>& key_directive() const {
    return key_directive_;
  }

  void set_key_directive(const cryptauthv2::KeyDirective& key_directive) {
    key_directive_ = key_directive;
  }

  // Returns nullptr if there is no active key.
  const CryptAuthKey* GetActiveKey() const;

  // If the key being added is active, all other keys in the bundle will be
  // deactivated. If the handle of the input key matches one in the bundle, the
  // existing key will be overwritten.
  // Note: All keys added to the bundle kUserKeyPair must have the handle
  // kCryptAuthFixedUserKeyPairHandle.
  void AddKey(const CryptAuthKey& key);

  // Activates the key corresponding to |handle| in the bundle and deactivates
  // the other keys.
  void SetActiveKey(const std::string& handle);

  // Sets all key statuses to kInactive.
  void DeactivateKeys();

  // Remove the key corresponding to |handle| from the bundle.
  void DeleteKey(const std::string& handle);

  base::Value::Dict AsDictionary() const;

  bool operator==(const CryptAuthKeyBundle& other) const;
  bool operator!=(const CryptAuthKeyBundle& other) const;

 private:
  using HandleToKeyMap = base::flat_map<std::string, CryptAuthKey>;
  Name name_;
  HandleToKeyMap handle_to_key_map_;
  std::optional<cryptauthv2::KeyDirective> key_directive_;
};

}  // namespace ash::device_sync

#endif  // CHROMEOS_ASH_SERVICES_DEVICE_SYNC_CRYPTAUTH_KEY_BUNDLE_H_