chromium/chrome/browser/ash/attestation/tpm_challenge_key_subtle.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_ATTESTATION_TPM_CHALLENGE_KEY_SUBTLE_H_
#define CHROME_BROWSER_ASH_ATTESTATION_TPM_CHALLENGE_KEY_SUBTLE_H_

#include <memory>
#include <optional>
#include <string>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key_result.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chromeos/ash/components/attestation/attestation_flow.h"
#include "chromeos/ash/components/dbus/attestation/attestation_ca.pb.h"
#include "chromeos/ash/components/dbus/attestation/attestation_client.h"
#include "chromeos/ash/components/dbus/attestation/interface.pb.h"
#include "chromeos/ash/components/dbus/constants/attestation_constants.h"
#include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/user.h"

class Profile;

namespace ash {
namespace attestation {

class MachineCertificateUploader;

//==================== TpmChallengeKeySubtleFactory ============================

class TpmChallengeKeySubtle;

class TpmChallengeKeySubtleFactory final {
 public:
  static std::unique_ptr<TpmChallengeKeySubtle> Create();

  // Recreates an object as it would be after |StartPrepareKeyStep| method call.
  // It is the caller's responsibility to guarantee that |StartPrepareKeyStep|
  // has successfully finished before and that only one call of
  // |StartSignChallengeStep| and/or |StartRegisterKeyStep| for a prepared key
  // pair will ever happen.
  // |profile| may be nullptr - then it is assumed that this is a device-wide
  // instance that is only intended to be used with machine keys.
  static std::unique_ptr<TpmChallengeKeySubtle> CreateForPreparedKey(
      ::attestation::VerifiedAccessFlow flow_type,
      bool will_register_key,
      ::attestation::KeyType key_crypto_type,
      const std::string& key_name,
      const std::string& public_key,
      Profile* profile);

  static void SetForTesting(std::unique_ptr<TpmChallengeKeySubtle> next_result);
  static bool WillReturnTestingInstance();

 private:
  static TpmChallengeKeySubtle* next_result_for_testing_;
};

//===================== TpmChallengeKeySubtle ==================================

using TpmChallengeKeyCallback =
    base::OnceCallback<void(const TpmChallengeKeyResult& result)>;

// Asynchronously runs the flow to challenge a key in the caller context.
// Consider using |TpmChallengeKey| class for simple cases.
// This class provides a detailed API for calculating Verified Access challenge
// response and manipulating keys that are used for that.
//
// The order of calling methods is important. Expected usage:
// 1. |StartPrepareKeyStep| should always be called first.
// 2. After that, if the object is destroyed, it can be recreated by using
// |TpmChallengeKeySubtleFactory::CreateForPreparedKey|.
// 3. |StartSignChallengeStep| allows to calculate challenge response, can be
// skipped.
// 4. As a last step, |StartRegisterKeyStep| allows change key type so it cannot
// sign challenges anymore, but can be used for general puprose cryptographic
// operations (via PlatformKeysService).
class TpmChallengeKeySubtle {
 public:
  TpmChallengeKeySubtle(const TpmChallengeKeySubtle&) = delete;
  TpmChallengeKeySubtle& operator=(const TpmChallengeKeySubtle&) = delete;
  virtual ~TpmChallengeKeySubtle() = default;

  // Checks that it is allowed to generate a VA challenge response and generates
  // a new key pair if necessary. Returns result via |callback|. In case of
  // success |TpmChallengeKeyResult::public_key| will be filled. If
  // |will_register_key| is true, challenge response will contain SPKAC and the
  // key can be registered using StartRegisterKeyStep method.
  virtual void StartPrepareKeyStep(
      ::attestation::VerifiedAccessFlow flow_type,
      bool will_register_key,
      ::attestation::KeyType key_crypto_type,
      const std::string& key_name,
      Profile* profile,
      TpmChallengeKeyCallback callback,
      const std::optional<std::string>& signals) = 0;

  // Generates a VA challenge response using the key pair prepared by
  // |PrepareKey| method. Returns VA challenge response via |callback|. In case
  // of success |TpmChallengeKeyResult::challenge_response| will be filled.
  virtual void StartSignChallengeStep(const std::string& challenge,
                                      TpmChallengeKeyCallback callback) = 0;

  // Registers the key that makes it available for general purpose cryptographic
  // operations.
  virtual void StartRegisterKeyStep(TpmChallengeKeyCallback callback) = 0;

 protected:
  // Allow access to |RestorePrepareKeyResult| method.
  friend class TpmChallengeKeySubtleFactory;

  // Use TpmChallengeKeySubtleFactory for creation.
  TpmChallengeKeySubtle() = default;

  // Restores internal state of the object as if it would be after
  // |StartPrepareKeyStep|. |public_key| is required only if |will_register_key|
  // is true.
  virtual void RestorePreparedKeyState(
      ::attestation::VerifiedAccessFlow flow_type,
      bool will_register_key,
      ::attestation::KeyType key_crypto_type,
      const std::string& key_name,
      const std::string& public_key,
      Profile* profile) = 0;
};

//================= TpmChallengeKeySubtleImpl ==================================

class TpmChallengeKeySubtleImpl final : public TpmChallengeKeySubtle {
 public:
  // Use TpmChallengeKeySubtleFactory for creation.
  TpmChallengeKeySubtleImpl();
  // Use only for testing.
  TpmChallengeKeySubtleImpl(
      AttestationFlow* attestation_flow_for_testing,
      MachineCertificateUploader* certificate_uploader_for_testing);

