aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrabal Singh <prabalsingh@google.com>2023-11-13 18:07:22 +0000
committerPrabal Singh <prabalsingh@google.com>2023-11-13 20:36:25 +0000
commit26b2c690fcfc019b5a7ae55fec2d1fd117809c52 (patch)
tree22502fb41773fc4aa5a688f7d38da93e3391359b
parentac7650269463beb0cfb4c26ebf79344f210d5da4 (diff)
parent3dea51443eaabde75c00cd325bc31e3b848a767f (diff)
downloadanonymous-counting-tokens-26b2c690fcfc019b5a7ae55fec2d1fd117809c52.tar.gz
Merge remote-tracking branch 'origin/upstream-main'
Change-Id: I180a85b5132d9a086533ca7824c6bf92173b195c
-rw-r--r--.bazelrc12
-rw-r--r--.gitignore4
-rw-r--r--CONTRIBUTING.md28
-rw-r--r--LICENSE202
-rw-r--r--README.md46
-rw-r--r--WORKSPACE51
-rw-r--r--act/BUILD65
-rw-r--r--act/act.h119
-rw-r--r--act/act.proto102
-rw-r--r--act/act_v0/BUILD109
-rw-r--r--act/act_v0/act_v0.cc918
-rw-r--r--act/act_v0/act_v0.h128
-rw-r--r--act/act_v0/act_v0.proto141
-rw-r--r--act/act_v0/act_v0_test.cc686
-rw-r--r--act/act_v0/parameters.cc122
-rw-r--r--act/act_v0/parameters.h60
-rw-r--r--act/act_v0/parameters_test.cc145
-rw-r--r--act/act_v0/testing/BUILD75
-rw-r--r--act/act_v0/testing/generate_transcript.cc175
-rw-r--r--act/act_v0/testing/golden_transcript_test.cc81
-rw-r--r--act/act_v0/testing/transcript.proto34
-rw-r--r--act/act_v0/testing/transcripts/golden_transcript_1_09122023bin0 -> 103186 bytes
-rw-r--r--act/act_v0/testing/transcripts/golden_transcript_2_09122023bin0 -> 103202 bytes
-rw-r--r--act/act_v0/testing/transcripts/golden_transcript_3_09122023bin0 -> 103197 bytes
-rw-r--r--act/fake_act.cc144
-rw-r--r--act/fake_act.h124
-rw-r--r--act/fake_act_test.cc93
27 files changed, 3664 insertions, 0 deletions
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..de1d3f6
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,12 @@
+# Options for compiling ACT code.
+# Include these in dependent workspaces by using the --bazelrc flag, or by
+# adding import %act_workspace%/bazel.rc to the .bazelrc file in the
+# dependent workspace.
+
+build -c opt
+build --cxxopt='-std=c++17'
+build --host_cxxopt='-std=c++17'
+
+test -c opt
+test --cxxopt='-std=c++17'
+build --host_cxxopt='-std=c++17' \ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b136f6f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Bazel generated symlinks
+bazel-*
+# Mac files
+.DS_Store \ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ebbb59e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,28 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). \ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6635e99
--- /dev/null
+++ b/README.md
@@ -0,0 +1,46 @@
+# An Implementation of Anonymous Counting Tokens.
+
+An anonymous counting token (ACT) scheme allows Clients to obtain blind
+signatures or MACs (aka tokens) on messages of their choice, while at the same
+time enabling Issuers to enforce rate limits on the number of tokens that a
+client can obtain for each message. Specifically,
+
+* Blind issuance: The Issuer doesn't see the message for which a token is
+ being requested
+* Unlinkability: When the Client redeems a token, the token cannot be linked
+ to the issuance phase
+* Throttled issuance on identical messages: The Issuer can detect if a
+ particular Client is requesting a token for a previously used message.
+
+This repository implements a variant of the scheme described in [1], which is
+secure in the random oracle model under the q-DDHI assumption (in a cyclic
+group) and the DCR assumption. The variant implemented here relaxes the proven
+soundness guarantee to the non-concurrent setting. It also assumes that the
+server generates its parameters correctly. Future versions will support server
+proofs for correct parameter generation.
+
+This implementation also supports batched token issuance. Batched token issuance
+can have significant performance benefits as compared to individual token
+issuance.
+
+> [[1] "Anonymous Counting Tokens." Fabrice Benhamouda, Mariana Raykova, Karn
+> Seth.](https://eprint.iacr.org/2023/320)
+
+## Building/Running Tests
+
+This repository requires Bazel. You can install Bazel by
+following the instructions for your platform on the
+[Bazel website](https://docs.bazel.build/versions/master/install.html).
+
+Once you have installed Bazel you can clone this repository and run all tests
+that are included by navigating into the root folder and running:
+
+```bash
+bazel test //...
+```
+
+
+## Disclaimer
+
+This is not an officially supported Google product. The code is provided as-is,
+with no guarantees of correctness or security. \ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..d303063
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,51 @@
+# Copyright 2023 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""WORKSPACE file for Anonymous Counting Tokens code."""
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
+
+# Private Join and Compute
+http_archive(
+ name = "private_join_and_compute",
+ sha256 = "9304a6fe62c7227657e7cecf08c6234c14dfb558bd6a2fa778de845056fb9dd3",
+ strip_prefix = "private-join-and-compute-f77f26fab7f37e5e1e2d43250662c0281bd7fa4a",
+ urls = ["https://github.com/google/private-join-and-compute/archive/f77f26fab7f37e5e1e2d43250662c0281bd7fa4a.zip"],
+)
+
+# loads boringssl, absl, googletest, protobuf.
+load("@private_join_and_compute//bazel:pjc_deps.bzl", "pjc_deps")
+pjc_deps()
+
+load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
+protobuf_deps()
+
+# gRPC
+# must be included separately, since we need to load transitive deps of grpc for
+# some of the pjc deps.
+http_archive(
+ name = "com_github_grpc_grpc",
+ sha256 = "feaeeb315133ea5e3b046c2c0231f5b86ef9d297e536a14b73e0393335f8b157",
+ strip_prefix = "grpc-1.51.3",
+ urls = [
+ "https://github.com/grpc/grpc/archive/v1.51.3.tar.gz",
+ ],
+)
+
+load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
+grpc_deps()
+
+load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps")
+grpc_extra_deps() \ No newline at end of file
diff --git a/act/BUILD b/act/BUILD
new file mode 100644
index 0000000..b93857a
--- /dev/null
+++ b/act/BUILD
@@ -0,0 +1,65 @@
+# Copyright 2023 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Contains implementations for Anonymous Counting Tokens.
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+proto_library(
+ name = "act_proto",
+ srcs = ["act.proto"],
+ deps = ["//act/act_v0:act_v0_proto"],
+)
+
+cc_proto_library(
+ name = "act_cc_proto",
+ deps = [":act_proto"],
+)
+
+cc_library(
+ name = "act",
+ hdrs = ["act.h"],
+ deps = [
+ ":act_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ ],
+)
+
+cc_library(
+ name = "fake_act",
+ srcs = ["fake_act.cc"],
+ hdrs = ["fake_act.h"],
+ deps = [
+ ":act",
+ ":act_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto:bn_util",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ ],
+)
+
+cc_test(
+ name = "fake_act_test",
+ srcs = [
+ "fake_act_test.cc",
+ ],
+ deps = [
+ ":fake_act",
+ "@com_github_google_googletest//:gtest_main",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ "@private_join_and_compute//private_join_and_compute/util:status_testing_includes",
+ ],
+)
diff --git a/act/act.h b/act/act.h
new file mode 100644
index 0000000..e426d21
--- /dev/null
+++ b/act/act.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_H_
+#define PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_H_
+
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "act/act.pb.h"
+#include "private_join_and_compute/util/status.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+// Abstract class for methods related to Anonymous Counting Tokens.
+class AnonymousCountingTokens {
+ public:
+ virtual ~AnonymousCountingTokens() = default;
+
+ // Implementations should return a fresh set of Server parameters
+ // corresponding to these SchemeParameters.
+ virtual StatusOr<ServerParameters> GenerateServerParameters(
+ const SchemeParameters& scheme_parameters) = 0;
+
+ // Implementations should return a fresh set of Client parameters
+ // corresponding to these SchemeParameters and ServerPublicParameters.
+ virtual StatusOr<ClientParameters> GenerateClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters) = 0;
+
+ // Implementations should verify the consistency of these
+ // ClientPublicParameters with the Server and scheme parameters.
+ virtual Status CheckClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) = 0;
+
+ // Implementations should generate a tuple of client_fingerprints,
+ // TokensRequest and TokensRequestPrivateState for the given set of messages.
+ virtual StatusOr<std::tuple<std::vector<std::string>, TokensRequest,
+ TokensRequestPrivateState>>
+ GenerateTokensRequest(
+ absl::Span<const std::string> messages,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) = 0;
+
+ // Implementations should return OkStatus on a valid request.
+ virtual Status CheckTokensRequest(
+ absl::Span<const std::string> client_fingerprints,
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) = 0;
+
+ // Implementations should return the TokensResponse.
+ virtual StatusOr<TokensResponse> GenerateTokensResponse(
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) = 0;
+
+ // Implementations should return OkStatus on a valid response.
+ virtual Status VerifyTokensResponse(
+ absl::Span<const std::string> messages,
+ const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) = 0;
+
+ // Implementations should return a vector of tokens corresponding to the
+ // supplied messages.
+ virtual StatusOr<std::vector<Token>> RecoverTokens(
+ absl::Span<const std::string> messages,
+ const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) = 0;
+
+ // Implementations should return OkStatus on valid tokens.
+ virtual Status VerifyToken(
+ std::string m, const Token& token,
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) = 0;
+
+ protected:
+ AnonymousCountingTokens() = default;
+};
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
+
+#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_H_
diff --git a/act/act.proto b/act/act.proto
new file mode 100644
index 0000000..1c39823
--- /dev/null
+++ b/act/act.proto
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package private_join_and_compute.anonymous_counting_tokens;
+
+import "act/act_v0/act_v0.proto";
+
+option java_multiple_files = true;
+
+// The parameters defining the ACT scheme.
+message SchemeParameters {
+ oneof scheme_parameters_oneof {
+ SchemeParametersV0 scheme_parameters_v0 = 1;
+ }
+}
+
+message ServerParameters {
+ ServerPublicParameters public_parameters = 1;
+ ServerPrivateParameters private_parameters = 2;
+}
+
+// The Server's public parameters for the ACT scheme.
+message ServerPublicParameters {
+ oneof server_public_parameters_oneof {
+ ServerPublicParametersV0 server_public_parameters_v0 = 1;
+ }
+}
+
+// The Server's private parameters for the ACT scheme.
+message ServerPrivateParameters {
+ oneof server_private_parameters_oneof {
+ ServerPrivateParametersV0 server_private_parameters_v0 = 1;
+ }
+}
+
+message ClientParameters {
+ ClientPublicParameters public_parameters = 1;
+ ClientPrivateParameters private_parameters = 2;
+}
+
+// The Client's public parameters for the ACT scheme.
+message ClientPublicParameters {
+ oneof client_public_parameters_oneof {
+ ClientPublicParametersV0 client_public_parameters_v0 = 1;
+ }
+}
+
+// The Client's private parameters for the ACT scheme.
+message ClientPrivateParameters {
+ oneof client_private_parameters_oneof {
+ ClientPrivateParametersV0 client_private_parameters_v0 = 1;
+ }
+}
+
+// The Client's token request. Can correspond to a batch of tokens.
+message TokensRequest {
+ oneof tokens_request_oneof {
+ TokensRequestV0 tokens_request_v0 = 1;
+ }
+}
+
+// Private state corresponding to the Client's token request, needed to recover
+// the tokens from the server's response.
+message TokensRequestPrivateState {
+ oneof tokens_request_private_state_oneof {
+ TokensRequestPrivateStateV0 tokens_request_private_state_v0 = 1;
+ }
+}
+
+// The Server's response to a TokensRequest. Can correspond to a batch of
+// tokens.
+message TokensResponse {
+ oneof tokens_response_oneof {
+ TokensResponseV0 tokens_response_v0 = 1;
+ }
+}
+
+// An actual token recovered from the TokenResponse.
+message Token {
+ reserved 1;
+
+ oneof token_oneof {
+ TokenV0 token_v0 = 2;
+ }
+
+ // Serialized BigNum corresponding to the nonce for this token.
+ bytes nonce_bytes = 3;
+}
diff --git a/act/act_v0/BUILD b/act/act_v0/BUILD
new file mode 100644
index 0000000..1414b83
--- /dev/null
+++ b/act/act_v0/BUILD
@@ -0,0 +1,109 @@
+# Copyright 2023 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+proto_library(
+ name = "act_v0_proto",
+ srcs = ["act_v0.proto"],
+ deps = [
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:bb_oblivious_signature_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:dy_verifiable_random_function_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:big_num_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:camenisch_shoup_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:pedersen_proto",
+ ],
+)
+
+cc_proto_library(
+ name = "act_v0_cc_proto",
+ deps = [":act_v0_proto"],
+)
+
+cc_library(
+ name = "act_v0",
+ srcs = ["act_v0.cc"],
+ hdrs = ["act_v0.h"],
+ deps = [
+ ":act_v0_cc_proto",
+ "//act",
+ "//act:act_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto:bn_util",
+ "@private_join_and_compute//private_join_and_compute/crypto:camenisch_shoup",
+ "@private_join_and_compute//private_join_and_compute/crypto:ec_util",
+ "@private_join_and_compute//private_join_and_compute/crypto:pedersen_over_zn",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:bb_oblivious_signature",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:bb_oblivious_signature_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:dy_verifiable_random_function",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:dy_verifiable_random_function_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:ec_point_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:proto_util",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ ],
+)
+
+cc_test(
+ name = "act_v0_test",
+ srcs = ["act_v0_test.cc"],
+ deps = [
+ ":act_v0",
+ ":act_v0_cc_proto",
+ ":parameters",
+ "//act",
+ "//act:act_cc_proto",
+ "@com_github_google_googletest//:gtest_main",
+ "@private_join_and_compute//private_join_and_compute/crypto:bn_util",
+ "@private_join_and_compute//private_join_and_compute/crypto:camenisch_shoup",
+ "@private_join_and_compute//private_join_and_compute/crypto:ec_util",
+ "@private_join_and_compute//private_join_and_compute/crypto:pedersen_over_zn",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:bb_oblivious_signature_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/dodis_yampolskiy_prf:dy_verifiable_random_function_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:big_num_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:camenisch_shoup_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/crypto/proto:pedersen_cc_proto",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ "@private_join_and_compute//private_join_and_compute/util:status_testing_includes",
+ ],
+)
+
+cc_library(
+ name = "parameters",
+ srcs = ["parameters.cc"],
+ hdrs = ["parameters.h"],
+ deps = [
+ ":act_v0_cc_proto",
+ "//act:act_cc_proto",
+ "@com_google_absl//absl/strings",
+ "@private_join_and_compute//private_join_and_compute/crypto:openssl_includes",
+ ],
+)
+
+cc_test(
+ name = "parameters_test",
+ size = "enormous",
+ srcs = ["parameters_test.cc"],
+ deps = [
+ ":act_v0",
+ ":parameters",
+ "//act",
+ "//act:act_cc_proto",
+ "@com_github_google_googletest//:gtest_main",
+ "@com_google_absl//absl/strings",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ "@private_join_and_compute//private_join_and_compute/util:status_testing_includes",
+ ],
+)
diff --git a/act/act_v0/act_v0.cc b/act/act_v0/act_v0.cc
new file mode 100644
index 0000000..228f0db
--- /dev/null
+++ b/act/act_v0/act_v0.cc
@@ -0,0 +1,918 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "act/act_v0/act_v0.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "act/act.pb.h"
+#include "act/act_v0/act_v0.pb.h"
+#include "private_join_and_compute/crypto/big_num.h"
+#include "private_join_and_compute/crypto/camenisch_shoup.h"
+#include "private_join_and_compute/crypto/context.h"
+#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.h"
+#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.pb.h"
+#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.h"
+#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.pb.h"
+#include "private_join_and_compute/crypto/ec_group.h"
+#include "private_join_and_compute/crypto/ec_point.h"
+#include "private_join_and_compute/crypto/pedersen_over_zn.h"
+#include "private_join_and_compute/crypto/proto/ec_point.pb.h"
+#include "private_join_and_compute/crypto/proto/proto_util.h"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+namespace {
+
+StatusOr<std::unique_ptr<BbObliviousSignature>> CreateBbObliviousSignature(
+ const SchemeParametersV0& scheme_parameters_v0,
+ const ServerPublicParametersV0& server_public_parameters_v0, Context* ctx,
+ ECGroup* ec_group, PedersenOverZn* pedersen,
+ PublicCamenischShoup* public_camenisch_shoup) {
+ proto::BbObliviousSignatureParameters bb_oblivious_signature_parameters;
+ bb_oblivious_signature_parameters.set_challenge_length_bits(
+ scheme_parameters_v0.challenge_length_bits());
+ bb_oblivious_signature_parameters.set_security_parameter(
+ scheme_parameters_v0.security_parameter());
+ bb_oblivious_signature_parameters.set_random_oracle_prefix(
+ scheme_parameters_v0.random_oracle_prefix());
+ bb_oblivious_signature_parameters.set_base_g(
+ server_public_parameters_v0.prf_base_g());
+ *bb_oblivious_signature_parameters.mutable_pedersen_parameters() =
+ server_public_parameters_v0.pedersen_parameters();
+ *bb_oblivious_signature_parameters.mutable_camenisch_shoup_public_key() =
+ server_public_parameters_v0.camenisch_shoup_public_key();
+
+ return BbObliviousSignature::Create(
+ std::move(bb_oblivious_signature_parameters), ctx, ec_group,
+ public_camenisch_shoup, pedersen);
+}
+
+StatusOr<std::unique_ptr<DyVerifiableRandomFunction>> CreateDyVrf(
+ const SchemeParametersV0& scheme_parameters_v0,
+ const ServerPublicParametersV0& server_public_parameters_v0, Context* ctx,
+ ECGroup* ec_group, PedersenOverZn* pedersen) {
+ proto::DyVrfParameters dy_vrf_parameters;
+ dy_vrf_parameters.set_challenge_length_bits(
+ scheme_parameters_v0.challenge_length_bits());
+ dy_vrf_parameters.set_security_parameter(
+ scheme_parameters_v0.security_parameter());
+ dy_vrf_parameters.set_random_oracle_prefix(
+ scheme_parameters_v0.random_oracle_prefix());
+ dy_vrf_parameters.set_dy_prf_base_g(server_public_parameters_v0.prf_base_g());
+ *dy_vrf_parameters.mutable_pedersen_parameters() =
+ server_public_parameters_v0.pedersen_parameters();
+
+ return DyVerifiableRandomFunction::Create(std::move(dy_vrf_parameters), ctx,
+ ec_group, pedersen);
+}
+
+// Used to generate the client-independent portion of the nonce. A different
+// nonce is chosen for each element in the batched token request.
+StatusOr<std::vector<BigNum>> GetNoncesForTokenRequest(
+ Context* ctx, const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const TokensRequestV0::Part1& tokens_request_part_1,
+ uint64_t num_messages) {
+ // Parses bit length of the random challenge from scheme parameters.
+ uint64_t challenge_length_bits =
+ scheme_parameters.scheme_parameters_v0().challenge_length_bits();
+ // Computes the upper bound of the challenge and input to the random oracle.
+ BigNum challenge_upper_bound = ctx->One().Lshift(challenge_length_bits);
+
+ // Note that the random oracle prefix is implicitly included as part of the
+ // parameters being serialized in the statement proto. We skip including it
+ // again here to avoid unnecessary duplication.
+ std::string challenge_string = "GetNoncesForTokenRequest:";
+ auto challenge_sos =
+ std::make_unique<google::protobuf::io::StringOutputStream>(
+ &challenge_string);
+ auto challenge_cos =
+ std::make_unique<google::protobuf::io::CodedOutputStream>(
+ challenge_sos.get());
+ challenge_cos->SetSerializationDeterministic(true);
+ challenge_cos->WriteVarint64(scheme_parameters.ByteSizeLong());
+ challenge_cos->WriteString(SerializeAsStringInOrder(scheme_parameters));
+ challenge_cos->WriteVarint64(server_public_parameters.ByteSizeLong());
+ challenge_cos->WriteString(
+ SerializeAsStringInOrder(server_public_parameters));
+ challenge_cos->WriteVarint64(client_public_parameters.ByteSizeLong());
+ challenge_cos->WriteString(
+ SerializeAsStringInOrder(client_public_parameters));
+ challenge_cos->WriteVarint64(tokens_request_part_1.ByteSizeLong());
+ challenge_cos->WriteString(SerializeAsStringInOrder(tokens_request_part_1));
+ challenge_cos->WriteVarint64(num_messages);
+
+ // Delete the serialization objects to make sure they clean up and write.
+ challenge_cos.reset();
+ challenge_sos.reset();
+
+ std::vector<BigNum> outputs;
+ outputs.reserve(num_messages);
+ for (uint64_t i = 0; i < num_messages; ++i) {
+ std::string random_oracle_input_i = absl::StrCat(challenge_string, ",", i);
+ outputs.push_back(
+ ctx->RandomOracleSha512(random_oracle_input_i, challenge_upper_bound));
+ }
+
+ return std::move(outputs);
+}
+
+} // namespace
+
+std::unique_ptr<AnonymousCountingTokens> AnonymousCountingTokensV0::Create() {
+ return absl::WrapUnique<AnonymousCountingTokensV0>(
+ new AnonymousCountingTokensV0());
+}
+
+// Returns a fresh set of Server parameters corresponding to these
+// SchemeParameters. Fails with InvalidArgument if the parameters don't
+// correspond to ACT v0.
+StatusOr<ServerParameters> AnonymousCountingTokensV0::GenerateServerParameters(
+ const SchemeParameters& scheme_parameters) {
+ if (!scheme_parameters.has_scheme_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::GenerateServerParameters: supplied "
+ "parameters do not correspond to ACTv0.");
+ }
+
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Choose base g.
+ ASSIGN_OR_RETURN(ECPoint dy_prf_base_g, ec_group.GetRandomGenerator());
+
+ // Generate RSA-Modulus and Camenisch-Shoup encryption key.
+ CamenischShoupKey camenisch_shoup_key = GenerateCamenischShoupKey(
+ &ctx, scheme_parameters_v0.modulus_length_bits(),
+ scheme_parameters_v0.camenisch_shoup_s(),
+ scheme_parameters_v0.vector_encryption_length());
+
+ BigNum n = camenisch_shoup_key.n;
+
+ auto camenisch_shoup_public_key = std::make_unique<CamenischShoupPublicKey>(
+ CamenischShoupPublicKey{camenisch_shoup_key.n, camenisch_shoup_key.s,
+ camenisch_shoup_key.vector_encryption_length,
+ camenisch_shoup_key.g, camenisch_shoup_key.ys});
+ auto camenisch_shoup_private_key = std::make_unique<CamenischShoupPrivateKey>(
+ CamenischShoupPrivateKey{camenisch_shoup_key.xs});
+
+ auto public_camenisch_shoup = std::make_unique<PublicCamenischShoup>(
+ &ctx, camenisch_shoup_public_key->n, camenisch_shoup_public_key->s,
+ camenisch_shoup_public_key->g, camenisch_shoup_public_key->ys);
+
+ // Generate Pedersen Parameters.
+ PedersenOverZn::Parameters pedersen_parameters =
+ PedersenOverZn::GenerateParameters(
+ &ctx, n, scheme_parameters_v0.pedersen_batch_size());
+
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::Create(&ctx, pedersen_parameters.gs,
+ pedersen_parameters.h, pedersen_parameters.n));
+
+ ServerParameters server_parameters;
+
+ ServerPublicParametersV0* server_public_parameters_v0 =
+ server_parameters.mutable_public_parameters()
+ ->mutable_server_public_parameters_v0();
+ ASSIGN_OR_RETURN(*server_public_parameters_v0->mutable_prf_base_g(),
+ dy_prf_base_g.ToBytesCompressed());
+ *server_public_parameters_v0->mutable_pedersen_parameters() =
+ PedersenOverZn::ParametersToProto(pedersen_parameters);
+ *server_public_parameters_v0->mutable_camenisch_shoup_public_key() =
+ CamenischShoupPublicKeyToProto(*camenisch_shoup_public_key);
+
+ ServerPrivateParametersV0* server_private_parameters_v0 =
+ server_parameters.mutable_private_parameters()
+ ->mutable_server_private_parameters_v0();
+ *server_private_parameters_v0->mutable_camenisch_shoup_private_key() =
+ CamenischShoupPrivateKeyToProto(*camenisch_shoup_private_key);
+
+ // Generate Boneh-Boyen Oblivious Signature object. This call is safe even
+ // with the partially-ready server_public_parameters.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<BbObliviousSignature> bb_oblivious_signature,
+ CreateBbObliviousSignature(scheme_parameters_v0,
+ *server_public_parameters_v0, &ctx, &ec_group,
+ pedersen.get(), public_camenisch_shoup.get()));
+
+ // Generate Boneh-Boyen Oblivious Signature key.
+ ASSIGN_OR_RETURN(
+ std::tie(*server_public_parameters_v0
+ ->mutable_bb_oblivious_signature_public_key(),
+ *server_private_parameters_v0
+ ->mutable_bb_oblivious_signature_private_key()),
+ bb_oblivious_signature->GenerateKeys());
+
+ return std::move(server_parameters);
+}
+
+// Returns a fresh set of Client parameters corresponding to these
+// SchemeParameters and ServerPublicParameters. Fails with InvalidArgument if
+// the parameters don't correspond to ACT v0.
+StatusOr<ClientParameters> AnonymousCountingTokensV0::GenerateClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ if (!scheme_parameters.has_scheme_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::GenerateClientParameters: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize Pedersen Params
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+
+ // Generate Client VRF object.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<DyVerifiableRandomFunction> dy_vrf,
+ CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx,
+ &ec_group, pedersen.get()));
+
+ ClientParameters client_parameters;
+ ClientPublicParametersV0* client_public_parameters_v0 =
+ client_parameters.mutable_public_parameters()
+ ->mutable_client_public_parameters_v0();
+ ClientPrivateParametersV0* client_private_parameters_v0 =
+ client_parameters.mutable_private_parameters()
+ ->mutable_client_private_parameters_v0();
+
+ ASSIGN_OR_RETURN(
+ std::tie(
+ *client_public_parameters_v0->mutable_dy_vrf_public_key(),
+ *client_private_parameters_v0->mutable_dy_vrf_private_key(),
+ *client_public_parameters_v0->mutable_dy_vrf_generate_keys_proof()),
+ dy_vrf->GenerateKeyPair());
+
+ return std::move(client_parameters);
+}
+
+// Verifies the consistency of the ClientPublicParameters with the Server and
+// scheme parameters. Fails with InvalidArgument if the parameters don't
+// correspond to ACT v0.
+Status AnonymousCountingTokensV0::CheckClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ if (!scheme_parameters.has_scheme_parameters_v0() ||
+ !client_public_parameters.has_client_public_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0() ||
+ !server_private_parameters.has_server_private_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::CheckClientParameters: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+ const ClientPublicParametersV0& client_public_parameters_v0 =
+ client_public_parameters.client_public_parameters_v0();
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize Pedersen Params
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+
+ // Generate Client VRF object.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<DyVerifiableRandomFunction> dy_vrf,
+ CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx,
+ &ec_group, pedersen.get()));
+
+ // Verify the proof for the Client's VRF key.
+ return dy_vrf->VerifyGenerateKeysProof(
+ client_public_parameters_v0.dy_vrf_public_key(),
+ client_public_parameters_v0.dy_vrf_generate_keys_proof());
+}
+
+// Returns a tuple of client_fingerprints, TokensRequest and
+// TokensRequestPrivateState for the given set of messages. Fails with
+// InvalidArgument if the parameters don't correspond to ACT v0.
+StatusOr<std::tuple<std::vector<std::string>, TokensRequest,
+ TokensRequestPrivateState>>
+AnonymousCountingTokensV0::GenerateTokensRequest(
+ absl::Span<const std::string> messages,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ if (!scheme_parameters.has_scheme_parameters_v0() ||
+ !client_public_parameters.has_client_public_parameters_v0() ||
+ !client_private_parameters.has_client_private_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::GenerateTokensRequest: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ClientPublicParametersV0& client_public_parameters_v0 =
+ client_public_parameters.client_public_parameters_v0();
+ const ClientPrivateParametersV0& client_private_parameters_v0 =
+ client_private_parameters.client_private_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+
+ TokensRequest tokens_request_proto;
+ TokensRequestV0* tokens_request_v0 =
+ tokens_request_proto.mutable_tokens_request_v0();
+ TokensRequestV0::Part1* tokens_request_v0_part_1 =
+ tokens_request_v0->mutable_part_1();
+ TokensRequestPrivateState tokens_request_private_state;
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize and create cryptographic objects.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup,
+ PublicCamenischShoup::FromProto(
+ &ctx, server_public_parameters_v0.camenisch_shoup_public_key()));
+ ASSIGN_OR_RETURN(
+ ECPoint dy_prf_base_g,
+ ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g()));
+
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<DyVerifiableRandomFunction> dy_vrf,
+ CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx,
+ &ec_group, pedersen.get()));
+
+ // Deserialize Boneh-Boyen Oblivious Signature parameters and keys
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<BbObliviousSignature> bb_oblivious_signature,
+ CreateBbObliviousSignature(scheme_parameters_v0,
+ server_public_parameters_v0, &ctx, &ec_group,
+ pedersen.get(), public_camenisch_shoup.get()));
+
+ // 1) Hash all messages to the exponent group/ BigNums.
+ std::vector<BigNum> hashed_messages;
+ hashed_messages.reserve(messages.size());
+ for (size_t i = 0; i < messages.size(); ++i) {
+ hashed_messages.push_back(
+ ctx.RandomOracleSha512(messages[i], ec_group.GetOrder()));
+ }
+
+ // 2) Commit to hashed messages.
+ ASSIGN_OR_RETURN(
+ PedersenOverZn::CommitmentAndOpening commit_and_open_messages,
+ pedersen->Commit(hashed_messages));
+ tokens_request_v0_part_1->set_commit_messages(
+ commit_and_open_messages.commitment.ToBytes());
+
+ // 3) Generate client nonces and commit to them.
+ std::vector<BigNum> client_nonces;
+ client_nonces.reserve(messages.size());
+ for (size_t i = 0; i < messages.size(); ++i) {
+ client_nonces.push_back(ec_group.GeneratePrivateKey());
+ }
+ ASSIGN_OR_RETURN(
+ PedersenOverZn::CommitmentAndOpening commit_and_open_client_nonces,
+ pedersen->Commit(client_nonces));
+ tokens_request_v0_part_1->set_commit_client_nonces(
+ commit_and_open_client_nonces.commitment.ToBytes());
+
+ // 4) Perform a VRF on the committed messages and serialize as fingerprints.
+ ASSIGN_OR_RETURN(
+ std::vector<ECPoint> prf_evaluations,
+ dy_vrf->Apply(hashed_messages,
+ client_private_parameters_v0.dy_vrf_private_key()));
+ std::vector<std::string> fingerprints;
+ fingerprints.reserve(prf_evaluations.size());
+ for (size_t i = 0; i < prf_evaluations.size(); ++i) {
+ ASSIGN_OR_RETURN(std::string fingerprint,
+ prf_evaluations[i].ToBytesCompressed());
+ fingerprints.push_back(std::move(fingerprint));
+ }
+
+ // Also create the proof that the fingerprints were correctly generated.
+ ASSIGN_OR_RETURN(*tokens_request_v0_part_1->mutable_fingerprints_proof(),
+ dy_vrf->GenerateApplyProof(
+ hashed_messages, prf_evaluations,
+ client_public_parameters_v0.dy_vrf_public_key(),
+ client_private_parameters_v0.dy_vrf_private_key(),
+ commit_and_open_messages));
+
+ // 5) Generate server nonces by hashing the preceding portion of the request.
+ ASSIGN_OR_RETURN(std::vector<BigNum> server_nonces,
+ GetNoncesForTokenRequest(
+ &ctx, scheme_parameters, server_public_parameters,
+ client_public_parameters, *tokens_request_v0_part_1,
+ messages.size()));
+ // We commit the "server_nonces" with randomness 0, which is ok since they
+ // are known to both parties, and furthermore will be homomorphically added to
+ // the "client_nonces" which have properly generated randomness.
+ ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces,
+ pedersen->CommitWithRand(server_nonces, ctx.Zero()));
+
+ // 6) Homomorphically compute commitments to the nonces (rs)
+ std::vector<BigNum> nonces;
+ nonces.reserve(messages.size());
+ for (size_t i = 0; i < messages.size(); ++i) {
+ // No mod performed here, since the homomorphic addition of the commitments
+ // will not be mod-ed, and we want consistency.
+ nonces.push_back(server_nonces[i] + client_nonces[i]);
+ }
+ PedersenOverZn::Commitment commit_nonce = pedersen->Add(
+ commit_server_nonces, commit_and_open_client_nonces.commitment);
+ PedersenOverZn::Opening commit_nonce_opening =
+ commit_and_open_client_nonces.opening;
+
+ *tokens_request_private_state.mutable_tokens_request_private_state_v0()
+ ->mutable_nonces() = BigNumVectorToProto(nonces);
+
+ // 7) Generate Boneh-Boyen Oblivious Signature Request request.
+ ASSIGN_OR_RETURN(
+ std::tie(
+ *tokens_request_v0->mutable_bb_oblivious_signature_request(),
+ *tokens_request_v0->mutable_bb_oblivious_signature_request_proof(),
+ *tokens_request_private_state
+ .mutable_tokens_request_private_state_v0()
+ ->mutable_bb_oblivious_signature_request_private_state()),
+ bb_oblivious_signature->GenerateRequestAndProof(
+ hashed_messages, nonces,
+ server_public_parameters_v0.bb_oblivious_signature_public_key(),
+ commit_and_open_messages, {commit_nonce, commit_nonce_opening}));
+
+ return std::make_tuple(std::move(fingerprints),
+ std::move(tokens_request_proto),
+ std::move(tokens_request_private_state));
+}
+
+// Returns OkStatus on a valid request. Fails with InvalidArgument if the
+// parameters don't correspond to ACT v0.
+Status AnonymousCountingTokensV0::CheckTokensRequest(
+ absl::Span<const std::string> client_fingerprints,
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ if (!tokens_request.has_tokens_request_v0() ||
+ !scheme_parameters.has_scheme_parameters_v0() ||
+ !client_public_parameters.has_client_public_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::GenerateTokensResponse: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0();
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ClientPublicParametersV0& client_public_parameters_v0 =
+ client_public_parameters.client_public_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize and create cryptographic objects.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup,
+ PublicCamenischShoup::FromProto(
+ &ctx, server_public_parameters_v0.camenisch_shoup_public_key()));
+
+ // Construct the DY VRF object
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<DyVerifiableRandomFunction> dy_vrf,
+ CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx,
+ &ec_group, pedersen.get()));
+
+ PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum(
+ tokens_request.tokens_request_v0().part_1().commit_messages());
+
+ std::vector<ECPoint> deserialized_fingerprints;
+ deserialized_fingerprints.reserve(client_fingerprints.size());
+ for (size_t i = 0; i < client_fingerprints.size(); ++i) {
+ ASSIGN_OR_RETURN(ECPoint deserialized_fingerprint,
+ ec_group.CreateECPoint(client_fingerprints[i]));
+
+ // Test that the deserialized fingerprint reserializes to the exact same
+ // value.
+ ASSIGN_OR_RETURN(std::string reserialized_fingerprint,
+ deserialized_fingerprint.ToBytesCompressed());
+ if (reserialized_fingerprint != client_fingerprints[i]) {
+ return absl::InvalidArgumentError(absl::StrCat(
+ "AnonymousCountingTokensV0::CheckTokensRequest: client_fingerprints[",
+ i,
+ "] comes out to a different value when serialized and "
+ "deserialized."));
+ }
+
+ deserialized_fingerprints.push_back(std::move(deserialized_fingerprint));
+ }
+
+ RETURN_IF_ERROR(dy_vrf->VerifyApplyProof(
+ deserialized_fingerprints,
+ client_public_parameters_v0.dy_vrf_public_key(), commit_messages,
+ tokens_request_v0.part_1().fingerprints_proof()));
+
+ // Deserialize Boneh-Boyen Oblivious Signature parameters and keys
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<BbObliviousSignature> bb_oblivious_signature,
+ CreateBbObliviousSignature(scheme_parameters_v0,
+ server_public_parameters_v0, &ctx, &ec_group,
+ pedersen.get(), public_camenisch_shoup.get()));
+
+ // Regenerate the commitments to messages and nonces (rs) by replaying the
+ // steps the client took to generate them.
+ PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum(
+ tokens_request.tokens_request_v0().part_1().commit_client_nonces());
+
+ ASSIGN_OR_RETURN(
+ std::vector<BigNum> server_nonces,
+ GetNoncesForTokenRequest(
+ &ctx, scheme_parameters, server_public_parameters,
+ client_public_parameters, tokens_request.tokens_request_v0().part_1(),
+ tokens_request.tokens_request_v0()
+ .bb_oblivious_signature_request()
+ .num_messages()));
+ ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces,
+ pedersen->CommitWithRand(server_nonces, ctx.Zero()));
+ PedersenOverZn::Commitment commit_nonce =
+ pedersen->Add(commit_server_nonces, commit_client_nonces);
+
+ return bb_oblivious_signature->VerifyRequest(
+ server_public_parameters_v0.bb_oblivious_signature_public_key(),
+ tokens_request_v0.bb_oblivious_signature_request(),
+ tokens_request_v0.bb_oblivious_signature_request_proof(), commit_messages,
+ commit_nonce);
+}
+
+// Returns the TokensResponse. Fails with InvalidArgument if the parameters
+// don't correspond to ACT v0.
+StatusOr<TokensResponse> AnonymousCountingTokensV0::GenerateTokensResponse(
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ if (!tokens_request.has_tokens_request_v0() ||
+ !scheme_parameters.has_scheme_parameters_v0() ||
+ !client_public_parameters.has_client_public_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0() ||
+ !server_private_parameters.has_server_private_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::GenerateTokensResponse: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0();
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+ const ServerPrivateParametersV0& server_private_parameters_v0 =
+ server_private_parameters.server_private_parameters_v0();
+
+ TokensResponse tokens_response;
+ TokensResponseV0* tokens_response_v0 =
+ tokens_response.mutable_tokens_response_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize and create cryptographic objects.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup,
+ PublicCamenischShoup::FromProto(
+ &ctx, server_public_parameters_v0.camenisch_shoup_public_key()));
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PrivateCamenischShoup> private_camenisch_shoup,
+ PrivateCamenischShoup::FromProto(
+ &ctx, server_public_parameters_v0.camenisch_shoup_public_key(),
+ server_private_parameters_v0.camenisch_shoup_private_key()));
+
+ // Deserialize Boneh-Boyen Oblivious Signature parameters and keys
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<BbObliviousSignature> bb_oblivious_signature,
+ CreateBbObliviousSignature(scheme_parameters_v0,
+ server_public_parameters_v0, &ctx, &ec_group,
+ pedersen.get(), public_camenisch_shoup.get()));
+
+ // Regenerate the commitments to messages and nonces (rs) by replaying the
+ // steps the client took to generate them.
+ PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum(
+ tokens_request.tokens_request_v0().part_1().commit_messages());
+ PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum(
+ tokens_request.tokens_request_v0().part_1().commit_client_nonces());
+
+ ASSIGN_OR_RETURN(
+ std::vector<BigNum> server_nonces,
+ GetNoncesForTokenRequest(
+ &ctx, scheme_parameters, server_public_parameters,
+ client_public_parameters, tokens_request.tokens_request_v0().part_1(),
+ tokens_request.tokens_request_v0()
+ .bb_oblivious_signature_request()
+ .num_messages()));
+ ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces,
+ pedersen->CommitWithRand(server_nonces, ctx.Zero()));
+ PedersenOverZn::Commitment commit_nonce =
+ pedersen->Add(commit_server_nonces, commit_client_nonces);
+
+ // Generate response and proof for the Boneh-Boyen Oblivious Signature.
+ ASSIGN_OR_RETURN(
+ std::tie(
+ *tokens_response_v0->mutable_bb_oblivious_signature_response(),
+ *tokens_response_v0->mutable_bb_oblivious_signature_response_proof()),
+ bb_oblivious_signature->GenerateResponseAndProof(
+ tokens_request_v0.bb_oblivious_signature_request(),
+ server_public_parameters_v0.bb_oblivious_signature_public_key(),
+ server_private_parameters_v0.bb_oblivious_signature_private_key(),
+ commit_messages, commit_nonce, private_camenisch_shoup.get()));
+
+ return std::move(tokens_response);
+}
+
+// Returns OkStatus on a valid response. Fails with InvalidArgument if the
+// parameters don't correspond to ACT v0.
+Status AnonymousCountingTokensV0::VerifyTokensResponse(
+ absl::Span<const std::string> messages, const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ if (!tokens_request.has_tokens_request_v0() ||
+ !tokens_response.has_tokens_response_v0() ||
+ !tokens_request_private_state.has_tokens_request_private_state_v0() ||
+ !scheme_parameters.has_scheme_parameters_v0() ||
+ !client_public_parameters.has_client_public_parameters_v0() ||
+ !client_private_parameters.has_client_private_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::VerifyTokensResponse: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0();
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+ const TokensResponseV0& tokens_response_v0 =
+ tokens_response.tokens_response_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize and create cryptographic objects.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup,
+ PublicCamenischShoup::FromProto(
+ &ctx, server_public_parameters_v0.camenisch_shoup_public_key()));
+
+ // Deserialize Boneh-Boyen Oblivious Signature parameters and keys
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<BbObliviousSignature> bb_oblivious_signature,
+ CreateBbObliviousSignature(scheme_parameters_v0,
+ server_public_parameters_v0, &ctx, &ec_group,
+ pedersen.get(), public_camenisch_shoup.get()));
+
+ // Regenerate the commitments to messages and nonces (rs) by replaying the
+ // steps the client took to generate them.
+ PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum(
+ tokens_request.tokens_request_v0().part_1().commit_messages());
+ PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum(
+ tokens_request.tokens_request_v0().part_1().commit_client_nonces());
+
+ ASSIGN_OR_RETURN(
+ std::vector<BigNum> server_nonces,
+ GetNoncesForTokenRequest(
+ &ctx, scheme_parameters, server_public_parameters,
+ client_public_parameters, tokens_request.tokens_request_v0().part_1(),
+ tokens_request.tokens_request_v0()
+ .bb_oblivious_signature_request()
+ .num_messages()));
+ ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces,
+ pedersen->CommitWithRand(server_nonces, ctx.Zero()));
+ PedersenOverZn::Commitment commit_nonce =
+ pedersen->Add(commit_server_nonces, commit_client_nonces);
+
+ return bb_oblivious_signature->VerifyResponse(
+ server_public_parameters_v0.bb_oblivious_signature_public_key(),
+ tokens_response_v0.bb_oblivious_signature_response(),
+ tokens_response_v0.bb_oblivious_signature_response_proof(),
+ tokens_request_v0.bb_oblivious_signature_request(), commit_messages,
+ commit_nonce);
+}
+
+// Returns a vector of tokens corresponding to the supplied messages. Fails
+// with InvalidArgument if the parameters don't correspond to ACT v0.
+StatusOr<std::vector<Token>> AnonymousCountingTokensV0::RecoverTokens(
+ absl::Span<const std::string> messages, const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ if (!tokens_request.has_tokens_request_v0() ||
+ !tokens_request_private_state.has_tokens_request_private_state_v0() ||
+ !tokens_response.has_tokens_response_v0() ||
+ !scheme_parameters.has_scheme_parameters_v0() ||
+ !client_public_parameters.has_client_public_parameters_v0() ||
+ !client_private_parameters.has_client_private_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::VerifyTokensResponse: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0();
+ const TokensRequestPrivateStateV0& tokens_request_private_state_v0 =
+ tokens_request_private_state.tokens_request_private_state_v0();
+ const TokensResponseV0& tokens_response_v0 =
+ tokens_response.tokens_response_v0();
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+
+ // Deserialize and create cryptographic objects.
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PedersenOverZn> pedersen,
+ PedersenOverZn::FromProto(
+ &ctx, server_public_parameters_v0.pedersen_parameters()));
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup,
+ PublicCamenischShoup::FromProto(
+ &ctx, server_public_parameters_v0.camenisch_shoup_public_key()));
+ ASSIGN_OR_RETURN(
+ ECPoint dy_prf_base_g,
+ ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g()));
+
+ // Deserialize Boneh-Boyen Oblivious Signature parameters and keys
+ ASSIGN_OR_RETURN(
+ std::unique_ptr<BbObliviousSignature> bb_oblivious_signature,
+ CreateBbObliviousSignature(scheme_parameters_v0,
+ server_public_parameters_v0, &ctx, &ec_group,
+ pedersen.get(), public_camenisch_shoup.get()));
+
+ // Extract message PRF evaluations
+ ASSIGN_OR_RETURN(std::vector<ECPoint> signatures,
+ bb_oblivious_signature->ExtractResults(
+ tokens_response_v0.bb_oblivious_signature_response(),
+ tokens_request_v0.bb_oblivious_signature_request(),
+ tokens_request_private_state_v0
+ .bb_oblivious_signature_request_private_state()));
+
+ // Package tokens.
+ std::vector<BigNum> nonces =
+ ParseBigNumVectorProto(&ctx, tokens_request_private_state_v0.nonces());
+
+ std::vector<Token> tokens;
+ tokens.reserve(messages.size());
+ for (size_t i = 0; i < messages.size(); ++i) {
+ Token token;
+ TokenV0* token_v0 = token.mutable_token_v0();
+ token.set_nonce_bytes(nonces[i].ToBytes());
+ ASSIGN_OR_RETURN(*token_v0->mutable_bb_signature(),
+ signatures[i].ToBytesCompressed());
+ tokens.push_back(std::move(token));
+ }
+
+ return std::move(tokens);
+}
+
+// Returns OkStatus on valid tokens. Fails with InvalidArgument if the
+// parameters don't correspond to ACT v0.
+Status AnonymousCountingTokensV0::VerifyToken(
+ std::string m, const Token& token,
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ if (!token.has_token_v0() || !scheme_parameters.has_scheme_parameters_v0() ||
+ !server_public_parameters.has_server_public_parameters_v0() ||
+ !server_private_parameters.has_server_private_parameters_v0()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::VerifyToken: supplied "
+ "parameters do not correspond to ACT v0.");
+ }
+
+ const TokenV0& token_v0 = token.token_v0();
+ const SchemeParametersV0& scheme_parameters_v0 =
+ scheme_parameters.scheme_parameters_v0();
+ const ServerPublicParametersV0& server_public_parameters_v0 =
+ server_public_parameters.server_public_parameters_v0();
+ const ServerPrivateParametersV0& server_private_parameters_v0 =
+ server_private_parameters.server_private_parameters_v0();
+
+ Context ctx;
+ ASSIGN_OR_RETURN(ECGroup ec_group,
+ ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx));
+ ASSIGN_OR_RETURN(
+ ECPoint dy_prf_base_g,
+ ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g()));
+ BigNum k = ctx.CreateBigNum(
+ server_private_parameters_v0.bb_oblivious_signature_private_key().k());
+ BigNum y = ctx.CreateBigNum(
+ server_private_parameters_v0.bb_oblivious_signature_private_key().y());
+
+ BigNum hashed_message = ctx.RandomOracleSha512(m, ec_group.GetOrder());
+ BigNum nonce = ctx.CreateBigNum(token.nonce_bytes());
+
+ // Verify that reserializing the nonce comes out to the same value.
+ if (nonce.ToBytes() != token.nonce_bytes()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::VerifyToken: nonce comes out to different "
+ "value when serialized and deserialized.");
+ }
+
+ ASSIGN_OR_RETURN(ECPoint signature_from_token,
+ ec_group.CreateECPoint(token_v0.bb_signature()));
+
+ // Verify that reserializing the signature comes out to the same value
+ ASSIGN_OR_RETURN(std::string reserialized_signature_from_token,
+ signature_from_token.ToBytesCompressed());
+ if (reserialized_signature_from_token != token_v0.bb_signature()) {
+ return absl::InvalidArgumentError(
+ "AnonymousCountingTokensV0::VerifyToken: bb_signature comes out to "
+ "different value when serialized and deserialized.");
+ }
+
+ ASSIGN_OR_RETURN(
+ BigNum inverted_exponent,
+ (hashed_message + k + (nonce * y)).ModInverse(ec_group.GetOrder()));
+ ASSIGN_OR_RETURN(ECPoint signature_by_evaluation,
+ dy_prf_base_g.Mul(inverted_exponent));
+ if (signature_by_evaluation != signature_from_token) {
+ return absl::InvalidArgumentError(
+ "ACTV0::VerifyToken: Boneh-boyen signature on message and nonce fails "
+ "to match the token.");
+ }
+
+ return absl::OkStatus();
+}
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
diff --git a/act/act_v0/act_v0.h b/act/act_v0/act_v0.h
new file mode 100644
index 0000000..c499a92
--- /dev/null
+++ b/act/act_v0/act_v0.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_ACT_V0_H_
+#define PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_ACT_V0_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "act/act.h"
+#include "act/act.pb.h"
+#include "private_join_and_compute/util/status.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+// An implementation of vO Anonymous Counting Tokens.
+class AnonymousCountingTokensV0 : public AnonymousCountingTokens {
+ public:
+ static std::unique_ptr<AnonymousCountingTokens> Create();
+
+ // Returns a fresh set of Server parameters corresponding to these
+ // SchemeParameters. Fails with InvalidArgument if the parameters don't
+ // correspond to ACT v0.
+ StatusOr<ServerParameters> GenerateServerParameters(
+ const SchemeParameters& scheme_parameters) override;
+
+ // Returns a fresh set of Client parameters corresponding to these
+ // SchemeParameters and ServerPublicParameters. Fails with InvalidArgument if
+ // the parameters don't correspond to ACT v0.
+ StatusOr<ClientParameters> GenerateClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Verifies the consistency of the ClientPublicParameters with the Server and
+ // scheme parameters. Fails with InvalidArgument if the parameters don't
+ // correspond to ACT v0.
+ Status CheckClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ // Returns a tuple of client_fingerprints, TokensRequest and
+ // TokensRequestPrivateState for the given set of messages. Fails with
+ // InvalidArgument if the parameters don't correspond to ACT v0.
+ StatusOr<std::tuple<std::vector<std::string>, TokensRequest,
+ TokensRequestPrivateState>>
+ GenerateTokensRequest(
+ absl::Span<const std::string> messages,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Returns OkStatus on a valid request. Fails with InvalidArgument if the
+ // parameters don't correspond to ACT v0.
+ Status CheckTokensRequest(
+ absl::Span<const std::string> client_fingerprints,
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ // Returns the TokensResponse. Fails with InvalidArgument if the parameters
+ // don't correspond to ACT v0.
+ StatusOr<TokensResponse> GenerateTokensResponse(
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ // Returns OkStatus on a valid response. Fails with InvalidArgument if the
+ // parameters don't correspond to ACT v0.
+ Status VerifyTokensResponse(
+ absl::Span<const std::string> messages,
+ const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Returns a vector of tokens corresponding to the supplied messages. Fails
+ // with InvalidArgument if the parameters don't correspond to ACT v0.
+ StatusOr<std::vector<Token>> RecoverTokens(
+ absl::Span<const std::string> messages,
+ const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Returns OkStatus on valid tokens. Fails with InvalidArgument if the
+ // parameters don't correspond to ACT v0.
+ Status VerifyToken(
+ std::string m, const Token& token,
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ protected:
+ AnonymousCountingTokensV0() = default;
+};
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
+
+#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_ACT_V0_H_
diff --git a/act/act_v0/act_v0.proto b/act/act_v0/act_v0.proto
new file mode 100644
index 0000000..7cc2f23
--- /dev/null
+++ b/act/act_v0/act_v0.proto
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package private_join_and_compute.anonymous_counting_tokens;
+
+import "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.proto";
+import "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.proto";
+import "private_join_and_compute/crypto/proto/big_num.proto";
+import "private_join_and_compute/crypto/proto/camenisch_shoup.proto";
+import "private_join_and_compute/crypto/proto/pedersen.proto";
+
+option java_multiple_files = true;
+
+// The parameters for ACTv0.
+message SchemeParametersV0 {
+ // How many masking bits (more than the challenge bits) to add in the sigma
+ // protocols.
+ uint64 security_parameter = 1;
+ // How many bits the challenge has.
+ uint64 challenge_length_bits = 2;
+ // Length for the RSA modulus.
+ uint64 modulus_length_bits = 3;
+ // Camenisch Shoup Damgard-Jurik parameter (message space is n^s).
+ uint64 camenisch_shoup_s = 4;
+ // Camenisch-Shoup vector-encryption parameter.
+ uint64 vector_encryption_length = 5;
+ // Number of messages that can be committed in a single Pedersen commitment.
+ uint64 pedersen_batch_size = 6;
+ // Specification of the EC Group in which the DY-PRF and Boneh Boyen Oblivious
+ // Signature will be computed.
+ uint64 prf_ec_group = 7;
+ // Prefix to attach to any random oracle query (optional).
+ string random_oracle_prefix = 8;
+}
+
+// The Server's public parameters for the ACTv0 scheme.
+message ServerPublicParametersV0 {
+ reserved 1, 4, 5;
+
+ // Parameters to use for commitments.
+ private_join_and_compute.proto.PedersenParameters pedersen_parameters = 2;
+ // Camenisch Shoup public key (for the Boneh-Boyen Oblivious Signature).
+ private_join_and_compute.proto.CamenischShoupPublicKey camenisch_shoup_public_key = 3;
+ // Public key for the Boneh-Boyen oblivious signature.
+ private_join_and_compute.proto.BbObliviousSignaturePublicKey
+ bb_oblivious_signature_public_key = 6;
+
+ // Serialized ECPoint corresponding to the base g to use for the DY PRF and
+ // Boneh-Boyen oblivious signature.
+ bytes prf_base_g = 7;
+
+}
+
+// The Server's private parameters for the ACTv0 scheme.
+message ServerPrivateParametersV0 {
+ reserved 2, 3;
+ // Camenisch Shoup private key (for the Boneh-Boyen Oblivious Signature).
+ private_join_and_compute.proto.CamenischShoupPrivateKey camenisch_shoup_private_key = 1;
+ // Private key for the Boneh-Boyen Oblivious Signature.
+ private_join_and_compute.proto.BbObliviousSignaturePrivateKey
+ bb_oblivious_signature_private_key = 4;
+}
+
+// The Client's public parameters for the ACT scheme.
+message ClientPublicParametersV0 {
+ // Public key for the DY Verifiable Random Function.
+ private_join_and_compute.proto.DyVrfPublicKey dy_vrf_public_key = 1;
+ private_join_and_compute.proto.DyVrfGenerateKeysProof dy_vrf_generate_keys_proof = 2;
+}
+
+// The Client's private parameters for the ACT scheme.
+message ClientPrivateParametersV0 {
+ // Private key for the DY Verifiable Random Function.
+ private_join_and_compute.proto.DyVrfPrivateKey dy_vrf_private_key = 1;
+}
+
+// The Client's token request. Can correspond to a batch of tokens.
+message TokensRequestV0 {
+ message Part1 {
+ bytes commit_messages = 1;
+ bytes commit_client_nonces = 2;
+
+ // Proofs that the fingerprints on the messages were generated correctly.
+ // The fingerprints correspond to PRF evaluations on the messages.
+ private_join_and_compute.proto.DyVrfApplyProof fingerprints_proof = 3;
+ }
+
+ // Part1 of the tokens request feeds into the Random Oracle to produce the
+ // client-independent portion of the nonces. (The nonce per-token consists
+ // of a client-chosen part, and a client-independent/not-chosen part. The
+ // latter is generated by a random oracle call.)
+ Part1 part_1 = 1;
+
+ // Boneh-Boyen Oblivious Signature request and proof for nonces.
+ private_join_and_compute.proto.BbObliviousSignatureRequest bb_oblivious_signature_request = 2;
+ private_join_and_compute.proto.BbObliviousSignatureRequestProof
+ bb_oblivious_signature_request_proof = 3;
+}
+
+// Private state corresponding to the Client's token request, needed to recover
+// the tokens from the server's response.
+message TokensRequestPrivateStateV0 {
+ private_join_and_compute.proto.BbObliviousSignatureRequestPrivateState
+ bb_oblivious_signature_request_private_state = 1;
+ // Stores the nonces generated as part of the request (includes the
+ // client-generated and random-oracle-generated portions combined).
+ private_join_and_compute.proto.BigNumVector nonces = 2;
+}
+
+// The Server's response to a TokensRequest. Can correspond to a batch of
+// tokens.
+message TokensResponseV0 {
+ // Boneh-Boyen Oblivious signature response and proof.
+ private_join_and_compute.proto.BbObliviousSignatureResponse bb_oblivious_signature_response =
+ 1;
+ private_join_and_compute.proto.BbObliviousSignatureResponseProof
+ bb_oblivious_signature_response_proof = 2;
+}
+
+// An actual token recovered from the TokenResponse.
+message TokenV0 {
+ // The nonce is stored in the enclosing Token proto.
+
+ // Serialized Boneh-Boyen signature on the message and nonce. Serialized
+ // ECPoint.
+ bytes bb_signature = 1;
+}
diff --git a/act/act_v0/act_v0_test.cc b/act/act_v0/act_v0_test.cc
new file mode 100644
index 0000000..2c17471
--- /dev/null
+++ b/act/act_v0/act_v0_test.cc
@@ -0,0 +1,686 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "act/act_v0/act_v0.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "act/act.h"
+#include "act/act.pb.h"
+#include "act/act_v0/act_v0.pb.h"
+#include "act/act_v0/parameters.h"
+#include "private_join_and_compute/crypto/camenisch_shoup.h"
+#include "private_join_and_compute/crypto/context.h"
+#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.pb.h"
+#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.pb.h"
+#include "private_join_and_compute/crypto/ec_group.h"
+#include "private_join_and_compute/crypto/ec_point.h"
+#include "private_join_and_compute/crypto/pedersen_over_zn.h"
+#include "private_join_and_compute/crypto/proto/big_num.pb.h"
+#include "private_join_and_compute/crypto/proto/camenisch_shoup.pb.h"
+#include "private_join_and_compute/crypto/proto/pedersen.pb.h"
+#include "private_join_and_compute/util/status.inc"
+#include "private_join_and_compute/util/status_testing.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+namespace {
+
+using ::testing::HasSubstr;
+using testing::StatusIs;
+
+const int kTestCurveId = NID_X9_62_prime256v1;
+
+class AnonymousCountingTokensV0Test : public ::testing::Test {
+ protected:
+ static std::string GetRandomOraclePrefix() {
+ return "TestRandomOraclePrefix";
+ }
+
+ static SchemeParameters GetSchemeParameters() {
+ return ActV0TestSchemeParameters();
+ }
+
+ static void SetUpTestSuite() {
+ std::unique_ptr<AnonymousCountingTokens> act =
+ AnonymousCountingTokensV0::Create();
+ ServerParameters server_parameters_temp =
+ act->GenerateServerParameters(GetSchemeParameters()).value();
+ server_parameters_ = new ServerParameters(server_parameters_temp);
+ }
+
+ static void TearDownTestSuite() { delete server_parameters_; }
+
+ void SetUp() override {
+ anonymous_counting_tokens_ = AnonymousCountingTokensV0::Create();
+
+ ASSERT_OK_AND_ASSIGN(auto ec_group_do_not_use_later,
+ ECGroup::Create(kTestCurveId, &ctx_));
+ ec_group_ = std::make_unique<ECGroup>(std::move(ec_group_do_not_use_later));
+
+ // Deserialize components of the precomputed server parameters.
+ ASSERT_OK_AND_ASSIGN(PedersenOverZn::Parameters pedersen_parameters,
+ PedersenOverZn::ParseParametersProto(
+ &ctx_, server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .pedersen_parameters()));
+ ASSERT_OK_AND_ASSIGN(
+ pedersen_,
+ PedersenOverZn::Create(&ctx_, pedersen_parameters.gs,
+ pedersen_parameters.h, pedersen_parameters.n));
+
+ dy_prf_base_g_ = std::make_unique<ECPoint>(
+ ec_group_
+ ->CreateECPoint(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .prf_base_g())
+ .value());
+
+ cs_public_key_ = std::make_unique<CamenischShoupPublicKey>(
+ ParseCamenischShoupPublicKeyProto(
+ &ctx_, server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .camenisch_shoup_public_key())
+ .value());
+ cs_private_key_ = std::make_unique<CamenischShoupPrivateKey>(
+ ParseCamenischShoupPrivateKeyProto(
+ &ctx_, server_parameters_->private_parameters()
+ .server_private_parameters_v0()
+ .camenisch_shoup_private_key())
+ .value());
+
+ public_camenisch_shoup_ = std::make_unique<PublicCamenischShoup>(
+ &ctx_, cs_public_key_->n, cs_public_key_->s, cs_public_key_->g,
+ cs_public_key_->ys);
+ private_camenisch_shoup_ = std::make_unique<PrivateCamenischShoup>(
+ &ctx_, cs_public_key_->n, cs_public_key_->s, cs_public_key_->g,
+ cs_public_key_->ys, cs_private_key_->xs);
+
+ ASSERT_OK_AND_ASSIGN(
+ client_parameters_,
+ anonymous_counting_tokens_->GenerateClientParameters(
+ GetSchemeParameters(), server_parameters_->public_parameters()));
+ }
+
+ // Holds a transcript for an EndToEnd request.
+ struct Transcript {
+ std::vector<std::string> fingerprints;
+ TokensRequest tokens_request;
+ TokensRequestPrivateState tokens_request_private_state;
+ TokensResponse tokens_response;
+ std::vector<Token> tokens;
+ };
+
+ // Generates an end-to-end request transcript. Does not verify request or
+ // response proofs.
+ StatusOr<Transcript> GenerateTranscript(
+ absl::Span<const std::string> messages) {
+ Transcript transcript;
+ ASSIGN_OR_RETURN(
+ std::tie(transcript.fingerprints, transcript.tokens_request,
+ transcript.tokens_request_private_state),
+ anonymous_counting_tokens_->GenerateTokensRequest(
+ messages, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+
+ ASSIGN_OR_RETURN(transcript.tokens_response,
+ anonymous_counting_tokens_->GenerateTokensResponse(
+ transcript.tokens_request, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()));
+
+ ASSIGN_OR_RETURN(
+ transcript.tokens,
+ anonymous_counting_tokens_->RecoverTokens(
+ messages, transcript.tokens_request,
+ transcript.tokens_request_private_state, transcript.tokens_response,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+
+ return std::move(transcript);
+ }
+
+ // Server Parameters, generated once and available to be reused across tests
+ // to save expensive safe modulus computation.
+ static ServerParameters* server_parameters_;
+
+ // Instance of AnonymousCountingTokensV0.
+ std::unique_ptr<AnonymousCountingTokens> anonymous_counting_tokens_;
+
+ Context ctx_;
+ std::unique_ptr<ECGroup> ec_group_;
+
+ // Deserialized objects from the saved serialized parameters above.
+ std::unique_ptr<PedersenOverZn> pedersen_;
+ std::unique_ptr<ECPoint> dy_prf_base_g_;
+ std::unique_ptr<CamenischShoupPublicKey> cs_public_key_;
+ std::unique_ptr<CamenischShoupPrivateKey> cs_private_key_;
+ std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup_;
+ std::unique_ptr<PrivateCamenischShoup> private_camenisch_shoup_;
+
+ // Client parameters for AnonymousCountingTokensV0.
+ ClientParameters client_parameters_;
+};
+
+ServerParameters* AnonymousCountingTokensV0Test::server_parameters_ = nullptr;
+
+TEST_F(AnonymousCountingTokensV0Test, ServerParametersHasNonEmptyFields) {
+ // Expect all fields are nonempty.
+ EXPECT_TRUE(server_parameters_->has_public_parameters());
+ EXPECT_TRUE(server_parameters_->public_parameters()
+ .has_server_public_parameters_v0());
+ EXPECT_FALSE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .prf_base_g()
+ .empty());
+ EXPECT_TRUE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .has_pedersen_parameters());
+ EXPECT_TRUE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .has_camenisch_shoup_public_key());
+ EXPECT_TRUE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .has_bb_oblivious_signature_public_key());
+
+ EXPECT_TRUE(server_parameters_->has_private_parameters());
+ EXPECT_TRUE(server_parameters_->private_parameters()
+ .has_server_private_parameters_v0());
+ EXPECT_TRUE(server_parameters_->private_parameters()
+ .server_private_parameters_v0()
+ .has_camenisch_shoup_private_key());
+ EXPECT_TRUE(server_parameters_->private_parameters()
+ .server_private_parameters_v0()
+ .has_bb_oblivious_signature_private_key());
+}
+
+TEST_F(AnonymousCountingTokensV0Test, GeneratesDifferentServerParameters) {
+ ASSERT_OK_AND_ASSIGN(ServerParameters other_server_parameters,
+ anonymous_counting_tokens_->GenerateServerParameters(
+ GetSchemeParameters()));
+
+ // Expect all fields in the public parameters are different across the 2
+ // keys.
+ EXPECT_NE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .prf_base_g(),
+ other_server_parameters.public_parameters()
+ .server_public_parameters_v0()
+ .prf_base_g());
+ EXPECT_NE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .pedersen_parameters()
+ .gs()
+ .serialized_big_nums(0),
+ other_server_parameters.public_parameters()
+ .server_public_parameters_v0()
+ .pedersen_parameters()
+ .gs()
+ .serialized_big_nums(0));
+ EXPECT_NE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .camenisch_shoup_public_key()
+ .ys()
+ .serialized_big_nums(0),
+ other_server_parameters.public_parameters()
+ .server_public_parameters_v0()
+ .camenisch_shoup_public_key()
+ .ys()
+ .serialized_big_nums(0));
+ EXPECT_NE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .bb_oblivious_signature_public_key()
+ .encrypted_k(0)
+ .u(),
+ other_server_parameters.public_parameters()
+ .server_public_parameters_v0()
+ .bb_oblivious_signature_public_key()
+ .encrypted_k(0)
+ .u());
+ EXPECT_NE(server_parameters_->public_parameters()
+ .server_public_parameters_v0()
+ .bb_oblivious_signature_public_key()
+ .encrypted_y(0)
+ .u(),
+ other_server_parameters.public_parameters()
+ .server_public_parameters_v0()
+ .bb_oblivious_signature_public_key()
+ .encrypted_y(0)
+ .u());
+}
+
+TEST_F(AnonymousCountingTokensV0Test, ClientParametersHaveValidFields) {
+ EXPECT_TRUE(client_parameters_.has_public_parameters());
+ EXPECT_TRUE(
+ client_parameters_.public_parameters().has_client_public_parameters_v0());
+ EXPECT_TRUE(client_parameters_.public_parameters()
+ .client_public_parameters_v0()
+ .has_dy_vrf_public_key());
+
+ EXPECT_TRUE(client_parameters_.has_private_parameters());
+ EXPECT_TRUE(client_parameters_.private_parameters()
+ .has_client_private_parameters_v0());
+ EXPECT_TRUE(client_parameters_.private_parameters()
+ .client_private_parameters_v0()
+ .has_dy_vrf_private_key());
+}
+
+TEST_F(AnonymousCountingTokensV0Test, GeneratesDifferentClientParameters) {
+ ASSERT_OK_AND_ASSIGN(
+ ClientParameters other_client_parameters,
+ anonymous_counting_tokens_->GenerateClientParameters(
+ GetSchemeParameters(), server_parameters_->public_parameters()));
+
+ EXPECT_NE(client_parameters_.public_parameters()
+ .client_public_parameters_v0()
+ .dy_vrf_public_key()
+ .commit_prf_key(),
+ other_client_parameters.public_parameters()
+ .client_public_parameters_v0()
+ .dy_vrf_public_key()
+ .commit_prf_key());
+}
+
+TEST_F(AnonymousCountingTokensV0Test, ProofFromOtherClientParametersFails) {
+ ASSERT_OK_AND_ASSIGN(
+ ClientParameters other_client_parameters,
+ anonymous_counting_tokens_->GenerateClientParameters(
+ GetSchemeParameters(), server_parameters_->public_parameters()));
+
+ *client_parameters_.mutable_public_parameters()
+ ->mutable_client_public_parameters_v0()
+ ->mutable_dy_vrf_generate_keys_proof() =
+ other_client_parameters.public_parameters()
+ .client_public_parameters_v0()
+ .dy_vrf_generate_keys_proof();
+
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckClientParameters(
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, ClientParametersPassCheck) {
+ EXPECT_OK(anonymous_counting_tokens_->CheckClientParameters(
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, ClientParametersWithoutActV0FailCheck) {
+ client_parameters_.mutable_public_parameters()
+ ->clear_client_public_parameters_v0();
+
+ EXPECT_THAT(anonymous_counting_tokens_->CheckClientParameters(
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("CheckClientParameters")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, EmptyClientParametersProofFailsCheck) {
+ client_parameters_.mutable_public_parameters()
+ ->mutable_client_public_parameters_v0()
+ ->clear_dy_vrf_generate_keys_proof();
+
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckClientParameters(
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, GeneratesTokenRequest) {
+ EXPECT_OK(anonymous_counting_tokens_->GenerateTokensRequest(
+ {"message_0", "message_1", "message_2"}, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, FingerprintsMatchOnlyForEqualMessages) {
+ std::vector<std::string> fingerprints_1;
+ ASSERT_OK_AND_ASSIGN(
+ std::tie(fingerprints_1, std::ignore, std::ignore),
+ anonymous_counting_tokens_->GenerateTokensRequest(
+ {"message_0", "message_1", "message_2"}, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+
+ std::vector<std::string> fingerprints_2;
+ ASSERT_OK_AND_ASSIGN(
+ std::tie(fingerprints_2, std::ignore, std::ignore),
+ anonymous_counting_tokens_->GenerateTokensRequest(
+ {"message_2", "message_3", "message_4"}, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+
+ // Fingerprints should be equal only for "message_2".
+ EXPECT_EQ(fingerprints_1[2], fingerprints_2[0]);
+
+ for (size_t i = 1; i < fingerprints_1.size(); ++i) {
+ for (size_t j = 0; j < fingerprints_2.size(); ++j) {
+ if (!(i == 2 && j == 0)) {
+ EXPECT_NE(fingerprints_1[i], fingerprints_2[j]);
+ }
+ }
+ }
+}
+
+TEST_F(AnonymousCountingTokensV0Test, TokensRequestIsValid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+ EXPECT_OK(anonymous_counting_tokens_->CheckTokensRequest(
+ transcript.fingerprints, transcript.tokens_request, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ TokensRequestProofFailsWithDifferentFingerprints) {
+ std::vector<std::string> messages_1 = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages_1));
+ std::vector<std::string> messages_2 = {"message_4", "message_5", "message_6"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages_2));
+ // fingerprints from the second transcript should not allow the proof to
+ // verify.
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript_2.fingerprints, transcript_1.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ TokensRequestProofFailsWithWrongNumberOfFingerprints) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+ // delete one of the fingerprints.
+ transcript.fingerprints.pop_back();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript.fingerprints, transcript.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Number")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ BbSignatureRequestFromDifferentTranscriptIsInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages));
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages));
+ // Replace the bb oblivious signature request from the first transcript with
+ // that from the second.
+ *transcript_1.tokens_request.mutable_tokens_request_v0()
+ ->mutable_bb_oblivious_signature_request() =
+ transcript_2.tokens_request.tokens_request_v0()
+ .bb_oblivious_signature_request();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript_1.fingerprints, transcript_1.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ FingerprintsProofFromDifferentTranscriptIsInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages));
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages));
+ // Replace the fingerprints proof from the first transcript with
+ // that from the second.
+ *transcript_1.tokens_request.mutable_tokens_request_v0()
+ ->mutable_part_1()
+ ->mutable_fingerprints_proof() =
+ transcript_2.tokens_request.tokens_request_v0()
+ .part_1()
+ .fingerprints_proof();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript_1.fingerprints, transcript_1.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ BbSignatureRequestProofFromDifferentTranscriptIsInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages));
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages));
+ // Replace the bb signature request proof from the first transcript with
+ // that from the second.
+ *transcript_1.tokens_request.mutable_tokens_request_v0()
+ ->mutable_bb_oblivious_signature_request_proof() =
+ transcript_2.tokens_request.tokens_request_v0()
+ .bb_oblivious_signature_request_proof();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript_1.fingerprints, transcript_1.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, EmptyRequestIsInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+ transcript.tokens_request.clear_tokens_request_v0();
+ EXPECT_THAT(anonymous_counting_tokens_->CheckTokensRequest(
+ transcript.fingerprints, transcript.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("supplied parameters")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ RequestWithoutFingerprintsProofIsInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+ transcript.tokens_request.mutable_tokens_request_v0()
+ ->mutable_part_1()
+ ->clear_fingerprints_proof();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript.fingerprints, transcript.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Number")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ RequestWithoutBbSignatureRequestProofIsInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+ transcript.tokens_request.mutable_tokens_request_v0()
+ ->clear_bb_oblivious_signature_request_proof();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->CheckTokensRequest(
+ transcript.fingerprints, transcript.tokens_request,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("number")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, GeneratesTokenResponse) {
+ TokensRequest tokens_request;
+ ASSERT_OK_AND_ASSIGN(
+ std::tie(std::ignore, tokens_request, std::ignore),
+ anonymous_counting_tokens_->GenerateTokensRequest(
+ {"message_0", "message_1", "message_2"}, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+
+ EXPECT_OK(anonymous_counting_tokens_->GenerateTokensResponse(
+ tokens_request, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, TokensResponseIsValid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+ EXPECT_OK(anonymous_counting_tokens_->VerifyTokensResponse(
+ messages, transcript.tokens_request,
+ transcript.tokens_request_private_state, transcript.tokens_response,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ TokensResponseIsInvalidForProofFromAnotherTranscript) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages));
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages));
+ // Put the proof from the second transcript into the first
+ *transcript_1.tokens_response.mutable_tokens_response_v0()
+ ->mutable_bb_oblivious_signature_response_proof() =
+ transcript_2.tokens_response.tokens_response_v0()
+ .bb_oblivious_signature_response_proof();
+ EXPECT_THAT(
+ anonymous_counting_tokens_->VerifyTokensResponse(
+ messages, transcript_1.tokens_request,
+ transcript_1.tokens_request_private_state,
+ transcript_1.tokens_response, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test,
+ TokensResponseIsInvalidForRequestFromAnotherTranscript) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages));
+ ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages));
+ // The request and state from transcript_1 should be inconsistent with the
+ // response from transcript_2.
+ EXPECT_THAT(
+ anonymous_counting_tokens_->VerifyTokensResponse(
+ messages, transcript_1.tokens_request,
+ transcript_1.tokens_request_private_state,
+ transcript_2.tokens_response, GetSchemeParameters(),
+ client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, EmptyResponseShouldBeInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+
+ transcript.tokens_response.clear_tokens_response_v0();
+
+ EXPECT_THAT(
+ anonymous_counting_tokens_->VerifyTokensResponse(
+ messages, transcript.tokens_request,
+ transcript.tokens_request_private_state, transcript.tokens_response,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("parameters")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, EmptyResponseProofShouldBeInvalid) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+
+ transcript.tokens_response.mutable_tokens_response_v0()
+ ->clear_bb_oblivious_signature_response_proof();
+
+ EXPECT_THAT(
+ anonymous_counting_tokens_->VerifyTokensResponse(
+ messages, transcript.tokens_request,
+ transcript.tokens_request_private_state, transcript.tokens_response,
+ GetSchemeParameters(), client_parameters_.public_parameters(),
+ client_parameters_.private_parameters(),
+ server_parameters_->public_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("number")));
+}
+
+TEST_F(AnonymousCountingTokensV0Test, ProducesValidTokens) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+
+ EXPECT_EQ(messages.size(), transcript.tokens.size());
+
+ for (size_t i = 0; i < messages.size(); ++i) {
+ EXPECT_OK(anonymous_counting_tokens_->VerifyToken(
+ messages[i], transcript.tokens[i], GetSchemeParameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()));
+ }
+}
+
+TEST_F(AnonymousCountingTokensV0Test, TokensDoNotVerifyWithWrongMessages) {
+ std::vector<std::string> messages = {"message_1", "message_2", "message_3"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+
+ for (size_t i = 0; i < messages.size(); ++i) {
+ EXPECT_THAT(
+ anonymous_counting_tokens_->VerifyToken(
+ "wrong_message", transcript.tokens[i], GetSchemeParameters(),
+ server_parameters_->public_parameters(),
+ server_parameters_->private_parameters()),
+ StatusIs(absl::StatusCode::kInvalidArgument,
+ HasSubstr("fails to match the token")));
+ }
+}
+
+TEST_F(AnonymousCountingTokensV0Test, TokensHaveUniqueNonces) {
+ std::vector<std::string> messages = {"message_1", "message_2"};
+ ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages));
+
+ EXPECT_NE(transcript.tokens[0].nonce_bytes(),
+ transcript.tokens[1].nonce_bytes());
+}
+
+} // namespace
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
diff --git a/act/act_v0/parameters.cc b/act/act_v0/parameters.cc
new file mode 100644
index 0000000..c43f7e1
--- /dev/null
+++ b/act/act_v0/parameters.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "act/act_v0/parameters.h"
+
+#include <string>
+
+#include "absl/strings/str_cat.h"
+#include "act/act_v0/act_v0.pb.h"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+SchemeParameters ActV0TestSchemeParameters() {
+ int test_modulus_length = 1536;
+ int batch_size = 3;
+ std::string random_oracle_prefix = "ActV0TestSchemeParameters";
+
+ SchemeParameters scheme_parameters;
+ SchemeParametersV0* scheme_parameters_v0 =
+ scheme_parameters.mutable_scheme_parameters_v0();
+ scheme_parameters_v0->set_security_parameter(kDefaultSecurityParameter);
+ scheme_parameters_v0->set_challenge_length_bits(kDefaultChallengeLength);
+ scheme_parameters_v0->set_modulus_length_bits(test_modulus_length);
+ scheme_parameters_v0->set_camenisch_shoup_s(kDefaultCamenischShoupS);
+ scheme_parameters_v0->set_vector_encryption_length(batch_size);
+ scheme_parameters_v0->set_pedersen_batch_size(batch_size);
+ scheme_parameters_v0->set_prf_ec_group(kDefaultCurveId);
+ scheme_parameters_v0->set_random_oracle_prefix(random_oracle_prefix);
+
+ return scheme_parameters;
+}
+
+SchemeParameters ActV0Batch16SchemeParameters() {
+ int batch_size = 16;
+ std::string random_oracle_prefix = "ActV0Batch16SchemeParameters";
+
+ SchemeParameters scheme_parameters;
+ SchemeParametersV0* scheme_parameters_v0 =
+ scheme_parameters.mutable_scheme_parameters_v0();
+ scheme_parameters_v0->set_security_parameter(kDefaultSecurityParameter);
+ scheme_parameters_v0->set_challenge_length_bits(kDefaultChallengeLength);
+ scheme_parameters_v0->set_modulus_length_bits(kDefaultModulusLengthBits);
+ scheme_parameters_v0->set_camenisch_shoup_s(kDefaultCamenischShoupS);
+ scheme_parameters_v0->set_vector_encryption_length(batch_size);
+ scheme_parameters_v0->set_pedersen_batch_size(batch_size);
+ scheme_parameters_v0->set_prf_ec_group(kDefaultCurveId);
+ scheme_parameters_v0->set_random_oracle_prefix(random_oracle_prefix);
+
+ return scheme_parameters;
+}
+
+SchemeParameters ActV0Batch32SchemeParameters() {
+ int batch_size = 32;
+ std::string random_oracle_prefix = "ActV0Batch32SchemeParameters";
+
+ SchemeParameters scheme_parameters;
+ SchemeParametersV0* scheme_parameters_v0 =
+ scheme_parameters.mutable_scheme_parameters_v0();
+ scheme_parameters_v0->set_security_parameter(kDefaultSecurityParameter);
+ scheme_parameters_v0->set_challenge_length_bits(kDefaultChallengeLength);
+ scheme_parameters_v0->set_modulus_length_bits(kDefaultModulusLengthBits);
+ scheme_parameters_v0->set_camenisch_shoup_s(kDefaultCamenischShoupS);
+ scheme_parameters_v0->set_vector_encryption_length(batch_size);
+ scheme_parameters_v0->set_pedersen_batch_size(batch_size);
+ scheme_parameters_v0->set_prf_ec_group(kDefaultCurveId);
+ scheme_parameters_v0->set_random_oracle_prefix(random_oracle_prefix);
+
+ return scheme_parameters;
+}
+
+// Returns parameters supporting 32 messages in a batch, with CS vector
+// encryption length set to 2, and modulus length 2048.
+SchemeParameters
+ActV0SchemeParametersPedersen32Modulus2048CamenischShoupVector2() {
+ int pedersen_batch_size = 32;
+ int modulus_length = 2048;
+ int camensich_shoup_vector_encryption_length = 2;
+
+ return ActV0SchemeParameters(pedersen_batch_size, modulus_length,
+ camensich_shoup_vector_encryption_length);
+}
+
+// Returns custom parameters.
+SchemeParameters ActV0SchemeParameters(int pedersen_batch_size,
+ int modulus_length_bits,
+ int camenisch_shoup_vector_length) {
+ std::string random_oracle_prefix = absl::StrCat(
+ "ActV0SchemeParametersPedersenBatchSize", pedersen_batch_size,
+ "ModulusLengthBits", modulus_length_bits, "CamenischShoupVectorLength",
+ camenisch_shoup_vector_length);
+
+ SchemeParameters scheme_parameters;
+ SchemeParametersV0* scheme_parameters_v0 =
+ scheme_parameters.mutable_scheme_parameters_v0();
+ scheme_parameters_v0->set_security_parameter(kDefaultSecurityParameter);
+ scheme_parameters_v0->set_challenge_length_bits(kDefaultChallengeLength);
+ scheme_parameters_v0->set_modulus_length_bits(modulus_length_bits);
+ scheme_parameters_v0->set_camenisch_shoup_s(kDefaultCamenischShoupS);
+ scheme_parameters_v0->set_vector_encryption_length(
+ camenisch_shoup_vector_length);
+ scheme_parameters_v0->set_pedersen_batch_size(pedersen_batch_size);
+ scheme_parameters_v0->set_prf_ec_group(kDefaultCurveId);
+ scheme_parameters_v0->set_random_oracle_prefix(random_oracle_prefix);
+
+ return scheme_parameters;
+}
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
diff --git a/act/act_v0/parameters.h b/act/act_v0/parameters.h
new file mode 100644
index 0000000..21cff96
--- /dev/null
+++ b/act/act_v0/parameters.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_PARAMETERS_H_
+#define PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_PARAMETERS_H_
+
+#include <string>
+
+#include "act/act.pb.h"
+#include "private_join_and_compute/crypto/openssl.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+const int kDefaultSecurityParameter = 128;
+const int kDefaultChallengeLength = 128;
+const int kDefaultCamenischShoupS = 1;
+const int kDefaultCurveId = NID_X9_62_prime256v1;
+const int kDefaultModulusLengthBits = 3072;
+
+// Returns parameters appropriate only for testing (smaller modulus of 1536
+// bits, smaller batch size of 3).
+SchemeParameters ActV0TestSchemeParameters();
+
+// Returns parameters supporting 16 messages in a batch, with both Pedersen and
+// CS parameters set to 16, and modulus length 3072.
+SchemeParameters ActV0Batch16SchemeParameters();
+
+// Returns parameters supporting 32 messages in a batch, with both Pedersen and
+// CS parameters set to 32, and modulus length 3072.
+SchemeParameters ActV0Batch32SchemeParameters();
+
+// Returns parameters supporting 32 messages in a batch, with CS vector
+// encryption length set to 2, and modulus length 2048.
+//
+// These parameters are currently the best-optimized for performance.
+SchemeParameters
+ActV0SchemeParametersPedersen32Modulus2048CamenischShoupVector2();
+
+// Returns custom parameters.
+SchemeParameters ActV0SchemeParameters(int pedersen_batch_size,
+ int modulus_length_bits,
+ int camenisch_shoup_vector_length);
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
+
+#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_PARAMETERS_H_
diff --git a/act/act_v0/parameters_test.cc b/act/act_v0/parameters_test.cc
new file mode 100644
index 0000000..b275c00
--- /dev/null
+++ b/act/act_v0/parameters_test.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "act/act_v0/parameters.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "act/act.h"
+#include "act/act.pb.h"
+#include "act/act_v0/act_v0.h"
+#include "private_join_and_compute/util/status.inc"
+#include "private_join_and_compute/util/status_testing.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+namespace {
+
+Status EndToEndTest(SchemeParameters scheme_parameters, int num_messages) {
+ std::unique_ptr<AnonymousCountingTokens> act =
+ AnonymousCountingTokensV0::Create();
+
+ // Generate server parameters.
+ ASSIGN_OR_RETURN(ServerParameters server_parameters,
+ act->GenerateServerParameters(scheme_parameters));
+
+ // Generate client parameters and check them.
+ ASSIGN_OR_RETURN(
+ ClientParameters client_parameters,
+ act->GenerateClientParameters(scheme_parameters,
+ server_parameters.public_parameters()));
+
+ RETURN_IF_ERROR(act->CheckClientParameters(
+ scheme_parameters, client_parameters.public_parameters(),
+ server_parameters.public_parameters(),
+ server_parameters.private_parameters()));
+
+ // Generate messages.
+ std::vector<std::string> messages;
+ messages.reserve(num_messages);
+ for (int i = 0; i < num_messages; ++i) {
+ messages.push_back(absl::StrCat("message", i));
+ }
+
+ // Generate Tokens Request and check it.
+ std::vector<std::string> client_fingerprints;
+ TokensRequest tokens_request;
+ TokensRequestPrivateState tokens_request_private_state;
+ ASSIGN_OR_RETURN(
+ std::tie(client_fingerprints, tokens_request,
+ tokens_request_private_state),
+ act->GenerateTokensRequest(messages, scheme_parameters,
+ client_parameters.public_parameters(),
+ client_parameters.private_parameters(),
+ server_parameters.public_parameters()));
+ RETURN_IF_ERROR(act->CheckTokensRequest(
+ client_fingerprints, tokens_request, scheme_parameters,
+ client_parameters.public_parameters(),
+ server_parameters.public_parameters(),
+ server_parameters.private_parameters()));
+
+ // Generate Tokens Response and check it.
+ ASSIGN_OR_RETURN(
+ TokensResponse tokens_response,
+ act->GenerateTokensResponse(tokens_request, scheme_parameters,
+ client_parameters.public_parameters(),
+ server_parameters.public_parameters(),
+ server_parameters.private_parameters()));
+ RETURN_IF_ERROR(act->VerifyTokensResponse(
+ messages, tokens_request, tokens_request_private_state, tokens_response,
+ scheme_parameters, client_parameters.public_parameters(),
+ client_parameters.private_parameters(),
+ server_parameters.public_parameters()));
+
+ // Extract Tokens.
+ ASSIGN_OR_RETURN(
+ std::vector<Token> tokens,
+ act->RecoverTokens(messages, tokens_request, tokens_request_private_state,
+ tokens_response, scheme_parameters,
+ client_parameters.public_parameters(),
+ client_parameters.private_parameters(),
+ server_parameters.public_parameters()));
+
+ // Verify Tokens.
+ if (tokens.size() != num_messages) {
+ return absl::InvalidArgumentError("Wrong number of tokens produced");
+ }
+ for (int i = 0; i < num_messages; ++i) {
+ RETURN_IF_ERROR(act->VerifyToken(messages[i], tokens[i], scheme_parameters,
+ server_parameters.public_parameters(),
+ server_parameters.private_parameters()));
+ }
+ return absl::OkStatus();
+}
+
+TEST(ActV0ParametersTest, EndToEndWithTestParameters) {
+ EXPECT_OK(EndToEndTest(ActV0TestSchemeParameters(), 3));
+}
+
+TEST(ActV0ParametersTest, EndToEndWithBatch16Parameters) {
+ EXPECT_OK(EndToEndTest(ActV0Batch16SchemeParameters(), 16));
+}
+
+TEST(ActV0ParametersTest, EndToEndWithBatch32Parameters) {
+ EXPECT_OK(EndToEndTest(ActV0Batch32SchemeParameters(), 32));
+}
+
+TEST(ActV0ParametersTest, EndToEndWithBatch32Cs2Modulus2048Parameters) {
+ EXPECT_OK(EndToEndTest(
+ ActV0SchemeParametersPedersen32Modulus2048CamenischShoupVector2(), 32));
+}
+
+TEST(ActV0ParametersTest, EndToEndWithCustomParameters) {
+ int pedersen_batch_size = 32;
+ int modulus_length_bits = 1576;
+ int camenisch_shoup_vector_length = 2;
+ EXPECT_OK(EndToEndTest(
+ ActV0SchemeParameters(pedersen_batch_size, modulus_length_bits,
+ camenisch_shoup_vector_length),
+ 32));
+}
+
+// More extensive tests are in act_v0_test.cc. These tests simply ensure that
+// the parameters are functional.
+
+} // namespace
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
diff --git a/act/act_v0/testing/BUILD b/act/act_v0/testing/BUILD
new file mode 100644
index 0000000..ccb192b
--- /dev/null
+++ b/act/act_v0/testing/BUILD
@@ -0,0 +1,75 @@
+# Copyright 2023 Google LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+proto_library(
+ name = "transcript_proto",
+ srcs = ["transcript.proto"],
+ deps = ["//act:act_proto"],
+)
+
+cc_proto_library(
+ name = "transcript_cc_proto",
+ deps = [":transcript_proto"],
+)
+
+cc_binary(
+ name = "generate_transcript",
+ srcs = ["generate_transcript.cc"],
+ deps = [
+ ":transcript_cc_proto",
+ "//act",
+ "//act:act_cc_proto",
+ "//act/act_v0",
+ "//act/act_v0:act_v0_cc_proto",
+ "//act/act_v0:parameters",
+ "@com_google_absl//absl/flags:flag",
+ "@com_google_absl//absl/flags:parse",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/log:check",
+ "@com_google_absl//absl/strings",
+ "@private_join_and_compute//private_join_and_compute/util:proto_util",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ ],
+)
+
+filegroup(
+ name = "transcripts",
+ testonly = 1,
+ srcs = glob(["transcripts/*"]),
+)
+
+cc_test(
+ name = "golden_transcript_test",
+ srcs = ["golden_transcript_test.cc"],
+ data = [
+ ":transcripts",
+ ],
+ deps = [
+ ":transcript_cc_proto",
+ "//act",
+ "//act:act_cc_proto",
+ "//act/act_v0",
+ "//act/act_v0:act_v0_cc_proto",
+ "//act/act_v0:parameters",
+ "@com_github_google_googletest//:gtest_main",
+ "@private_join_and_compute//private_join_and_compute/util:proto_util",
+ "@private_join_and_compute//private_join_and_compute/util:status_includes",
+ "@private_join_and_compute//private_join_and_compute/util:status_testing_includes",
+ ],
+)
diff --git a/act/act_v0/testing/generate_transcript.cc b/act/act_v0/testing/generate_transcript.cc
new file mode 100644
index 0000000..915b960
--- /dev/null
+++ b/act/act_v0/testing/generate_transcript.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstddef>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "absl/log/check.h"
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "act/act.h"
+#include "act/act.pb.h"
+#include "act/act_v0/act_v0.h"
+#include "act/act_v0/act_v0.pb.h"
+#include "act/act_v0/parameters.h"
+#include "act/act_v0/testing/transcript.pb.h"
+#include "private_join_and_compute/util/proto_util.h"
+#include "private_join_and_compute/util/status.inc"
+
+ABSL_FLAG(std::string, transcript_path, "",
+ "Prefix of file to which the generated transcript will be "
+ "written/read from.");
+
+ABSL_FLAG(bool, verify, false,
+ "If true, will attempt to read the transcript from the specified "
+ "path to verify it.");
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+namespace {
+
+absl::Status GenerateTranscript(absl::string_view transcript_path) {
+ SchemeParameters scheme_parameters =
+ private_join_and_compute::anonymous_counting_tokens::
+ ActV0SchemeParametersPedersen32Modulus2048CamenischShoupVector2();
+ auto act = AnonymousCountingTokensV0::Create();
+
+ ASSIGN_OR_RETURN(ServerParameters server_parameters,
+ act->GenerateServerParameters(scheme_parameters));
+ ASSIGN_OR_RETURN(
+ ClientParameters client_parameters,
+ act->GenerateClientParameters(scheme_parameters,
+ server_parameters.public_parameters()));
+ std::vector<std::string> messages;
+ size_t num_messages =
+ scheme_parameters.scheme_parameters_v0().pedersen_batch_size();
+ messages.reserve(num_messages);
+ for (int i = 0; i < num_messages; ++i) {
+ messages.push_back(absl::StrCat("message", i));
+ }
+ std::vector<std::string> client_fingerprints;
+ TokensRequest tokens_request;
+ TokensRequestPrivateState tokens_request_private_state;
+ ASSIGN_OR_RETURN(
+ std::tie(client_fingerprints, tokens_request,
+ tokens_request_private_state),
+ act->GenerateTokensRequest(messages, scheme_parameters,
+ client_parameters.public_parameters(),
+ client_parameters.private_parameters(),
+ server_parameters.public_parameters()));
+
+ ASSIGN_OR_RETURN(
+ TokensResponse tokens_response,
+ act->GenerateTokensResponse(tokens_request, scheme_parameters,
+ client_parameters.public_parameters(),
+ server_parameters.public_parameters(),
+ server_parameters.private_parameters()));
+
+ ASSIGN_OR_RETURN(
+ std::vector<Token> tokens,
+ act->RecoverTokens(messages, tokens_request, tokens_request_private_state,
+ tokens_response, scheme_parameters,
+ client_parameters.public_parameters(),
+ client_parameters.private_parameters(),
+ server_parameters.public_parameters()));
+
+ Transcript transcript;
+ *transcript.mutable_scheme_parameters() = scheme_parameters;
+ *transcript.mutable_server_parameters() = server_parameters;
+ *transcript.mutable_client_parameters() = client_parameters;
+ *transcript.mutable_messages() = {messages.begin(), messages.end()};
+ *transcript.mutable_fingerprints() = {client_fingerprints.begin(),
+ client_fingerprints.end()};
+ *transcript.mutable_tokens_request() = tokens_request;
+ *transcript.mutable_tokens_request_private_state() =
+ tokens_request_private_state;
+ *transcript.mutable_tokens_response() = tokens_response;
+ *transcript.mutable_tokens() = {tokens.begin(), tokens.end()};
+
+ return ProtoUtils::WriteProtoToFile(transcript, transcript_path);
+}
+
+absl::Status VerifyTranscript(absl::string_view transcript_path) {
+ ASSIGN_OR_RETURN(Transcript transcript,
+ ProtoUtils::ReadProtoFromFile<Transcript>(transcript_path));
+
+ auto act = AnonymousCountingTokensV0::Create();
+
+ if (!transcript.has_scheme_parameters() ||
+ !transcript.scheme_parameters().has_scheme_parameters_v0() ||
+ transcript.scheme_parameters()
+ .scheme_parameters_v0()
+ .pedersen_batch_size() <= 0) {
+ return InvalidArgumentError(
+ "VerifyTranscript: transcript should have a SchemeParametersV0 with a "
+ "positive pedersen_batch_size.");
+ }
+
+ RETURN_IF_ERROR(act->CheckClientParameters(
+ transcript.scheme_parameters(),
+ transcript.client_parameters().public_parameters(),
+ transcript.server_parameters().public_parameters(),
+ transcript.server_parameters().private_parameters()));
+
+ std::vector<std::string> client_fingerprints(
+ transcript.fingerprints().begin(), transcript.fingerprints().end());
+ RETURN_IF_ERROR(act->CheckTokensRequest(
+ client_fingerprints, transcript.tokens_request(),
+ transcript.scheme_parameters(),
+ transcript.client_parameters().public_parameters(),
+ transcript.server_parameters().public_parameters(),
+ transcript.server_parameters().private_parameters()));
+
+ std::vector<std::string> messages(transcript.messages().begin(),
+ transcript.messages().end());
+ RETURN_IF_ERROR(act->VerifyTokensResponse(
+ messages, transcript.tokens_request(),
+ transcript.tokens_request_private_state(), transcript.tokens_response(),
+ transcript.scheme_parameters(),
+ transcript.client_parameters().public_parameters(),
+ transcript.client_parameters().private_parameters(),
+ transcript.server_parameters().public_parameters()));
+
+ return OkStatus();
+}
+
+} // namespace
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
+
+int main(int argc, char** argv) {
+ absl::ParseCommandLine(argc, argv);
+ std::string transcript_path = absl::GetFlag(FLAGS_transcript_path);
+
+ bool verify = absl::GetFlag(FLAGS_verify);
+ if (verify) {
+ CHECK_OK(
+ private_join_and_compute::anonymous_counting_tokens::VerifyTranscript(
+ transcript_path));
+ LOG(INFO) << "Successfully verified transcript.";
+ } else {
+ CHECK_OK(
+ private_join_and_compute::anonymous_counting_tokens::GenerateTranscript(
+ transcript_path));
+ LOG(INFO) << "Successfully generated transcript.";
+ }
+
+ return 0;
+}
diff --git a/act/act_v0/testing/golden_transcript_test.cc b/act/act_v0/testing/golden_transcript_test.cc
new file mode 100644
index 0000000..df4efb0
--- /dev/null
+++ b/act/act_v0/testing/golden_transcript_test.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "act/act.h"
+#include "act/act.pb.h"
+#include "act/act_v0/act_v0.h"
+#include "act/act_v0/testing/transcript.pb.h"
+#include "private_join_and_compute/util/proto_util.h"
+#include "private_join_and_compute/util/status_testing.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+namespace {
+
+const char kTranscriptPathBase[] = "act/act_v0/testing/transcripts/";
+
+TEST(GoldenTranscriptTest, TranscriptPassesValidityTests) {
+ auto act = AnonymousCountingTokensV0::Create();
+
+ std::vector<std::string> transcript_paths;
+
+ for (const auto& entry :
+ std::filesystem::directory_iterator(kTranscriptPathBase)) {
+ transcript_paths.push_back(std::string(entry.path()));
+ }
+
+ for (const auto& transcript_path : transcript_paths) {
+ ASSERT_OK_AND_ASSIGN(
+ Transcript transcript,
+ ProtoUtils::ReadProtoFromFile<Transcript>(transcript_path));
+
+ EXPECT_OK(act->CheckClientParameters(
+ transcript.scheme_parameters(),
+ transcript.client_parameters().public_parameters(),
+ transcript.server_parameters().public_parameters(),
+ transcript.server_parameters().private_parameters()));
+
+ std::vector<std::string> client_fingerprints(
+ transcript.fingerprints().begin(), transcript.fingerprints().end());
+ EXPECT_OK(act->CheckTokensRequest(
+ client_fingerprints, transcript.tokens_request(),
+ transcript.scheme_parameters(),
+ transcript.client_parameters().public_parameters(),
+ transcript.server_parameters().public_parameters(),
+ transcript.server_parameters().private_parameters()));
+
+ std::vector<std::string> messages(transcript.messages().begin(),
+ transcript.messages().end());
+ EXPECT_OK(act->VerifyTokensResponse(
+ messages, transcript.tokens_request(),
+ transcript.tokens_request_private_state(), transcript.tokens_response(),
+ transcript.scheme_parameters(),
+ transcript.client_parameters().public_parameters(),
+ transcript.client_parameters().private_parameters(),
+ transcript.server_parameters().public_parameters()));
+ }
+}
+
+} // namespace
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
diff --git a/act/act_v0/testing/transcript.proto b/act/act_v0/testing/transcript.proto
new file mode 100644
index 0000000..957335d
--- /dev/null
+++ b/act/act_v0/testing/transcript.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package private_join_and_compute.anonymous_counting_tokens;
+
+import "act/act.proto";
+
+option java_multiple_files = true;
+
+message Transcript {
+ SchemeParameters scheme_parameters = 1;
+ ServerParameters server_parameters = 2;
+ ClientParameters client_parameters = 3;
+ repeated string messages = 4;
+ repeated bytes fingerprints = 5;
+ TokensRequest tokens_request = 6;
+ TokensRequestPrivateState tokens_request_private_state = 7;
+ TokensResponse tokens_response = 8;
+ repeated Token tokens = 9;
+}
diff --git a/act/act_v0/testing/transcripts/golden_transcript_1_09122023 b/act/act_v0/testing/transcripts/golden_transcript_1_09122023
new file mode 100644
index 0000000..8787824
--- /dev/null
+++ b/act/act_v0/testing/transcripts/golden_transcript_1_09122023
Binary files differ
diff --git a/act/act_v0/testing/transcripts/golden_transcript_2_09122023 b/act/act_v0/testing/transcripts/golden_transcript_2_09122023
new file mode 100644
index 0000000..5e99965
--- /dev/null
+++ b/act/act_v0/testing/transcripts/golden_transcript_2_09122023
Binary files differ
diff --git a/act/act_v0/testing/transcripts/golden_transcript_3_09122023 b/act/act_v0/testing/transcripts/golden_transcript_3_09122023
new file mode 100644
index 0000000..fcd1e82
--- /dev/null
+++ b/act/act_v0/testing/transcripts/golden_transcript_3_09122023
Binary files differ
diff --git a/act/fake_act.cc b/act/fake_act.cc
new file mode 100644
index 0000000..48cf40f
--- /dev/null
+++ b/act/fake_act.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "act/fake_act.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "act/act.pb.h"
+#include "private_join_and_compute/crypto/context.h"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+// Returns an instance of FakeAnonymousCountingTokens.
+std::unique_ptr<AnonymousCountingTokens> FakeAnonymousCountingTokens::Create() {
+ return absl::WrapUnique<FakeAnonymousCountingTokens>(
+ new FakeAnonymousCountingTokens());
+}
+
+// Returns empty Server parameters.
+StatusOr<ServerParameters>
+FakeAnonymousCountingTokens::GenerateServerParameters(
+ const SchemeParameters& scheme_parameters) {
+ return ServerParameters::default_instance();
+}
+
+// Returns empty Client parameters.
+StatusOr<ClientParameters>
+FakeAnonymousCountingTokens::GenerateClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ return ClientParameters::default_instance();
+}
+
+// Always returns "Ok".
+Status FakeAnonymousCountingTokens::CheckClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ return absl::OkStatus();
+}
+
+// For this fake implementation, the client fingerprints are the same as the
+// messages (this is insecure).
+StatusOr<std::tuple<std::vector<std::string>, TokensRequest,
+ TokensRequestPrivateState>>
+FakeAnonymousCountingTokens::GenerateTokensRequest(
+ absl::Span<const std::string> messages,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ return std::make_tuple(
+ std::vector<std::string>(messages.begin(), messages.end()),
+ TokensRequest::default_instance(),
+ TokensRequestPrivateState::default_instance());
+}
+
+// Always returns "Ok".
+Status FakeAnonymousCountingTokens::CheckTokensRequest(
+ absl::Span<const std::string> client_fingerprints,
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ return absl::OkStatus();
+}
+
+// Returns an empty TokensResponse.
+StatusOr<TokensResponse> FakeAnonymousCountingTokens::GenerateTokensResponse(
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ return TokensResponse::default_instance();
+}
+
+// Always returns "Ok".
+Status FakeAnonymousCountingTokens::VerifyTokensResponse(
+ absl::Span<const std::string> messages, const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ return absl::OkStatus();
+}
+
+// Returns a set of tokens containing randomly generated "nonce" values, and
+// all other fields empty.
+StatusOr<std::vector<Token>> FakeAnonymousCountingTokens::RecoverTokens(
+ absl::Span<const std::string> messages, const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) {
+ Context context;
+ BigNum nonce_bound = context.One().Lshift(kFakeTokenNonceLengthBits);
+
+ std::vector<Token> result;
+ result.reserve(messages.size());
+ for (size_t i = 0; i < messages.size(); ++i) {
+ Token fake_token;
+ fake_token.set_nonce_bytes(
+ context.GenerateRandLessThan(nonce_bound).ToBytes());
+ result.push_back(fake_token);
+ }
+
+ return std::move(result);
+}
+
+// Always returns "Ok".
+Status FakeAnonymousCountingTokens::VerifyToken(
+ std::string m, const Token& token,
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) {
+ return absl::OkStatus();
+}
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
diff --git a/act/fake_act.h b/act/fake_act.h
new file mode 100644
index 0000000..c90aead
--- /dev/null
+++ b/act/fake_act.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_FAKE_ACT_H_
+#define PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_FAKE_ACT_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "act/act.h"
+#include "act/act.pb.h"
+#include "private_join_and_compute/util/status.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+
+// A fake, insecure implementation of Anonymous Counting Tokens for
+// testing/stubbing purposes only. This should NOT be used in production: it
+// doesn't have any of the desired security properties.
+class FakeAnonymousCountingTokens : public AnonymousCountingTokens {
+ public:
+ static const size_t kFakeTokenNonceLengthBits = 256;
+
+ // Returns an instance of FakeAnonymousCountingTokens.
+ static std::unique_ptr<AnonymousCountingTokens> Create();
+
+ ~FakeAnonymousCountingTokens() override = default;
+
+ // Returns empty Server parameters.
+ StatusOr<ServerParameters> GenerateServerParameters(
+ const SchemeParameters& scheme_parameters) override;
+
+ // Returns empty Client parameters.
+ StatusOr<ClientParameters> GenerateClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Always returns "Ok".
+ Status CheckClientParameters(
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ // For this fake implementation, the client fingerprints are the same as the
+ // messages (this is insecure).
+ StatusOr<std::tuple<std::vector<std::string>, TokensRequest,
+ TokensRequestPrivateState>>
+ GenerateTokensRequest(
+ absl::Span<const std::string> messages,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Always returns "Ok".
+ Status CheckTokensRequest(
+ absl::Span<const std::string> client_fingerprints,
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ // Returns an empty TokensResponse.
+ StatusOr<TokensResponse> GenerateTokensResponse(
+ const TokensRequest& tokens_request,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ // Always returns "Ok".
+ Status VerifyTokensResponse(
+ absl::Span<const std::string> messages,
+ const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Returns a set of tokens containing randomly generated "nonce" values, and
+ // all other fields empty.
+ StatusOr<std::vector<Token>> RecoverTokens(
+ absl::Span<const std::string> messages,
+ const TokensRequest& tokens_request,
+ const TokensRequestPrivateState& tokens_request_private_state,
+ const TokensResponse& tokens_response,
+ const SchemeParameters& scheme_parameters,
+ const ClientPublicParameters& client_public_parameters,
+ const ClientPrivateParameters& client_private_parameters,
+ const ServerPublicParameters& server_public_parameters) override;
+
+ // Always returns "Ok".
+ Status VerifyToken(
+ std::string m, const Token& token,
+ const SchemeParameters& scheme_parameters,
+ const ServerPublicParameters& server_public_parameters,
+ const ServerPrivateParameters& server_private_parameters) override;
+
+ protected:
+ FakeAnonymousCountingTokens() = default;
+};
+
+} // namespace anonymous_counting_tokens
+} // namespace private_join_and_compute
+
+#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_FAKE_ACT_H_
diff --git a/act/fake_act_test.cc b/act/fake_act_test.cc
new file mode 100644
index 0000000..cc478b2
--- /dev/null
+++ b/act/fake_act_test.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "act/fake_act.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "private_join_and_compute/util/status.inc"
+#include "private_join_and_compute/util/status_testing.inc"
+
+namespace private_join_and_compute {
+namespace anonymous_counting_tokens {
+namespace {
+
+TEST(FakeActTest, FingerprintsAreEqualExactlyWhenMessagesAreEqual) {
+ auto fake_act = FakeAnonymousCountingTokens::Create();
+
+ std::vector<std::string> messages_1 = {"m1", "m2"};
+ std::vector<std::string> fingerprints_1;
+
+ std::vector<std::string> messages_2 = {"m3", "m1"};
+ std::vector<std::string> fingerprints_2;
+
+ // Empty parameters for testing the fake.
+ SchemeParameters scheme_parameters;
+ ClientPublicParameters client_public_parameters;
+ ClientPrivateParameters client_private_parameters;
+ ServerPublicParameters server_public_parameters;
+
+ ASSERT_OK_AND_ASSIGN(
+ std::tie(fingerprints_1, std::ignore, std::ignore),
+ fake_act->GenerateTokensRequest(
+ messages_1, scheme_parameters, client_public_parameters,
+ client_private_parameters, server_public_parameters));
+ ASSERT_OK_AND_ASSIGN(
+ std::tie(fingerprints_2, std::ignore, std::ignore),
+ fake_act->GenerateTokensRequest(
+ messages_2, scheme_parameters, client_public_parameters,
+ client_private_parameters, server_public_parameters));
+
+ // Only the fingerprints for "m1" should be the same.
+ EXPECT_EQ(fingerprints_1[0], fingerprints_2[1]);
+ EXPECT_NE(fingerprints_1[0], fingerprints_2[0]);
+ EXPECT_NE(fingerprints_1[1], fingerprints_2[1]);
+ EXPECT_NE(fingerprints_1[1], fingerprints_2[0]);
+}
+
+TEST(FakeActTest, DifferentTokensAreNotEqual) {
+ auto fake_act = FakeAnonymousCountingTokens::Create();
+
+ std::vector<std::string> messages = {"m1", "m2"};
+ // Empty parameters and messages for testing the fake.
+ TokensRequest tokens_request;
+ TokensRequestPrivateState tokens_request_private_state;
+ TokensResponse tokens_response;
+ SchemeParameters scheme_parameters;
+ ClientPublicParameters client_public_parameters;
+ ClientPrivateParameters client_private_parameters;
+ ServerPublicParameters server_public_parameters;
+
+ ASSERT_OK_AND_ASSIGN(
+ std::vector<Token> tokens,
+ fake_act->RecoverTokens(
+ messages, tokens_request, tokens_request_private_state,
+ tokens_response, scheme_parameters, client_public_parameters,
+ client_private_parameters, server_public_parameters));
+
+ EXPECT_NE(tokens[0].SerializeAsString(), tokens[1].SerializeAsString());
+}
+
+} // namespace
+} // namespace anonymous_counting_tokens
+
+} // namespace private_join_and_compute