1 /*
2 * Copyright 2019 Google LLC.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * https://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "private_join_and_compute/crypto/pedersen_over_zn.h"
17
18 #include <algorithm>
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "absl/strings/str_cat.h"
25 #include "private_join_and_compute/crypto/big_num.h"
26 #include "private_join_and_compute/crypto/proto/big_num.pb.h"
27 #include "private_join_and_compute/crypto/proto/pedersen.pb.h"
28 #include "private_join_and_compute/crypto/proto/proto_util.h"
29 #include "private_join_and_compute/util/status.inc"
30
31 namespace private_join_and_compute {
32
PedersenOverZn(Context * ctx,std::vector<BigNum> gs,const BigNum & h,const BigNum & n,std::unique_ptr<SimultaneousFixedBasesExp<ZnElement,ZnContext>> simultaneous_fixed_bases_exp)33 PedersenOverZn::PedersenOverZn(
34 Context* ctx, std::vector<BigNum> gs, const BigNum& h, const BigNum& n,
35 std::unique_ptr<SimultaneousFixedBasesExp<ZnElement, ZnContext>>
36 simultaneous_fixed_bases_exp)
37 : ctx_(ctx),
38 gs_(std::move(gs)),
39 h_(h),
40 n_(n),
41 simultaneous_fixed_bases_exp_(std::move(simultaneous_fixed_bases_exp)) {}
42
Create(Context * ctx,std::vector<BigNum> gs,const BigNum & h,const BigNum & n,size_t num_simultaneous_exponentiations)43 StatusOr<std::unique_ptr<PedersenOverZn>> PedersenOverZn::Create(
44 Context* ctx, std::vector<BigNum> gs, const BigNum& h, const BigNum& n,
45 size_t num_simultaneous_exponentiations) {
46 // The set of bases is gs_, with h_ appended at the end.
47 std::vector<private_join_and_compute::BigNum> bases = gs;
48 bases.push_back(h);
49
50 std::unique_ptr<ZnContext> zn_context(new ZnContext({n}));
51
52 int adjusted_num_simultaneous_exponentiations =
53 std::min(bases.size(), num_simultaneous_exponentiations);
54
55 auto simultaneous_fixed_bases_exp =
56 SimultaneousFixedBasesExp<ZnElement, ZnContext>::Create(
57 bases, ctx->One(), adjusted_num_simultaneous_exponentiations,
58 std::move(zn_context))
59 .value();
60
61 return absl::WrapUnique(new PedersenOverZn(
62 ctx, std::move(gs), h, n, std::move(simultaneous_fixed_bases_exp)));
63 }
64
FromProto(Context * ctx,const proto::PedersenParameters & parameters_proto,size_t num_simultaneous_exponentiations)65 StatusOr<std::unique_ptr<PedersenOverZn>> PedersenOverZn::FromProto(
66 Context* ctx, const proto::PedersenParameters& parameters_proto,
67 size_t num_simultaneous_exponentiations) {
68 ASSIGN_OR_RETURN(PedersenOverZn::Parameters parameters,
69 PedersenOverZn::ParseParametersProto(ctx, parameters_proto));
70 return PedersenOverZn::Create(ctx, std::move(parameters.gs), parameters.h,
71 parameters.n, num_simultaneous_exponentiations);
72 }
73
74 PedersenOverZn::~PedersenOverZn() = default;
75
Commit(const std::vector<BigNum> & messages) const76 StatusOr<PedersenOverZn::CommitmentAndOpening> PedersenOverZn::Commit(
77 const std::vector<BigNum>& messages) const {
78 BigNum r = ctx_->GenerateRandLessThan(n_);
79 ASSIGN_OR_RETURN(auto commitment,
80 PedersenOverZn::CommitWithRand(messages, r));
81 return {{std::move(commitment), std::move(r)}};
82 }
83
CommitWithRand(const std::vector<BigNum> & messages,const BigNum & rand) const84 StatusOr<PedersenOverZn::Commitment> PedersenOverZn::CommitWithRand(
85 const std::vector<BigNum>& messages, const BigNum& rand) const {
86 if (messages.size() > gs_.size()) {
87 return InvalidArgumentError(
88 "PedersenOverZn::Commit() : too many messages provided");
89 }
90
91 for (const auto& message : messages) {
92 if (!message.IsNonNegative()) {
93 return InvalidArgumentError(
94 "PedersenOverZn::Commit(): cannot commit to negative value.");
95 }
96 }
97 if (!rand.IsNonNegative()) {
98 return InvalidArgumentError(
99 "PedersenOverZn::CommitWithRand(): randomness must be nonnegative.");
100 }
101
102 std::vector<BigNum> exponents = messages;
103 // Add dummy 0s if fewer messages were provided.
104 while (exponents.size() < gs_.size()) {
105 exponents.push_back(ctx_->Zero());
106 }
107 // Push back the exponent for h_.
108 exponents.push_back(rand);
109 ASSIGN_OR_RETURN(BigNum product,
110 simultaneous_fixed_bases_exp_->SimultaneousExp(exponents));
111
112 return std::move(product);
113 }
114
Add(const PedersenOverZn::Commitment & com1,const PedersenOverZn::Commitment & com2) const115 PedersenOverZn::Commitment PedersenOverZn::Add(
116 const PedersenOverZn::Commitment& com1,
117 const PedersenOverZn::Commitment& com2) const {
118 return com1.ModMul(com2, n_);
119 }
120
Multiply(const PedersenOverZn::Commitment & com,const BigNum & scalar) const121 PedersenOverZn::Commitment PedersenOverZn::Multiply(
122 const PedersenOverZn::Commitment& com, const BigNum& scalar) const {
123 return com.ModExp(scalar, n_);
124 }
125
Verify(const PedersenOverZn::Commitment & commitment,const std::vector<BigNum> & messages,const PedersenOverZn::Opening & opening) const126 StatusOr<bool> PedersenOverZn::Verify(
127 const PedersenOverZn::Commitment& commitment,
128 const std::vector<BigNum>& messages,
129 const PedersenOverZn::Opening& opening) const {
130 if (messages.size() > gs_.size()) {
131 return InvalidArgumentError(
132 "PedersenOverZn::Verify() : too many messages provided");
133 }
134
135 for (const auto& message : messages) {
136 if (!message.IsNonNegative()) {
137 return InvalidArgumentError(
138 "PedersenOverZn::Verify(): message in the opening is negative.");
139 }
140 }
141 if (!opening.IsNonNegative()) {
142 return InvalidArgumentError(
143 "PedersenOverZn::Verify(): randomness in the opening is negative.");
144 }
145
146 std::vector<BigNum> exponents = messages;
147 // Add dummy 0s if fewer messages were provided.
148 while (exponents.size() < gs_.size()) {
149 exponents.push_back(ctx_->Zero());
150 }
151 // Push back the exponent for h_.
152 exponents.push_back(opening);
153 ASSIGN_OR_RETURN(BigNum product,
154 simultaneous_fixed_bases_exp_->SimultaneousExp(exponents));
155
156 return commitment == product;
157 }
158
GenerateParameters(Context * ctx,const BigNum & n,int64_t num_gs)159 PedersenOverZn::Parameters PedersenOverZn::GenerateParameters(Context* ctx,
160 const BigNum& n,
161 int64_t num_gs) {
162 // Chooses a random quadratic residue as h = (x^2) mod n for random x. Except
163 // with probability O(1/n), this is a generator for the subgroup of order
164 // (p-1)(q-1)/4 in Z*n.
165 BigNum x = ctx->RelativelyPrimeRandomLessThan(n);
166 BigNum h = x.ModSqr(n);
167
168 std::vector<BigNum> gs;
169 std::vector<BigNum> rs;
170 for (int i = 0; i < num_gs; i++) {
171 BigNum r =
172 ctx->GenerateRandLessThan(n.DivAndTruncate(ctx->CreateBigNum(4)));
173 gs.push_back(h.ModExp(r, n));
174 rs.push_back(std::move(r));
175 }
176 return {std::move(gs), std::move(h), n, std::move(rs)};
177 }
178
GetGenProofChallenge(Context * ctx,const BigNum & g,const BigNum & h,const BigNum & n,const std::vector<std::unique_ptr<BigNum>> & dummy_gs,int num_repetitions)179 std::vector<uint8_t> PedersenOverZn::GetGenProofChallenge(
180 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
181 const std::vector<std::unique_ptr<BigNum>>& dummy_gs, int num_repetitions) {
182 std::string bytes;
183 bytes.append(g.ToBytes());
184 bytes.append(h.ToBytes());
185 bytes.append(n.ToBytes());
186 for (auto& dummy_g : dummy_gs) {
187 bytes.append(dummy_g->ToBytes());
188 }
189
190 // Generates a single combined challenge, and then derive the individual
191 // challenges by breaking down the combined challenge into its individual
192 // bits.
193 BigNum combined_challenge =
194 ctx->RandomOracleSha512(bytes, ctx->One().Lshift(num_repetitions));
195
196 std::vector<uint8_t> challenges;
197 for (int i = 0; i < num_repetitions; i++) {
198 uint8_t challenge = combined_challenge.IsBitSet(0);
199 challenges.push_back(challenge);
200 combined_challenge = combined_challenge.Rshift(1);
201 }
202
203 return challenges;
204 }
205
206 StatusOr<PedersenOverZn::ProofOfGen>
ProveParametersCorrectlyGenerated(Context * ctx,const BigNum & g,const BigNum & h,const BigNum & n,const BigNum & r,int num_repetitions,int zk_quality)207 PedersenOverZn::ProveParametersCorrectlyGenerated(
208 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
209 const BigNum& r, int num_repetitions, int zk_quality) {
210 if (num_repetitions <= 0) {
211 return InvalidArgumentError(
212 "PedersenOverZn::ProveParametersCorrectlyGenerated :: number of "
213 "repetitions "
214 "must be positive.");
215 }
216 if (zk_quality <= 0) {
217 return InvalidArgumentError(
218 "PedersenOverZn::ProveParametersCorrectlyGenerated :: zk_quality "
219 "parameter "
220 "must be positive.");
221 }
222 if (h.Gcd(n) != ctx->One()) {
223 return InvalidArgumentError(
224 "PedersenOverZn::ProveParametersCorrectlyGenerated :: parameters are "
225 "not "
226 "valid: h is not relatively prime to n.");
227 }
228 if (g != h.ModExp(r, n)) {
229 return InvalidArgumentError(
230 "PedersenOverZn::ProveParametersCorrectlyGenerated :: parameters are "
231 "not "
232 "valid: g != h^r mod n.");
233 }
234
235 // Generate first prover message for each repetition of the sigma protocol.
236 std::vector<std::unique_ptr<BigNum>> dummy_rs;
237 std::vector<std::unique_ptr<BigNum>> dummy_gs;
238 for (int i = 0; i < num_repetitions; i++) {
239 std::unique_ptr<BigNum> dummy_r(
240 new BigNum(ctx->GenerateRandLessThan(n.Lshift(1 + zk_quality))));
241 std::unique_ptr<BigNum> dummy_g(new BigNum(h.ModExp(*dummy_r, n)));
242
243 dummy_rs.push_back(std::move(dummy_r));
244 dummy_gs.push_back(std::move(dummy_g));
245 }
246
247 // Generate boolean challenges for each repetition of the sigma protocol
248 std::vector<uint8_t> challenges =
249 GetGenProofChallenge(ctx, g, h, n, dummy_gs, num_repetitions);
250
251 // Generate responses for each proof repetition. If the challenge for the
252 // repetition was "1", the response is dummy_r + r, otherwise, it is simply
253 // dummy_r.
254 std::vector<std::unique_ptr<BigNum>> responses;
255 for (int i = 0; i < num_repetitions; i++) {
256 std::unique_ptr<BigNum> response;
257 if (challenges[i] == 1) {
258 response = std::make_unique<BigNum>(dummy_rs[i]->Add(r));
259 } else {
260 response = std::make_unique<BigNum>(*dummy_rs[i]);
261 }
262
263 responses.push_back(std::move(response));
264 }
265
266 return PedersenOverZn::ProofOfGen{num_repetitions, std::move(dummy_gs),
267 std::move(responses)};
268 }
269
VerifyParamsProof(Context * ctx,const BigNum & g,const BigNum & h,const BigNum & n,const PedersenOverZn::ProofOfGen & proof)270 Status PedersenOverZn::VerifyParamsProof(
271 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
272 const PedersenOverZn::ProofOfGen& proof) {
273 if (proof.num_repetitions <= 0) {
274 return InvalidArgumentError(
275 "PedersenOverZn::VerifyParamsProof :: proof is not valid: number of "
276 "repetitions must be positive.");
277 }
278 if (proof.dummy_gs.size() != proof.num_repetitions) {
279 return InvalidArgumentError(
280 "PedersenOverZn::VerifyParamsProof :: proof is not valid: number of "
281 "dummy_gs is different from number of repetitions specified.");
282 }
283 if (proof.responses.size() != proof.num_repetitions) {
284 return InvalidArgumentError(
285 "PedersenOverZn::VerifyParamsProof :: proof is not valid: number of "
286 "responses is different from number of repetitions specified.");
287 }
288 if (h.Gcd(n) != ctx->One()) {
289 return InvalidArgumentError(
290 "PedersenOverZn::VerifyParamsProof :: parameters are not valid, h is "
291 "not "
292 "relatively prime to n.");
293 }
294
295 // reconstruct the challenges
296 std::vector<uint8_t> challenges = PedersenOverZn::GetGenProofChallenge(
297 ctx, g, h, n, proof.dummy_gs, proof.num_repetitions);
298
299 // checks each response to make sure it is valid for the challenge.
300 for (int i = 0; i < proof.num_repetitions; i++) {
301 BigNum expected_output = *proof.dummy_gs[i];
302 if (challenges[i] == 1) {
303 expected_output = expected_output.ModMul(g, n);
304 }
305 if (h.ModExp(*(proof.responses[i]), n) != expected_output) {
306 return InvalidArgumentError(absl::StrCat(
307 "PedersenOverZn::VerifyParamsProof :: the proof verification formula "
308 "fails at index ",
309 i, "."));
310 }
311 }
312
313 return OkStatus();
314 }
315
GetTrustedGenProofChallenge(Context * ctx,const BigNum & g,const BigNum & h,const BigNum & n,const BigNum & dummy_g,int challenge_length)316 BigNum PedersenOverZn::GetTrustedGenProofChallenge(
317 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
318 const BigNum& dummy_g, int challenge_length) {
319 std::string bytes;
320 bytes.append(g.ToBytes());
321 bytes.append(h.ToBytes());
322 bytes.append(n.ToBytes());
323 bytes.append(dummy_g.ToBytes());
324 BigNum challenge =
325 ctx->RandomOracleSha512(bytes, ctx->One().Lshift(challenge_length));
326 return challenge;
327 }
328
329 StatusOr<PedersenOverZn::ProofOfGenForTrustedModulus>
ProveParametersCorrectlyGeneratedForTrustedModulus(Context * ctx,const BigNum & g,const BigNum & h,const BigNum & n,const BigNum & r,int challenge_length,int zk_quality)330 PedersenOverZn::ProveParametersCorrectlyGeneratedForTrustedModulus(
331 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
332 const BigNum& r, int challenge_length, int zk_quality) {
333 if (challenge_length <= 0) {
334 return InvalidArgumentError(
335 "PedersenOverZn::ProveParametersCorrectlyGeneratedForTrustedModulus :: "
336 "challenge length must be positive.");
337 }
338 if (zk_quality <= 0) {
339 return InvalidArgumentError(
340 "PedersenOverZn::ProveParametersCorrectlyGeneratedForTrustedModulus :: "
341 "zk_quality parameter must be positive.");
342 }
343 if (h.Gcd(n) != ctx->One()) {
344 return InvalidArgumentError(
345 "PedersenOverZn::ProveParametersCorrectlyGeneratedForTrustedModulus :: "
346 "parameters are not valid: h is not relatively prime to n.");
347 }
348 if (g != h.ModExp(r, n)) {
349 return InvalidArgumentError(
350 "PedersenOverZn::ProveParametersCorrectlyGeneratedForTrustedModulus :: "
351 "parameters are not valid: g != h^r mod n.");
352 }
353
354 BigNum dummy_r =
355 ctx->GenerateRandLessThan(n.Lshift(challenge_length + zk_quality));
356 BigNum dummy_g = h.ModExp(dummy_r, n);
357
358 BigNum challenge = PedersenOverZn::GetTrustedGenProofChallenge(
359 ctx, g, h, n, dummy_g, challenge_length);
360
361 BigNum response = dummy_r + (challenge * r);
362
363 return {{challenge_length, std::move(dummy_g), std::move(response)}};
364 }
365
VerifyParamsProofForTrustedModulus(Context * ctx,const BigNum & g,const BigNum & h,const BigNum & n,const PedersenOverZn::ProofOfGenForTrustedModulus & proof)366 Status PedersenOverZn::VerifyParamsProofForTrustedModulus(
367 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
368 const PedersenOverZn::ProofOfGenForTrustedModulus& proof) {
369 if (proof.challenge_length <= 0) {
370 return InvalidArgumentError(
371 "PedersenOverZn::VerifyParamsProofForTrustedModulus :: proof is not "
372 "valid: "
373 "challenge length must be positive.");
374 }
375 if (h.Gcd(n) != ctx->One()) {
376 return InvalidArgumentError(
377 "PedersenOverZn::VerifyParamsProofForTrustedModulus :: parameters are "
378 "not "
379 "valid, h is not relatively prime to n.");
380 }
381
382 BigNum challenge = PedersenOverZn::GetTrustedGenProofChallenge(
383 ctx, g, h, n, proof.dummy_g, proof.challenge_length);
384
385 // checks h^response == g^challenge * dummy_g mod n.
386 if (h.ModExp(proof.response, n) !=
387 g.ModExp(challenge, n).ModMul(proof.dummy_g, n)) {
388 return InvalidArgumentError(
389 "PedersenOverZn::VerifyParamsProofForTrustedModulus :: the proof "
390 "verification formula fails.");
391 }
392
393 return OkStatus();
394 }
395
ParametersToProto(const PedersenOverZn::Parameters & parameters)396 proto::PedersenParameters PedersenOverZn::ParametersToProto(
397 const PedersenOverZn::Parameters& parameters) {
398 proto::PedersenParameters parameters_proto;
399 parameters_proto.set_n(parameters.n.ToBytes());
400 *parameters_proto.mutable_gs() = BigNumVectorToProto(parameters.gs);
401 parameters_proto.set_h(parameters.h.ToBytes());
402 return parameters_proto;
403 }
404
ParseParametersProto(Context * ctx,const proto::PedersenParameters & parameters_proto)405 StatusOr<PedersenOverZn::Parameters> PedersenOverZn::ParseParametersProto(
406 Context* ctx, const proto::PedersenParameters& parameters_proto) {
407 BigNum n = ctx->CreateBigNum(parameters_proto.n());
408 if (n <= ctx->Zero()) {
409 return absl::InvalidArgumentError(
410 "PedersenOverZn::FromProto: n must be positive.");
411 }
412 std::vector<BigNum> gs = ::private_join_and_compute::ParseBigNumVectorProto(
413 ctx, parameters_proto.gs());
414 for (const BigNum& g : gs) {
415 if (g <= ctx->Zero() || g >= n || g.Gcd(n) != ctx->One()) {
416 return absl::InvalidArgumentError(
417 "PedersenOverZn::FromProto: g must be in (0, n) and relatively prime "
418 "to n.");
419 }
420 }
421 BigNum h = ctx->CreateBigNum(parameters_proto.h());
422 if (h <= ctx->Zero() || h >= n || h.Gcd(n) != ctx->One()) {
423 return absl::InvalidArgumentError(
424 "PedersenOverZn::FromProto: h must be in (0, n) and relatively prime "
425 "to n.");
426 }
427 return PedersenOverZn::Parameters{
428 std::move(gs), std::move(h), std::move(n), {}};
429 }
430
431 } // namespace private_join_and_compute
432