  TpmChallengeKeySubtleImpl(const TpmChallengeKeySubtleImpl&) = delete;
  TpmChallengeKeySubtleImpl& operator=(const TpmChallengeKeySubtleImpl&) =
      delete;
  ~TpmChallengeKeySubtleImpl() override;

  // TpmChallengeKeySubtle
  void StartPrepareKeyStep(::attestation::VerifiedAccessFlow flow_type,
                           bool will_register_key,
                           ::attestation::KeyType key_crypto_type,
                           const std::string& key_name,
                           Profile* profile,
                           TpmChallengeKeyCallback callback,
                           const std::optional<std::string>& signals) override;
  void StartSignChallengeStep(const std::string& challenge,
                              TpmChallengeKeyCallback callback) override;
  void StartRegisterKeyStep(TpmChallengeKeyCallback callback) override;

 private:
  // TpmChallengeKeySubtle
  void RestorePreparedKeyState(::attestation::VerifiedAccessFlow flow_type,
                               bool will_register_key,
                               ::attestation::KeyType key_crypto_type,
                               const std::string& key_name,
                               const std::string& public_key,
                               Profile* profile) override;

  void PrepareEnterpriseUserFlow();
  void PrepareEnterpriseMachineFlow();
  void PrepareDeviceTrustConnectorFlow();

  // Returns true if the user is managed.
  // If this is a device-wide instance without a user-associated `profile_`,
  // returns false.
  bool IsUserManaged() const;

  // Returns true if the user is managed and is affiliated with the domain the
  // device is enrolled to.
  // If this is a device-wide instance without a user-associated |profile_|,
  // returns false.
  bool IsUserAffiliated() const;

  // Returns the user email (for user key) or an empty string (for machine key).
  std::string GetEmail() const;
  AttestationCertificateProfile GetCertificateProfile() const;
  // Returns the User* associated with |profile_|. May return nullptr (if there
  // is no |profile_| or if e.g. |profile_| is a sign-in profile).
  const user_manager::User* GetUser() const;
  // Returns the AccountId associated with |profile_|. Will return
  // EmptyAccountId() if GetUser() returns nullptr.
  AccountId GetAccountId() const;
  // Returns `GetAccountId()` if the flow type uses a user key, returns empty
  // `AccountId` if the flow type uses a device key.
  AccountId GetAccountIdForAttestationFlow() const;
  // Returns the account id in string if the flow type uses a user key, returns
  // an empty string if the flow type uses a device key.
  std::string GetUsernameForAttestationClient() const;
  // Returns whether or not the challenge response should include the
  // certificate of the signing key depending on the VA flow type.
  bool ShouldIncludeSigningKeyCertificate() const;
  // Returns whether or not the challenge response should include the device
  // management / enterprise obfuscated customer ID of the device.
  bool ShouldIncludeCustomerId() const;

  // Actually prepares a key after all checks are passed and if `can_continue`
  // is true.
  void PrepareKey(bool can_continue);
  // Returns a public key (or an error) via `callback_`.
  void PrepareKeyFinished(const ::attestation::GetKeyInfoReply& reply);

  void SignChallengeCallback(
      const ::attestation::SignEnterpriseChallengeReply& reply);

  void RegisterKeyCallback(
      const ::attestation::RegisterKeyWithChapsTokenReply& reply);
  void MarkCorporateKeyCallback(chromeos::platform_keys::Status status);

  void GetEnrollmentPreparationsCallback(
      const ::attestation::GetEnrollmentPreparationsReply& reply);
  void PrepareKeyErrorHandlerCallback(
      const ::tpm_manager::GetTpmNonsensitiveStatusReply& reply);
  void DoesKeyExistCallback(const ::attestation::GetKeyInfoReply& reply);
  void AskForUserConsent(base::OnceCallback<void(bool)> callback) const;
  void AskForUserConsentCallback(bool result);
  void GetCertificateCallback(AttestationStatus status,
                              const std::string& pem_certificate_chain);
  void GetPublicKey();

  // Runs |callback_| and resets it. Resetting it in this function and checking
  // it in public functions prevents simultaneous calls on the same object.
  // |this| may be destructed during the |callback_| run.
  void RunCallback(const TpmChallengeKeyResult& result);

  std::unique_ptr<AttestationFlow> default_attestation_flow_;
  raw_ptr<AttestationFlow, LeakedDanglingUntriaged> attestation_flow_ = nullptr;
  // Can be nullptr.
  raw_ptr<MachineCertificateUploader, LeakedDanglingUntriaged>
      machine_certificate_uploader_ = nullptr;

  TpmChallengeKeyCallback callback_;
  // |profile_| may be nullptr if this is an instance that is used device-wide
  // and only intended to work with machine keys.
  raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr;

  ::attestation::VerifiedAccessFlow flow_type_ =
      ::attestation::ENTERPRISE_MACHINE;
  bool will_register_key_ = false;
  ::attestation::KeyType key_crypto_type_ = ::attestation::KEY_TYPE_RSA;
  // See the comment for TpmChallengeKey::BuildResponse for more context about
  // different cases of using this variable.
  std::string key_name_;
  // In case the key is going to be registered, the public key is stored here
  // (after PrepareKeyFinished method is finished). It is used to mark the key
  // as corporate.
  std::string public_key_;
  // Signals from Context Aware Access.
  std::optional<std::string> signals_;

  SEQUENCE_CHECKER(sequence_checker_);

  base::WeakPtrFactory<TpmChallengeKeySubtleImpl> weak_factory_{this};
};

}  // namespace attestation
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_ATTESTATION_TPM_CHALLENGE_KEY_SUBTLE_H_