blob: 482181376a72161d9ee368b329f19140d3ff9f0a [file] [log] [blame]
// 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_