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