| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_REQUEST_ISSUANCE_HELPER_H_ |
| #define SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_REQUEST_ISSUANCE_HELPER_H_ |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback_forward.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/strings/string_piece_forward.h" |
| #include "net/log/net_log_with_source.h" |
| #include "services/network/public/mojom/trust_tokens.mojom-shared.h" |
| #include "services/network/trust_tokens/local_trust_token_operation_delegate.h" |
| #include "services/network/trust_tokens/proto/public.pb.h" |
| #include "services/network/trust_tokens/suitable_trust_token_origin.h" |
| #include "services/network/trust_tokens/trust_token_key_commitment_getter.h" |
| #include "services/network/trust_tokens/trust_token_request_helper.h" |
| #include "url/origin.h" |
| |
| namespace net { |
| class URLRequest; |
| } // namespace net |
| |
| namespace network { |
| class TrustTokenStore; |
| |
| namespace mojom { |
| class URLResponseHead; |
| } // namespace mojom |
| |
| // Class TrustTokenRequestIssuanceHelper handles a single trust token issuance |
| // operation (https://github.com/wicg/trust-token-api): it generates blinded, |
| // unsigned tokens using an underlying cryptographic library, asks a token |
| // issuer to sign the tokens, verifies the result, and unblinds and stores the |
| // tokens. The normal case involves a total of two network requests: one to get |
| // an up-to-date view of a key set the issuer provides for verifying its |
| // signatures, and another to send blinded tokens to the issuer. |
| class TrustTokenRequestIssuanceHelper : public TrustTokenRequestHelper { |
| public: |
| // Class Cryptographer executes the underlying cryptographic |
| // operations required for issuance. The API is intended to correspond closely |
| // to the BoringSSL API. |
| // |
| // Usage: |
| // 1. 1x successful Initialize (without a success, don't call AddKey), then |
| // 2. >= 1x successful AddKey (without at least one success, don't call |
| // BeginIssuance), then |
| // 3. 1x successful BeginIssuance (without a success, don't call |
| // ConfirmIssuance), then |
| // 4. 1x ConfirmIssuance. |
| class Cryptographer { |
| public: |
| virtual ~Cryptographer() = default; |
| |
| // Initializes the delegate. |issuer_configured_version| and |
| // |issuer_configured_batch_size| must be the "protocol_version" and |
| // "batchsize" values from an issuer-provided key commitment result. |
| // |
| // Returns true on success and false if the batch size is unacceptable or an |
| // internal error occurred in the underlying cryptographic library. |
| virtual bool Initialize( |
| mojom::TrustTokenProtocolVersion issuer_configured_version, |
| int issuer_configured_batch_size) = 0; |
| |
| // Stores a Trust Tokens issuance verification key for subsequent use |
| // verifying signed tokens in |ConfirmIssuance|. May be called multiple |
| // times to add multiple keys permissible for use during this issuance. |
| // (Typically, an issuer will have around three simultaneously active public |
| // keys.) |
| // |
| // Returns true on success and false if the key is malformed or if an |
| // internal error occurred in the underlying cryptographic library. Does not |
| // forbid adding duplicates; however, duplicates might contribute to an |
| // overall limit on the number of permitted keys, so the caller may wish to |
| // ensure this is called at most once per distinct key. |
| virtual bool AddKey(base::StringPiece key) = 0; |
| |
| // On success, returns a base64-encoded string representing |num_tokens| |
| // many blinded, unsigned trust tokens; on error, returns nullopt. The |
| // format of this string will eventually be specified, but it is currently |
| // considered an implementation detail of the underlying cryptographic code. |
| virtual base::Optional<std::string> BeginIssuance(size_t num_tokens) = 0; |
| |
| struct UnblindedTokens { |
| UnblindedTokens(); |
| ~UnblindedTokens(); |
| |
| std::vector<std::string> tokens; |
| // The verification key that led to the tokens' successful verification. |
| // This will be associated with the tokens in persistent storage and, |
| // subsequently, used to determine whether the tokens are still alive. |
| std::string body_of_verifying_key; |
| }; |
| |
| // Given a base64-encoded issuance response header, attempts to unblind |
| // the tokens represented by the header using the keys previously added by |
| // AddKey. If successful, returns a vector of tokens (as bytestrings), along |
| // with the key (from the issuer's key commitment registry) that |
| // successfully validated the signed tokens. Otherwise, returns nullptr. |
| virtual std::unique_ptr<UnblindedTokens> ConfirmIssuance( |
| base::StringPiece response_header) = 0; |
| }; |
| |
| class MetricsDelegate { |
| public: |
| virtual ~MetricsDelegate() = default; |
| |
| // Indicates that this delegate is about to attempt to execute its |
| // issuance operation through a call to the provided |
| // LocalTrustTokenOperationDelegate. |
| virtual void WillExecutePlatformProvidedOperation() = 0; |
| }; |
| |
| // Creates a new issuance helper. |
| // |
| // - |top_level_origin| is the top-level origin of the request subsequently |
| // passed to Begin; |top_level_origin|'s scheme must be both (1) HTTP or |
| // HTTPS and (2) "potentially trustworthy". This precondition is slightly |
| // involved because there are two needs: |
| // 1. HTTP or HTTPS so that the scheme serializes in a sensible manner in |
| // order to serve as a key for persisting state. |
| // 2. potentially trustworthy origin to satisfy Web security requirements. |
| // - |token_store| will be responsible for storing underlying Trust Tokens |
| // state. It must outlive this object. |
| // - |key_commitment_getter|, |cryptographer|, and |local_operation_delegate| |
| // are delegates that help execute the protocol; see their class comments. |
| // They must live outlive this object. |
| // - |is_current_os_callback| should return whether, when the given Os is |
| // present in the issuer's "request_issuance_locally_on" field in its key |
| // commitment, the issuance helper should attempt to forward requests to the |
| // local operation delegate. |
| // - |metrics_delegate|, which must outlive this object, will learn about |
| // certain aspects of this operation's execution in order to slice the metrics |
| // that it reports. |
| // |
| // REQUIRES: |token_store|, |key_commitment_getter|, |cryptographer|, |
| // |local_operation_delegate|, |is_current_os_callback|, and |
| // |metrics_delegate| must be non-null. |
| TrustTokenRequestIssuanceHelper( |
| SuitableTrustTokenOrigin top_level_origin, |
| TrustTokenStore* token_store, |
| const TrustTokenKeyCommitmentGetter* key_commitment_getter, |
| std::unique_ptr<Cryptographer> cryptographer, |
| std::unique_ptr<LocalTrustTokenOperationDelegate> |
| local_operation_delegate, |
| base::RepeatingCallback<bool(mojom::TrustTokenKeyCommitmentResult::Os)> |
| is_current_os_callback, |
| MetricsDelegate* metrics_delegate, |
| net::NetLogWithSource net_log = net::NetLogWithSource()); |
| ~TrustTokenRequestIssuanceHelper() override; |
| |
| // Executes the outbound part of a Trust Tokens issuance operation, |
| // interpreting |request|'s URL's origin as the token issuer origin; |
| // 1. Checks preconditions (see "Returns" below); if unsuccessful, fails |
| // 2. Executes a Trust Tokens key commitment request against the issuer; if |
| // unsuccessful, fails |
| // 3. In a request header, adds a number of signed, unblinded tokens equal to |
| // the lesser of: |
| // * the issuer's configured batch size |
| // * a fixed limit on maximum number of tokens to send per request (see |
| // trust_token_parameterization.h). |
| // |
| // Returns: |
| // * kOk on success |
| // * kInternalError if generating blinded, unsigned tokens fails (this is a |
| // cryptographic operation not depending on protocol state) |
| // * kResourceExhausted if there is no space to store more tokens |
| // corresponding to this issuer, or if the top-level origin provided to this |
| // object's constructor has already reached its number-of-issuers limit |
| // * kFailedPrecondition if preconditions fail, including receiving a |
| // malformed or otherwise invalid key commitmetment record from the issuer |
| // |
| // |request|'s initiator, and its destination URL's origin, must be both (1) |
| // HTTP or HTTPS and (2) "potentially trustworthy" in the sense of |
| // network::IsOriginPotentiallyTrustworthy. (See the justification in the |
| // constructor's comment.) |
| void Begin( |
| net::URLRequest* request, |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done) override; |
| |
| // Performs the second half of Trust Token issuance's client side, |
| // corresponding to the "To process an issuance response" section in the |
| // normative pseudocode: |
| // 1. Checks |response| for an issuance response header. |
| // 2. If the header is present, strips it from the response and passes its |
| // value to an underlying cryptographic library, which parses and validates |
| // the header and splits it into a number of signed, unblinded tokens. |
| // |
| // If both of these steps are successful, stores the tokens in |token_store_| |
| // and returns kOk. Otherwise, returns kBadResponse. |
| void Finalize( |
| mojom::URLResponseHead* response, |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done) override; |
| |
| // These internal structs are in the public namespace so that |
| // anonymous-namespace functions in the .cc file can construct them. |
| struct CryptographerAndBlindedTokens; |
| struct CryptographerAndUnblindedTokens; |
| |
| mojom::TrustTokenOperationResultPtr CollectOperationResultWithStatus( |
| mojom::TrustTokenOperationStatus status) override; |
| |
| private: |
| // Continuation of |Begin| after asynchronous key commitment fetching |
| // concludes. |
| // |
| // |request| and |done| are |Begin|'s parameters, passed on to the |
| // continuation; |commitment_result| is the result of the key commitment |
| // fetch. |
| void OnGotKeyCommitment( |
| net::URLRequest* request, |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done, |
| mojom::TrustTokenKeyCommitmentResultPtr commitment_result); |
| |
| // Continuation of |Begin| after a call to the cryptography delegate to |
| // execute the bulk of the outbound half of the issuance operation. Receives |
| // ownership of the cryptographer back from the asynchronous callback and |
| // should store the cryptographer back in |cryptographer_| to reuse during |
| // |Finalize|. |
| void OnDelegateBeginIssuanceCallComplete( |
| net::URLRequest* request, |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done, |
| CryptographerAndBlindedTokens cryptographer_and_blinded_tokens); |
| |
| // Continuation of |Finalize| after extracting the base64-encoded issuance |
| // response from a response header (or receiving it from a locally executed |
| // operation). |
| void ProcessIssuanceResponse( |
| std::string issuance_response, |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done); |
| |
| // Continuation of |Finalize| after processing the received issuance response, |
| // which typically involves an off-thread call to the cryptography delegate to |
| // execute the bulk of the inbound half of the issuance operation. |
| // Receives ownership of the cryptographer back from the asynchronous |
| // callback. |
| void OnDoneProcessingIssuanceResponse( |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done, |
| CryptographerAndUnblindedTokens cryptographer_and_unblinded_tokens); |
| |
| // DoneRequestingLocallyFulfilledIssuance proceeds with a locally fulfilled |
| // issuance operation by handling the operation's response |answer|, |
| // either terminating the operation with an error passed to |done| (if |
| // |answer| contains an error) or continuing to finalize the operation (if |
| // |answer| contains an issuance response). |
| void DoneRequestingLocallyFulfilledIssuance( |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done, |
| mojom::FulfillTrustTokenIssuanceAnswerPtr answer); |
| |
| // DoneFinalizingLocallyFulfilledIssuance determines a final response status |
| // to pass to the caller via |done|, then calls the callback. |
| // |
| // If the operation succeeded, calls |done| with |
| // kOperationSuccessfullyFulfilledLocally. Otherwise, calls |done| with |
| // |status|. |
| void DoneFinalizingLocallyFulfilledIssuance( |
| base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done, |
| mojom::TrustTokenOperationStatus status); |
| |
| // |issuer_| needs to be a nullable type because it is initialized in |Begin|, |
| // but, once initialized, it will never be empty over the course of the |
| // operation's execution. |
| base::Optional<SuitableTrustTokenOrigin> issuer_; |
| const SuitableTrustTokenOrigin top_level_origin_; |
| TrustTokenStore* const token_store_; |
| const TrustTokenKeyCommitmentGetter* const key_commitment_getter_; |
| |
| mojom::TrustTokenProtocolVersion protocol_version_; |
| |
| // Set once we read the issuer's key commitment, |
| // |should_divert_issuance_request_to_os_| specifies whether, at the time we |
| // would return from an otherwise successful Begin half of this issuance |
| // operation, we should pass the issuance request to |
| // |local_operation_delegate_| rather than returning to the caller. |
| bool should_divert_issuance_request_to_os_ = false; |
| |
| // Relinquishes ownership during posted tasks for the potentially |
| // computationally intensive cryptographic operations |
| // (Cryptographer::BeginIssuance, Cryptographer::ConfirmIssuance); repopulated |
| // when regaining ownership upon receiving each operation's results. |
| std::unique_ptr<Cryptographer> cryptographer_; |
| |
| // |local_operation_delegate_| is responsible for attempting to satisfying |
| // "platform-provided" issuance operations (e.g. by talking to a system |
| // service capable of replying to an issuance request). |
| std::unique_ptr<LocalTrustTokenOperationDelegate> local_operation_delegate_; |
| |
| base::RepeatingCallback<bool(mojom::TrustTokenKeyCommitmentResult::Os)> |
| is_current_os_callback_; |
| |
| MetricsDelegate* const metrics_delegate_; |
| |
| net::NetLogWithSource net_log_; |
| base::Optional<size_t> num_obtained_tokens_; |
| base::WeakPtrFactory<TrustTokenRequestIssuanceHelper> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace network |
| |
| #endif // SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_REQUEST_ISSUANCE_HELPER_H_ |