BlindSignAuth: Switch to WebSafeBase64EscapeWithPadding for token and extensions in PrivacyPassTokenData, per version 02 of draft-wood-privacypass-auth-scheme-extensions. Tested using `google3/gfe/privacy_proxy/util/testing/client/privacy_proxy_client_bin.cc` in two-hop mode against both ProxyBs, padded tokens + extensions were successfully spent. PiperOrigin-RevId: 649149482
diff --git a/quiche/blind_sign_auth/blind_sign_auth.cc b/quiche/blind_sign_auth/blind_sign_auth.cc index f61862f..2f6c4e1 100644 --- a/quiche/blind_sign_auth/blind_sign_auth.cc +++ b/quiche/blind_sign_auth/blind_sign_auth.cc
@@ -13,6 +13,7 @@ #include <utility> #include <vector> +#include "absl/algorithm/container.h" #include "absl/functional/bind_front.h" #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -336,9 +337,9 @@ privacy::ppn::PrivacyPassTokenData privacy_pass_token_data; privacy_pass_token_data.mutable_token()->assign( - absl::WebSafeBase64Escape(*marshaled_token)); + ConvertBase64ToWebSafeBase64(absl::Base64Escape(*marshaled_token))); privacy_pass_token_data.mutable_encoded_extensions()->assign( - absl::WebSafeBase64Escape(encoded_extensions)); + ConvertBase64ToWebSafeBase64(absl::Base64Escape(encoded_extensions))); privacy_pass_token_data.set_use_case_override(use_case); tokens_vec.push_back( BlindSignToken{privacy_pass_token_data.SerializeAsString(), @@ -360,6 +361,13 @@ } } +std::string BlindSignAuth::ConvertBase64ToWebSafeBase64( + std::string base64_string) { + absl::c_replace(base64_string, /*old_value=*/'+', /*new_value=*/'-'); + absl::c_replace(base64_string, /*old_value=*/'/', /*new_value=*/'_'); + return base64_string; +} + std::string BlindSignAuthServiceTypeToString( quiche::BlindSignAuthServiceType service_type) { switch (service_type) {
diff --git a/quiche/blind_sign_auth/blind_sign_auth.h b/quiche/blind_sign_auth/blind_sign_auth.h index 39d8fa6..37acf27 100644 --- a/quiche/blind_sign_auth/blind_sign_auth.h +++ b/quiche/blind_sign_auth/blind_sign_auth.h
@@ -60,6 +60,8 @@ absl::StatusOr<BlindSignMessageResponse> response); privacy::ppn::ProxyLayer QuicheProxyLayerToPpnProxyLayer( quiche::ProxyLayer proxy_layer); + // Replaces '+' and '/' with '-' and '_' in a Base64 string. + std::string ConvertBase64ToWebSafeBase64(std::string base64_string); BlindSignMessageInterface* fetcher_ = nullptr; privacy::ppn::BlindSignAuthOptions auth_options_;
diff --git a/quiche/blind_sign_auth/blind_sign_auth_test.cc b/quiche/blind_sign_auth/blind_sign_auth_test.cc index f293bca..c9ee00d 100644 --- a/quiche/blind_sign_auth/blind_sign_auth_test.cc +++ b/quiche/blind_sign_auth/blind_sign_auth_test.cc
@@ -215,25 +215,6 @@ sign_response_ = response; } - void ValidateGetTokensOutput(absl::Span<BlindSignToken> tokens) { - for (const auto& token : tokens) { - privacy::ppn::SpendTokenData spend_token_data; - ASSERT_TRUE(spend_token_data.ParseFromString(token.token)); - // Validate token structure. - EXPECT_EQ(spend_token_data.public_metadata().SerializeAsString(), - public_metadata_info_.public_metadata().SerializeAsString()); - EXPECT_THAT(spend_token_data.unblinded_token(), StartsWith("blind:")); - EXPECT_GE(spend_token_data.unblinded_token_signature().size(), - spend_token_data.unblinded_token().size()); - EXPECT_EQ(spend_token_data.signing_key_version(), - public_key_proto_.key_version()); - EXPECT_NE(spend_token_data.use_case(), - anonymous_tokens::AnonymousTokensUseCase:: - ANONYMOUS_TOKENS_USE_CASE_UNDEFINED); - EXPECT_NE(spend_token_data.message_mask(), ""); - } - } - void ValidatePrivacyPassTokensOutput(absl::Span<BlindSignToken> tokens) { for (const auto& token : tokens) { privacy::ppn::PrivacyPassTokenData privacy_pass_token_data; @@ -242,6 +223,8 @@ std::string decoded_token; ASSERT_TRUE(absl::WebSafeBase64Unescape(privacy_pass_token_data.token(), &decoded_token)); + // Extensions should be padded and web-safe. + EXPECT_EQ(privacy_pass_token_data.encoded_extensions().back(), '='); std::string decoded_extensions; ASSERT_TRUE(absl::WebSafeBase64Unescape( privacy_pass_token_data.encoded_extensions(), &decoded_extensions));