From 827d2aa796804f9ed28fc1c35ada56e0c62800be Mon Sep 17 00:00:00 2001 From: Karn Seth Date: Wed, 19 Apr 2023 11:12:58 -0400 Subject: initial commit --- .bazelrc | 12 + .gitignore | 4 + CONTRIBUTING.md | 28 ++ LICENSE | 202 +++++++++ README.md | 46 +++ WORKSPACE | 51 +++ act/BUILD | 65 +++ act/act.h | 119 ++++++ act/act.proto | 100 +++++ act/act_v0/BUILD | 114 ++++++ act/act_v0/act_v0.cc | 929 ++++++++++++++++++++++++++++++++++++++++++ act/act_v0/act_v0.h | 128 ++++++ act/act_v0/act_v0.proto | 141 +++++++ act/act_v0/act_v0_test.cc | 685 +++++++++++++++++++++++++++++++ act/act_v0/parameters.cc | 84 ++++ act/act_v0/parameters.h | 46 +++ act/act_v0/parameters_test.cc | 116 ++++++ act/fake_act.cc | 143 +++++++ act/fake_act.h | 124 ++++++ act/fake_act_test.cc | 93 +++++ 20 files changed, 3230 insertions(+) create mode 100644 .bazelrc create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 WORKSPACE create mode 100644 act/BUILD create mode 100644 act/act.h create mode 100644 act/act.proto create mode 100644 act/act_v0/BUILD create mode 100644 act/act_v0/act_v0.cc create mode 100644 act/act_v0/act_v0.h create mode 100644 act/act_v0/act_v0.proto create mode 100644 act/act_v0/act_v0_test.cc create mode 100644 act/act_v0/parameters.cc create mode 100644 act/act_v0/parameters.h create mode 100644 act/act_v0/parameters_test.cc create mode 100644 act/fake_act.cc create mode 100644 act/fake_act.h create mode 100644 act/fake_act_test.cc 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 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..747032c --- /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 = "6026c6522b0119e48b697492d184ee60be97071344c2788095fcb2a489ad905f", + strip_prefix = "private-join-and-compute-e028e59420a9c36328705ed5064408de03d229a8", + urls = ["https://github.com/google/private-join-and-compute/archive/e028e59420a9c36328705ed5064408de03d229a8.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 +#include +#include +#include + +#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 GenerateServerParameters( + const SchemeParameters& scheme_parameters) = 0; + + // Implementations should return a fresh set of Client parameters + // corresponding to these SchemeParameters and ServerPublicParameters. + virtual StatusOr 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, TokensRequest, + TokensRequestPrivateState>> + GenerateTokensRequest( + absl::Span 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 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 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 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> RecoverTokens( + absl::Span 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..f40f608 --- /dev/null +++ b/act/act.proto @@ -0,0 +1,100 @@ +/* + * 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 { + // Serialized BigNum corresponding to the nonce for this token. + string nonce = 1; + + oneof token_oneof { + TokenV0 token_v0 = 2; + } +} diff --git a/act/act_v0/BUILD b/act/act_v0/BUILD new file mode 100644 index 0000000..64dacac --- /dev/null +++ b/act/act_v0/BUILD @@ -0,0 +1,114 @@ +# 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: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:ec_point_cc_proto", + "@private_join_and_compute//private_join_and_compute/crypto/proto:pedersen_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", + "@com_google_absl//absl/strings", + "@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: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", + "@private_join_and_compute//private_join_and_compute/crypto:ec_util", + ], +) + +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..a0bab22 --- /dev/null +++ b/act/act_v0/act_v0.cc @@ -0,0 +1,929 @@ +/* + * 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 +#include +#include +#include +#include + +#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> 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> 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()); + + 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> 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( + &challenge_string); + auto challenge_cos = + std::make_unique( + challenge_sos.get()); + challenge_cos->SetSerializationDeterministic(true); + challenge_cos->WriteVarint64(scheme_parameters.ByteSizeLong()); + if (!scheme_parameters.SerializeToCodedStream(challenge_cos.get())) { + return absl::InternalError( + "GetNoncesForTokenRequest: Failed to serialize scheme_parameters."); + } + challenge_cos->WriteVarint64(server_public_parameters.ByteSizeLong()); + if (!server_public_parameters.SerializeToCodedStream(challenge_cos.get())) { + return absl::InternalError( + "GetNoncesForTokenRequest: Failed to serialize " + "server_public_parameters."); + } + challenge_cos->WriteVarint64(client_public_parameters.ByteSizeLong()); + if (!client_public_parameters.SerializeToCodedStream(challenge_cos.get())) { + return absl::InternalError( + "GetNoncesForTokenRequest: Failed to serialize " + "client_public_parameters."); + } + challenge_cos->WriteVarint64(tokens_request_part_1.ByteSizeLong()); + if (!tokens_request_part_1.SerializeToCodedStream(challenge_cos.get())) { + return absl::InternalError( + "GetNoncesForTokenRequest: Failed to serialize " + "client_public_parameters."); + } + 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 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 AnonymousCountingTokensV0::Create() { + return absl::WrapUnique( + 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 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{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{camenisch_shoup_key.xs}); + + auto public_camenisch_shoup = std::make_unique( + &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 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 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 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + + // Generate Client VRF object. + ASSIGN_OR_RETURN( + std::unique_ptr 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + + // Generate Client VRF object. + ASSIGN_OR_RETURN( + std::unique_ptr 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, TokensRequest, + TokensRequestPrivateState>> +AnonymousCountingTokensV0::GenerateTokensRequest( + absl::Span 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr 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 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 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 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 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 prf_evaluations, + dy_vrf->Apply(hashed_messages, + client_private_parameters_v0.dy_vrf_private_key())); + std::vector 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 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 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 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr 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 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 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 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 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 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + std::unique_ptr 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 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 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 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr 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 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 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> AnonymousCountingTokensV0::RecoverTokens( + absl::Span 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 pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr 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 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 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 nonces = + ParseBigNumVectorProto(&ctx, tokens_request_private_state_v0.nonces()); + + std::vector 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(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()); + + // Verify that reserializing the nonce comes out to the same value. + if (nonce.ToBytes() != token.nonce()) { + 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 +#include +#include +#include + +#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 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 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 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, TokensRequest, + TokensRequestPrivateState>> + GenerateTokensRequest( + absl::Span 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 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 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 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> RecoverTokens( + absl::Span 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..387a993 --- /dev/null +++ b/act/act_v0/act_v0_test.cc @@ -0,0 +1,685 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#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 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(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( + ec_group_ + ->CreateECPoint(server_parameters_->public_parameters() + .server_public_parameters_v0() + .prf_base_g()) + .value()); + + cs_public_key_ = std::make_unique( + ParseCamenischShoupPublicKeyProto( + &ctx_, server_parameters_->public_parameters() + .server_public_parameters_v0() + .camenisch_shoup_public_key()) + .value()); + cs_private_key_ = std::make_unique( + ParseCamenischShoupPrivateKeyProto( + &ctx_, server_parameters_->private_parameters() + .server_private_parameters_v0() + .camenisch_shoup_private_key()) + .value()); + + public_camenisch_shoup_ = std::make_unique( + &ctx_, cs_public_key_->n, cs_public_key_->s, cs_public_key_->g, + cs_public_key_->ys); + private_camenisch_shoup_ = std::make_unique( + &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 fingerprints; + TokensRequest tokens_request; + TokensRequestPrivateState tokens_request_private_state; + TokensResponse tokens_response; + std::vector tokens; + }; + + // Generates an end-to-end request transcript. Does not verify request or + // response proofs. + StatusOr GenerateTranscript( + absl::Span 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 anonymous_counting_tokens_; + + Context ctx_; + std::unique_ptr ec_group_; + + // Deserialized objects from the saved serialized parameters above. + std::unique_ptr pedersen_; + std::unique_ptr dy_prf_base_g_; + std::unique_ptr cs_public_key_; + std::unique_ptr cs_private_key_; + std::unique_ptr public_camenisch_shoup_; + std::unique_ptr 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 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 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 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 messages_1 = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages_1)); + std::vector 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 messages = {"message_1", "message_2"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + + EXPECT_NE(transcript.tokens[0].nonce(), transcript.tokens[1].nonce()); +} + +} // 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..8a2e6c5 --- /dev/null +++ b/act/act_v0/parameters.cc @@ -0,0 +1,84 @@ +/* + * 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 + +#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; +} + +} // 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..288f237 --- /dev/null +++ b/act/act_v0/parameters.h @@ -0,0 +1,46 @@ +/* + * 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 + +#include "act/act.pb.h" +#include "private_join_and_compute/crypto/ec_group.h" + +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. +SchemeParameters ActV0Batch16SchemeParameters(); + +// Returns parameters supporting 32 messages in a batch. +SchemeParameters ActV0Batch32SchemeParameters(); + +} // 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..04927e5 --- /dev/null +++ b/act/act_v0/parameters_test.cc @@ -0,0 +1,116 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#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 EndToEndTestNoVerification(SchemeParameters scheme_parameters, + int num_messages) { + std::unique_ptr act = + AnonymousCountingTokensV0::Create(); + + // Generate server parameters. + ASSIGN_OR_RETURN(ServerParameters server_parameters, + act->GenerateServerParameters(scheme_parameters)); + + // Generate client parameters. + ASSIGN_OR_RETURN( + ClientParameters client_parameters, + act->GenerateClientParameters(scheme_parameters, + server_parameters.public_parameters())); + + // Generate messages. + std::vector messages; + messages.reserve(num_messages); + for (int i = 0; i < num_messages; ++i) { + messages.push_back(absl::StrCat("message", i)); + } + + // Generate Tokens Request. + std::vector 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())); + + // Generate Tokens Response. + ASSIGN_OR_RETURN( + TokensResponse tokens_response, + act->GenerateTokensResponse(tokens_request, scheme_parameters, + client_parameters.public_parameters(), + server_parameters.public_parameters(), + server_parameters.private_parameters())); + + // Extract Tokens. + ASSIGN_OR_RETURN( + std::vector 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(EndToEndTestNoVerification(ActV0TestSchemeParameters(), 3)); +} + +TEST(ActV0ParametersTest, EndToEndWithBatch16Parameters) { + EXPECT_OK(EndToEndTestNoVerification(ActV0Batch16SchemeParameters(), 16)); +} + +TEST(ActV0ParametersTest, EndToEndWithBatch32Parameters) { + EXPECT_OK(EndToEndTestNoVerification(ActV0Batch32SchemeParameters(), 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/fake_act.cc b/act/fake_act.cc new file mode 100644 index 0000000..349a1cb --- /dev/null +++ b/act/fake_act.cc @@ -0,0 +1,143 @@ +/* + * 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 +#include +#include +#include +#include + +#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 FakeAnonymousCountingTokens::Create() { + return absl::WrapUnique( + new FakeAnonymousCountingTokens()); +} + +// Returns empty Server parameters. +StatusOr +FakeAnonymousCountingTokens::GenerateServerParameters( + const SchemeParameters& scheme_parameters) { + return ServerParameters::default_instance(); +} + +// Returns empty Client parameters. +StatusOr +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, TokensRequest, + TokensRequestPrivateState>> +FakeAnonymousCountingTokens::GenerateTokensRequest( + absl::Span 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(messages.begin(), messages.end()), + TokensRequest::default_instance(), + TokensRequestPrivateState::default_instance()); +} + +// Always returns "Ok". +Status FakeAnonymousCountingTokens::CheckTokensRequest( + absl::Span 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 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 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> FakeAnonymousCountingTokens::RecoverTokens( + absl::Span 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 result; + result.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + Token fake_token; + fake_token.set_nonce(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 +#include +#include +#include + +#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 Create(); + + ~FakeAnonymousCountingTokens() override = default; + + // Returns empty Server parameters. + StatusOr GenerateServerParameters( + const SchemeParameters& scheme_parameters) override; + + // Returns empty Client parameters. + StatusOr 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, TokensRequest, + TokensRequestPrivateState>> + GenerateTokensRequest( + absl::Span 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 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 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 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> RecoverTokens( + absl::Span 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 +#include + +#include +#include +#include +#include +#include + +#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 messages_1 = {"m1", "m2"}; + std::vector fingerprints_1; + + std::vector messages_2 = {"m3", "m1"}; + std::vector 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 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 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 -- cgit v1.2.3 From ad12d055c6eb4bee2565d9dc125d9ebe95db1dc4 Mon Sep 17 00:00:00 2001 From: Karn Seth Date: Thu, 27 Apr 2023 17:32:03 -0400 Subject: adds additional parameters, updates protos --- act/act.proto | 6 ++++-- act/act_v0/BUILD | 6 ------ act/act_v0/act_v0.cc | 8 +++++--- act/act_v0/act_v0_test.cc | 3 ++- act/act_v0/parameters.cc | 37 +++++++++++++++++++++++++++++++++++ act/act_v0/parameters.h | 18 +++++++++++++++-- act/act_v0/parameters_test.cc | 45 +++++++++++++++++++++++++++++++++++-------- act/fake_act.cc | 3 ++- 8 files changed, 103 insertions(+), 23 deletions(-) diff --git a/act/act.proto b/act/act.proto index f40f608..1c39823 100644 --- a/act/act.proto +++ b/act/act.proto @@ -91,10 +91,12 @@ message TokensResponse { // An actual token recovered from the TokenResponse. message Token { - // Serialized BigNum corresponding to the nonce for this token. - string nonce = 1; + 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 index 64dacac..073d2fa 100644 --- a/act/act_v0/BUILD +++ b/act/act_v0/BUILD @@ -50,10 +50,7 @@ cc_library( "@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: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:ec_point_cc_proto", - "@private_join_and_compute//private_join_and_compute/crypto/proto:pedersen_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", ], @@ -69,14 +66,11 @@ cc_test( "//act", "//act:act_cc_proto", "@com_github_google_googletest//:gtest_main", - "@com_google_absl//absl/strings", "@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:big_num_cc_proto", "@private_join_and_compute//private_join_and_compute/crypto/proto:camenisch_shoup_cc_proto", diff --git a/act/act_v0/act_v0.cc b/act/act_v0/act_v0.cc index a0bab22..01921a5 100644 --- a/act/act_v0/act_v0.cc +++ b/act/act_v0/act_v0.cc @@ -77,6 +77,8 @@ StatusOr> CreateDyVrf( 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); @@ -846,7 +848,7 @@ StatusOr> AnonymousCountingTokensV0::RecoverTokens( for (size_t i = 0; i < messages.size(); ++i) { Token token; TokenV0* token_v0 = token.mutable_token_v0(); - token.set_nonce(nonces[i].ToBytes()); + token.set_nonce_bytes(nonces[i].ToBytes()); ASSIGN_OR_RETURN(*token_v0->mutable_bb_signature(), signatures[i].ToBytesCompressed()); tokens.push_back(std::move(token)); @@ -890,10 +892,10 @@ Status AnonymousCountingTokensV0::VerifyToken( 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()); + BigNum nonce = ctx.CreateBigNum(token.nonce_bytes()); // Verify that reserializing the nonce comes out to the same value. - if (nonce.ToBytes() != token.nonce()) { + if (nonce.ToBytes() != token.nonce_bytes()) { return absl::InvalidArgumentError( "AnonymousCountingTokensV0::VerifyToken: nonce comes out to different " "value when serialized and deserialized."); diff --git a/act/act_v0/act_v0_test.cc b/act/act_v0/act_v0_test.cc index 387a993..2c17471 100644 --- a/act/act_v0/act_v0_test.cc +++ b/act/act_v0/act_v0_test.cc @@ -677,7 +677,8 @@ TEST_F(AnonymousCountingTokensV0Test, TokensHaveUniqueNonces) { std::vector messages = {"message_1", "message_2"}; ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); - EXPECT_NE(transcript.tokens[0].nonce(), transcript.tokens[1].nonce()); + EXPECT_NE(transcript.tokens[0].nonce_bytes(), + transcript.tokens[1].nonce_bytes()); } } // namespace diff --git a/act/act_v0/parameters.cc b/act/act_v0/parameters.cc index 8a2e6c5..e585177 100644 --- a/act/act_v0/parameters.cc +++ b/act/act_v0/parameters.cc @@ -80,5 +80,42 @@ SchemeParameters ActV0Batch32SchemeParameters() { 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 index 288f237..5583adc 100644 --- a/act/act_v0/parameters.h +++ b/act/act_v0/parameters.h @@ -34,12 +34,26 @@ const int kDefaultModulusLengthBits = 3072; // bits, smaller batch size of 3). SchemeParameters ActV0TestSchemeParameters(); -// Returns parameters supporting 16 messages in a batch. +// 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. +// 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 diff --git a/act/act_v0/parameters_test.cc b/act/act_v0/parameters_test.cc index 04927e5..b275c00 100644 --- a/act/act_v0/parameters_test.cc +++ b/act/act_v0/parameters_test.cc @@ -33,8 +33,7 @@ namespace private_join_and_compute { namespace anonymous_counting_tokens { namespace { -Status EndToEndTestNoVerification(SchemeParameters scheme_parameters, - int num_messages) { +Status EndToEndTest(SchemeParameters scheme_parameters, int num_messages) { std::unique_ptr act = AnonymousCountingTokensV0::Create(); @@ -42,12 +41,17 @@ Status EndToEndTestNoVerification(SchemeParameters scheme_parameters, ASSIGN_OR_RETURN(ServerParameters server_parameters, act->GenerateServerParameters(scheme_parameters)); - // Generate client 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 messages; messages.reserve(num_messages); @@ -55,7 +59,7 @@ Status EndToEndTestNoVerification(SchemeParameters scheme_parameters, messages.push_back(absl::StrCat("message", i)); } - // Generate Tokens Request. + // Generate Tokens Request and check it. std::vector client_fingerprints; TokensRequest tokens_request; TokensRequestPrivateState tokens_request_private_state; @@ -66,14 +70,24 @@ Status EndToEndTestNoVerification(SchemeParameters 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. + // 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( @@ -97,15 +111,30 @@ Status EndToEndTestNoVerification(SchemeParameters scheme_parameters, } TEST(ActV0ParametersTest, EndToEndWithTestParameters) { - EXPECT_OK(EndToEndTestNoVerification(ActV0TestSchemeParameters(), 3)); + EXPECT_OK(EndToEndTest(ActV0TestSchemeParameters(), 3)); } TEST(ActV0ParametersTest, EndToEndWithBatch16Parameters) { - EXPECT_OK(EndToEndTestNoVerification(ActV0Batch16SchemeParameters(), 16)); + EXPECT_OK(EndToEndTest(ActV0Batch16SchemeParameters(), 16)); } TEST(ActV0ParametersTest, EndToEndWithBatch32Parameters) { - EXPECT_OK(EndToEndTestNoVerification(ActV0Batch32SchemeParameters(), 32)); + 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 diff --git a/act/fake_act.cc b/act/fake_act.cc index 349a1cb..48cf40f 100644 --- a/act/fake_act.cc +++ b/act/fake_act.cc @@ -123,7 +123,8 @@ StatusOr> FakeAnonymousCountingTokens::RecoverTokens( result.reserve(messages.size()); for (size_t i = 0; i < messages.size(); ++i) { Token fake_token; - fake_token.set_nonce(context.GenerateRandLessThan(nonce_bound).ToBytes()); + fake_token.set_nonce_bytes( + context.GenerateRandLessThan(nonce_bound).ToBytes()); result.push_back(fake_token); } -- cgit v1.2.3 From 6263f3d94d1278ebea947fd855c3ccfb48b371f1 Mon Sep 17 00:00:00 2001 From: Karn Seth Date: Thu, 4 May 2023 14:46:34 -0400 Subject: fixes deps --- act/act_v0/BUILD | 3 ++- act/act_v0/parameters.cc | 1 + act/act_v0/parameters.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/act/act_v0/BUILD b/act/act_v0/BUILD index 073d2fa..1414b83 100644 --- a/act/act_v0/BUILD +++ b/act/act_v0/BUILD @@ -87,7 +87,8 @@ cc_library( deps = [ ":act_v0_cc_proto", "//act:act_cc_proto", - "@private_join_and_compute//private_join_and_compute/crypto:ec_util", + "@com_google_absl//absl/strings", + "@private_join_and_compute//private_join_and_compute/crypto:openssl_includes", ], ) diff --git a/act/act_v0/parameters.cc b/act/act_v0/parameters.cc index e585177..c43f7e1 100644 --- a/act/act_v0/parameters.cc +++ b/act/act_v0/parameters.cc @@ -17,6 +17,7 @@ #include +#include "absl/strings/str_cat.h" #include "act/act_v0/act_v0.pb.h" namespace private_join_and_compute { diff --git a/act/act_v0/parameters.h b/act/act_v0/parameters.h index 5583adc..21cff96 100644 --- a/act/act_v0/parameters.h +++ b/act/act_v0/parameters.h @@ -19,7 +19,7 @@ #include #include "act/act.pb.h" -#include "private_join_and_compute/crypto/ec_group.h" +#include "private_join_and_compute/crypto/openssl.inc" namespace private_join_and_compute { namespace anonymous_counting_tokens { -- cgit v1.2.3 From 3dea51443eaabde75c00cd325bc31e3b848a767f Mon Sep 17 00:00:00 2001 From: Karn Seth Date: Mon, 25 Sep 2023 16:18:43 -0400 Subject: adds golden transcripts --- WORKSPACE | 6 +- act/act_v0/act_v0.cc | 25 +-- act/act_v0/testing/BUILD | 75 +++++++++ act/act_v0/testing/generate_transcript.cc | 175 +++++++++++++++++++++ act/act_v0/testing/golden_transcript_test.cc | 81 ++++++++++ act/act_v0/testing/transcript.proto | 34 ++++ .../transcripts/golden_transcript_1_09122023 | Bin 0 -> 103186 bytes .../transcripts/golden_transcript_2_09122023 | Bin 0 -> 103202 bytes .../transcripts/golden_transcript_3_09122023 | Bin 0 -> 103197 bytes 9 files changed, 374 insertions(+), 22 deletions(-) create mode 100644 act/act_v0/testing/BUILD create mode 100644 act/act_v0/testing/generate_transcript.cc create mode 100644 act/act_v0/testing/golden_transcript_test.cc create mode 100644 act/act_v0/testing/transcript.proto create mode 100644 act/act_v0/testing/transcripts/golden_transcript_1_09122023 create mode 100644 act/act_v0/testing/transcripts/golden_transcript_2_09122023 create mode 100644 act/act_v0/testing/transcripts/golden_transcript_3_09122023 diff --git a/WORKSPACE b/WORKSPACE index 747032c..d303063 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -20,9 +20,9 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") # Private Join and Compute http_archive( name = "private_join_and_compute", - sha256 = "6026c6522b0119e48b697492d184ee60be97071344c2788095fcb2a489ad905f", - strip_prefix = "private-join-and-compute-e028e59420a9c36328705ed5064408de03d229a8", - urls = ["https://github.com/google/private-join-and-compute/archive/e028e59420a9c36328705ed5064408de03d229a8.zip"], + 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. diff --git a/act/act_v0/act_v0.cc b/act/act_v0/act_v0.cc index 01921a5..228f0db 100644 --- a/act/act_v0/act_v0.cc +++ b/act/act_v0/act_v0.cc @@ -110,28 +110,15 @@ StatusOr> GetNoncesForTokenRequest( challenge_sos.get()); challenge_cos->SetSerializationDeterministic(true); challenge_cos->WriteVarint64(scheme_parameters.ByteSizeLong()); - if (!scheme_parameters.SerializeToCodedStream(challenge_cos.get())) { - return absl::InternalError( - "GetNoncesForTokenRequest: Failed to serialize scheme_parameters."); - } + challenge_cos->WriteString(SerializeAsStringInOrder(scheme_parameters)); challenge_cos->WriteVarint64(server_public_parameters.ByteSizeLong()); - if (!server_public_parameters.SerializeToCodedStream(challenge_cos.get())) { - return absl::InternalError( - "GetNoncesForTokenRequest: Failed to serialize " - "server_public_parameters."); - } + challenge_cos->WriteString( + SerializeAsStringInOrder(server_public_parameters)); challenge_cos->WriteVarint64(client_public_parameters.ByteSizeLong()); - if (!client_public_parameters.SerializeToCodedStream(challenge_cos.get())) { - return absl::InternalError( - "GetNoncesForTokenRequest: Failed to serialize " - "client_public_parameters."); - } + challenge_cos->WriteString( + SerializeAsStringInOrder(client_public_parameters)); challenge_cos->WriteVarint64(tokens_request_part_1.ByteSizeLong()); - if (!tokens_request_part_1.SerializeToCodedStream(challenge_cos.get())) { - return absl::InternalError( - "GetNoncesForTokenRequest: Failed to serialize " - "client_public_parameters."); - } + 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. 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 +#include +#include +#include + +#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 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 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 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_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 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 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 +#include + +#include +#include +#include +#include + +#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 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_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 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 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 Binary files /dev/null and b/act/act_v0/testing/transcripts/golden_transcript_1_09122023 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 Binary files /dev/null and b/act/act_v0/testing/transcripts/golden_transcript_2_09122023 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 Binary files /dev/null and b/act/act_v0/testing/transcripts/golden_transcript_3_09122023 differ -- cgit v1.2.3