blob: 44d3b304fd8180dfbb46288efa22e4d3ccac97c8 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/fetch/trust_token_to_mojom.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_private_token.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
namespace blink {
using VersionType = V8PrivateTokenVersion::Enum;
using OperationType = V8OperationType::Enum;
using RefreshPolicy = V8RefreshPolicy::Enum;
using network::mojom::blink::TrustTokenOperationType;
PSTFeatures GetPSTFeatures(const ExecutionContext& execution_context) {
PSTFeatures features;
features.issuance_enabled = execution_context.IsFeatureEnabled(
mojom::blink::PermissionsPolicyFeature::kPrivateStateTokenIssuance);
features.redemption_enabled = execution_context.IsFeatureEnabled(
mojom::blink::PermissionsPolicyFeature::kTrustTokenRedemption);
return features;
}
bool ConvertTrustTokenToMojomAndCheckPermissions(
const PrivateToken& in,
const PSTFeatures& pst_features,
ExceptionState* exception_state,
network::mojom::blink::TrustTokenParams* out) {
// The current implementation always has these fields; the implementation
// always initializes them, and the hasFoo functions always return true. These
// DCHECKs serve as canaries for implementation changes.
DCHECK(in.hasOperation());
DCHECK(in.hasVersion());
// only version 1 exists at this time
DCHECK_EQ(in.version().AsEnum(), VersionType::k1);
if (in.operation().AsEnum() == OperationType::kTokenRequest) {
out->operation = network::mojom::blink::TrustTokenOperationType::kIssuance;
} else if (in.operation().AsEnum() == OperationType::kTokenRedemption) {
out->operation =
network::mojom::blink::TrustTokenOperationType::kRedemption;
DCHECK(in.hasRefreshPolicy()); // default is defined
if (in.refreshPolicy().AsEnum() == RefreshPolicy::kNone) {
out->refresh_policy =
network::mojom::blink::TrustTokenRefreshPolicy::kUseCached;
} else if (in.refreshPolicy().AsEnum() == RefreshPolicy::kRefresh) {
out->refresh_policy =
network::mojom::blink::TrustTokenRefreshPolicy::kRefresh;
}
} else {
// The final possible value of the type enum.
DCHECK_EQ(in.operation().AsEnum(), OperationType::kSendRedemptionRecord);
out->operation = network::mojom::blink::TrustTokenOperationType::kSigning;
if (in.hasIssuers() && !in.issuers().empty()) {
for (const String& issuer : in.issuers()) {
// Two conditions on the issuers:
// 1. HTTP or HTTPS (because much Trust Tokens protocol state is
// stored keyed by issuer origin, requiring HTTP or HTTPS is a way to
// ensure these origins serialize to unique values);
// 2. potentially trustworthy (a security requirement).
KURL parsed_url = KURL(issuer);
if (!parsed_url.ProtocolIsInHTTPFamily()) {
exception_state->ThrowTypeError(
"privateToken: operation type 'send-redemption-record' requires "
"that "
"the 'issuers' "
"fields' members parse to HTTP(S) origins, but one did not: " +
issuer);
return false;
}
out->issuers.push_back(blink::SecurityOrigin::Create(parsed_url));
DCHECK(out->issuers.back()); // SecurityOrigin::Create cannot fail.
if (!out->issuers.back()->IsPotentiallyTrustworthy()) {
exception_state->ThrowTypeError(
"privateToken: operation type 'send-redemption-record' requires "
"that "
"the 'issuers' "
"fields' members parse to secure origins, but one did not: " +
issuer);
return false;
}
}
} else {
exception_state->ThrowTypeError(
"privateToken: operation type 'send-redemption-record' requires that "
"the 'issuers' field be present and contain at least one secure, "
"HTTP(S) URL, but it was missing or empty.");
return false;
}
}
switch (out->operation) {
case TrustTokenOperationType::kRedemption:
case TrustTokenOperationType::kSigning:
if (!pst_features.redemption_enabled) {
exception_state->ThrowDOMException(
DOMExceptionCode::kNotAllowedError,
"Private State Token Redemption ('token-redemption') and signing "
"('send-redemption-record') operations require that the "
"private-state-token-redemption "
"Permissions Policy feature be enabled.");
return false;
}
break;
case TrustTokenOperationType::kIssuance:
if (!pst_features.issuance_enabled) {
exception_state->ThrowDOMException(
DOMExceptionCode::kNotAllowedError,
"Private State Token Issuance ('token-request') operation "
"requires that the private-state-token-issuance "
"Permissions Policy feature be enabled.");
return false;
}
break;
}
return true;
}
DOMException* TrustTokenErrorToDOMException(
network::mojom::blink::TrustTokenOperationStatus error) {
// This should only be called on failure.
DCHECK_NE(error, network::mojom::blink::TrustTokenOperationStatus::kOk);
switch (error) {
case network::mojom::blink::TrustTokenOperationStatus::kAlreadyExists:
return DOMException::Create(
"Redemption operation aborted due to Signed Redemption Record "
"cache hit",
DOMException::GetErrorName(
DOMExceptionCode::kNoModificationAllowedError));
case network::mojom::blink::TrustTokenOperationStatus::
kOperationSuccessfullyFulfilledLocally:
return DOMException::Create(
"Trust Tokens operation satisfied locally, without needing to send "
"the request to its initial destination",
DOMException::GetErrorName(
DOMExceptionCode::kNoModificationAllowedError));
case network::mojom::blink::TrustTokenOperationStatus::kMissingIssuerKeys:
return DOMException::Create(
"No keys currently available for PST issuer. Issuer may need to "
"register their key commitments.",
DOMException::GetErrorName(DOMExceptionCode::kInvalidStateError));
case network::mojom::blink::TrustTokenOperationStatus::kFailedPrecondition:
return DOMException::Create(
"Precondition failed during Trust Tokens operation",
DOMException::GetErrorName(DOMExceptionCode::kInvalidStateError));
default:
return DOMException::Create(
"Error executing Trust Tokens operation",
DOMException::GetErrorName(DOMExceptionCode::kOperationError));
}
}
} // namespace blink