chromium/chrome/browser/ash/attestation/tpm_challenge_key.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 CHROME_BROWSER_ASH_ATTESTATION_TPM_CHALLENGE_KEY_H_
#define CHROME_BROWSER_ASH_ATTESTATION_TPM_CHALLENGE_KEY_H_

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

#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key_result.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key_subtle.h"
#include "chromeos/ash/components/dbus/attestation/attestation_ca.pb.h"
#include "chromeos/ash/components/dbus/attestation/keystore.pb.h"
#include "chromeos/ash/components/dbus/constants/attestation_constants.h"

class Profile;
class AttestationFlow;

namespace ash {
namespace attestation {

// Prefix for naming machine keys used for SignedPublicKeyAndChallenge when
// challenging the EMK with register=true.
const char kEnterpriseMachineKeyForSpkacPrefix[] = "attest-ent-machine-";

//========================= TpmChallengeKeyFactory =============================

class TpmChallengeKey;

class TpmChallengeKeyFactory final {
 public:
  static std::unique_ptr<TpmChallengeKey> Create();
  static void SetForTesting(std::unique_ptr<TpmChallengeKey> next_result);

 private:
  static TpmChallengeKey* next_result_for_testing_;
};

//=========================== TpmChallengeKey ==================================

// Asynchronously runs the flow to challenge a key in the caller context. This
// is a wrapper around TpmChallengeKeySubtle with an easier-to-use interface.
// TpmChallengeKeySubtle can be used directly to get more control over main
// steps to build the response.
class TpmChallengeKey {
 public:
  TpmChallengeKey(const TpmChallengeKey&) = delete;
  TpmChallengeKey& operator=(const TpmChallengeKey&) = delete;
  virtual ~TpmChallengeKey() = default;

  // Should be called only once for every instance. |TpmChallengeKey| object
  // should live as long as response from |BuildResponse| function via
  // |callback| is expected. On destruction it stops challenge process and
  // silently discards callback.
  // The response consists of up to two parts: 1) a response to the challenge
  // and optionally 2) an SPKAC. They can be generated using different keys:
  // A) ENTERPRISE_MACHINE && !register_key
  // => 1) Stable device key + 2) Empty
  // B) ENTERPRISE_MACHINE && register_key
  // => 1) Stable device key + 2) Key(key_name)
  // C) ENTERPRISE_USER && !register_key
  // => 1) Key(key_name) + 2) Empty
  // D) ENTERPRISE_USER && register_key
  // => 1) Key(key_name) + 2) Key(key_name)
  // E) DEVICE_TRUST_CONNECTOR && !register_key
  // => 1) Key(key_name) + 2) Empty
  // In case B) |key_name| cannot be empty. In case C), D) some default name
  // will be used if |key_name| is empty.
  // When using DEVICE_TRUST_CONNECTOR, `register_key` is not supported and
  // `key_name` cannot be empty.
  // The response can also contain |signals| which consist of a set of
  // information about the device that is given to the IdP after the challenge
  // response has been verified. These signals can be used as input to an AuthN
  // decision. Signals are collected in a dictionary and are JSON stringified.
  // The signals are optional since they can be null when no signals are set on
  // the response, empty when no signals were collected (i.e empty signals
  // dictionary), or non empty. More information on signals collection can be
  // found in the |SignalsService|.

  virtual void BuildResponse(::attestation::VerifiedAccessFlow flow_type,
                             Profile* profile,
                             TpmChallengeKeyCallback callback,
                             const std::string& challenge,
                             bool register_key,
                             ::attestation::KeyType key_crypto_type,
                             const std::string& key_name,
                             const std::optional<std::string>& signals) = 0;

 protected:
  // Use TpmChallengeKeyFactory for creation.
  TpmChallengeKey() = default;
};

//=========================== TpmChallengeKeyImpl ==============================

class TpmChallengeKeyImpl final : public TpmChallengeKey {
 public:
  // Use TpmChallengeKeyFactory for creation.
  TpmChallengeKeyImpl();
  // Use only for testing.
  explicit TpmChallengeKeyImpl(
      AttestationFlow* attestation_flow_for_testing,
      MachineCertificateUploader* certificate_uploader_for_testing);
  TpmChallengeKeyImpl(const TpmChallengeKeyImpl&) = delete;
  TpmChallengeKeyImpl& operator=(const TpmChallengeKeyImpl&) = delete;
  ~TpmChallengeKeyImpl() override;

  // TpmChallengeKey
  void BuildResponse(::attestation::VerifiedAccessFlow flow_type,
                     Profile* profile,
                     TpmChallengeKeyCallback callback,
                     const std::string& challenge,
                     bool register_key,
                     ::attestation::KeyType key_crypto_type,
                     const std::string& key_name,
                     const std::optional<std::string>& signals) override;

 private:
  void OnPrepareKeyDone(const TpmChallengeKeyResult& prepare_key_result);
  void OnSignChallengeDone(const TpmChallengeKeyResult& sign_challenge_result);
  void OnRegisterKeyDone(const TpmChallengeKeyResult& challenge_response,
                         const TpmChallengeKeyResult& register_key_result);

  bool register_key_ = false;
  std::string challenge_;
  TpmChallengeKeyResult challenge_response_;
  TpmChallengeKeyCallback callback_;
  std::unique_ptr<TpmChallengeKeySubtle> tpm_challenge_key_subtle_;

  SEQUENCE_CHECKER(sequence_checker_);
  base::WeakPtrFactory<TpmChallengeKeyImpl> weak_factory_{this};
};

}  // namespace attestation
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_ATTESTATION_TPM_CHALLENGE_KEY_H_