diff options
Diffstat (limited to 'act/act_v0/act_v0.cc')
-rw-r--r-- | act/act_v0/act_v0.cc | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/act/act_v0/act_v0.cc b/act/act_v0/act_v0.cc new file mode 100644 index 0000000..228f0db --- /dev/null +++ b/act/act_v0/act_v0.cc @@ -0,0 +1,918 @@ +/* + * Copyright 2023 Google LLC. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "act/act_v0/act_v0.h" + +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "act/act.pb.h" +#include "act/act_v0/act_v0.pb.h" +#include "private_join_and_compute/crypto/big_num.h" +#include "private_join_and_compute/crypto/camenisch_shoup.h" +#include "private_join_and_compute/crypto/context.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.pb.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.h" +#include "private_join_and_compute/crypto/dodis_yampolskiy_prf/dy_verifiable_random_function.pb.h" +#include "private_join_and_compute/crypto/ec_group.h" +#include "private_join_and_compute/crypto/ec_point.h" +#include "private_join_and_compute/crypto/pedersen_over_zn.h" +#include "private_join_and_compute/crypto/proto/ec_point.pb.h" +#include "private_join_and_compute/crypto/proto/proto_util.h" + +namespace private_join_and_compute { +namespace anonymous_counting_tokens { + +namespace { + +StatusOr<std::unique_ptr<BbObliviousSignature>> CreateBbObliviousSignature( + const SchemeParametersV0& scheme_parameters_v0, + const ServerPublicParametersV0& server_public_parameters_v0, Context* ctx, + ECGroup* ec_group, PedersenOverZn* pedersen, + PublicCamenischShoup* public_camenisch_shoup) { + proto::BbObliviousSignatureParameters bb_oblivious_signature_parameters; + bb_oblivious_signature_parameters.set_challenge_length_bits( + scheme_parameters_v0.challenge_length_bits()); + bb_oblivious_signature_parameters.set_security_parameter( + scheme_parameters_v0.security_parameter()); + bb_oblivious_signature_parameters.set_random_oracle_prefix( + scheme_parameters_v0.random_oracle_prefix()); + bb_oblivious_signature_parameters.set_base_g( + server_public_parameters_v0.prf_base_g()); + *bb_oblivious_signature_parameters.mutable_pedersen_parameters() = + server_public_parameters_v0.pedersen_parameters(); + *bb_oblivious_signature_parameters.mutable_camenisch_shoup_public_key() = + server_public_parameters_v0.camenisch_shoup_public_key(); + + return BbObliviousSignature::Create( + std::move(bb_oblivious_signature_parameters), ctx, ec_group, + public_camenisch_shoup, pedersen); +} + +StatusOr<std::unique_ptr<DyVerifiableRandomFunction>> CreateDyVrf( + const SchemeParametersV0& scheme_parameters_v0, + const ServerPublicParametersV0& server_public_parameters_v0, Context* ctx, + ECGroup* ec_group, PedersenOverZn* pedersen) { + proto::DyVrfParameters dy_vrf_parameters; + dy_vrf_parameters.set_challenge_length_bits( + scheme_parameters_v0.challenge_length_bits()); + dy_vrf_parameters.set_security_parameter( + scheme_parameters_v0.security_parameter()); + dy_vrf_parameters.set_random_oracle_prefix( + scheme_parameters_v0.random_oracle_prefix()); + dy_vrf_parameters.set_dy_prf_base_g(server_public_parameters_v0.prf_base_g()); + *dy_vrf_parameters.mutable_pedersen_parameters() = + server_public_parameters_v0.pedersen_parameters(); + + return DyVerifiableRandomFunction::Create(std::move(dy_vrf_parameters), ctx, + ec_group, pedersen); +} + +// Used to generate the client-independent portion of the nonce. A different +// nonce is chosen for each element in the batched token request. +StatusOr<std::vector<BigNum>> GetNoncesForTokenRequest( + Context* ctx, const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ClientPublicParameters& client_public_parameters, + const TokensRequestV0::Part1& tokens_request_part_1, + uint64_t num_messages) { + // Parses bit length of the random challenge from scheme parameters. + uint64_t challenge_length_bits = + scheme_parameters.scheme_parameters_v0().challenge_length_bits(); + // Computes the upper bound of the challenge and input to the random oracle. + BigNum challenge_upper_bound = ctx->One().Lshift(challenge_length_bits); + + // Note that the random oracle prefix is implicitly included as part of the + // parameters being serialized in the statement proto. We skip including it + // again here to avoid unnecessary duplication. + std::string challenge_string = "GetNoncesForTokenRequest:"; + auto challenge_sos = + std::make_unique<google::protobuf::io::StringOutputStream>( + &challenge_string); + auto challenge_cos = + std::make_unique<google::protobuf::io::CodedOutputStream>( + challenge_sos.get()); + challenge_cos->SetSerializationDeterministic(true); + challenge_cos->WriteVarint64(scheme_parameters.ByteSizeLong()); + challenge_cos->WriteString(SerializeAsStringInOrder(scheme_parameters)); + challenge_cos->WriteVarint64(server_public_parameters.ByteSizeLong()); + challenge_cos->WriteString( + SerializeAsStringInOrder(server_public_parameters)); + challenge_cos->WriteVarint64(client_public_parameters.ByteSizeLong()); + challenge_cos->WriteString( + SerializeAsStringInOrder(client_public_parameters)); + challenge_cos->WriteVarint64(tokens_request_part_1.ByteSizeLong()); + challenge_cos->WriteString(SerializeAsStringInOrder(tokens_request_part_1)); + challenge_cos->WriteVarint64(num_messages); + + // Delete the serialization objects to make sure they clean up and write. + challenge_cos.reset(); + challenge_sos.reset(); + + std::vector<BigNum> outputs; + outputs.reserve(num_messages); + for (uint64_t i = 0; i < num_messages; ++i) { + std::string random_oracle_input_i = absl::StrCat(challenge_string, ",", i); + outputs.push_back( + ctx->RandomOracleSha512(random_oracle_input_i, challenge_upper_bound)); + } + + return std::move(outputs); +} + +} // namespace + +std::unique_ptr<AnonymousCountingTokens> AnonymousCountingTokensV0::Create() { + return absl::WrapUnique<AnonymousCountingTokensV0>( + new AnonymousCountingTokensV0()); +} + +// Returns a fresh set of Server parameters corresponding to these +// SchemeParameters. Fails with InvalidArgument if the parameters don't +// correspond to ACT v0. +StatusOr<ServerParameters> AnonymousCountingTokensV0::GenerateServerParameters( + const SchemeParameters& scheme_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateServerParameters: supplied " + "parameters do not correspond to ACTv0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Choose base g. + ASSIGN_OR_RETURN(ECPoint dy_prf_base_g, ec_group.GetRandomGenerator()); + + // Generate RSA-Modulus and Camenisch-Shoup encryption key. + CamenischShoupKey camenisch_shoup_key = GenerateCamenischShoupKey( + &ctx, scheme_parameters_v0.modulus_length_bits(), + scheme_parameters_v0.camenisch_shoup_s(), + scheme_parameters_v0.vector_encryption_length()); + + BigNum n = camenisch_shoup_key.n; + + auto camenisch_shoup_public_key = std::make_unique<CamenischShoupPublicKey>( + CamenischShoupPublicKey{camenisch_shoup_key.n, camenisch_shoup_key.s, + camenisch_shoup_key.vector_encryption_length, + camenisch_shoup_key.g, camenisch_shoup_key.ys}); + auto camenisch_shoup_private_key = std::make_unique<CamenischShoupPrivateKey>( + CamenischShoupPrivateKey{camenisch_shoup_key.xs}); + + auto public_camenisch_shoup = std::make_unique<PublicCamenischShoup>( + &ctx, camenisch_shoup_public_key->n, camenisch_shoup_public_key->s, + camenisch_shoup_public_key->g, camenisch_shoup_public_key->ys); + + // Generate Pedersen Parameters. + PedersenOverZn::Parameters pedersen_parameters = + PedersenOverZn::GenerateParameters( + &ctx, n, scheme_parameters_v0.pedersen_batch_size()); + + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::Create(&ctx, pedersen_parameters.gs, + pedersen_parameters.h, pedersen_parameters.n)); + + ServerParameters server_parameters; + + ServerPublicParametersV0* server_public_parameters_v0 = + server_parameters.mutable_public_parameters() + ->mutable_server_public_parameters_v0(); + ASSIGN_OR_RETURN(*server_public_parameters_v0->mutable_prf_base_g(), + dy_prf_base_g.ToBytesCompressed()); + *server_public_parameters_v0->mutable_pedersen_parameters() = + PedersenOverZn::ParametersToProto(pedersen_parameters); + *server_public_parameters_v0->mutable_camenisch_shoup_public_key() = + CamenischShoupPublicKeyToProto(*camenisch_shoup_public_key); + + ServerPrivateParametersV0* server_private_parameters_v0 = + server_parameters.mutable_private_parameters() + ->mutable_server_private_parameters_v0(); + *server_private_parameters_v0->mutable_camenisch_shoup_private_key() = + CamenischShoupPrivateKeyToProto(*camenisch_shoup_private_key); + + // Generate Boneh-Boyen Oblivious Signature object. This call is safe even + // with the partially-ready server_public_parameters. + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + *server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Generate Boneh-Boyen Oblivious Signature key. + ASSIGN_OR_RETURN( + std::tie(*server_public_parameters_v0 + ->mutable_bb_oblivious_signature_public_key(), + *server_private_parameters_v0 + ->mutable_bb_oblivious_signature_private_key()), + bb_oblivious_signature->GenerateKeys()); + + return std::move(server_parameters); +} + +// Returns a fresh set of Client parameters corresponding to these +// SchemeParameters and ServerPublicParameters. Fails with InvalidArgument if +// the parameters don't correspond to ACT v0. +StatusOr<ClientParameters> AnonymousCountingTokensV0::GenerateClientParameters( + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateClientParameters: supplied " + "parameters do not correspond to ACT v0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize Pedersen Params + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + + // Generate Client VRF object. + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + ClientParameters client_parameters; + ClientPublicParametersV0* client_public_parameters_v0 = + client_parameters.mutable_public_parameters() + ->mutable_client_public_parameters_v0(); + ClientPrivateParametersV0* client_private_parameters_v0 = + client_parameters.mutable_private_parameters() + ->mutable_client_private_parameters_v0(); + + ASSIGN_OR_RETURN( + std::tie( + *client_public_parameters_v0->mutable_dy_vrf_public_key(), + *client_private_parameters_v0->mutable_dy_vrf_private_key(), + *client_public_parameters_v0->mutable_dy_vrf_generate_keys_proof()), + dy_vrf->GenerateKeyPair()); + + return std::move(client_parameters); +} + +// Verifies the consistency of the ClientPublicParameters with the Server and +// scheme parameters. Fails with InvalidArgument if the parameters don't +// correspond to ACT v0. +Status AnonymousCountingTokensV0::CheckClientParameters( + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0() || + !server_private_parameters.has_server_private_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::CheckClientParameters: supplied " + "parameters do not correspond to ACT v0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const ClientPublicParametersV0& client_public_parameters_v0 = + client_public_parameters.client_public_parameters_v0(); + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize Pedersen Params + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + + // Generate Client VRF object. + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + // Verify the proof for the Client's VRF key. + return dy_vrf->VerifyGenerateKeysProof( + client_public_parameters_v0.dy_vrf_public_key(), + client_public_parameters_v0.dy_vrf_generate_keys_proof()); +} + +// Returns a tuple of client_fingerprints, TokensRequest and +// TokensRequestPrivateState for the given set of messages. Fails with +// InvalidArgument if the parameters don't correspond to ACT v0. +StatusOr<std::tuple<std::vector<std::string>, TokensRequest, + TokensRequestPrivateState>> +AnonymousCountingTokensV0::GenerateTokensRequest( + absl::Span<const std::string> messages, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !client_private_parameters.has_client_private_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateTokensRequest: supplied " + "parameters do not correspond to ACT v0."); + } + + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ClientPublicParametersV0& client_public_parameters_v0 = + client_public_parameters.client_public_parameters_v0(); + const ClientPrivateParametersV0& client_private_parameters_v0 = + client_private_parameters.client_private_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + TokensRequest tokens_request_proto; + TokensRequestV0* tokens_request_v0 = + tokens_request_proto.mutable_tokens_request_v0(); + TokensRequestV0::Part1* tokens_request_v0_part_1 = + tokens_request_v0->mutable_part_1(); + TokensRequestPrivateState tokens_request_private_state; + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + ECPoint dy_prf_base_g, + ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g())); + + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // 1) Hash all messages to the exponent group/ BigNums. + std::vector<BigNum> hashed_messages; + hashed_messages.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + hashed_messages.push_back( + ctx.RandomOracleSha512(messages[i], ec_group.GetOrder())); + } + + // 2) Commit to hashed messages. + ASSIGN_OR_RETURN( + PedersenOverZn::CommitmentAndOpening commit_and_open_messages, + pedersen->Commit(hashed_messages)); + tokens_request_v0_part_1->set_commit_messages( + commit_and_open_messages.commitment.ToBytes()); + + // 3) Generate client nonces and commit to them. + std::vector<BigNum> client_nonces; + client_nonces.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + client_nonces.push_back(ec_group.GeneratePrivateKey()); + } + ASSIGN_OR_RETURN( + PedersenOverZn::CommitmentAndOpening commit_and_open_client_nonces, + pedersen->Commit(client_nonces)); + tokens_request_v0_part_1->set_commit_client_nonces( + commit_and_open_client_nonces.commitment.ToBytes()); + + // 4) Perform a VRF on the committed messages and serialize as fingerprints. + ASSIGN_OR_RETURN( + std::vector<ECPoint> prf_evaluations, + dy_vrf->Apply(hashed_messages, + client_private_parameters_v0.dy_vrf_private_key())); + std::vector<std::string> fingerprints; + fingerprints.reserve(prf_evaluations.size()); + for (size_t i = 0; i < prf_evaluations.size(); ++i) { + ASSIGN_OR_RETURN(std::string fingerprint, + prf_evaluations[i].ToBytesCompressed()); + fingerprints.push_back(std::move(fingerprint)); + } + + // Also create the proof that the fingerprints were correctly generated. + ASSIGN_OR_RETURN(*tokens_request_v0_part_1->mutable_fingerprints_proof(), + dy_vrf->GenerateApplyProof( + hashed_messages, prf_evaluations, + client_public_parameters_v0.dy_vrf_public_key(), + client_private_parameters_v0.dy_vrf_private_key(), + commit_and_open_messages)); + + // 5) Generate server nonces by hashing the preceding portion of the request. + ASSIGN_OR_RETURN(std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, *tokens_request_v0_part_1, + messages.size())); + // We commit the "server_nonces" with randomness 0, which is ok since they + // are known to both parties, and furthermore will be homomorphically added to + // the "client_nonces" which have properly generated randomness. + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + + // 6) Homomorphically compute commitments to the nonces (rs) + std::vector<BigNum> nonces; + nonces.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + // No mod performed here, since the homomorphic addition of the commitments + // will not be mod-ed, and we want consistency. + nonces.push_back(server_nonces[i] + client_nonces[i]); + } + PedersenOverZn::Commitment commit_nonce = pedersen->Add( + commit_server_nonces, commit_and_open_client_nonces.commitment); + PedersenOverZn::Opening commit_nonce_opening = + commit_and_open_client_nonces.opening; + + *tokens_request_private_state.mutable_tokens_request_private_state_v0() + ->mutable_nonces() = BigNumVectorToProto(nonces); + + // 7) Generate Boneh-Boyen Oblivious Signature Request request. + ASSIGN_OR_RETURN( + std::tie( + *tokens_request_v0->mutable_bb_oblivious_signature_request(), + *tokens_request_v0->mutable_bb_oblivious_signature_request_proof(), + *tokens_request_private_state + .mutable_tokens_request_private_state_v0() + ->mutable_bb_oblivious_signature_request_private_state()), + bb_oblivious_signature->GenerateRequestAndProof( + hashed_messages, nonces, + server_public_parameters_v0.bb_oblivious_signature_public_key(), + commit_and_open_messages, {commit_nonce, commit_nonce_opening})); + + return std::make_tuple(std::move(fingerprints), + std::move(tokens_request_proto), + std::move(tokens_request_private_state)); +} + +// Returns OkStatus on a valid request. Fails with InvalidArgument if the +// parameters don't correspond to ACT v0. +Status AnonymousCountingTokensV0::CheckTokensRequest( + absl::Span<const std::string> client_fingerprints, + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ClientPublicParametersV0& client_public_parameters_v0 = + client_public_parameters.client_public_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + + // Construct the DY VRF object + ASSIGN_OR_RETURN( + std::unique_ptr<DyVerifiableRandomFunction> dy_vrf, + CreateDyVrf(scheme_parameters_v0, server_public_parameters_v0, &ctx, + &ec_group, pedersen.get())); + + PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_messages()); + + std::vector<ECPoint> deserialized_fingerprints; + deserialized_fingerprints.reserve(client_fingerprints.size()); + for (size_t i = 0; i < client_fingerprints.size(); ++i) { + ASSIGN_OR_RETURN(ECPoint deserialized_fingerprint, + ec_group.CreateECPoint(client_fingerprints[i])); + + // Test that the deserialized fingerprint reserializes to the exact same + // value. + ASSIGN_OR_RETURN(std::string reserialized_fingerprint, + deserialized_fingerprint.ToBytesCompressed()); + if (reserialized_fingerprint != client_fingerprints[i]) { + return absl::InvalidArgumentError(absl::StrCat( + "AnonymousCountingTokensV0::CheckTokensRequest: client_fingerprints[", + i, + "] comes out to a different value when serialized and " + "deserialized.")); + } + + deserialized_fingerprints.push_back(std::move(deserialized_fingerprint)); + } + + RETURN_IF_ERROR(dy_vrf->VerifyApplyProof( + deserialized_fingerprints, + client_public_parameters_v0.dy_vrf_public_key(), commit_messages, + tokens_request_v0.part_1().fingerprints_proof())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Regenerate the commitments to messages and nonces (rs) by replaying the + // steps the client took to generate them. + PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_client_nonces()); + + ASSIGN_OR_RETURN( + std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, tokens_request.tokens_request_v0().part_1(), + tokens_request.tokens_request_v0() + .bb_oblivious_signature_request() + .num_messages())); + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + PedersenOverZn::Commitment commit_nonce = + pedersen->Add(commit_server_nonces, commit_client_nonces); + + return bb_oblivious_signature->VerifyRequest( + server_public_parameters_v0.bb_oblivious_signature_public_key(), + tokens_request_v0.bb_oblivious_signature_request(), + tokens_request_v0.bb_oblivious_signature_request_proof(), commit_messages, + commit_nonce); +} + +// Returns the TokensResponse. Fails with InvalidArgument if the parameters +// don't correspond to ACT v0. +StatusOr<TokensResponse> AnonymousCountingTokensV0::GenerateTokensResponse( + const TokensRequest& tokens_request, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0() || + !server_private_parameters.has_server_private_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::GenerateTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const ServerPrivateParametersV0& server_private_parameters_v0 = + server_private_parameters.server_private_parameters_v0(); + + TokensResponse tokens_response; + TokensResponseV0* tokens_response_v0 = + tokens_response.mutable_tokens_response_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + std::unique_ptr<PrivateCamenischShoup> private_camenisch_shoup, + PrivateCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key(), + server_private_parameters_v0.camenisch_shoup_private_key())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Regenerate the commitments to messages and nonces (rs) by replaying the + // steps the client took to generate them. + PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_messages()); + PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_client_nonces()); + + ASSIGN_OR_RETURN( + std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, tokens_request.tokens_request_v0().part_1(), + tokens_request.tokens_request_v0() + .bb_oblivious_signature_request() + .num_messages())); + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + PedersenOverZn::Commitment commit_nonce = + pedersen->Add(commit_server_nonces, commit_client_nonces); + + // Generate response and proof for the Boneh-Boyen Oblivious Signature. + ASSIGN_OR_RETURN( + std::tie( + *tokens_response_v0->mutable_bb_oblivious_signature_response(), + *tokens_response_v0->mutable_bb_oblivious_signature_response_proof()), + bb_oblivious_signature->GenerateResponseAndProof( + tokens_request_v0.bb_oblivious_signature_request(), + server_public_parameters_v0.bb_oblivious_signature_public_key(), + server_private_parameters_v0.bb_oblivious_signature_private_key(), + commit_messages, commit_nonce, private_camenisch_shoup.get())); + + return std::move(tokens_response); +} + +// Returns OkStatus on a valid response. Fails with InvalidArgument if the +// parameters don't correspond to ACT v0. +Status AnonymousCountingTokensV0::VerifyTokensResponse( + absl::Span<const std::string> messages, const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !tokens_response.has_tokens_response_v0() || + !tokens_request_private_state.has_tokens_request_private_state_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !client_private_parameters.has_client_private_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const TokensResponseV0& tokens_response_v0 = + tokens_response.tokens_response_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Regenerate the commitments to messages and nonces (rs) by replaying the + // steps the client took to generate them. + PedersenOverZn::Commitment commit_messages = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_messages()); + PedersenOverZn::Commitment commit_client_nonces = ctx.CreateBigNum( + tokens_request.tokens_request_v0().part_1().commit_client_nonces()); + + ASSIGN_OR_RETURN( + std::vector<BigNum> server_nonces, + GetNoncesForTokenRequest( + &ctx, scheme_parameters, server_public_parameters, + client_public_parameters, tokens_request.tokens_request_v0().part_1(), + tokens_request.tokens_request_v0() + .bb_oblivious_signature_request() + .num_messages())); + ASSIGN_OR_RETURN(PedersenOverZn::Commitment commit_server_nonces, + pedersen->CommitWithRand(server_nonces, ctx.Zero())); + PedersenOverZn::Commitment commit_nonce = + pedersen->Add(commit_server_nonces, commit_client_nonces); + + return bb_oblivious_signature->VerifyResponse( + server_public_parameters_v0.bb_oblivious_signature_public_key(), + tokens_response_v0.bb_oblivious_signature_response(), + tokens_response_v0.bb_oblivious_signature_response_proof(), + tokens_request_v0.bb_oblivious_signature_request(), commit_messages, + commit_nonce); +} + +// Returns a vector of tokens corresponding to the supplied messages. Fails +// with InvalidArgument if the parameters don't correspond to ACT v0. +StatusOr<std::vector<Token>> AnonymousCountingTokensV0::RecoverTokens( + absl::Span<const std::string> messages, const TokensRequest& tokens_request, + const TokensRequestPrivateState& tokens_request_private_state, + const TokensResponse& tokens_response, + const SchemeParameters& scheme_parameters, + const ClientPublicParameters& client_public_parameters, + const ClientPrivateParameters& client_private_parameters, + const ServerPublicParameters& server_public_parameters) { + if (!tokens_request.has_tokens_request_v0() || + !tokens_request_private_state.has_tokens_request_private_state_v0() || + !tokens_response.has_tokens_response_v0() || + !scheme_parameters.has_scheme_parameters_v0() || + !client_public_parameters.has_client_public_parameters_v0() || + !client_private_parameters.has_client_private_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyTokensResponse: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokensRequestV0& tokens_request_v0 = tokens_request.tokens_request_v0(); + const TokensRequestPrivateStateV0& tokens_request_private_state_v0 = + tokens_request_private_state.tokens_request_private_state_v0(); + const TokensResponseV0& tokens_response_v0 = + tokens_response.tokens_response_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + + // Deserialize and create cryptographic objects. + ASSIGN_OR_RETURN( + std::unique_ptr<PedersenOverZn> pedersen, + PedersenOverZn::FromProto( + &ctx, server_public_parameters_v0.pedersen_parameters())); + ASSIGN_OR_RETURN( + std::unique_ptr<PublicCamenischShoup> public_camenisch_shoup, + PublicCamenischShoup::FromProto( + &ctx, server_public_parameters_v0.camenisch_shoup_public_key())); + ASSIGN_OR_RETURN( + ECPoint dy_prf_base_g, + ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g())); + + // Deserialize Boneh-Boyen Oblivious Signature parameters and keys + ASSIGN_OR_RETURN( + std::unique_ptr<BbObliviousSignature> bb_oblivious_signature, + CreateBbObliviousSignature(scheme_parameters_v0, + server_public_parameters_v0, &ctx, &ec_group, + pedersen.get(), public_camenisch_shoup.get())); + + // Extract message PRF evaluations + ASSIGN_OR_RETURN(std::vector<ECPoint> signatures, + bb_oblivious_signature->ExtractResults( + tokens_response_v0.bb_oblivious_signature_response(), + tokens_request_v0.bb_oblivious_signature_request(), + tokens_request_private_state_v0 + .bb_oblivious_signature_request_private_state())); + + // Package tokens. + std::vector<BigNum> nonces = + ParseBigNumVectorProto(&ctx, tokens_request_private_state_v0.nonces()); + + std::vector<Token> tokens; + tokens.reserve(messages.size()); + for (size_t i = 0; i < messages.size(); ++i) { + Token token; + TokenV0* token_v0 = token.mutable_token_v0(); + token.set_nonce_bytes(nonces[i].ToBytes()); + ASSIGN_OR_RETURN(*token_v0->mutable_bb_signature(), + signatures[i].ToBytesCompressed()); + tokens.push_back(std::move(token)); + } + + return std::move(tokens); +} + +// Returns OkStatus on valid tokens. Fails with InvalidArgument if the +// parameters don't correspond to ACT v0. +Status AnonymousCountingTokensV0::VerifyToken( + std::string m, const Token& token, + const SchemeParameters& scheme_parameters, + const ServerPublicParameters& server_public_parameters, + const ServerPrivateParameters& server_private_parameters) { + if (!token.has_token_v0() || !scheme_parameters.has_scheme_parameters_v0() || + !server_public_parameters.has_server_public_parameters_v0() || + !server_private_parameters.has_server_private_parameters_v0()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyToken: supplied " + "parameters do not correspond to ACT v0."); + } + + const TokenV0& token_v0 = token.token_v0(); + const SchemeParametersV0& scheme_parameters_v0 = + scheme_parameters.scheme_parameters_v0(); + const ServerPublicParametersV0& server_public_parameters_v0 = + server_public_parameters.server_public_parameters_v0(); + const ServerPrivateParametersV0& server_private_parameters_v0 = + server_private_parameters.server_private_parameters_v0(); + + Context ctx; + ASSIGN_OR_RETURN(ECGroup ec_group, + ECGroup::Create(scheme_parameters_v0.prf_ec_group(), &ctx)); + ASSIGN_OR_RETURN( + ECPoint dy_prf_base_g, + ec_group.CreateECPoint(server_public_parameters_v0.prf_base_g())); + BigNum k = ctx.CreateBigNum( + server_private_parameters_v0.bb_oblivious_signature_private_key().k()); + BigNum y = ctx.CreateBigNum( + server_private_parameters_v0.bb_oblivious_signature_private_key().y()); + + BigNum hashed_message = ctx.RandomOracleSha512(m, ec_group.GetOrder()); + BigNum nonce = ctx.CreateBigNum(token.nonce_bytes()); + + // Verify that reserializing the nonce comes out to the same value. + if (nonce.ToBytes() != token.nonce_bytes()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyToken: nonce comes out to different " + "value when serialized and deserialized."); + } + + ASSIGN_OR_RETURN(ECPoint signature_from_token, + ec_group.CreateECPoint(token_v0.bb_signature())); + + // Verify that reserializing the signature comes out to the same value + ASSIGN_OR_RETURN(std::string reserialized_signature_from_token, + signature_from_token.ToBytesCompressed()); + if (reserialized_signature_from_token != token_v0.bb_signature()) { + return absl::InvalidArgumentError( + "AnonymousCountingTokensV0::VerifyToken: bb_signature comes out to " + "different value when serialized and deserialized."); + } + + ASSIGN_OR_RETURN( + BigNum inverted_exponent, + (hashed_message + k + (nonce * y)).ModInverse(ec_group.GetOrder())); + ASSIGN_OR_RETURN(ECPoint signature_by_evaluation, + dy_prf_base_g.Mul(inverted_exponent)); + if (signature_by_evaluation != signature_from_token) { + return absl::InvalidArgumentError( + "ACTV0::VerifyToken: Boneh-boyen signature on message and nonce fails " + "to match the token."); + } + + return absl::OkStatus(); +} + +} // namespace anonymous_counting_tokens +} // namespace private_join_and_compute |