// 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_