diff options
author | Kevin Graney <kmg@google.com> | 2023-05-30 10:21:08 -0400 |
---|---|---|
committer | Kevin Graney <kmg@google.com> | 2023-05-30 10:23:04 -0400 |
commit | e1220b40f5e6e540b7d695b7f9e6f2c394553288 (patch) | |
tree | ff80f136457a035fbc9a3df80e00eafcc9ed7b86 /act | |
parent | ac7650269463beb0cfb4c26ebf79344f210d5da4 (diff) | |
download | anonymous-counting-tokens-e1220b40f5e6e540b7d695b7f9e6f2c394553288.tar.gz |
Import content from http://ag/23451840upstream-import
Squashed commit of the following:
commit 00c2b21c8fa9004aa697c01961c46fe0329cffe8
Author: Kevin Graney <kmg@google.com>
Date: Tue May 9 14:39:03 2023 -0400
Add TEST_MAPPING
commit ed8ac8d9261350a5387039f36027584491e04745
Author: Kevin Graney <kmg@google.com>
Date: Tue May 9 10:48:45 2023 -0400
Depend on private-join-and-compute abseil
commit 1907d032ccf04e256a3900d0db55d56e861f0ba6
Author: Kevin Graney <kmg@google.com>
Date: Thu May 4 15:48:54 2023 -0400
Fix METADATA filename typo
commit b77a752a66406082974f12ee3455742337e02f85
Author: Kevin Graney <kmg@google.com>
Date: Thu May 4 10:24:04 2023 -0400
Add build rules
commit 132bec1222d439625cf8ae692c03ce518945cba2
Author: Kevin Graney <kmg@google.com>
Date: Thu May 4 10:24:26 2023 -0400
Add abseil
commit 410c261bf1629f65651d2b7bc53a188e5dba2211
Author: Kevin Graney <kmg@google.com>
Date: Tue Apr 25 13:27:39 2023 -0400
Add metadata files
commit 827d2aa796804f9ed28fc1c35ada56e0c62800be
Author: Karn Seth <karn@google.com>
Date: Wed Apr 19 11:12:58 2023 -0400
initial commit
Change-Id: I8f92eeb3857f7e65fdf495e76a4967ae69ed5878
Diffstat (limited to 'act')
-rw-r--r-- | act/BUILD | 65 | ||||
-rw-r--r-- | act/act.h | 119 | ||||
-rw-r--r-- | act/act.proto | 100 | ||||
-rw-r--r-- | act/act_v0/BUILD | 114 | ||||
-rw-r--r-- | act/act_v0/act_v0.cc | 930 | ||||
-rw-r--r-- | act/act_v0/act_v0.h | 128 | ||||
-rw-r--r-- | act/act_v0/act_v0.proto | 141 | ||||
-rw-r--r-- | act/act_v0/act_v0_test.cc | 685 | ||||
-rw-r--r-- | act/act_v0/parameters.cc | 84 | ||||
-rw-r--r-- | act/act_v0/parameters.h | 46 | ||||
-rw-r--r-- | act/act_v0/parameters_test.cc | 116 | ||||
-rw-r--r-- | act/fake_act.cc | 143 | ||||
-rw-r--r-- | act/fake_act.h | 124 | ||||
-rw-r--r-- | act/fake_act_test.cc | 93 |
14 files changed, 2888 insertions, 0 deletions
diff --git a/act/BUILD b/act/BUILD new file mode 100644 index 0000000..b93857a --- /dev/null +++ b/act/BUILD @@ -0,0 +1,65 @@ +# Copyright 2023 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Contains implementations for Anonymous Counting Tokens. + +load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "act_proto", + srcs = ["act.proto"], + deps = ["//act/act_v0:act_v0_proto"], +) + +cc_proto_library( + name = "act_cc_proto", + deps = [":act_proto"], +) + +cc_library( + name = "act", + hdrs = ["act.h"], + deps = [ + ":act_cc_proto", + "@private_join_and_compute//private_join_and_compute/util:status_includes", + ], +) + +cc_library( + name = "fake_act", + srcs = ["fake_act.cc"], + hdrs = ["fake_act.h"], + deps = [ + ":act", + ":act_cc_proto", + "@private_join_and_compute//private_join_and_compute/crypto:bn_util", + "@private_join_and_compute//private_join_and_compute/util:status_includes", + ], +) + +cc_test( + name = "fake_act_test", + srcs = [ + "fake_act_test.cc", + ], + deps = [ + ":fake_act", + "@com_github_google_googletest//:gtest_main", + "@private_join_and_compute//private_join_and_compute/util:status_includes", + "@private_join_and_compute//private_join_and_compute/util:status_testing_includes", + ], +) diff --git a/act/act.h b/act/act.h new file mode 100644 index 0000000..e426d21 --- /dev/null +++ b/act/act.h @@ -0,0 +1,119 @@ +/* + * Copyright 2023 Google LLC. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_H_ +#define PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_H_ + +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "act/act.pb.h" +#include "private_join_and_compute/util/status.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +// Abstract class for methods related to Anonymous Counting Tokens. +class AnonymousCountingTokens { + public: + virtual ~AnonymousCountingTokens() = default; + + // Implementations should return a fresh set of Server parameters + // corresponding to these SchemeParameters. + virtual StatusOr<ServerParameters> GenerateServerParameters( + const SchemeParameters& scheme_parameters) = 0; + + // Implementations should return a fresh set of Client parameters + // corresponding to these SchemeParameters and ServerPublicParameters. + virtual StatusOr<ClientParameters> GenerateClientParameters( + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters) = 0; + + // Implementations should verify the consistency of these + // ClientPublicParameters with the Server and scheme parameters. + virtual Status CheckClientParameters( + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) = 0; + + // Implementations should generate a tuple of client_fingerprints, + // TokensRequest and TokensRequestPrivateState for the given set of messages. + virtual StatusOr<std::tuple<std::vector<std::string>, TokensRequest, + TokensRequestPrivateState>> + GenerateTokensRequest( + absl::Span<const std::string> messages, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) = 0; + + // Implementations should return OkStatus on a valid request. + virtual Status CheckTokensRequest( + absl::Span<const std::string> client_fingerprints, + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) = 0; + + // Implementations should return the TokensResponse. + virtual StatusOr<TokensResponse> GenerateTokensResponse( + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) = 0; + + // Implementations should return OkStatus on a valid response. + virtual Status VerifyTokensResponse( + absl::Span<const std::string> messages, + const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) = 0; + + // Implementations should return a vector of tokens corresponding to the + // supplied messages. + virtual StatusOr<std::vector<Token>> RecoverTokens( + absl::Span<const std::string> messages, + const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) = 0; + + // Implementations should return OkStatus on valid tokens. + virtual Status VerifyToken( + std::string m, const Token& token, + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) = 0; + + protected: + AnonymousCountingTokens() = default; +}; + +} // namespace anonymous_counting_tokens +} // namespace private_join_and_compute + +#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_H_ diff --git a/act/act.proto b/act/act.proto new file mode 100644 index 0000000..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..74dc1dd --- /dev/null +++ b/act/act_v0/act_v0.cc @@ -0,0 +1,930 @@ +/* + * Copyright 2023 Google LLC. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "act/act_v0/act_v0.h" + +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> +#include "act/act.pb.h" +#include "act/act_v0/act_v0.pb.h" +#include "private_join_and_compute/crypto/big_num.h" +#include "private_join_and_compute/crypto/camenisch_shoup.h" +#include "private_join_and_compute/crypto/context.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.pb.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.pb.h" +#include "private_join_and_compute/crypto/ec_group.h" +#include "private_join_and_compute/crypto/ec_point.h" +#include "private_join_and_compute/crypto/pedersen_over_zn.h" +#include "private_join_and_compute/crypto/proto/ec_point.pb.h" +#include "private_join_and_compute/crypto/proto/proto_util.h" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +namespace { + +StatusOr<std::unique_ptr<BbObliviousSignature>> CreateBbObliviousSignature( + const SchemeParametersV0& scheme_parameters_v0, + const ServerPublicParametersV0& server_public_parameters_v0, Context* ctx, + ECGroup* ec_group, PedersenOverZn* pedersen, + PublicCamenischShoup* public_camenisch_shoup) { + proto::BbObliviousSignatureParameters bb_oblivious_signature_parameters; + bb_oblivious_signature_parameters.set_challenge_length_bits( + scheme_parameters_v0.challenge_length_bits()); + bb_oblivious_signature_parameters.set_security_parameter( + scheme_parameters_v0.security_parameter()); + bb_oblivious_signature_parameters.set_random_oracle_prefix( + scheme_parameters_v0.random_oracle_prefix()); + bb_oblivious_signature_parameters.set_base_g( + server_public_parameters_v0.prf_base_g()); + *bb_oblivious_signature_parameters.mutable_pedersen_parameters() = + server_public_parameters_v0.pedersen_parameters(); + *bb_oblivious_signature_parameters.mutable_camenisch_shoup_public_key() = + server_public_parameters_v0.camenisch_shoup_public_key(); + + return BbObliviousSignature::Create( + std::move(bb_oblivious_signature_parameters), ctx, ec_group, + public_camenisch_shoup, pedersen); +} + +StatusOr<std::unique_ptr<DyVerifiableRandomFunction>> CreateDyVrf( + const SchemeParametersV0& scheme_parameters_v0, + const ServerPublicParametersV0& server_public_parameters_v0, Context* ctx, + ECGroup* ec_group, PedersenOverZn* pedersen) { + proto::DyVrfParameters dy_vrf_parameters; + dy_vrf_parameters.set_challenge_length_bits( + scheme_parameters_v0.challenge_length_bits()); + dy_vrf_parameters.set_security_parameter( + scheme_parameters_v0.security_parameter()); + dy_vrf_parameters.set_random_oracle_prefix( + scheme_parameters_v0.random_oracle_prefix()); + dy_vrf_parameters.set_dy_prf_base_g(server_public_parameters_v0.prf_base_g()); + + return DyVerifiableRandomFunction::Create(std::move(dy_vrf_parameters), ctx, + ec_group, pedersen); +} + +// Used to generate the client-independent portion of the nonce. A different +// nonce is chosen for each element in the batched token request. +StatusOr<std::vector<BigNum>> GetNoncesForTokenRequest( + Context* ctx, const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ClientPublicParameters& client_public_parameters, + const TokensRequestV0::Part1& tokens_request_part_1, + uint64_t num_messages) { + // Parses bit length of the random challenge from scheme parameters. + uint64_t challenge_length_bits = + scheme_parameters.scheme_parameters_v0().challenge_length_bits(); + // Computes the upper bound of the challenge and input to the random oracle. + BigNum challenge_upper_bound = ctx->One().Lshift(challenge_length_bits); + + // Note that the random oracle prefix is implicitly included as part of the + // parameters being serialized in the statement proto. We skip including it + // again here to avoid unnecessary duplication. + std::string challenge_string = "GetNoncesForTokenRequest:"; + auto challenge_sos = + std::make_unique<google::protobuf::io::StringOutputStream>( + &challenge_string); + auto challenge_cos = + std::make_unique<google::protobuf::io::CodedOutputStream>( + challenge_sos.get()); + challenge_cos->SetSerializationDeterministic(true); + challenge_cos->WriteVarint64(scheme_parameters.ByteSizeLong()); + 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<BigNum> outputs; + outputs.reserve(num_messages); + for (uint64_t i = 0; i < num_messages; ++i) { + std::string random_oracle_input_i = absl::StrCat(challenge_string, ",", i); + outputs.push_back( + ctx->RandomOracleSha512(random_oracle_input_i, challenge_upper_bound)); + } + + return std::move(outputs); +} + +} // namespace + +std::unique_ptr<AnonymousCountingTokens> AnonymousCountingTokensV0::Create() { + return absl::WrapUnique<AnonymousCountingTokensV0>( + new AnonymousCountingTokensV0()); +} + +// Returns a fresh set of Server parameters corresponding to these +// SchemeParameters. Fails with InvalidArgument if the parameters don't +// correspond to ACT v0. +StatusOr<ServerParameters> AnonymousCountingTokensV0::GenerateServerParameters( + const SchemeParameters& scheme_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateServerParameters: supplied " + "parameters do not correspond to ACTv0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Choose base g. + ASSIGN_OR_RETURN(ECPoint dy_prf_base_g, ec_group.GetRandomGenerator()); + + // Generate RSA-Modulus and Camenisch-Shoup encryption key. + CamenischShoupKey camenisch_shoup_key = GenerateCamenischShoupKey( + &ctx, scheme_parameters_v0.modulus_length_bits(), + scheme_parameters_v0.camenisch_shoup_s(), + scheme_parameters_v0.vector_encryption_length()); + + BigNum n = camenisch_shoup_key.n; + + auto camenisch_shoup_public_key = std::make_unique<CamenischShoupPublicKey>( + CamenischShoupPublicKey{camenisch_shoup_key.n, camenisch_shoup_key.s, + camenisch_shoup_key.vector_encryption_length, + camenisch_shoup_key.g, camenisch_shoup_key.ys}); + auto camenisch_shoup_private_key = std::make_unique<CamenischShoupPrivateKey>( + CamenischShoupPrivateKey{camenisch_shoup_key.xs}); + + auto public_camenisch_shoup = std::make_unique<PublicCamenischShoup>( + &ctx, camenisch_shoup_public_key->n, camenisch_shoup_public_key->s, + camenisch_shoup_public_key->g, camenisch_shoup_public_key->ys); + + // Generate Pedersen Parameters. + PedersenOverZn::Parameters pedersen_parameters = + PedersenOverZn::GenerateParameters( + &ctx, n, scheme_parameters_v0.pedersen_batch_size()); + + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::Create(&ctx, pedersen_parameters.gs, + pedersen_parameters.h, pedersen_parameters.n)); + + ServerParameters server_parameters; + + ServerPublicParametersV0* server_public_parameters_v0 = + server_parameters.mutable_public_parameters() + ->mutable_server_public_parameters_v0(); + ASSIGN_OR_RETURN(*server_public_parameters_v0->mutable_prf_base_g(), + dy_prf_base_g.ToBytesCompressed()); + *server_public_parameters_v0->mutable_pedersen_parameters() = + PedersenOverZn::ParametersToProto(pedersen_parameters); + *server_public_parameters_v0->mutable_camenisch_shoup_public_key() = + CamenischShoupPublicKeyToProto(*camenisch_shoup_public_key); + + ServerPrivateParametersV0* server_private_parameters_v0 = + server_parameters.mutable_private_parameters() + ->mutable_server_private_parameters_v0(); + *server_private_parameters_v0->mutable_camenisch_shoup_private_key() = + CamenischShoupPrivateKeyToProto(*camenisch_shoup_private_key); + + // Generate Boneh-Boyen Oblivious Signature object. This call is safe even + // with the partially-ready server_public_parameters. + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + *server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Generate Boneh-Boyen Oblivious Signature key. + ASSIGN_OR_RETURN( + std::tie(*server_public_parameters_v0 + ->mutable_bb_oblivious_signature_public_key(), + *server_private_parameters_v0 + ->mutable_bb_oblivious_signature_private_key()), + bb_oblivious_signature->GenerateKeys()); + + return std::move(server_parameters); +} + +// Returns a fresh set of Client parameters corresponding to these +// SchemeParameters and ServerPublicParameters. Fails with InvalidArgument if +// the parameters don't correspond to ACT v0. +StatusOr<ClientParameters> AnonymousCountingTokensV0::GenerateClientParameters( + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateClientParameters: supplied " + "parameters do not correspond to ACT v0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize Pedersen Params + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + + // Generate Client VRF object. + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + ClientParameters client_parameters; + ClientPublicParametersV0* client_public_parameters_v0 = + client_parameters.mutable_public_parameters() + ->mutable_client_public_parameters_v0(); + ClientPrivateParametersV0* client_private_parameters_v0 = + client_parameters.mutable_private_parameters() + ->mutable_client_private_parameters_v0(); + + ASSIGN_OR_RETURN( + std::tie( + *client_public_parameters_v0->mutable_dy_vrf_public_key(), + *client_private_parameters_v0->mutable_dy_vrf_private_key(), + *client_public_parameters_v0->mutable_dy_vrf_generate_keys_proof()), + dy_vrf->GenerateKeyPair()); + + return std::move(client_parameters); +} + +// Verifies the consistency of the ClientPublicParameters with the Server and +// scheme parameters. Fails with InvalidArgument if the parameters don't +// correspond to ACT v0. +Status AnonymousCountingTokensV0::CheckClientParameters( + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0() || + !server_private_parameters.has_server_private_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::CheckClientParameters: supplied " + "parameters do not correspond to ACT v0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const ClientPublicParametersV0& client_public_parameters_v0 = + client_public_parameters.client_public_parameters_v0(); + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize Pedersen Params + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + + // Generate Client VRF object. + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + // Verify the proof for the Client's VRF key. + return dy_vrf->VerifyGenerateKeysProof( + client_public_parameters_v0.dy_vrf_public_key(), + client_public_parameters_v0.dy_vrf_generate_keys_proof()); +} + +// Returns a tuple of client_fingerprints, TokensRequest and +// TokensRequestPrivateState for the given set of messages. Fails with +// InvalidArgument if the parameters don't correspond to ACT v0. +StatusOr<std::tuple<std::vector<std::string>, TokensRequest, + TokensRequestPrivateState>> +AnonymousCountingTokensV0::GenerateTokensRequest( + absl::Span<const std::string> messages, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !client_private_parameters.has_client_private_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateTokensRequest: supplied " + "parameters do not correspond to ACT v0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ClientPublicParametersV0& client_public_parameters_v0 = + client_public_parameters.client_public_parameters_v0(); + const ClientPrivateParametersV0& client_private_parameters_v0 = + client_private_parameters.client_private_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + TokensRequest tokens_request_proto; + TokensRequestV0* tokens_request_v0 = + tokens_request_proto.mutable_tokens_request_v0(); + TokensRequestV0::Part1* tokens_request_v0_part_1 = + tokens_request_v0->mutable_part_1(); + TokensRequestPrivateState tokens_request_private_state; + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + ECPoint dy_prf_base_g, + ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g())); + + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // 1) Hash all messages to the exponent group/ BigNums. + std::vector<BigNum> hashed_messages; + hashed_messages.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + hashed_messages.push_back( + ctx.RandomOracleSha512(messages[i], ec_group.GetOrder())); + } + + // 2) Commit to hashed messages. + ASSIGN_OR_RETURN( + PedersenOverZn::CommitmentAndOpening commit_and_open_messages, + pedersen->Commit(hashed_messages)); + tokens_request_v0_part_1->set_commit_messages( + commit_and_open_messages.commitment.ToBytes()); + + // 3) Generate client nonces and commit to them. + std::vector<BigNum> client_nonces; + client_nonces.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + client_nonces.push_back(ec_group.GeneratePrivateKey()); + } + ASSIGN_OR_RETURN( + PedersenOverZn::CommitmentAndOpening commit_and_open_client_nonces, + pedersen->Commit(client_nonces)); + tokens_request_v0_part_1->set_commit_client_nonces( + commit_and_open_client_nonces.commitment.ToBytes()); + + // 4) Perform a VRF on the committed messages and serialize as fingerprints. + ASSIGN_OR_RETURN( + std::vector<ECPoint> prf_evaluations, + dy_vrf->Apply(hashed_messages, + client_private_parameters_v0.dy_vrf_private_key())); + std::vector<std::string> fingerprints; + fingerprints.reserve(prf_evaluations.size()); + for (size_t i = 0; i < prf_evaluations.size(); ++i) { + ASSIGN_OR_RETURN(std::string fingerprint, + prf_evaluations[i].ToBytesCompressed()); + fingerprints.push_back(std::move(fingerprint)); + } + + // Also create the proof that the fingerprints were correctly generated. + ASSIGN_OR_RETURN(*tokens_request_v0_part_1->mutable_fingerprints_proof(), + dy_vrf->GenerateApplyProof( + hashed_messages, prf_evaluations, + client_public_parameters_v0.dy_vrf_public_key(), + client_private_parameters_v0.dy_vrf_private_key(), + commit_and_open_messages)); + + // 5) Generate server nonces by hashing the preceding portion of the request. + ASSIGN_OR_RETURN(std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, *tokens_request_v0_part_1, + messages.size())); + // We commit the "server_nonces" with randomness 0, which is ok since they + // are known to both parties, and furthermore will be homomorphically added to + // the "client_nonces" which have properly generated randomness. + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + + // 6) Homomorphically compute commitments to the nonces (rs) + std::vector<BigNum> nonces; + nonces.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + // No mod performed here, since the homomorphic addition of the commitments + // will not be mod-ed, and we want consistency. + nonces.push_back(server_nonces[i] + client_nonces[i]); + } + PedersenOverZn::Commitment commit_nonce = pedersen->Add( + commit_server_nonces, commit_and_open_client_nonces.commitment); + PedersenOverZn::Opening commit_nonce_opening = + commit_and_open_client_nonces.opening; + + *tokens_request_private_state.mutable_tokens_request_private_state_v0() + ->mutable_nonces() = BigNumVectorToProto(nonces); + + // 7) Generate Boneh-Boyen Oblivious Signature Request request. + ASSIGN_OR_RETURN( + std::tie( + *tokens_request_v0->mutable_bb_oblivious_signature_request(), + *tokens_request_v0->mutable_bb_oblivious_signature_request_proof(), + *tokens_request_private_state + .mutable_tokens_request_private_state_v0() + ->mutable_bb_oblivious_signature_request_private_state()), + bb_oblivious_signature->GenerateRequestAndProof( + hashed_messages, nonces, + server_public_parameters_v0.bb_oblivious_signature_public_key(), + commit_and_open_messages, {commit_nonce, commit_nonce_opening})); + + return std::make_tuple(std::move(fingerprints), + std::move(tokens_request_proto), + std::move(tokens_request_private_state)); +} + +// Returns OkStatus on a valid request. Fails with InvalidArgument if the +// parameters don't correspond to ACT v0. +Status AnonymousCountingTokensV0::CheckTokensRequest( + absl::Span<const std::string> client_fingerprints, + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ClientPublicParametersV0& client_public_parameters_v0 = + client_public_parameters.client_public_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + + // Construct the DY VRF object + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_messages()); + + std::vector<ECPoint> deserialized_fingerprints; + deserialized_fingerprints.reserve(client_fingerprints.size()); + for (size_t i = 0; i < client_fingerprints.size(); ++i) { + ASSIGN_OR_RETURN(ECPoint deserialized_fingerprint, + ec_group.CreateECPoint(client_fingerprints[i])); + + // Test that the deserialized fingerprint reserializes to the exact same + // value. + ASSIGN_OR_RETURN(std::string reserialized_fingerprint, + deserialized_fingerprint.ToBytesCompressed()); + if (reserialized_fingerprint != client_fingerprints[i]) { + return absl::InvalidArgumentError(absl::StrCat( + "AnonymousCountingTokensV0::CheckTokensRequest: client_fingerprints[", + i, + "] comes out to a different value when serialized and " + "deserialized.")); + } + + deserialized_fingerprints.push_back(std::move(deserialized_fingerprint)); + } + + RETURN_IF_ERROR(dy_vrf->VerifyApplyProof( + deserialized_fingerprints, + client_public_parameters_v0.dy_vrf_public_key(), commit_messages, + tokens_request_v0.part_1().fingerprints_proof())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Regenerate the commitments to messages and nonces (rs) by replaying the + // steps the client took to generate them. + PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_client_nonces()); + + ASSIGN_OR_RETURN( + std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, tokens_request.tokens_request_v0().part_1(), + tokens_request.tokens_request_v0() + .bb_oblivious_signature_request() + .num_messages())); + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + PedersenOverZn::Commitment commit_nonce = + pedersen->Add(commit_server_nonces, commit_client_nonces); + + return bb_oblivious_signature->VerifyRequest( + server_public_parameters_v0.bb_oblivious_signature_public_key(), + tokens_request_v0.bb_oblivious_signature_request(), + tokens_request_v0.bb_oblivious_signature_request_proof(), commit_messages, + commit_nonce); +} + +// Returns the TokensResponse. Fails with InvalidArgument if the parameters +// don't correspond to ACT v0. +StatusOr<TokensResponse> AnonymousCountingTokensV0::GenerateTokensResponse( + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0() || + !server_private_parameters.has_server_private_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const ServerPrivateParametersV0& server_private_parameters_v0 = + server_private_parameters.server_private_parameters_v0(); + + TokensResponse tokens_response; + TokensResponseV0* tokens_response_v0 = + tokens_response.mutable_tokens_response_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + std::unique_ptr<PrivateCamenischShoup> private_camenisch_shoup, + PrivateCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key(), + server_private_parameters_v0.camenisch_shoup_private_key())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Regenerate the commitments to messages and nonces (rs) by replaying the + // steps the client took to generate them. + PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_messages()); + PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_client_nonces()); + + ASSIGN_OR_RETURN( + std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, tokens_request.tokens_request_v0().part_1(), + tokens_request.tokens_request_v0() + .bb_oblivious_signature_request() + .num_messages())); + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + PedersenOverZn::Commitment commit_nonce = + pedersen->Add(commit_server_nonces, commit_client_nonces); + + // Generate response and proof for the Boneh-Boyen Oblivious Signature. + ASSIGN_OR_RETURN( + std::tie( + *tokens_response_v0->mutable_bb_oblivious_signature_response(), + *tokens_response_v0->mutable_bb_oblivious_signature_response_proof()), + bb_oblivious_signature->GenerateResponseAndProof( + tokens_request_v0.bb_oblivious_signature_request(), + server_public_parameters_v0.bb_oblivious_signature_public_key(), + server_private_parameters_v0.bb_oblivious_signature_private_key(), + commit_messages, commit_nonce, private_camenisch_shoup.get())); + + return std::move(tokens_response); +} + +// Returns OkStatus on a valid response. Fails with InvalidArgument if the +// parameters don't correspond to ACT v0. +Status AnonymousCountingTokensV0::VerifyTokensResponse( + absl::Span<const std::string> messages, const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !tokens_response.has_tokens_response_v0() || + !tokens_request_private_state.has_tokens_request_private_state_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !client_private_parameters.has_client_private_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const TokensResponseV0& tokens_response_v0 = + tokens_response.tokens_response_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Regenerate the commitments to messages and nonces (rs) by replaying the + // steps the client took to generate them. + PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_messages()); + PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_client_nonces()); + + ASSIGN_OR_RETURN( + std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, tokens_request.tokens_request_v0().part_1(), + tokens_request.tokens_request_v0() + .bb_oblivious_signature_request() + .num_messages())); + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + PedersenOverZn::Commitment commit_nonce = + pedersen->Add(commit_server_nonces, commit_client_nonces); + + return bb_oblivious_signature->VerifyResponse( + server_public_parameters_v0.bb_oblivious_signature_public_key(), + tokens_response_v0.bb_oblivious_signature_response(), + tokens_response_v0.bb_oblivious_signature_response_proof(), + tokens_request_v0.bb_oblivious_signature_request(), commit_messages, + commit_nonce); +} + +// Returns a vector of tokens corresponding to the supplied messages. Fails +// with InvalidArgument if the parameters don't correspond to ACT v0. +StatusOr<std::vector<Token>> AnonymousCountingTokensV0::RecoverTokens( + absl::Span<const std::string> messages, const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !tokens_request_private_state.has_tokens_request_private_state_v0() || + !tokens_response.has_tokens_response_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !client_private_parameters.has_client_private_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const TokensRequestPrivateStateV0& tokens_request_private_state_v0 = + tokens_request_private_state.tokens_request_private_state_v0(); + const TokensResponseV0& tokens_response_v0 = + tokens_response.tokens_response_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + ECPoint dy_prf_base_g, + ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Extract message PRF evaluations + ASSIGN_OR_RETURN(std::vector<ECPoint> signatures, + bb_oblivious_signature->ExtractResults( + tokens_response_v0.bb_oblivious_signature_response(), + tokens_request_v0.bb_oblivious_signature_request(), + tokens_request_private_state_v0 + .bb_oblivious_signature_request_private_state())); + + // Package tokens. + std::vector<BigNum> nonces = + ParseBigNumVectorProto(&ctx, tokens_request_private_state_v0.nonces()); + + std::vector<Token> tokens; + tokens.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + Token token; + TokenV0* token_v0 = token.mutable_token_v0(); + token.set_nonce(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 <memory> +#include <string> +#include <tuple> +#include <vector> + +#include "act/act.h" +#include "act/act.pb.h" +#include "private_join_and_compute/util/status.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +// An implementation of vO Anonymous Counting Tokens. +class AnonymousCountingTokensV0 : public AnonymousCountingTokens { + public: + static std::unique_ptr<AnonymousCountingTokens> Create(); + + // Returns a fresh set of Server parameters corresponding to these + // SchemeParameters. Fails with InvalidArgument if the parameters don't + // correspond to ACT v0. + StatusOr<ServerParameters> GenerateServerParameters( + const SchemeParameters& scheme_parameters) override; + + // Returns a fresh set of Client parameters corresponding to these + // SchemeParameters and ServerPublicParameters. Fails with InvalidArgument if + // the parameters don't correspond to ACT v0. + StatusOr<ClientParameters> GenerateClientParameters( + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Verifies the consistency of the ClientPublicParameters with the Server and + // scheme parameters. Fails with InvalidArgument if the parameters don't + // correspond to ACT v0. + Status CheckClientParameters( + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + // Returns a tuple of client_fingerprints, TokensRequest and + // TokensRequestPrivateState for the given set of messages. Fails with + // InvalidArgument if the parameters don't correspond to ACT v0. + StatusOr<std::tuple<std::vector<std::string>, TokensRequest, + TokensRequestPrivateState>> + GenerateTokensRequest( + absl::Span<const std::string> messages, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Returns OkStatus on a valid request. Fails with InvalidArgument if the + // parameters don't correspond to ACT v0. + Status CheckTokensRequest( + absl::Span<const std::string> client_fingerprints, + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + // Returns the TokensResponse. Fails with InvalidArgument if the parameters + // don't correspond to ACT v0. + StatusOr<TokensResponse> GenerateTokensResponse( + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + // Returns OkStatus on a valid response. Fails with InvalidArgument if the + // parameters don't correspond to ACT v0. + Status VerifyTokensResponse( + absl::Span<const std::string> messages, + const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Returns a vector of tokens corresponding to the supplied messages. Fails + // with InvalidArgument if the parameters don't correspond to ACT v0. + StatusOr<std::vector<Token>> RecoverTokens( + absl::Span<const std::string> messages, + const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Returns OkStatus on valid tokens. Fails with InvalidArgument if the + // parameters don't correspond to ACT v0. + Status VerifyToken( + std::string m, const Token& token, + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + protected: + AnonymousCountingTokensV0() = default; +}; + +} // namespace anonymous_counting_tokens +} // namespace private_join_and_compute + +#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_ACT_V0_ACT_V0_H_ diff --git a/act/act_v0/act_v0.proto b/act/act_v0/act_v0.proto new file mode 100644 index 0000000..7cc2f23 --- /dev/null +++ b/act/act_v0/act_v0.proto @@ -0,0 +1,141 @@ +/* + * Copyright 2023 Google LLC. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +package private_join_and_compute.anonymous_counting_tokens; + +import "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.proto"; +import "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.proto"; +import "private_join_and_compute/crypto/proto/big_num.proto"; +import "private_join_and_compute/crypto/proto/camenisch_shoup.proto"; +import "private_join_and_compute/crypto/proto/pedersen.proto"; + +option java_multiple_files = true; + +// The parameters for ACTv0. +message SchemeParametersV0 { + // How many masking bits (more than the challenge bits) to add in the sigma + // protocols. + uint64 security_parameter = 1; + // How many bits the challenge has. + uint64 challenge_length_bits = 2; + // Length for the RSA modulus. + uint64 modulus_length_bits = 3; + // Camenisch Shoup Damgard-Jurik parameter (message space is n^s). + uint64 camenisch_shoup_s = 4; + // Camenisch-Shoup vector-encryption parameter. + uint64 vector_encryption_length = 5; + // Number of messages that can be committed in a single Pedersen commitment. + uint64 pedersen_batch_size = 6; + // Specification of the EC Group in which the DY-PRF and Boneh Boyen Oblivious + // Signature will be computed. + uint64 prf_ec_group = 7; + // Prefix to attach to any random oracle query (optional). + string random_oracle_prefix = 8; +} + +// The Server's public parameters for the ACTv0 scheme. +message ServerPublicParametersV0 { + reserved 1, 4, 5; + + // Parameters to use for commitments. + private_join_and_compute.proto.PedersenParameters pedersen_parameters = 2; + // Camenisch Shoup public key (for the Boneh-Boyen Oblivious Signature). + private_join_and_compute.proto.CamenischShoupPublicKey camenisch_shoup_public_key = 3; + // Public key for the Boneh-Boyen oblivious signature. + private_join_and_compute.proto.BbObliviousSignaturePublicKey + bb_oblivious_signature_public_key = 6; + + // Serialized ECPoint corresponding to the base g to use for the DY PRF and + // Boneh-Boyen oblivious signature. + bytes prf_base_g = 7; + +} + +// The Server's private parameters for the ACTv0 scheme. +message ServerPrivateParametersV0 { + reserved 2, 3; + // Camenisch Shoup private key (for the Boneh-Boyen Oblivious Signature). + private_join_and_compute.proto.CamenischShoupPrivateKey camenisch_shoup_private_key = 1; + // Private key for the Boneh-Boyen Oblivious Signature. + private_join_and_compute.proto.BbObliviousSignaturePrivateKey + bb_oblivious_signature_private_key = 4; +} + +// The Client's public parameters for the ACT scheme. +message ClientPublicParametersV0 { + // Public key for the DY Verifiable Random Function. + private_join_and_compute.proto.DyVrfPublicKey dy_vrf_public_key = 1; + private_join_and_compute.proto.DyVrfGenerateKeysProof dy_vrf_generate_keys_proof = 2; +} + +// The Client's private parameters for the ACT scheme. +message ClientPrivateParametersV0 { + // Private key for the DY Verifiable Random Function. + private_join_and_compute.proto.DyVrfPrivateKey dy_vrf_private_key = 1; +} + +// The Client's token request. Can correspond to a batch of tokens. +message TokensRequestV0 { + message Part1 { + bytes commit_messages = 1; + bytes commit_client_nonces = 2; + + // Proofs that the fingerprints on the messages were generated correctly. + // The fingerprints correspond to PRF evaluations on the messages. + private_join_and_compute.proto.DyVrfApplyProof fingerprints_proof = 3; + } + + // Part1 of the tokens request feeds into the Random Oracle to produce the + // client-independent portion of the nonces. (The nonce per-token consists + // of a client-chosen part, and a client-independent/not-chosen part. The + // latter is generated by a random oracle call.) + Part1 part_1 = 1; + + // Boneh-Boyen Oblivious Signature request and proof for nonces. + private_join_and_compute.proto.BbObliviousSignatureRequest bb_oblivious_signature_request = 2; + private_join_and_compute.proto.BbObliviousSignatureRequestProof + bb_oblivious_signature_request_proof = 3; +} + +// Private state corresponding to the Client's token request, needed to recover +// the tokens from the server's response. +message TokensRequestPrivateStateV0 { + private_join_and_compute.proto.BbObliviousSignatureRequestPrivateState + bb_oblivious_signature_request_private_state = 1; + // Stores the nonces generated as part of the request (includes the + // client-generated and random-oracle-generated portions combined). + private_join_and_compute.proto.BigNumVector nonces = 2; +} + +// The Server's response to a TokensRequest. Can correspond to a batch of +// tokens. +message TokensResponseV0 { + // Boneh-Boyen Oblivious signature response and proof. + private_join_and_compute.proto.BbObliviousSignatureResponse bb_oblivious_signature_response = + 1; + private_join_and_compute.proto.BbObliviousSignatureResponseProof + bb_oblivious_signature_response_proof = 2; +} + +// An actual token recovered from the TokenResponse. +message TokenV0 { + // The nonce is stored in the enclosing Token proto. + + // Serialized Boneh-Boyen signature on the message and nonce. Serialized + // ECPoint. + bytes bb_signature = 1; +} diff --git a/act/act_v0/act_v0_test.cc b/act/act_v0/act_v0_test.cc new file mode 100644 index 0000000..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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "act/act.h" +#include "act/act.pb.h" +#include "act/act_v0/act_v0.pb.h" +#include "act/act_v0/parameters.h" +#include "private_join_and_compute/crypto/camenisch_shoup.h" +#include "private_join_and_compute/crypto/context.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.pb.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.pb.h" +#include "private_join_and_compute/crypto/ec_group.h" +#include "private_join_and_compute/crypto/ec_point.h" +#include "private_join_and_compute/crypto/pedersen_over_zn.h" +#include "private_join_and_compute/crypto/proto/big_num.pb.h" +#include "private_join_and_compute/crypto/proto/camenisch_shoup.pb.h" +#include "private_join_and_compute/crypto/proto/pedersen.pb.h" +#include "private_join_and_compute/util/status.inc" +#include "private_join_and_compute/util/status_testing.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { +namespace { + +using ::testing::HasSubstr; +using testing::StatusIs; + +const int kTestCurveId = NID_X9_62_prime256v1; + +class AnonymousCountingTokensV0Test : public ::testing::Test { + protected: + static std::string GetRandomOraclePrefix() { + return "TestRandomOraclePrefix"; + } + + static SchemeParameters GetSchemeParameters() { + return ActV0TestSchemeParameters(); + } + + static void SetUpTestSuite() { + std::unique_ptr<AnonymousCountingTokens> act = + AnonymousCountingTokensV0::Create(); + ServerParameters server_parameters_temp = + act->GenerateServerParameters(GetSchemeParameters()).value(); + server_parameters_ = new ServerParameters(server_parameters_temp); + } + + static void TearDownTestSuite() { delete server_parameters_; } + + void SetUp() override { + anonymous_counting_tokens_ = AnonymousCountingTokensV0::Create(); + + ASSERT_OK_AND_ASSIGN(auto ec_group_do_not_use_later, + ECGroup::Create(kTestCurveId, &ctx_)); + ec_group_ = std::make_unique<ECGroup>(std::move(ec_group_do_not_use_later)); + + // Deserialize components of the precomputed server parameters. + ASSERT_OK_AND_ASSIGN(PedersenOverZn::Parameters pedersen_parameters, + PedersenOverZn::ParseParametersProto( + &ctx_, server_parameters_->public_parameters() + .server_public_parameters_v0() + .pedersen_parameters())); + ASSERT_OK_AND_ASSIGN( + pedersen_, + PedersenOverZn::Create(&ctx_, pedersen_parameters.gs, + pedersen_parameters.h, pedersen_parameters.n)); + + dy_prf_base_g_ = std::make_unique<ECPoint>( + ec_group_ + ->CreateECPoint(server_parameters_->public_parameters() + .server_public_parameters_v0() + .prf_base_g()) + .value()); + + cs_public_key_ = std::make_unique<CamenischShoupPublicKey>( + ParseCamenischShoupPublicKeyProto( + &ctx_, server_parameters_->public_parameters() + .server_public_parameters_v0() + .camenisch_shoup_public_key()) + .value()); + cs_private_key_ = std::make_unique<CamenischShoupPrivateKey>( + ParseCamenischShoupPrivateKeyProto( + &ctx_, server_parameters_->private_parameters() + .server_private_parameters_v0() + .camenisch_shoup_private_key()) + .value()); + + public_camenisch_shoup_ = std::make_unique<PublicCamenischShoup>( + &ctx_, cs_public_key_->n, cs_public_key_->s, cs_public_key_->g, + cs_public_key_->ys); + private_camenisch_shoup_ = std::make_unique<PrivateCamenischShoup>( + &ctx_, cs_public_key_->n, cs_public_key_->s, cs_public_key_->g, + cs_public_key_->ys, cs_private_key_->xs); + + ASSERT_OK_AND_ASSIGN( + client_parameters_, + anonymous_counting_tokens_->GenerateClientParameters( + GetSchemeParameters(), server_parameters_->public_parameters())); + } + + // Holds a transcript for an EndToEnd request. + struct Transcript { + std::vector<std::string> fingerprints; + TokensRequest tokens_request; + TokensRequestPrivateState tokens_request_private_state; + TokensResponse tokens_response; + std::vector<Token> tokens; + }; + + // Generates an end-to-end request transcript. Does not verify request or + // response proofs. + StatusOr<Transcript> GenerateTranscript( + absl::Span<const std::string> messages) { + Transcript transcript; + ASSIGN_OR_RETURN( + std::tie(transcript.fingerprints, transcript.tokens_request, + transcript.tokens_request_private_state), + anonymous_counting_tokens_->GenerateTokensRequest( + messages, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); + + ASSIGN_OR_RETURN(transcript.tokens_response, + anonymous_counting_tokens_->GenerateTokensResponse( + transcript.tokens_request, GetSchemeParameters(), + client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters())); + + ASSIGN_OR_RETURN( + transcript.tokens, + anonymous_counting_tokens_->RecoverTokens( + messages, transcript.tokens_request, + transcript.tokens_request_private_state, transcript.tokens_response, + GetSchemeParameters(), client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); + + return std::move(transcript); + } + + // Server Parameters, generated once and available to be reused across tests + // to save expensive safe modulus computation. + static ServerParameters* server_parameters_; + + // Instance of AnonymousCountingTokensV0. + std::unique_ptr<AnonymousCountingTokens> anonymous_counting_tokens_; + + Context ctx_; + std::unique_ptr<ECGroup> ec_group_; + + // Deserialized objects from the saved serialized parameters above. + std::unique_ptr<PedersenOverZn> pedersen_; + std::unique_ptr<ECPoint> dy_prf_base_g_; + std::unique_ptr<CamenischShoupPublicKey> cs_public_key_; + std::unique_ptr<CamenischShoupPrivateKey> cs_private_key_; + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup_; + std::unique_ptr<PrivateCamenischShoup> private_camenisch_shoup_; + + // Client parameters for AnonymousCountingTokensV0. + ClientParameters client_parameters_; +}; + +ServerParameters* AnonymousCountingTokensV0Test::server_parameters_ = nullptr; + +TEST_F(AnonymousCountingTokensV0Test, ServerParametersHasNonEmptyFields) { + // Expect all fields are nonempty. + EXPECT_TRUE(server_parameters_->has_public_parameters()); + EXPECT_TRUE(server_parameters_->public_parameters() + .has_server_public_parameters_v0()); + EXPECT_FALSE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .prf_base_g() + .empty()); + EXPECT_TRUE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .has_pedersen_parameters()); + EXPECT_TRUE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .has_camenisch_shoup_public_key()); + EXPECT_TRUE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .has_bb_oblivious_signature_public_key()); + + EXPECT_TRUE(server_parameters_->has_private_parameters()); + EXPECT_TRUE(server_parameters_->private_parameters() + .has_server_private_parameters_v0()); + EXPECT_TRUE(server_parameters_->private_parameters() + .server_private_parameters_v0() + .has_camenisch_shoup_private_key()); + EXPECT_TRUE(server_parameters_->private_parameters() + .server_private_parameters_v0() + .has_bb_oblivious_signature_private_key()); +} + +TEST_F(AnonymousCountingTokensV0Test, GeneratesDifferentServerParameters) { + ASSERT_OK_AND_ASSIGN(ServerParameters other_server_parameters, + anonymous_counting_tokens_->GenerateServerParameters( + GetSchemeParameters())); + + // Expect all fields in the public parameters are different across the 2 + // keys. + EXPECT_NE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .prf_base_g(), + other_server_parameters.public_parameters() + .server_public_parameters_v0() + .prf_base_g()); + EXPECT_NE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .pedersen_parameters() + .gs() + .serialized_big_nums(0), + other_server_parameters.public_parameters() + .server_public_parameters_v0() + .pedersen_parameters() + .gs() + .serialized_big_nums(0)); + EXPECT_NE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .camenisch_shoup_public_key() + .ys() + .serialized_big_nums(0), + other_server_parameters.public_parameters() + .server_public_parameters_v0() + .camenisch_shoup_public_key() + .ys() + .serialized_big_nums(0)); + EXPECT_NE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .bb_oblivious_signature_public_key() + .encrypted_k(0) + .u(), + other_server_parameters.public_parameters() + .server_public_parameters_v0() + .bb_oblivious_signature_public_key() + .encrypted_k(0) + .u()); + EXPECT_NE(server_parameters_->public_parameters() + .server_public_parameters_v0() + .bb_oblivious_signature_public_key() + .encrypted_y(0) + .u(), + other_server_parameters.public_parameters() + .server_public_parameters_v0() + .bb_oblivious_signature_public_key() + .encrypted_y(0) + .u()); +} + +TEST_F(AnonymousCountingTokensV0Test, ClientParametersHaveValidFields) { + EXPECT_TRUE(client_parameters_.has_public_parameters()); + EXPECT_TRUE( + client_parameters_.public_parameters().has_client_public_parameters_v0()); + EXPECT_TRUE(client_parameters_.public_parameters() + .client_public_parameters_v0() + .has_dy_vrf_public_key()); + + EXPECT_TRUE(client_parameters_.has_private_parameters()); + EXPECT_TRUE(client_parameters_.private_parameters() + .has_client_private_parameters_v0()); + EXPECT_TRUE(client_parameters_.private_parameters() + .client_private_parameters_v0() + .has_dy_vrf_private_key()); +} + +TEST_F(AnonymousCountingTokensV0Test, GeneratesDifferentClientParameters) { + ASSERT_OK_AND_ASSIGN( + ClientParameters other_client_parameters, + anonymous_counting_tokens_->GenerateClientParameters( + GetSchemeParameters(), server_parameters_->public_parameters())); + + EXPECT_NE(client_parameters_.public_parameters() + .client_public_parameters_v0() + .dy_vrf_public_key() + .commit_prf_key(), + other_client_parameters.public_parameters() + .client_public_parameters_v0() + .dy_vrf_public_key() + .commit_prf_key()); +} + +TEST_F(AnonymousCountingTokensV0Test, ProofFromOtherClientParametersFails) { + ASSERT_OK_AND_ASSIGN( + ClientParameters other_client_parameters, + anonymous_counting_tokens_->GenerateClientParameters( + GetSchemeParameters(), server_parameters_->public_parameters())); + + *client_parameters_.mutable_public_parameters() + ->mutable_client_public_parameters_v0() + ->mutable_dy_vrf_generate_keys_proof() = + other_client_parameters.public_parameters() + .client_public_parameters_v0() + .dy_vrf_generate_keys_proof(); + + EXPECT_THAT( + anonymous_counting_tokens_->CheckClientParameters( + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, ClientParametersPassCheck) { + EXPECT_OK(anonymous_counting_tokens_->CheckClientParameters( + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters())); +} + +TEST_F(AnonymousCountingTokensV0Test, ClientParametersWithoutActV0FailCheck) { + client_parameters_.mutable_public_parameters() + ->clear_client_public_parameters_v0(); + + EXPECT_THAT(anonymous_counting_tokens_->CheckClientParameters( + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("CheckClientParameters"))); +} + +TEST_F(AnonymousCountingTokensV0Test, EmptyClientParametersProofFailsCheck) { + client_parameters_.mutable_public_parameters() + ->mutable_client_public_parameters_v0() + ->clear_dy_vrf_generate_keys_proof(); + + EXPECT_THAT( + anonymous_counting_tokens_->CheckClientParameters( + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, GeneratesTokenRequest) { + EXPECT_OK(anonymous_counting_tokens_->GenerateTokensRequest( + {"message_0", "message_1", "message_2"}, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); +} + +TEST_F(AnonymousCountingTokensV0Test, FingerprintsMatchOnlyForEqualMessages) { + std::vector<std::string> fingerprints_1; + ASSERT_OK_AND_ASSIGN( + std::tie(fingerprints_1, std::ignore, std::ignore), + anonymous_counting_tokens_->GenerateTokensRequest( + {"message_0", "message_1", "message_2"}, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); + + std::vector<std::string> fingerprints_2; + ASSERT_OK_AND_ASSIGN( + std::tie(fingerprints_2, std::ignore, std::ignore), + anonymous_counting_tokens_->GenerateTokensRequest( + {"message_2", "message_3", "message_4"}, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); + + // Fingerprints should be equal only for "message_2". + EXPECT_EQ(fingerprints_1[2], fingerprints_2[0]); + + for (size_t i = 1; i < fingerprints_1.size(); ++i) { + for (size_t j = 0; j < fingerprints_2.size(); ++j) { + if (!(i == 2 && j == 0)) { + EXPECT_NE(fingerprints_1[i], fingerprints_2[j]); + } + } + } +} + +TEST_F(AnonymousCountingTokensV0Test, TokensRequestIsValid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + EXPECT_OK(anonymous_counting_tokens_->CheckTokensRequest( + transcript.fingerprints, transcript.tokens_request, GetSchemeParameters(), + client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters())); +} + +TEST_F(AnonymousCountingTokensV0Test, + TokensRequestProofFailsWithDifferentFingerprints) { + std::vector<std::string> messages_1 = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages_1)); + std::vector<std::string> messages_2 = {"message_4", "message_5", "message_6"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages_2)); + // fingerprints from the second transcript should not allow the proof to + // verify. + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript_2.fingerprints, transcript_1.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + TokensRequestProofFailsWithWrongNumberOfFingerprints) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + // delete one of the fingerprints. + transcript.fingerprints.pop_back(); + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript.fingerprints, transcript.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Number"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + BbSignatureRequestFromDifferentTranscriptIsInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages)); + ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages)); + // Replace the bb oblivious signature request from the first transcript with + // that from the second. + *transcript_1.tokens_request.mutable_tokens_request_v0() + ->mutable_bb_oblivious_signature_request() = + transcript_2.tokens_request.tokens_request_v0() + .bb_oblivious_signature_request(); + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript_1.fingerprints, transcript_1.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + FingerprintsProofFromDifferentTranscriptIsInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages)); + ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages)); + // Replace the fingerprints proof from the first transcript with + // that from the second. + *transcript_1.tokens_request.mutable_tokens_request_v0() + ->mutable_part_1() + ->mutable_fingerprints_proof() = + transcript_2.tokens_request.tokens_request_v0() + .part_1() + .fingerprints_proof(); + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript_1.fingerprints, transcript_1.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + BbSignatureRequestProofFromDifferentTranscriptIsInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages)); + ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages)); + // Replace the bb signature request proof from the first transcript with + // that from the second. + *transcript_1.tokens_request.mutable_tokens_request_v0() + ->mutable_bb_oblivious_signature_request_proof() = + transcript_2.tokens_request.tokens_request_v0() + .bb_oblivious_signature_request_proof(); + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript_1.fingerprints, transcript_1.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, EmptyRequestIsInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + transcript.tokens_request.clear_tokens_request_v0(); + EXPECT_THAT(anonymous_counting_tokens_->CheckTokensRequest( + transcript.fingerprints, transcript.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("supplied parameters"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + RequestWithoutFingerprintsProofIsInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + transcript.tokens_request.mutable_tokens_request_v0() + ->mutable_part_1() + ->clear_fingerprints_proof(); + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript.fingerprints, transcript.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Number"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + RequestWithoutBbSignatureRequestProofIsInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + transcript.tokens_request.mutable_tokens_request_v0() + ->clear_bb_oblivious_signature_request_proof(); + EXPECT_THAT( + anonymous_counting_tokens_->CheckTokensRequest( + transcript.fingerprints, transcript.tokens_request, + GetSchemeParameters(), client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("number"))); +} + +TEST_F(AnonymousCountingTokensV0Test, GeneratesTokenResponse) { + TokensRequest tokens_request; + ASSERT_OK_AND_ASSIGN( + std::tie(std::ignore, tokens_request, std::ignore), + anonymous_counting_tokens_->GenerateTokensRequest( + {"message_0", "message_1", "message_2"}, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); + + EXPECT_OK(anonymous_counting_tokens_->GenerateTokensResponse( + tokens_request, GetSchemeParameters(), + client_parameters_.public_parameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters())); +} + +TEST_F(AnonymousCountingTokensV0Test, TokensResponseIsValid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + EXPECT_OK(anonymous_counting_tokens_->VerifyTokensResponse( + messages, transcript.tokens_request, + transcript.tokens_request_private_state, transcript.tokens_response, + GetSchemeParameters(), client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters())); +} + +TEST_F(AnonymousCountingTokensV0Test, + TokensResponseIsInvalidForProofFromAnotherTranscript) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages)); + ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages)); + // Put the proof from the second transcript into the first + *transcript_1.tokens_response.mutable_tokens_response_v0() + ->mutable_bb_oblivious_signature_response_proof() = + transcript_2.tokens_response.tokens_response_v0() + .bb_oblivious_signature_response_proof(); + EXPECT_THAT( + anonymous_counting_tokens_->VerifyTokensResponse( + messages, transcript_1.tokens_request, + transcript_1.tokens_request_private_state, + transcript_1.tokens_response, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, + TokensResponseIsInvalidForRequestFromAnotherTranscript) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript_1, GenerateTranscript(messages)); + ASSERT_OK_AND_ASSIGN(Transcript transcript_2, GenerateTranscript(messages)); + // The request and state from transcript_1 should be inconsistent with the + // response from transcript_2. + EXPECT_THAT( + anonymous_counting_tokens_->VerifyTokensResponse( + messages, transcript_1.tokens_request, + transcript_1.tokens_request_private_state, + transcript_2.tokens_response, GetSchemeParameters(), + client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Failed"))); +} + +TEST_F(AnonymousCountingTokensV0Test, EmptyResponseShouldBeInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + + transcript.tokens_response.clear_tokens_response_v0(); + + EXPECT_THAT( + anonymous_counting_tokens_->VerifyTokensResponse( + messages, transcript.tokens_request, + transcript.tokens_request_private_state, transcript.tokens_response, + GetSchemeParameters(), client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("parameters"))); +} + +TEST_F(AnonymousCountingTokensV0Test, EmptyResponseProofShouldBeInvalid) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + + transcript.tokens_response.mutable_tokens_response_v0() + ->clear_bb_oblivious_signature_response_proof(); + + EXPECT_THAT( + anonymous_counting_tokens_->VerifyTokensResponse( + messages, transcript.tokens_request, + transcript.tokens_request_private_state, transcript.tokens_response, + GetSchemeParameters(), client_parameters_.public_parameters(), + client_parameters_.private_parameters(), + server_parameters_->public_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("number"))); +} + +TEST_F(AnonymousCountingTokensV0Test, ProducesValidTokens) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + + EXPECT_EQ(messages.size(), transcript.tokens.size()); + + for (size_t i = 0; i < messages.size(); ++i) { + EXPECT_OK(anonymous_counting_tokens_->VerifyToken( + messages[i], transcript.tokens[i], GetSchemeParameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters())); + } +} + +TEST_F(AnonymousCountingTokensV0Test, TokensDoNotVerifyWithWrongMessages) { + std::vector<std::string> messages = {"message_1", "message_2", "message_3"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + + for (size_t i = 0; i < messages.size(); ++i) { + EXPECT_THAT( + anonymous_counting_tokens_->VerifyToken( + "wrong_message", transcript.tokens[i], GetSchemeParameters(), + server_parameters_->public_parameters(), + server_parameters_->private_parameters()), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("fails to match the token"))); + } +} + +TEST_F(AnonymousCountingTokensV0Test, TokensHaveUniqueNonces) { + std::vector<std::string> messages = {"message_1", "message_2"}; + ASSERT_OK_AND_ASSIGN(Transcript transcript, GenerateTranscript(messages)); + + EXPECT_NE(transcript.tokens[0].nonce(), 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 <string> + +#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..0371f7f --- /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 <string> + +#include "act/act.pb.h" +#include "private_join_and_compute/crypto/openssl.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +const int kDefaultSecurityParameter = 128; +const int kDefaultChallengeLength = 128; +const int kDefaultCamenischShoupS = 1; +const int kDefaultCurveId = NID_X9_62_prime256v1; +const int kDefaultModulusLengthBits = 3072; + +// Returns parameters appropriate only for testing (smaller modulus of 1536 +// bits, smaller batch size of 3). +SchemeParameters ActV0TestSchemeParameters(); + +// Returns parameters supporting 16 messages in a batch. +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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "act/act.h" +#include "act/act.pb.h" +#include "act/act_v0/act_v0.h" +#include "private_join_and_compute/util/status.inc" +#include "private_join_and_compute/util/status_testing.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { +namespace { + +Status EndToEndTestNoVerification(SchemeParameters scheme_parameters, + int num_messages) { + std::unique_ptr<AnonymousCountingTokens> act = + AnonymousCountingTokensV0::Create(); + + // Generate server parameters. + ASSIGN_OR_RETURN(ServerParameters server_parameters, + act->GenerateServerParameters(scheme_parameters)); + + // Generate client parameters. + ASSIGN_OR_RETURN( + ClientParameters client_parameters, + act->GenerateClientParameters(scheme_parameters, + server_parameters.public_parameters())); + + // Generate messages. + std::vector<std::string> messages; + messages.reserve(num_messages); + for (int i = 0; i < num_messages; ++i) { + messages.push_back(absl::StrCat("message", i)); + } + + // Generate Tokens Request. + std::vector<std::string> client_fingerprints; + TokensRequest tokens_request; + TokensRequestPrivateState tokens_request_private_state; + ASSIGN_OR_RETURN( + std::tie(client_fingerprints, tokens_request, + tokens_request_private_state), + act->GenerateTokensRequest(messages, scheme_parameters, + client_parameters.public_parameters(), + client_parameters.private_parameters(), + server_parameters.public_parameters())); + + // 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<Token> tokens, + act->RecoverTokens(messages, tokens_request, tokens_request_private_state, + tokens_response, scheme_parameters, + client_parameters.public_parameters(), + client_parameters.private_parameters(), + server_parameters.public_parameters())); + + // Verify Tokens. + if (tokens.size() != num_messages) { + return absl::InvalidArgumentError("Wrong number of tokens produced"); + } + for (int i = 0; i < num_messages; ++i) { + RETURN_IF_ERROR(act->VerifyToken(messages[i], tokens[i], scheme_parameters, + server_parameters.public_parameters(), + server_parameters.private_parameters())); + } + return absl::OkStatus(); +} + +TEST(ActV0ParametersTest, EndToEndWithTestParameters) { + EXPECT_OK(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 <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "act/act.pb.h" +#include "private_join_and_compute/crypto/context.h" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +// Returns an instance of FakeAnonymousCountingTokens. +std::unique_ptr<AnonymousCountingTokens> FakeAnonymousCountingTokens::Create() { + return absl::WrapUnique<FakeAnonymousCountingTokens>( + new FakeAnonymousCountingTokens()); +} + +// Returns empty Server parameters. +StatusOr<ServerParameters> +FakeAnonymousCountingTokens::GenerateServerParameters( + const SchemeParameters& scheme_parameters) { + return ServerParameters::default_instance(); +} + +// Returns empty Client parameters. +StatusOr<ClientParameters> +FakeAnonymousCountingTokens::GenerateClientParameters( + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters) { + return ClientParameters::default_instance(); +} + +// Always returns "Ok". +Status FakeAnonymousCountingTokens::CheckClientParameters( + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + return absl::OkStatus(); +} + +// For this fake implementation, the client fingerprints are the same as the +// messages (this is insecure). +StatusOr<std::tuple<std::vector<std::string>, TokensRequest, + TokensRequestPrivateState>> +FakeAnonymousCountingTokens::GenerateTokensRequest( + absl::Span<const std::string> messages, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + return std::make_tuple( + std::vector<std::string>(messages.begin(), messages.end()), + TokensRequest::default_instance(), + TokensRequestPrivateState::default_instance()); +} + +// Always returns "Ok". +Status FakeAnonymousCountingTokens::CheckTokensRequest( + absl::Span<const std::string> client_fingerprints, + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + return absl::OkStatus(); +} + +// Returns an empty TokensResponse. +StatusOr<TokensResponse> FakeAnonymousCountingTokens::GenerateTokensResponse( + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + return TokensResponse::default_instance(); +} + +// Always returns "Ok". +Status FakeAnonymousCountingTokens::VerifyTokensResponse( + absl::Span<const std::string> messages, const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + return absl::OkStatus(); +} + +// Returns a set of tokens containing randomly generated "nonce" values, and +// all other fields empty. +StatusOr<std::vector<Token>> FakeAnonymousCountingTokens::RecoverTokens( + absl::Span<const std::string> messages, const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + Context context; + BigNum nonce_bound = context.One().Lshift(kFakeTokenNonceLengthBits); + + std::vector<Token> result; + result.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + Token fake_token; + fake_token.set_nonce(context.GenerateRandLessThan(nonce_bound).ToBytes()); + result.push_back(fake_token); + } + + return std::move(result); +} + +// Always returns "Ok". +Status FakeAnonymousCountingTokens::VerifyToken( + std::string m, const Token& token, + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + return absl::OkStatus(); +} + +} // namespace anonymous_counting_tokens +} // namespace private_join_and_compute diff --git a/act/fake_act.h b/act/fake_act.h new file mode 100644 index 0000000..c90aead --- /dev/null +++ b/act/fake_act.h @@ -0,0 +1,124 @@ +/* + * Copyright 2023 Google LLC. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_FAKE_ACT_H_ +#define PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_FAKE_ACT_H_ + +#include <memory> +#include <string> +#include <tuple> +#include <vector> + +#include "act/act.h" +#include "act/act.pb.h" +#include "private_join_and_compute/util/status.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +// A fake, insecure implementation of Anonymous Counting Tokens for +// testing/stubbing purposes only. This should NOT be used in production: it +// doesn't have any of the desired security properties. +class FakeAnonymousCountingTokens : public AnonymousCountingTokens { + public: + static const size_t kFakeTokenNonceLengthBits = 256; + + // Returns an instance of FakeAnonymousCountingTokens. + static std::unique_ptr<AnonymousCountingTokens> Create(); + + ~FakeAnonymousCountingTokens() override = default; + + // Returns empty Server parameters. + StatusOr<ServerParameters> GenerateServerParameters( + const SchemeParameters& scheme_parameters) override; + + // Returns empty Client parameters. + StatusOr<ClientParameters> GenerateClientParameters( + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Always returns "Ok". + Status CheckClientParameters( + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + // For this fake implementation, the client fingerprints are the same as the + // messages (this is insecure). + StatusOr<std::tuple<std::vector<std::string>, TokensRequest, + TokensRequestPrivateState>> + GenerateTokensRequest( + absl::Span<const std::string> messages, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Always returns "Ok". + Status CheckTokensRequest( + absl::Span<const std::string> client_fingerprints, + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + // Returns an empty TokensResponse. + StatusOr<TokensResponse> GenerateTokensResponse( + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + // Always returns "Ok". + Status VerifyTokensResponse( + absl::Span<const std::string> messages, + const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Returns a set of tokens containing randomly generated "nonce" values, and + // all other fields empty. + StatusOr<std::vector<Token>> RecoverTokens( + absl::Span<const std::string> messages, + const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) override; + + // Always returns "Ok". + Status VerifyToken( + std::string m, const Token& token, + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) override; + + protected: + FakeAnonymousCountingTokens() = default; +}; + +} // namespace anonymous_counting_tokens +} // namespace private_join_and_compute + +#endif // PRIVATE_JOIN_AND_COMPUTE_ANONYMOUS_COUNTING_TOKENS_FAKE_ACT_H_ diff --git a/act/fake_act_test.cc b/act/fake_act_test.cc new file mode 100644 index 0000000..cc478b2 --- /dev/null +++ b/act/fake_act_test.cc @@ -0,0 +1,93 @@ +/* + * Copyright 2023 Google LLC. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "act/fake_act.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "private_join_and_compute/util/status.inc" +#include "private_join_and_compute/util/status_testing.inc" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { +namespace { + +TEST(FakeActTest, FingerprintsAreEqualExactlyWhenMessagesAreEqual) { + auto fake_act = FakeAnonymousCountingTokens::Create(); + + std::vector<std::string> messages_1 = {"m1", "m2"}; + std::vector<std::string> fingerprints_1; + + std::vector<std::string> messages_2 = {"m3", "m1"}; + std::vector<std::string> fingerprints_2; + + // Empty parameters for testing the fake. + SchemeParameters scheme_parameters; + ClientPublicParameters client_public_parameters; + ClientPrivateParameters client_private_parameters; + ServerPublicParameters server_public_parameters; + + ASSERT_OK_AND_ASSIGN( + std::tie(fingerprints_1, std::ignore, std::ignore), + fake_act->GenerateTokensRequest( + messages_1, scheme_parameters, client_public_parameters, + client_private_parameters, server_public_parameters)); + ASSERT_OK_AND_ASSIGN( + std::tie(fingerprints_2, std::ignore, std::ignore), + fake_act->GenerateTokensRequest( + messages_2, scheme_parameters, client_public_parameters, + client_private_parameters, server_public_parameters)); + + // Only the fingerprints for "m1" should be the same. + EXPECT_EQ(fingerprints_1[0], fingerprints_2[1]); + EXPECT_NE(fingerprints_1[0], fingerprints_2[0]); + EXPECT_NE(fingerprints_1[1], fingerprints_2[1]); + EXPECT_NE(fingerprints_1[1], fingerprints_2[0]); +} + +TEST(FakeActTest, DifferentTokensAreNotEqual) { + auto fake_act = FakeAnonymousCountingTokens::Create(); + + std::vector<std::string> messages = {"m1", "m2"}; + // Empty parameters and messages for testing the fake. + TokensRequest tokens_request; + TokensRequestPrivateState tokens_request_private_state; + TokensResponse tokens_response; + SchemeParameters scheme_parameters; + ClientPublicParameters client_public_parameters; + ClientPrivateParameters client_private_parameters; + ServerPublicParameters server_public_parameters; + + ASSERT_OK_AND_ASSIGN( + std::vector<Token> tokens, + fake_act->RecoverTokens( + messages, tokens_request, tokens_request_private_state, + tokens_response, scheme_parameters, client_public_parameters, + client_private_parameters, server_public_parameters)); + + EXPECT_NE(tokens[0].SerializeAsString(), tokens[1].SerializeAsString()); +} + +} // namespace +} // namespace anonymous_counting_tokens + +} // namespace private_join_and_compute |