chromium/chromeos/ash/components/cryptohome/auth_factor.h

// Copyright 2022 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_COMPONENTS_CRYPTOHOME_AUTH_FACTOR_H_
#define CHROMEOS_ASH_COMPONENTS_CRYPTOHOME_AUTH_FACTOR_H_

#include <optional>
#include <string>

#include "base/component_export.h"
#include "base/containers/enum_set.h"
#include "base/time/time.h"
#include "chromeos/ash/components/cryptohome/common_types.h"
#include "third_party/abseil-cpp/absl/types/variant.h"

namespace cryptohome {

// All authfactors created/modified after AuthFactor API is launched should
// have OS/Chrome versions filled in CommonMetadata.
// However, any factor created before the launch would miss such information.
// In order to keep rest of the code simple, this "zero" value would be used
// upon receiving AuthFactor from cryptohome.
// Note that this value should not be passed back to cryptohome, this
// is guarded by CHECKs in serialization code.
COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
extern const char kFallbackFactorVersion[];

enum class AuthFactorType {
  // Special edge case - on old ChromeOS versions Kiosk keys and passwords for
  // regular users had no metadata to distinguish them on cryptohome level,
  // only Chrome can do that based on UserType.
  // This type can be returned when retrieving data from cryptohome,
  // but should not be used in any data passed from chrome to cryptohome.
  // This factor type is not included in `AuthFactorsSet`.
  kUnknownLegacy,

  // This is a synthetic factor, there is no actual key associated with this
  // factor, it is only used as indicator of specific authentication mode.
  // As such it can never be returned by cryptohome, and is not included in
  // `AuthFactorsSet`.
  kLegacyFingerprint,

  kPassword,
  kPin,
  kRecovery,
  kSmartCard,
  kKiosk,
  kFingerprint,
};

using AuthFactorsSet = base::
    EnumSet<AuthFactorType, AuthFactorType::kPassword, AuthFactorType::kKiosk>;

// Reference to a particular AuthFactor.
// While `label` uniquely identifies factor across all factor types,
// it is convenient to pass AuthFactorType along.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) AuthFactorRef {
 public:
  AuthFactorRef(AuthFactorType type, KeyLabel label);

  AuthFactorRef(AuthFactorRef&&);
  AuthFactorRef& operator=(AuthFactorRef&&);

  AuthFactorRef(const AuthFactorRef&);
  AuthFactorRef& operator=(const AuthFactorRef&);

  ~AuthFactorRef();

  bool operator==(const AuthFactorRef& other) const;

  AuthFactorType type() const { return type_; }

  const KeyLabel& label() const { return label_; }

 private:
  AuthFactorType type_;
  KeyLabel label_;
};

// Each auth factor supported by cryptohome has 4 types of data associated with
// it:
//   * factor identifiers: type and label (though label can be changed by
//     cryptohome);
//   * factor input: part of data that is write-only by Chrome, e.g.
//     during setting up a factor, or attempting an authentication;
//   * factor status: data that is set by cryptohome and is read-only on the
//     Chrome side, e.g. PIN lockout status;
//   * factor metadata: non-identifying data associated with factor that can
//     be both read and written by Chrome.

// Common metadata that should be defined for each auth factor.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
    AuthFactorCommonMetadata {
 public:
  // This constructor should be used for AuthFactors created on the Chrome side.
  // It fills in current Chrome version, leaving ChromeOS version empty.
  AuthFactorCommonMetadata();
  AuthFactorCommonMetadata(ComponentVersion chrome, ComponentVersion chromeos);
  ~AuthFactorCommonMetadata();

  AuthFactorCommonMetadata(AuthFactorCommonMetadata&&) noexcept;
  AuthFactorCommonMetadata& operator=(AuthFactorCommonMetadata&&) noexcept;

  AuthFactorCommonMetadata(const AuthFactorCommonMetadata&);
  AuthFactorCommonMetadata& operator=(const AuthFactorCommonMetadata&);

  // Should only be used for testing purposes.
  bool operator==(const AuthFactorCommonMetadata& other) const;

  const ComponentVersion& chrome_version_last_updated() const {
    return chrome_version_last_updated_;
  }

  const ComponentVersion& chromeos_version_last_updated() const {
    return chromeos_version_last_updated_;
  }

 private:
  ComponentVersion chrome_version_last_updated_;
  ComponentVersion chromeos_version_last_updated_;
};

// Per-factor statuses (read-only properties set by cryptohomed):
// TODO(b/341733466): Individual per-factor statuses are discouraged.
// A general auth factor status is already returned by cryptohomed for
// each AuthFactorWithStatus.

// PinStatus provides the pin status info returned from cryptohomed.
// PinStatus only represents a status snapshot at the time of its
// construction. Care must be taken in checking the freshness of
// any PinStatus object.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) PinStatus {
 public:
  // Default constructor: the pin factor is immediately available.
  PinStatus();
  // Constructor takes one TimeDelta value:
  // |available_in| indicates a timeout after which the factor will become
  // available.
  //   0 means the factor is immediately available.
  //   TimeDelta::Max() means the factor is locked out indefinitely.
  PinStatus(base::TimeDelta available_in);

  PinStatus(PinStatus&&) noexcept;
  PinStatus& operator=(PinStatus&&) noexcept;

  PinStatus(const PinStatus&);
  PinStatus& operator=(const PinStatus&);

  ~PinStatus();

  // The time when the pin auth factor will be available.
  // If locked out indefinitely, return Time::Max().
  base::Time AvailableAt() const;

  // Indicates a not-avaiable pin.
  bool IsLockedFactor() const;

 private:
  base::Time available_at_;
};

