blob: a9902cfe31c6009e3481d357c7d51e3752c1a9c0 [file] [log] [blame]
// Copyright (c) 2023 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.
#include "quiche/blind_sign_auth/blind_sign_auth.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/bind_front.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/types/span.h"
#include "anonymous_tokens/cpp/client/anonymous_tokens_rsa_bssa_client.h"
#include "anonymous_tokens/cpp/crypto/crypto_utils.h"
#include "anonymous_tokens/cpp/privacy_pass/rsa_bssa_public_metadata_client.h"
#include "anonymous_tokens/cpp/privacy_pass/token_encodings.h"
#include "anonymous_tokens/cpp/shared/proto_utils.h"
#include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
#include "quiche/blind_sign_auth/blind_sign_auth_protos.h"
#include "quiche/blind_sign_auth/blind_sign_http_interface.h"
#include "quiche/blind_sign_auth/blind_sign_http_response.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_endian.h"
#include "quiche/common/quiche_random.h"
namespace quiche {
namespace {
template <typename T>
std::string OmitDefault(T value) {
return value == 0 ? "" : absl::StrCat(value);
}
constexpr absl::string_view kIssuerHostname =
"https://ipprotection-ppissuer.googleapis.com";
} // namespace
void BlindSignAuth::GetTokens(std::string oauth_token, int num_tokens,
ProxyLayer proxy_layer,
SignedTokenCallback callback) {
// Create GetInitialData RPC.
privacy::ppn::GetInitialDataRequest request;
request.set_use_attestation(false);
request.set_service_type("chromeipblinding");
request.set_location_granularity(
privacy::ppn::GetInitialDataRequest_LocationGranularity_CITY_GEOS);
// Validation version must be 2 to use ProxyLayer.
request.set_validation_version(2);
request.set_proxy_layer(QuicheProxyLayerToPpnProxyLayer(proxy_layer));
// Call GetInitialData on the HttpFetcher.
std::string body = request.SerializeAsString();
BlindSignHttpCallback initial_data_callback = absl::bind_front(
&BlindSignAuth::GetInitialDataCallback, this, oauth_token, num_tokens,
proxy_layer, std::move(callback));
http_fetcher_->DoRequest(BlindSignHttpRequestType::kGetInitialData,
oauth_token, body, std::move(initial_data_callback));
}
void BlindSignAuth::GetInitialDataCallback(
std::string oauth_token, int num_tokens, ProxyLayer proxy_layer,
SignedTokenCallback callback,
absl::StatusOr<BlindSignHttpResponse> response) {
if (!response.ok()) {
QUICHE_LOG(WARNING) << "GetInitialDataRequest failed: "
<< response.status();
std::move(callback)(response.status());
return;
}
absl::StatusCode code = HttpCodeToStatusCode(response->status_code());
if (code != absl::StatusCode::kOk) {
std::string message =
absl::StrCat("GetInitialDataRequest failed with code: ", code);
QUICHE_LOG(WARNING) << message;
std::move(callback)(absl::Status(code, message));
return;
}
// Parse GetInitialDataResponse.
privacy::ppn::GetInitialDataResponse initial_data_response;
if (!initial_data_response.ParseFromString(response->body())) {
QUICHE_LOG(WARNING) << "Failed to parse GetInitialDataResponse";
std::move(callback)(
absl::InternalError("Failed to parse GetInitialDataResponse"));
return;
}
absl::StatusOr<absl::Time> public_metadata_expiry_time =
anonymous_tokens::TimeFromProto(
initial_data_response.public_metadata_info()
.public_metadata()
.expiration());
if (!public_metadata_expiry_time.ok()) {
std::move(callback)(
absl::InternalError("Failed to parse public metadata expiration time"));
return;
}
// Create token signing requests.
bool use_privacy_pass_client =
initial_data_response.has_privacy_pass_data() &&
auth_options_.enable_privacy_pass();
if (use_privacy_pass_client) {
QUICHE_DVLOG(1) << "Using Privacy Pass client";
GeneratePrivacyPassTokens(
initial_data_response, *public_metadata_expiry_time,
std::move(oauth_token), num_tokens, proxy_layer, std::move(callback));
} else {
QUICHE_DVLOG(1) << "Using public metadata client";
GenerateRsaBssaTokens(initial_data_response, *public_metadata_expiry_time,
std::move(oauth_token), num_tokens, proxy_layer,
std::move(callback));
}
}
void BlindSignAuth::GeneratePrivacyPassTokens(
privacy::ppn::GetInitialDataResponse initial_data_response,
absl::Time public_metadata_expiry_time, std::string oauth_token,
int num_tokens, ProxyLayer proxy_layer, SignedTokenCallback callback) {
// Set up values used in the token generation loop.
anonymous_tokens::RSAPublicKey public_key_proto;
if (!public_key_proto.ParseFromString(
initial_data_response.at_public_metadata_public_key()
.serialized_public_key())) {
std::move(callback)(
absl::InvalidArgumentError("Failed to parse Privacy Pass public key"));
return;
}
absl::StatusOr<bssl::UniquePtr<RSA>> bssl_rsa_key =
anonymous_tokens::CreatePublicKeyRSA(
public_key_proto.n(), public_key_proto.e());
if (!bssl_rsa_key.ok()) {
std::move(callback)(bssl_rsa_key.status());
return;
}
absl::StatusOr<anonymous_tokens::Extensions> extensions =
anonymous_tokens::DecodeExtensions(
initial_data_response.privacy_pass_data()
.public_metadata_extensions());
if (!extensions.ok()) {
QUICHE_LOG(WARNING) << "Failed to decode extensions: "
<< extensions.status();
std::move(callback)(extensions.status());
return;
}
std::vector<uint16_t> kExpectedExtensionTypes = {
/*ExpirationTimestamp=*/0x0001, /*GeoHint=*/0x0002,
/*ServiceType=*/0xF001, /*DebugMode=*/0xF002, /*ProxyLayer=*/0xF003};
absl::Status result =
anonymous_tokens::ValidateExtensionsOrderAndValues(
*extensions, absl::MakeSpan(kExpectedExtensionTypes), absl::Now());
if (!result.ok()) {
QUICHE_LOG(WARNING) << "Failed to validate extensions: " << result;
std::move(callback)(result);
return;
}
// Create token challenge.
anonymous_tokens::TokenChallenge challenge;
challenge.issuer_name = kIssuerHostname;
absl::StatusOr<std::string> token_challenge =
anonymous_tokens::MarshalTokenChallenge(challenge);
if (!token_challenge.ok()) {
QUICHE_LOG(WARNING) << "Failed to marshal token challenge: "
<< token_challenge.status();
std::move(callback)(token_challenge.status());
return;
}
QuicheRandom* random = QuicheRandom::GetInstance();
// Create vector of Privacy Pass clients, one for each token.
std::vector<anonymous_tokens::ExtendedTokenRequest>
extended_token_requests;
std::vector<std::unique_ptr<anonymous_tokens::
PrivacyPassRsaBssaPublicMetadataClient>>
privacy_pass_clients;
std::vector<std::string> privacy_pass_blinded_tokens;
for (int i = 0; i < num_tokens; i++) {
// Create client.
auto client = anonymous_tokens::
PrivacyPassRsaBssaPublicMetadataClient::Create(*bssl_rsa_key.value());
if (!client.ok()) {
QUICHE_LOG(WARNING) << "Failed to create Privacy Pass client: "
<< client.status();
std::move(callback)(client.status());
return;
}
// Create nonce.
std::string nonce_rand(32, '\0');
random->RandBytes(nonce_rand.data(), nonce_rand.size());
// Create token request.
absl::StatusOr<anonymous_tokens::ExtendedTokenRequest>
extended_token_request = client.value()->CreateTokenRequest(
*token_challenge, nonce_rand,
initial_data_response.privacy_pass_data().token_key_id(),
*extensions);
if (!extended_token_request.ok()) {
QUICHE_LOG(WARNING) << "Failed to create ExtendedTokenRequest: "
<< extended_token_request.status();
std::move(callback)(extended_token_request.status());
return;
}
privacy_pass_clients.push_back(*std::move(client));
extended_token_requests.push_back(*extended_token_request);
privacy_pass_blinded_tokens.push_back(absl::Base64Escape(
extended_token_request->request.blinded_token_request));
}
privacy::ppn::AuthAndSignRequest sign_request;
sign_request.set_oauth_token(oauth_token);
sign_request.set_service_type("chromeipblinding");
sign_request.set_key_type(privacy::ppn::AT_PUBLIC_METADATA_KEY_TYPE);
sign_request.set_key_version(
initial_data_response.at_public_metadata_public_key().key_version());
sign_request.mutable_blinded_token()->Assign(
privacy_pass_blinded_tokens.begin(), privacy_pass_blinded_tokens.end());
sign_request.mutable_public_metadata_extensions()->assign(
initial_data_response.privacy_pass_data().public_metadata_extensions());
// TODO(b/295924807): deprecate this option after AT server defaults to it
sign_request.set_do_not_use_rsa_public_exponent(true);
sign_request.set_proxy_layer(QuicheProxyLayerToPpnProxyLayer(proxy_layer));
absl::StatusOr<anonymous_tokens::AnonymousTokensUseCase>
use_case = anonymous_tokens::ParseUseCase(
initial_data_response.at_public_metadata_public_key().use_case());
if (!use_case.ok()) {
QUICHE_LOG(WARNING) << "Failed to parse use case: " << use_case.status();
std::move(callback)(use_case.status());
return;
}
BlindSignHttpCallback auth_and_sign_callback =
absl::bind_front(&BlindSignAuth::PrivacyPassAuthAndSignCallback, this,
std::move(initial_data_response.privacy_pass_data()
.public_metadata_extensions()),
public_metadata_expiry_time, *use_case,
std::move(privacy_pass_clients), std::move(callback));
// TODO(b/304811277): remove other usages of string.data()
http_fetcher_->DoRequest(BlindSignHttpRequestType::kAuthAndSign, oauth_token,
sign_request.SerializeAsString(),
std::move(auth_and_sign_callback));
}
void BlindSignAuth::GenerateRsaBssaTokens(
privacy::ppn::GetInitialDataResponse initial_data_response,
absl::Time public_metadata_expiry_time, std::string oauth_token,
int num_tokens, ProxyLayer proxy_layer, SignedTokenCallback callback) {
// Create public metadata client.
auto bssa_client =
anonymous_tokens::AnonymousTokensRsaBssaClient::
Create(initial_data_response.at_public_metadata_public_key());
if (!bssa_client.ok()) {
QUICHE_LOG(WARNING) << "Failed to create AT BSSA client: "
<< bssa_client.status();
std::move(callback)(bssa_client.status());
return;
}
// Create plaintext tokens.
// Client blinds plaintext tokens (random 32-byte strings) in CreateRequest.
QuicheRandom* random = QuicheRandom::GetInstance();
std::vector<
anonymous_tokens::PlaintextMessageWithPublicMetadata>
plaintext_tokens;
for (int i = 0; i < num_tokens; i++) {
// Create random 32-byte string prefixed with "blind:".
anonymous_tokens::PlaintextMessageWithPublicMetadata
plaintext_message;
std::string rand_bytes(32, '\0');
random->RandBytes(rand_bytes.data(), rand_bytes.size());
plaintext_message.set_plaintext_message(absl::StrCat("blind:", rand_bytes));
uint64_t fingerprint = 0;
absl::Status fingerprint_status = FingerprintPublicMetadata(
initial_data_response.public_metadata_info().public_metadata(),
&fingerprint);
if (!fingerprint_status.ok()) {
QUICHE_LOG(WARNING) << "Failed to fingerprint public metadata: "
<< fingerprint_status;
std::move(callback)(fingerprint_status);
return;
}
uint64_t fingerprint_big_endian = QuicheEndian::HostToNet64(fingerprint);
std::string key;
key.resize(sizeof(fingerprint_big_endian));
memcpy(key.data(), &fingerprint_big_endian, sizeof(fingerprint_big_endian));
plaintext_message.set_public_metadata(key);
plaintext_tokens.push_back(plaintext_message);
}
absl::StatusOr<
anonymous_tokens::AnonymousTokensSignRequest>
at_sign_request = bssa_client.value()->CreateRequest(plaintext_tokens);
if (!at_sign_request.ok()) {
QUICHE_LOG(WARNING) << "Failed to create AT Sign Request: "
<< at_sign_request.status();
std::move(callback)(at_sign_request.status());
return;
}
// Create AuthAndSign RPC.
privacy::ppn::AuthAndSignRequest sign_request;
sign_request.set_oauth_token(std::string(oauth_token));
sign_request.set_service_type("chromeipblinding");
sign_request.set_key_type(privacy::ppn::AT_PUBLIC_METADATA_KEY_TYPE);
sign_request.set_key_version(
initial_data_response.at_public_metadata_public_key().key_version());
*sign_request.mutable_public_metadata_info() =
initial_data_response.public_metadata_info();
for (int i = 0; i < at_sign_request->blinded_tokens_size(); i++) {
sign_request.add_blinded_token(absl::Base64Escape(
at_sign_request->blinded_tokens().at(i).serialized_token()));
}
// TODO(b/295924807): deprecate this option after AT server defaults to it
sign_request.set_do_not_use_rsa_public_exponent(true);
sign_request.set_proxy_layer(QuicheProxyLayerToPpnProxyLayer(proxy_layer));
privacy::ppn::PublicMetadataInfo public_metadata_info =
initial_data_response.public_metadata_info();
BlindSignHttpCallback auth_and_sign_callback = absl::bind_front(
&BlindSignAuth::AuthAndSignCallback, this, public_metadata_info,
public_metadata_expiry_time, *at_sign_request, *std::move(bssa_client),
std::move(callback));
http_fetcher_->DoRequest(BlindSignHttpRequestType::kAuthAndSign,
oauth_token.data(), sign_request.SerializeAsString(),
std::move(auth_and_sign_callback));
}
void BlindSignAuth::AuthAndSignCallback(
privacy::ppn::PublicMetadataInfo public_metadata_info,
absl::Time public_key_expiry_time,
anonymous_tokens::AnonymousTokensSignRequest
at_sign_request,
std::unique_ptr<
anonymous_tokens::AnonymousTokensRsaBssaClient>
bssa_client,
SignedTokenCallback callback,
absl::StatusOr<BlindSignHttpResponse> response) {
// Validate response.
if (!response.ok()) {
QUICHE_LOG(WARNING) << "AuthAndSign failed: " << response.status();
std::move(callback)(response.status());
return;
}
absl::StatusCode code = HttpCodeToStatusCode(response->status_code());
if (code != absl::StatusCode::kOk) {
std::string message = absl::StrCat("AuthAndSign failed with code: ", code);
QUICHE_LOG(WARNING) << message;
std::move(callback)(absl::Status(code, message));
return;
}
// Decode AuthAndSignResponse.
privacy::ppn::AuthAndSignResponse sign_response;
if (!sign_response.ParseFromString(response->body())) {
QUICHE_LOG(WARNING) << "Failed to parse AuthAndSignResponse";
std::move(callback)(
absl::InternalError("Failed to parse AuthAndSignResponse"));
return;
}
// Create vector of unblinded anonymous tokens.
anonymous_tokens::AnonymousTokensSignResponse
at_sign_response;
if (sign_response.blinded_token_signature_size() !=
at_sign_request.blinded_tokens_size()) {
QUICHE_LOG(WARNING)
<< "Response signature size does not equal request tokens size";
std::move(callback)(absl::InternalError(
"Response signature size does not equal request tokens size"));
return;
}
// This depends on the signing server returning the signatures in the order
// that the tokens were sent. Phosphor does guarantee this.
for (int i = 0; i < sign_response.blinded_token_signature_size(); i++) {
std::string blinded_token;
if (!absl::Base64Unescape(sign_response.blinded_token_signature(i),
&blinded_token)) {
QUICHE_LOG(WARNING) << "Failed to unescape blinded token signature";
std::move(callback)(
absl::InternalError("Failed to unescape blinded token signature"));
return;
}
anonymous_tokens::AnonymousTokensSignResponse::
AnonymousToken anon_token_proto;
*anon_token_proto.mutable_use_case() =
at_sign_request.blinded_tokens(i).use_case();
anon_token_proto.set_key_version(
at_sign_request.blinded_tokens(i).key_version());
*anon_token_proto.mutable_public_metadata() =
at_sign_request.blinded_tokens(i).public_metadata();
*anon_token_proto.mutable_serialized_blinded_message() =
at_sign_request.blinded_tokens(i).serialized_token();
*anon_token_proto.mutable_serialized_token() = blinded_token;
anon_token_proto.set_do_not_use_rsa_public_exponent(true);
at_sign_response.add_anonymous_tokens()->Swap(&anon_token_proto);
}
auto signed_tokens = bssa_client->ProcessResponse(at_sign_response);
if (!signed_tokens.ok()) {
QUICHE_LOG(WARNING) << "AuthAndSign ProcessResponse failed: "
<< signed_tokens.status();
std::move(callback)(signed_tokens.status());
return;
}
if (signed_tokens->size() !=
static_cast<size_t>(at_sign_response.anonymous_tokens_size())) {
QUICHE_LOG(WARNING)
<< "ProcessResponse did not output the right number of signed tokens";
std::move(callback)(absl::InternalError(
"ProcessResponse did not output the right number of signed tokens"));
return;
}
// Output SpendTokenData with data for the redeemer to make a SpendToken RPC.
std::vector<BlindSignToken> tokens_vec;
for (size_t i = 0; i < signed_tokens->size(); i++) {
privacy::ppn::SpendTokenData spend_token_data;
*spend_token_data.mutable_public_metadata() =
public_metadata_info.public_metadata();
*spend_token_data.mutable_unblinded_token() =
signed_tokens->at(i).input().plaintext_message();
*spend_token_data.mutable_unblinded_token_signature() =
signed_tokens->at(i).token().token();
spend_token_data.set_signing_key_version(
at_sign_response.anonymous_tokens(i).key_version());
auto use_case = anonymous_tokens::ParseUseCase(
at_sign_response.anonymous_tokens(i).use_case());
if (!use_case.ok()) {
QUICHE_LOG(WARNING) << "Failed to parse use case: " << use_case.status();
std::move(callback)(use_case.status());
return;
}
spend_token_data.set_use_case(*use_case);
spend_token_data.set_message_mask(
signed_tokens->at(i).token().message_mask());
tokens_vec.push_back(BlindSignToken{spend_token_data.SerializeAsString(),
public_key_expiry_time});
}
std::move(callback)(absl::Span<BlindSignToken>(tokens_vec));
}
void BlindSignAuth::PrivacyPassAuthAndSignCallback(
std::string encoded_extensions, absl::Time public_key_expiry_time,
anonymous_tokens::AnonymousTokensUseCase use_case,
std::vector<std::unique_ptr<anonymous_tokens::
PrivacyPassRsaBssaPublicMetadataClient>>
privacy_pass_clients,
SignedTokenCallback callback,
absl::StatusOr<BlindSignHttpResponse> response) {
// Validate response.
if (!response.ok()) {
QUICHE_LOG(WARNING) << "AuthAndSign failed: " << response.status();
std::move(callback)(response.status());
return;
}
absl::StatusCode code = HttpCodeToStatusCode(response->status_code());
if (code != absl::StatusCode::kOk) {
std::string message = absl::StrCat("AuthAndSign failed with code: ", code);
QUICHE_LOG(WARNING) << message;
std::move(callback)(absl::Status(code, message));
return;
}
// Decode AuthAndSignResponse.
privacy::ppn::AuthAndSignResponse sign_response;
if (!sign_response.ParseFromString(response->body())) {
QUICHE_LOG(WARNING) << "Failed to parse AuthAndSignResponse";
std::move(callback)(
absl::InternalError("Failed to parse AuthAndSignResponse"));
return;
}
if (static_cast<size_t>(sign_response.blinded_token_signature_size()) !=
privacy_pass_clients.size()) {
QUICHE_LOG(WARNING) << "Number of signatures does not equal number of "
"Privacy Pass tokens sent";
std::move(callback)(
absl::InternalError("Number of signatures does not equal number of "
"Privacy Pass tokens sent"));
return;
}
// Create tokens using blinded signatures.
std::vector<BlindSignToken> tokens_vec;
for (int i = 0; i < sign_response.blinded_token_signature_size(); i++) {
std::string unescaped_blinded_sig;
if (!absl::Base64Unescape(sign_response.blinded_token_signature()[i],
&unescaped_blinded_sig)) {
QUICHE_LOG(WARNING) << "Failed to unescape blinded signature";
std::move(callback)(
absl::InternalError("Failed to unescape blinded signature"));
return;
}
absl::StatusOr<anonymous_tokens::Token> token =
privacy_pass_clients[i]->FinalizeToken(unescaped_blinded_sig);
if (!token.ok()) {
QUICHE_LOG(WARNING) << "Failed to finalize token: " << token.status();
std::move(callback)(token.status());
return;
}
absl::StatusOr<std::string> marshaled_token =
anonymous_tokens::MarshalToken(*token);
if (!marshaled_token.ok()) {
QUICHE_LOG(WARNING) << "Failed to marshal token: "
<< marshaled_token.status();
std::move(callback)(marshaled_token.status());
return;
}
privacy::ppn::PrivacyPassTokenData privacy_pass_token_data;
privacy_pass_token_data.mutable_token()->assign(
absl::WebSafeBase64Escape(*marshaled_token));
privacy_pass_token_data.mutable_encoded_extensions()->assign(
absl::WebSafeBase64Escape(encoded_extensions));
privacy_pass_token_data.set_use_case_override(use_case);
tokens_vec.push_back(BlindSignToken{
privacy_pass_token_data.SerializeAsString(), public_key_expiry_time});
}
std::move(callback)(absl::Span<BlindSignToken>(tokens_vec));
}
absl::Status BlindSignAuth::FingerprintPublicMetadata(
const privacy::ppn::PublicMetadata& metadata, uint64_t* fingerprint) {
const EVP_MD* hasher = EVP_sha256();
std::string digest;
digest.resize(EVP_MAX_MD_SIZE);
uint32_t digest_length = 0;
// Concatenate fields in tag number order, omitting fields whose values match
// the default. This enables new fields to be added without changing the
// resulting encoding. The signer needs to ensure that | is not allowed in any
// metadata value so intentional collisions cannot be created.
const std::vector<std::string> parts = {
metadata.exit_location().country(),
metadata.exit_location().city_geo_id(),
metadata.service_type(),
OmitDefault(metadata.expiration().seconds()),
OmitDefault(metadata.expiration().nanos()),
OmitDefault(metadata.debug_mode()),
};
const std::string input = absl::StrJoin(parts, "|");
if (EVP_Digest(input.data(), input.length(),
reinterpret_cast<uint8_t*>(&digest[0]), &digest_length, hasher,
nullptr) != 1) {
return absl::InternalError("EVP_Digest failed");
}
// Return the first uint64_t of the SHA-256 hash.
memcpy(fingerprint, digest.data(), sizeof(*fingerprint));
return absl::OkStatus();
}
absl::StatusCode BlindSignAuth::HttpCodeToStatusCode(int http_code) {
// copybara:strip_begin(golink)
// This mapping is from go/http-canonical-mapping
// copybara:strip_end
if (http_code >= 200 && http_code < 300) {
return absl::StatusCode::kOk;
} else if (http_code >= 300 && http_code < 400) {
return absl::StatusCode::kUnknown;
} else if (http_code == 400) {
return absl::StatusCode::kInvalidArgument;
} else if (http_code == 401) {
return absl::StatusCode::kUnauthenticated;
} else if (http_code == 403) {
return absl::StatusCode::kPermissionDenied;
} else if (http_code == 404) {
return absl::StatusCode::kNotFound;
} else if (http_code == 409) {
return absl::StatusCode::kAborted;
} else if (http_code == 416) {
return absl::StatusCode::kOutOfRange;
} else if (http_code == 429) {
return absl::StatusCode::kResourceExhausted;
} else if (http_code == 499) {
return absl::StatusCode::kCancelled;
} else if (http_code >= 400 && http_code < 500) {
return absl::StatusCode::kFailedPrecondition;
} else if (http_code == 501) {
return absl::StatusCode::kUnimplemented;
} else if (http_code == 503) {
return absl::StatusCode::kUnavailable;
} else if (http_code == 504) {
return absl::StatusCode::kDeadlineExceeded;
} else if (http_code >= 500 && http_code < 600) {
return absl::StatusCode::kInternal;
}
return absl::StatusCode::kUnknown;
}
privacy::ppn::ProxyLayer BlindSignAuth::QuicheProxyLayerToPpnProxyLayer(
quiche::ProxyLayer proxy_layer) {
switch (proxy_layer) {
case ProxyLayer::kProxyA: {
return privacy::ppn::ProxyLayer::PROXY_A;
}
case ProxyLayer::kProxyB: {
return privacy::ppn::ProxyLayer::PROXY_B;
}
}
}
} // namespace quiche