blob: c9954d58c55540d0ed0adae4d051d941c4610cb6 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_PASSKEY_BROWSER_BINDER_H_
#define COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_PASSKEY_BROWSER_BINDER_H_
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/payments/content/browser_binding/browser_bound_key_store.h"
#include "components/payments/content/web_payments_web_data_service.h"
#include "components/webdata/common/web_data_service_consumer.h"
namespace payments {
class BrowserBoundKey;
struct BrowserBoundKeyMetadata;
// Facilitates binding browser bound keys to passkeys.
//
// Browser bound keys (BBK) are temporarily not bound to passkeys while creating
// the passkey. The BBK needs to be created before the passkey since the BBK's
// public key will be set in the client data json and itself is an input to
// creating the passkey. Once the passkey has been created then its credential
// id becomes known and the BBK can be bound to the passkey.
//
// Example passkey creation usage:
//
// auto binder = PasskeyBrowserBinder(
// std::move(key_store), web_payments_web_data_service);
//
// // Create an unbound key.
// PasskeyBrowserBinder::UnboundKey unbound_key =
// binder.CreateUnboundKey(std::move(allowed_algorithms));
//
// // Get the public key and include it in some authenticator creation request.
// auto passkey = CreatePasskey(...,
// unbound_key.Get()->GetPublicKeyAsCoseKey());
//
// // Finally bind the key given the passkey's credential identifier.
// binder.BindKey(
// std::move(unbound_key), passkey.GetCredentialId(), relying_party_id);
//
//
// Example retrieval usage:
//
// auto passkey = ...; // After retrieving the passkey.
//
// binder_ = std::make_unique<PasskeyBrowserBinder>(
// std::move(key_store), web_payments_web_data_service);
//
// binder_->GetOrCreateBoundKeyForPasskey(
// passkey.GetCredentialId(),
// passkey.GetRelyingPartyId(),
// allowed_algorithms,
// [](std::unique_ptr<BrowserBoundKey> key) {
// if (!key) {
// // Handle the browser bound key could not be found nor created.
// }
// // Use the browser bound key to sign something.
// key->Sign(client_data_json);
// });
//
// This class depends on an implementation of BrowserBoundKeyStore and the
// payment manifest web data service for storing the BBK and passkey
// identifiers.
class PasskeyBrowserBinder {
public:
// `key_store` and `web_data_service` are required and must be set.
PasskeyBrowserBinder(
scoped_refptr<BrowserBoundKeyStore> key_store,
scoped_refptr<WebPaymentsWebDataService> web_data_service);
PasskeyBrowserBinder(const PasskeyBrowserBinder&) = delete;
PasskeyBrowserBinder& operator=(const PasskeyBrowserBinder&) = delete;
virtual ~PasskeyBrowserBinder();
// Represents a browser bound key that has not yet been associated. If
// BindKey() is not called when this class goes out of scope, the wrapped
// BrowserBoundKey will be deleted.
class UnboundKey {
public:
// Creates an UnboundKey. `browser_bound_key_id` will be used to delete the
// key from `key_store` when UnboundKey goes out of scope without having
// been bound. UnboundKey takes ownership of `browser_bound_key` which can
// be accessed via Get() while the key has not yet been bound.
UnboundKey(std::vector<uint8_t> browser_bound_key_id,
std::unique_ptr<BrowserBoundKey> browser_bound_key,
scoped_refptr<BrowserBoundKeyStore> key_store);
UnboundKey(const UnboundKey&) = delete;
UnboundKey& operator=(const UnboundKey&) = delete;
UnboundKey(UnboundKey&&);
UnboundKey& operator=(UnboundKey&&);
~UnboundKey();
// Returns a reference to the underlying browser bound key, this is the only
// way by which the browser bound key can be accessed before having been
// associated.
BrowserBoundKey& Get();
private:
friend PasskeyBrowserBinder;
// Marks the BrowserBoundKey as bound. After calling, destruction of this
// UnboundKey will not delete the browser bound key.
//
// Do not call other methods on UnboundKey after calling
// MarkKeyBoundAndReset().
void MarkKeyBoundAndReset();
// The browser bound key id. This is passed to the key store if the BBK
// needs to be deleted.
std::vector<uint8_t> browser_bound_key_id_;
// An owned reference to the browser bound key. This member may be `nullptr`
// in some PasskeyBrowserBinder internal usages of UnboundKey.
std::unique_ptr<BrowserBoundKey> browser_bound_key_;
// A reference to the key store. This key store is invoked if the browser
// bound key needs to be deleted.
scoped_refptr<BrowserBoundKeyStore> key_store_;
};
// Creates a browser bound key that is not yet associated to a passkey. The
// UnboundKey should be bound using BindKey() after the credential id has
// been created.
std::optional<UnboundKey> CreateUnboundKey(
const BrowserBoundKeyStore::CredentialInfoList& allowed_algorithms);
// Gets a browser bound key for the given `credential_id` and `relying_party`
// only if a browser bound key already exists for the credential.
void GetBoundKeyForPasskey(
std::vector<uint8_t> credential_id,
std::string relying_party,
base::OnceCallback<void(std::unique_ptr<BrowserBoundKey>)> callback);
// Gets or creates a browser bound key for the given `credential_id`,
// `relying_party` and `allowed_algorithms` returning the browser bound key
// by running `callback`. An optional `last_used` timestamp can be provided
// to record when the browser bound key was created. If a key already
// exists, `last_used` will be ignored.
void GetOrCreateBoundKeyForPasskey(
std::vector<uint8_t> credential_id,
std::string relying_party,
const BrowserBoundKeyStore::CredentialInfoList& allowed_algorithms,
std::optional<base::Time> last_used,
base::OnceCallback<void(bool is_new, std::unique_ptr<BrowserBoundKey>)>
callback);
// Stores the association of the `key` to a `credential_id` and
// `relying_party`. The UnboundKey must be std::moved and is thus
// intentionally no longer available to the caller. If the BrowserBoundKey is
// needed thereafter, then retrieve it using BoundKeyForPasskey(). An optional
// `last_used` timestamp can be provided to record when the browser bound key
// was last used.
void BindKey(UnboundKey key,
const std::vector<uint8_t>& credential_id,
const std::string& relying_party,
std::optional<base::Time> last_used);
// Updates the browser bound key's `last_used` timestamp to the current
// system time.
void UpdateKeyLastUsedToNow(const std::vector<uint8_t>& credential_id,
const std::string& relying_party);
// Retrieves all browser bound keys from the web data service and runs
// `callback` with the result.
virtual void GetAllBrowserBoundKeys(
base::OnceCallback<void(std::vector<BrowserBoundKeyMetadata>)> callback);
// Deletes the provided browser bound keys from the web data service.
// `callback` is run once the database operation completes.
virtual void DeleteBrowserBoundKeys(
base::OnceClosure callback,
std::vector<BrowserBoundKeyMetadata> bbk_metas);
// Injects the random bytes function for testing.
void SetRandomBytesAsVectorCallbackForTesting(
base::RepeatingCallback<std::vector<uint8_t>(size_t length)>);
BrowserBoundKeyStore* GetBrowserBoundKeyStoreForTesting();
WebPaymentsWebDataService* GetWebDataServiceForTesting();
private:
// Called after retrieving the possibly empty `existing_browser_bound_key_id`
// to retrieve the matching browser bound key. Runs `callback` with nullptr if
// there is no matching browser bound key.
void GetBrowserBoundKey(
base::OnceCallback<void(std::unique_ptr<BrowserBoundKey>)> callback,
std::vector<uint8_t> existing_browser_bound_key_id);
// Called after retrieving the possibly empty `existing_browser_bound_key_id`
// to retrieve the matching browser bound key. Otherwise creates a new browser
// bound key and saves its id. The browser bound key is returned by running
// `callback` with a boolean indicating whether the browser bound key is new.
void GetOrCreateBrowserBoundKey(
std::vector<uint8_t> credential_id,
std::string relying_party,
BrowserBoundKeyStore::CredentialInfoList allowed_algorithms,
std::optional<base::Time> last_used,
base::OnceCallback<void(bool is_new, std::unique_ptr<BrowserBoundKey>)>
callback,
std::vector<uint8_t> existing_browser_bound_key_id);
// Records a creation or retrieval metric.
void RecordCreationOrRetrieval(bool is_creation, bool did_succeed);
scoped_refptr<BrowserBoundKeyStore> key_store_;
scoped_refptr<WebPaymentsWebDataService> web_data_service_;
base::RepeatingCallback<std::vector<uint8_t>(size_t)>
random_bytes_as_vector_callback_;
base::WeakPtrFactory<PasskeyBrowserBinder> weak_ptr_factory_{this};
};
} // namespace payments
#endif // COMPONENTS_PAYMENTS_CONTENT_BROWSER_BINDING_PASSKEY_BROWSER_BINDER_H_