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
|
// Copyright 2013 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_PLATFORM_VERIFICATION_FLOW_H_
#define CHROME_BROWSER_ASH_ATTESTATION_PLATFORM_VERIFICATION_FLOW_H_
#include <memory>
#include <set>
#include <string>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/dbus/attestation/interface.pb.h"
#include "chromeos/ash/components/dbus/constants/attestation_constants.h"
#include "components/account_id/account_id.h"
class AccountId;
namespace content {
class WebContents;
} // namespace content
namespace user_manager {
class User;
} // namespace user_manager
namespace ash {
class AttestationClient;
namespace attestation {
class AttestationFlow;
class PlatformVerificationFlowTest;
// This class allows platform verification for the content protection use case.
// All methods must only be called on the UI thread. Example:
// scoped_refptr<PlatformVerificationFlow> verifier =
// new PlatformVerificationFlow();
// PlatformVerificationFlow::ChallengeCallback callback =
// base::BindOnce(&MyCallback);
// verifier->ChallengePlatformKey(my_web_contents, "my_id", "some_challenge",
// std::move(callback));
//
// This class is RefCountedThreadSafe because it may need to outlive its caller.
// The attestation flow that needs to happen to establish a certified platform
// key may take minutes on some hardware. This class will timeout after a much
// shorter time so the caller can proceed without platform verification but it
// is important that the pending operation be allowed to finish. If the
// attestation flow is aborted at any stage, it will need to start over. If we
// use weak pointers, the attestation flow will stop when the next callback is
// run. So we need the instance to stay alive until the platform key is fully
// certified so the next time ChallengePlatformKey() is invoked it will be
// quick.
class PlatformVerificationFlow
: public base::RefCountedThreadSafe<PlatformVerificationFlow> {
public:
// These values are reported to UMA. DO NOT CHANGE THE EXISTING VALUES!
enum Result {
SUCCESS, // The operation succeeded.
INTERNAL_ERROR, // The operation failed unexpectedly.
PLATFORM_NOT_VERIFIED, // The platform cannot be verified. For example:
// - It is not a Chrome device.
// - It is not running a verified OS image.
POLICY_REJECTED, // The operation is not allowed by policy/settings.
TIMEOUT, // The operation timed out.
RESULT_MAX
};
// These values are reported to UMA. DO NOT CHANGE THE EXISTING VALUES!
enum ExpiryStatus {
EXPIRY_STATUS_OK,
EXPIRY_STATUS_EXPIRING_SOON,
EXPIRY_STATUS_EXPIRED,
EXPIRY_STATUS_INVALID_PEM_CHAIN,
EXPIRY_STATUS_INVALID_X509,
EXPIRY_STATUS_MAX
};
// An interface which allows settings and UI to be abstracted for testing
// purposes. For normal operation the default implementation should be used.
class Delegate {
public:
virtual ~Delegate() = default;
// Returns true iff the device is in a mode that supports platform
// verification. For example, platform verification is not supported in dev
// mode unless overridden by a flag.
virtual bool IsInSupportedMode() = 0;
};
// This callback will be called when a challenge operation completes. If
// |result| is SUCCESS then |signed_data| holds the data which was signed
// by the platform key (this is the original challenge appended with a random
// nonce) and |signature| holds the RSA-PKCS1-v1.5 signature. The
// |platform_key_certificate| certifies the key used to generate the
// signature. This key may be generated on demand and is not guaranteed to
// persist across multiple calls to this method. The browser does not check
// the validity of |signature| or |platform_key_certificate|.
using ChallengeCallback =
base::OnceCallback<void(Result result,
const std::string& signed_data,
const std::string& signature,
const std::string& platform_key_certificate)>;
// A constructor that uses the default implementation of all dependencies
// including Delegate.
PlatformVerificationFlow();
// An alternate constructor which specifies dependent objects explicitly.
// This is useful in testing. The caller retains ownership of all pointers.
PlatformVerificationFlow(AttestationFlow* attestation_flow,
AttestationClient* attestation_client,
Delegate* delegate);
PlatformVerificationFlow(const PlatformVerificationFlow&) = delete;
PlatformVerificationFlow& operator=(const PlatformVerificationFlow&) = delete;
// Invokes an asynchronous operation to challenge a platform key. Any user
// interaction will be associated with |web_contents|. The |service_id| is an
// arbitrary value but it should uniquely identify the origin of the request
// and should not be determined by that origin; its purpose is to prevent
// collusion between multiple services. The |challenge| is also an arbitrary
// value but it should be time sensitive or associated to some kind of session
// because its purpose is to prevent certificate replay. The |callback| will
// be called when the operation completes. The duration of the operation can
// vary depending on system state, hardware capabilities, and interaction with
// the user.
void ChallengePlatformKey(content::WebContents* web_contents,
const std::string& service_id,
const std::string& challenge,
ChallengeCallback callback);
// Identical to ChallengePlatformKey above except the User has been extracted
// from the input |web_contents|. The former is needed since non-Ash callsites
// of this class cannot directly reference User*.
void ChallengePlatformKey(const user_manager::User* user,
const std::string& service_id,
const std::string& challenge,
ChallengeCallback callback);
void set_timeout_delay(const base::TimeDelta& timeout_delay) {
timeout_delay_ = timeout_delay;
}
// Public for tests.
static bool IsAttestationAllowedByPolicy();
private:
friend class base::RefCountedThreadSafe<PlatformVerificationFlow>;
friend class PlatformVerificationFlowTest;
// Holds the arguments of a ChallengePlatformKey call. This is convenient for
// use with base::Bind so we don't get too many arguments.
struct ChallengeContext {
ChallengeContext(const AccountId& account_id,
const std::string& service_id,
const std::string& challenge,
ChallengeCallback callback);
ChallengeContext(ChallengeContext&& other);
~ChallengeContext();
AccountId account_id;
std::string service_id;
std::string challenge;
ChallengeCallback callback;
};
~PlatformVerificationFlow();
// Callback for attestation preparation. The arguments to ChallengePlatformKey
// are in |context|, and |reply| is the result of |GetEnrollmentPreparations|.
void OnAttestationPrepared(
ChallengeContext context,
const ::attestation::GetEnrollmentPreparationsReply& reply);
// Initiates the flow to get a platform key certificate. The arguments to
// ChallengePlatformKey are in |context|. If |force_new_key| is true then any
// existing key for the same user and service will be ignored and a new key
// will be generated and certified.
void GetCertificate(
scoped_refptr<base::RefCountedData<ChallengeContext>> context,
bool force_new_key);
// A callback called when an attestation certificate request operation
// completes. The arguments to ChallengePlatformKey are in |context|.
// |account_id| identifies the user for which the certificate was requested.
// |operation_success| is true iff the certificate request operation
// succeeded. |certificate_chain| holds the certificate for the platform
// key on success. If the certificate request was successful, this method
// invokes a request to sign the challenge. If the operation timed out
// prior to this method being called, this method does nothing - notably,
// the callback is not invoked.
void OnCertificateReady(
scoped_refptr<base::RefCountedData<ChallengeContext>> context,
const AccountId& account_id,
std::unique_ptr<base::OneShotTimer> timer,
AttestationStatus operation_status,
const std::string& certificate_chain);
// A callback run after a constant delay to handle timeouts for lengthy
// certificate requests. |context.callback| will be invoked with a TIMEOUT
// result.
void OnCertificateTimeout(
scoped_refptr<base::RefCountedData<ChallengeContext>> context);
// A callback called when a challenge signing request has completed. The
// `certificate_chain` is the platform certificate chain for the key which
// signed the `challenge`. The arguments to ChallengePlatformKey are in
// `context`. `account_id` identifies the user for which the certificate was
// requested. `is_expiring_soon` will be set iff a certificate in the
// `certificate_chain` is expiring soon. `reply` is returned from
// `AttestationClient`. Upon success, the method will invoke
// `context.callback`.
void OnChallengeReady(ChallengeContext context,
const AccountId& account_id,
const std::string& certificate_chain,
bool is_expiring_soon,
const ::attestation::SignSimpleChallengeReply& reply);
// Checks if |certificate_chain| is a PEM certificate chain that contains a
// certificate this is expired or expiring soon. Returns the expiry status.
ExpiryStatus CheckExpiry(const std::string& certificate_chain);
// An AttestationFlow::CertificateCallback that handles renewal completion.
// |old_certificate_chain| contains the chain that has been replaced.
void RenewCertificateCallback(const std::string& old_certificate_chain,
AttestationStatus operation_status,
const std::string& certificate_chain);
raw_ptr<AttestationFlow> attestation_flow_;
std::unique_ptr<AttestationFlow> default_attestation_flow_;
const raw_ptr<AttestationClient, DanglingUntriaged> attestation_client_;
raw_ptr<Delegate> delegate_;
std::unique_ptr<Delegate> default_delegate_;
base::TimeDelta timeout_delay_;
std::set<std::string> renewals_in_progress_;
};
} // namespace attestation
} // namespace ash
#endif // CHROME_BROWSER_ASH_ATTESTATION_PLATFORM_VERIFICATION_FLOW_H_
|