// Common types used in factor-specific metadata:

// The hash information of an auth factor that is used to generate the actual
// secret. This can be used to generate the recoverable key store for the auth
// factor.
struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
    KnowledgeFactorHashInfo {
  KnowledgeFactorHashAlgorithmWrapper algorithm;
  std::string salt;
  bool should_generate_key_store;
};

// Factor-specific metadata:

struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) SmartCardMetadata {
  std::string public_key_spki_der;
};

struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME)
    CryptohomeRecoveryMetadata {
  std::string mediator_pub_key;
};

class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) PasswordMetadata {
 public:
  // Constructors that do/don't include the hash info field. Difference with
  // online and local password metadata is that only local password metadata
  // needs to generate recoverable key stores.
  // Generating recoverable key stores will make the password a recovery
  // factor for some Google sync services, and we only want to do it for local
  // passwords but not GAIA passwords. This is because the user already needs
  // their GAIA password to access the Google sync services, so making the GAIA
  // password a sync recovery factor doesn't provide any extra layer of
  // security.
  static PasswordMetadata CreateWithoutSalt();
  static PasswordMetadata CreateForOnlinePassword(SystemSalt salt);
  static PasswordMetadata CreateForLocalPassword(SystemSalt salt);

  PasswordMetadata(PasswordMetadata&&) noexcept;
  PasswordMetadata& operator=(PasswordMetadata&&) noexcept;

  PasswordMetadata(const PasswordMetadata&);
  PasswordMetadata& operator=(const PasswordMetadata&);

  ~PasswordMetadata();

  const std::optional<KnowledgeFactorHashInfo>& hash_info() const {
    return hash_info_;
  }

 private:
  PasswordMetadata(std::optional<KnowledgeFactorHashInfo> hash_info);

  std::optional<KnowledgeFactorHashInfo> hash_info_;
};

class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) PinMetadata {
 public:
  static PinMetadata CreateWithoutSalt();
  static PinMetadata Create(PinSalt salt);

  PinMetadata(PinMetadata&&) noexcept;
  PinMetadata& operator=(PinMetadata&&) noexcept;

  PinMetadata(const PinMetadata&);
  PinMetadata& operator=(const PinMetadata&);

  ~PinMetadata();

  const std::optional<KnowledgeFactorHashInfo>& hash_info() const {
    return hash_info_;
  }

 private:
  PinMetadata(std::optional<KnowledgeFactorHashInfo> hash_info);

  std::optional<KnowledgeFactorHashInfo> hash_info_;
};

class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) FingerprintMetadata {
};

// AuthFactor definition.
// If it is obtainted from `cryptohome` it will contain factor-specific status,
// otherwise it would only contain identity and metadata.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_CRYPTOHOME) AuthFactor {
 public:
  AuthFactor(AuthFactorRef ref, AuthFactorCommonMetadata metadata);
  AuthFactor(AuthFactorRef ref,
             AuthFactorCommonMetadata metadata,
             SmartCardMetadata smartcard_metadata);
  AuthFactor(AuthFactorRef ref,
             AuthFactorCommonMetadata metadata,
             CryptohomeRecoveryMetadata recovery_metadata);
  AuthFactor(AuthFactorRef ref,
             AuthFactorCommonMetadata metadata,
             PasswordMetadata password_metadata);
  AuthFactor(AuthFactorRef ref,
             AuthFactorCommonMetadata metadata,
             PinMetadata pin_metadata);
  AuthFactor(AuthFactorRef ref,
             AuthFactorCommonMetadata metadata,
             PinMetadata pin_metadata,
             PinStatus status);
  AuthFactor(AuthFactorRef ref,
             AuthFactorCommonMetadata metadata,
             FingerprintMetadata fingerprint_metadata);

  AuthFactor(AuthFactor&&) noexcept;
  AuthFactor& operator=(AuthFactor&&) noexcept;
  AuthFactor(const AuthFactor&);
  AuthFactor& operator=(const AuthFactor&);

  ~AuthFactor();

  // Should only be used for testing purposes.
  bool operator==(const AuthFactor& other) const;

  const AuthFactorRef& ref() const;
  const AuthFactorCommonMetadata& GetCommonMetadata() const;

  // Fails if type does not match:
  const PinStatus& GetPinStatus() const;
  const SmartCardMetadata& GetSmartCardMetadata() const;
  const CryptohomeRecoveryMetadata& GetCryptohomeRecoveryMetadata() const;
  const PasswordMetadata& GetPasswordMetadata() const;
  const PinMetadata& GetPinMetadata() const;
  const FingerprintMetadata& GetFingerprintMetadata() const;

 private:
  AuthFactorRef ref_;
  AuthFactorCommonMetadata common_metadata_;
  absl::variant<absl::monostate,
                SmartCardMetadata,
                CryptohomeRecoveryMetadata,
                PasswordMetadata,
                PinMetadata,
                FingerprintMetadata>
      factor_metadata_;
  absl::variant<absl::monostate, PinStatus> factor_status_;
};

}  // namespace cryptohome

#endif  // CHROMEOS_ASH_COMPONENTS_CRYPTOHOME_AUTH_FACTOR_H_