| // 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 "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 WebDataServiceConsumer { |
| 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; |
| ~PasskeyBrowserBinder() override; |
| |
| // 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`. |
| void GetOrCreateBoundKeyForPasskey( |
| std::vector<uint8_t> credential_id, |
| std::string relying_party, |
| const BrowserBoundKeyStore::CredentialInfoList& allowed_algorithms, |
| 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(). |
| void BindKey(UnboundKey key, |
| const std::vector<uint8_t>& credential_id, |
| const std::string& relying_party); |
| |
| // Deletes all unknown browser bound keys, querying using the provided |
| // `get_matching_credential_ids_callback` to find credentials matching each |
| // relying party in the BBK storage. The |
| // `get_matching_credential_ids_callback` must be valid until `callback` is |
| // invoked. `callback` may hold and release the reference to this |
| // PasskeyBrowserBinder object (DeleteAllUnknownBrowserBoundKeys will run |
| // `callback` as its last action). |
| void DeleteAllUnknownBrowserBoundKeys( |
| base::RepeatingCallback< |
| void(const std::string& relying_party_id, |
| const std::vector<std::vector<uint8_t>>& credential_ids, |
| bool require_third_party_payment_bit_set, |
| base::OnceCallback<void(std::vector<std::vector<uint8_t>>)>)> |
| get_matching_credential_ids_callback, |
| base::OnceClosure callback); |
| |
| // WebDataServiceConsumer: |
| void OnWebDataServiceRequestDone( |
| WebDataServiceBase::Handle h, |
| std::unique_ptr<WDTypedResult> result) override; |
| |
| // 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, |
| base::OnceCallback<void(bool is_new, std::unique_ptr<BrowserBoundKey>)> |
| callback, |
| std::vector<uint8_t> existing_browser_bound_key_id); |
| |
| // Called after internal authenticator was called to find stale BBKs. |
| // `callback` is Run once the database operation completes. |
| void DeleteBrowserBoundKeys( |
| base::OnceClosure callback, |
| std::vector<BrowserBoundKeyMetadata> stale_bbk_metas); |
| |
| // 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_; |
| std::map<WebDataServiceBase::Handle, base::OnceCallback<void(bool)>> |
| set_browser_bound_key_handlers_; |
| std::map<WebDataServiceBase::Handle, |
| base::OnceCallback<void(std::vector<uint8_t>)>> |
| get_browser_bound_key_handlers_; |
| 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_ |