BlindSignAuth: Add token issuance APIs for Private Aratea. This use case requires the caller to generate and pass in attestation certificates. This CL adds the API, implementation will be in follow-up CLs. PiperOrigin-RevId: 769136951
diff --git a/quiche/blind_sign_auth/blind_sign_auth.cc b/quiche/blind_sign_auth/blind_sign_auth.cc index c814495..d7f8542 100644 --- a/quiche/blind_sign_auth/blind_sign_auth.cc +++ b/quiche/blind_sign_auth/blind_sign_auth.cc
@@ -368,9 +368,29 @@ case ProxyLayer::kProxyB: { return privacy::ppn::ProxyLayer::PROXY_B; } + case ProxyLayer::kTerminalLayer: { + return privacy::ppn::ProxyLayer::TERMINAL_LAYER; + } } } +void BlindSignAuth::GetAttestationTokens(int /*num_tokens*/, + ProxyLayer /*layer*/, + AttestationDataCallback callback) { + // TODO(b/421236538): Implement GetAttestationTokens. + std::move(callback)( + absl::UnimplementedError("GetAttestationTokens is not implemented")); +} + +void BlindSignAuth::AttestAndSign( + int /*num_tokens*/, ProxyLayer /*layer*/, std::string /*attestation_data*/, + std::optional<std::string> /*token_challenge*/, + SignedTokenCallback callback) { + // TODO(b/421236538): Implement AttestAndSign. + std::move(callback)( + absl::UnimplementedError("AttestAndSign is not implemented")); +} + std::string BlindSignAuth::ConvertBase64ToWebSafeBase64( std::string base64_string) { absl::c_replace(base64_string, /*old_value=*/'+', /*new_value=*/'-'); @@ -393,6 +413,9 @@ // type. return "chromeipblinding"; } + case BlindSignAuthServiceType::kPrivateAratea: { + return "pixel_private_aratea"; + } } }
diff --git a/quiche/blind_sign_auth/blind_sign_auth.h b/quiche/blind_sign_auth/blind_sign_auth.h index 37acf27..ccc3118 100644 --- a/quiche/blind_sign_auth/blind_sign_auth.h +++ b/quiche/blind_sign_auth/blind_sign_auth.h
@@ -38,6 +38,29 @@ ProxyLayer proxy_layer, BlindSignAuthServiceType service_type, SignedTokenCallback callback) override; + // Returns an attestation challenge in a callback. + // GetAttestationTokens callbacks will run on the same thread as the + // BlindSignMessageInterface callbacks. + // Callers can make multiple concurrent requests to GetTokens. + // AttestationDataCallback should call AttestAndSign with a separate callback + // in order to complete the token issuance protocol. + void GetAttestationTokens(int num_tokens, ProxyLayer layer, + AttestationDataCallback callback) override; + + // Returns signed unblinded tokens and their expiration time in a callback. + // Tokens are single-use and restricted to the PI use case. + // The GetTokens callback will run on the same thread as the + // BlindSignMessageInterface callbacks. + // This function should be called after the caller has generated + // AttestationData using Keystore and the challenge returned in + // AttestationDataCallback. If a token challenge is provided, it will be used + // in creating the token. Otherwise a default challenge will be used + // containing the issuer hostname. + void AttestAndSign(int num_tokens, ProxyLayer layer, + std::string attestation_data, + std::optional<std::string> token_challenge, + SignedTokenCallback callback) override; + private: void GetInitialDataCallback( std::optional<std::string> oauth_token, int num_tokens,
diff --git a/quiche/blind_sign_auth/blind_sign_auth_interface.h b/quiche/blind_sign_auth/blind_sign_auth_interface.h index 12b6204..7fe3f4b 100644 --- a/quiche/blind_sign_auth/blind_sign_auth_interface.h +++ b/quiche/blind_sign_auth/blind_sign_auth_interface.h
@@ -9,6 +9,7 @@ #include <string> #include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" #include "anonymous_tokens/cpp/privacy_pass/token_encodings.h" @@ -23,6 +24,7 @@ enum class ProxyLayer : int { kProxyA, kProxyB, + kTerminalLayer, }; // BlindSignAuthServiceType indicates which service that tokens will be @@ -31,6 +33,7 @@ kChromeIpBlinding, kCronetIpBlinding, kWebviewIpBlinding, + kPrivateAratea, }; // A BlindSignToken is used to authenticate a request to a privacy proxy. @@ -45,6 +48,16 @@ using SignedTokenCallback = SingleUseCallback<void(absl::StatusOr<absl::Span<BlindSignToken>>)>; +// AttestationDataCallback returns a serialized +// privacy::ppn::PrepareAttestationData proto, which contains an attestation +// challenge from the issuer server. +// If the request fails, the callback will return an appropriate error based on +// the response's HTTP status code. +// If the request succeeds but the server does not issue a challenge, the +// callback will return an absl::InternalError. +using AttestationDataCallback = + SingleUseCallback<void(absl::StatusOr<absl::string_view>)>; + // BlindSignAuth provides signed, unblinded tokens to callers. class QUICHE_EXPORT BlindSignAuthInterface { public: @@ -55,6 +68,30 @@ ProxyLayer proxy_layer, BlindSignAuthServiceType service_type, SignedTokenCallback callback) = 0; + + // Returns an attestation challenge in a callback. + // GetAttestationTokens callbacks will run on the same thread as the + // BlindSignMessageInterface callbacks. + // Callers can make multiple concurrent requests to GetTokens. + // ProxyLayer must be either ProxyB or TerminalLayer, NOT ProxyA. + // AttestationDataCallback should call AttestAndSign with a separate callback + // in order to complete the token issuance protocol. + virtual void GetAttestationTokens(int num_tokens, ProxyLayer layer, + AttestationDataCallback callback) = 0; + + // Returns signed unblinded tokens and their expiration time in a callback. + // Tokens are single-use and restricted to the PI use case. + // The GetTokens callback will run on the same thread as the + // BlindSignMessageInterface callbacks. + // This function should be called after the caller has generated + // AttestationData using Keystore and the challenge returned in + // AttestationDataCallback. If a token challenge is provided, it will be used + // in creating the token. Otherwise a default challenge will be used + // containing the issuer hostname. + virtual void AttestAndSign(int num_tokens, ProxyLayer layer, + std::string attestation_data, + std::optional<std::string> token_challenge, + SignedTokenCallback callback) = 0; }; } // namespace quiche
diff --git a/quiche/blind_sign_auth/cached_blind_sign_auth.cc b/quiche/blind_sign_auth/cached_blind_sign_auth.cc index 883e2aa..ee5e675 100644 --- a/quiche/blind_sign_auth/cached_blind_sign_auth.cc +++ b/quiche/blind_sign_auth/cached_blind_sign_auth.cc
@@ -132,4 +132,21 @@ } } +void CachedBlindSignAuth::GetAttestationTokens( + int /*num_tokens*/, ProxyLayer /*layer*/, + AttestationDataCallback callback) { + // TODO(b/421236538): Implement GetAttestationTokens. + std::move(callback)( + absl::UnimplementedError("GetAttestationTokens is not implemented")); +} + +void CachedBlindSignAuth::AttestAndSign( + int /*num_tokens*/, ProxyLayer /*layer*/, std::string /*attestation_data*/, + std::optional<std::string> /*token_challenge*/, + SignedTokenCallback callback) { + // TODO(b/421236538): Implement AttestAndSign. + std::move(callback)( + absl::UnimplementedError("AttestAndSign is not implemented")); +} + } // namespace quiche
diff --git a/quiche/blind_sign_auth/cached_blind_sign_auth.h b/quiche/blind_sign_auth/cached_blind_sign_auth.h index 7eaa677..15ee48a 100644 --- a/quiche/blind_sign_auth/cached_blind_sign_auth.h +++ b/quiche/blind_sign_auth/cached_blind_sign_auth.h
@@ -50,6 +50,30 @@ cached_tokens_.clear(); } + // Returns an attestation challenge in a callback. + // GetAttestationTokens callbacks will run on the same thread as the + // BlindSignMessageInterface callbacks. + // Callers can make multiple concurrent requests to GetTokens. + // ProxyLayer must be either ProxyB or TerminalLayer, NOT ProxyA. + // AttestationDataCallback should call AttestAndSign with a separate callback + // in order to complete the token issuance protocol. + void GetAttestationTokens(int num_tokens, ProxyLayer layer, + AttestationDataCallback callback) override; + + // Returns signed unblinded tokens and their expiration time in a callback. + // Tokens are single-use and restricted to the PI use case. + // The GetTokens callback will run on the same thread as the + // BlindSignMessageInterface callbacks. + // This function should be called after the caller has generated + // AttestationData using Keystore and the challenge returned in + // AttestationDataCallback. If a token challenge is provided, it will be used + // in creating the token. Otherwise a default challenge will be used + // containing the issuer hostname. + void AttestAndSign(int num_tokens, ProxyLayer layer, + std::string attestation_data, + std::optional<std::string> token_challenge, + SignedTokenCallback callback) override; + private: void HandleGetTokensResponse( SignedTokenCallback callback, int num_tokens,
diff --git a/quiche/blind_sign_auth/proto/proxy_layer.proto b/quiche/blind_sign_auth/proto/proxy_layer.proto index 1b4895a..521d336 100644 --- a/quiche/blind_sign_auth/proto/proxy_layer.proto +++ b/quiche/blind_sign_auth/proto/proxy_layer.proto
@@ -10,4 +10,5 @@ PROXY_LAYER_UNSPECIFIED = 0; PROXY_A = 1; PROXY_B = 2; + TERMINAL_LAYER = 3; }
diff --git a/quiche/blind_sign_auth/test_tools/mock_blind_sign_auth_interface.h b/quiche/blind_sign_auth/test_tools/mock_blind_sign_auth_interface.h index 6b7981c..22c171f 100644 --- a/quiche/blind_sign_auth/test_tools/mock_blind_sign_auth_interface.h +++ b/quiche/blind_sign_auth/test_tools/mock_blind_sign_auth_interface.h
@@ -22,6 +22,15 @@ ProxyLayer proxy_layer, BlindSignAuthServiceType service_type, SignedTokenCallback callback), (override)); + MOCK_METHOD(void, GetAttestationTokens, + (int num_tokens, ProxyLayer layer, + AttestationDataCallback callback), + (override)); + MOCK_METHOD(void, AttestAndSign, + (int num_tokens, ProxyLayer layer, std::string attestation_data, + std::optional<std::string> token_challenge, + SignedTokenCallback callback), + (override)); }; } // namespace quiche::test