1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
|
// 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_
|