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 // Implementation of Pedersen's commitment scheme over Z_n.
17 // Pedersen, Torben Pryds. "Non-interactive and information-theoretic secure
18 // verifiable secret sharing." (1998).
19 //
20 // A commitment scheme allows a party to commit to a message, and later open the
21 // commitment to reveal the message underlying the commitment. It has two key
22 // security properties: hiding, meaning that given only the commitment, the
23 // message is hidden, and binding, which means that once a commitment to m has
24 // been created, it is hard for the creator to open it to a different value m'.
25 //
26 // Pedersen commitments can be created over any group where discrete logarithm
27 // is hard. The example below is in terms of the group Z*_n, for n the product
28 // of two large primes p and q. An alternative is to use the modulus n = p, a
29 // safe prime.
30 //
31 // Usage pattern:
32 // Let P1 and P2 be two parties.
33 // P1 : n <- GenerateSafeModulus(ctx, modulus_length);
34 // {g, h, n, r} <- PedersenOverZn::GenerateParams(ctx, n);
35 // proof <- PedersenOverZn::
36 // ProveParamsCorrectlyGenerated (
37 // ctx, g, h, n, r)
38 // OR
39 // Pedersen::ProveParamsCorrectlyGeneratedFor
40 // TrustedModulus(ctx, g, h, n, r)
41 // Send (g, h, n) and proof to P2.
42 // (NOTE: params.r should be kept secret, see security note below)
43 //
44 // P2 : Check PedersenOverZn::VerifyParamsProof(ctx, g, h, n, proof).ok()
45 // pedersen <- new Pedersen(ctx, g, h, n)
46 // commitment, opening <- pedersen.Commit(m)
47 // Send commitment to P1
48 //
49 // < P1 and P2 do some work>
50 //
51 // P2 : Send opening to P1
52 //
53 // P1 : Reads m from open
54 // b <- pedersen.Verify(commitment, opening)
55 // If b is true, P1 accepts that "commitment" contained m.
56 //
57 // Variant: the parameters could be selected such that there are multiple gs. In
58 // this case, each g_i should be in <h>, and the discrete log of g_i with
59 // respect to h should be hidden from the committing party. If this is the case,
60 // when we use k gs, we can commit k messages in a single commitment. We call
61 // these vector-commitments.
62 //
63 // Important Security Notes:
64 // It is important above that the commitment parameters should be generated by
65 // someone other than the committing party, or in some oblivious manner. In
66 // particular, in order to guarantee binding, it is important that the
67 // committing party not know "r", the discrete logarithm of h with respect to g.
68 // In the case where n is the product of 2 primes, the committing party should
69 // also not know the prime decomposition of n, or it can break the binding
70 // property of the commitment scheme.
71 //
72 // For the case where n is composite, it is also usually appropriate for the
73 // party generating the parameters (P1 in the example above) to send a proof
74 // that the parameters g,h,n were correctly generated, and in particular, that g
75 // is in <h>. This is important because if g is not in <h>, then P1 can
76 // potentially break the hiding property of commitments sent by P2. A sigma
77 // protocol of knowledge of r such that g = h^r mod n is sufficient to prove g
78 // is in <h>, and there are two implementations provided here, one when the
79 // modulus is completely untrusted, and one when the modulus has previously been
80 // proven to be the product of 2 safe primes. (See go/untrusted_sigma_protocols
81 // for a more detailed discussion of sigma protocols when the modulus is
82 // untrusted)
83
84 #ifndef PRIVATE_JOIN_AND_COMPUTE_CRYPTO_PEDERSEN_OVER_ZN_H_
85 #define PRIVATE_JOIN_AND_COMPUTE_CRYPTO_PEDERSEN_OVER_ZN_H_
86
87 #include <memory>
88 #include <vector>
89
90 #include "private_join_and_compute/crypto/big_num.h"
91 #include "private_join_and_compute/crypto/context.h"
92 #include "private_join_and_compute/crypto/proto/pedersen.pb.h"
93 #include "private_join_and_compute/crypto/simultaneous_fixed_bases_exp.h"
94 #include "private_join_and_compute/util/status.inc"
95
96 namespace private_join_and_compute {
97
98 // Splits vector into subvectors of size subvector_size. Requires copy
99 // constructor. This is helpful for splitting a long vector into subvectors
100 // small enough to each fit into a single commitment.
101 template <typename T>
SplitVector(std::vector<T> input,int subvector_size)102 std::vector<std::vector<T>> SplitVector(std::vector<T> input,
103 int subvector_size) {
104 int num_batches = (input.size() + subvector_size - 1) / subvector_size;
105
106 std::vector<std::vector<T>> output;
107 output.reserve(num_batches);
108
109 for (int i = 0; i < num_batches; i++) {
110 size_t start_offset = i * subvector_size;
111 size_t end_offset = std::min(input.size(), start_offset + subvector_size);
112 output.emplace_back(input.begin() + start_offset,
113 input.begin() + end_offset);
114 }
115
116 return output;
117 }
118
119 // Class to allowing committing to and verifying Pedersen's commitments over Zn.
120 class PedersenOverZn {
121 public:
122 // Will compute ((2^num_simultaneous * number_of_bases) / num_simultaneous) -
123 // sized table for fixed base exponentiation.
124 static const size_t kDefaultMaxNumSimultaneousExponentiations = 10;
125
126 typedef BigNum Commitment;
127 typedef BigNum Opening;
128
129 struct CommitmentAndOpening {
130 Commitment commitment;
131 Opening opening;
132 };
133
134 struct Parameters {
135 std::vector<BigNum> gs; // generators for the messages.
136 BigNum h; // generator for randomness
137 BigNum n; // modulus
138 // secret parameters used to prove that each entry of gs is in <h>.
139 // Optional.
140 std::vector<BigNum> rs;
141 };
142
143 // This proof has several repetitions, each with a boolean challenge.
144 // The challenges are implicit, and must be reconstructed at verification time
145 // as the output of RandomOracle(g, h, n, dummy_gs).
146 struct ProofOfGen {
147 int num_repetitions;
148 std::vector<std::unique_ptr<BigNum>> dummy_gs;
149 std::vector<std::unique_ptr<BigNum>> responses;
150 };
151
152 // This proof has a single, long challenge of "challenge_length" bits.
153 // The challenge is implicit, and must be reconstructed at verification time
154 // as the output of RandomOracle(g, h, n, dummy_g).
155 struct ProofOfGenForTrustedModulus {
156 int challenge_length;
157 BigNum dummy_g;
158 BigNum response;
159 };
160
161 // Takes a Context, a modulus n, generators gs and h for the large subgroup
162 // of Z*n such that each g is in <h>. Does not take ownership of the Context.
163 // n may be either a safe prime ( n = 2q + 1 for some prime q), or the product
164 // of 2 safe primes (n = pq, where p = 2p' +1 and q = 2q'+1). h should be a
165 // generator.
166 //
167 // Note: when gs and h are supplied by a different party, it is appropriate to
168 // require a proof that each g is in <h> before using them for commitments,
169 // otherwise the secrecy of the commitment can be compromised. When n is a
170 // safe prime with n = 2q+1, this is equivalent to checking that g_i^q ==1 and
171 // h^q == 1.
172 //
173 // num_simultaneous_exponentiations (k for short) controls the amount of
174 // precomputation performed. If there are fewer bases than this value, it will
175 // be reset to gs.size(). It will result in (2^k)*(gs.size()/k) amount of
176 // precomputation, which speeds up commitment by a factor of k.
177 //
178 // Returns INTERNAL if any of the precomputation operations fail.
179 static StatusOr<std::unique_ptr<PedersenOverZn>> Create(
180 Context* ctx, std::vector<BigNum> gs, const BigNum& h, const BigNum& n,
181 size_t num_simultaneous_exponentiations =
182 kDefaultMaxNumSimultaneousExponentiations);
183
184 // Parses parameters and creates a Pedersen object. Fails when the underlying
185 // "Create" call fails.
186 static StatusOr<std::unique_ptr<PedersenOverZn>> FromProto(
187 Context* ctx, const proto::PedersenParameters& parameters_proto,
188 size_t num_simultaneous_exponentiations =
189 kDefaultMaxNumSimultaneousExponentiations);
190
191 // Pedersen is neither copyable nor movable.
192 PedersenOverZn(const PedersenOverZn&) = delete;
193 PedersenOverZn& operator=(const PedersenOverZn&) = delete;
194
195 virtual ~PedersenOverZn();
196
197 // Getters
gs()198 inline const std::vector<BigNum>& gs() { return gs_; }
h()199 inline const BigNum& h() { return h_; }
n()200 inline const BigNum& n() { return n_; }
201
202 // Creates a commitment to a vector of messages m, returning the commitment
203 // and the corresponding opening. If fewer than gs.size() messages are
204 // provided, the remaining messages are assumed to be 0.
205 //
206 // Returns "INVALID_ARGUMENT" status if any m is not >= 0, or if
207 // messages.size() > gs.size().
208 //
209 // Note: a commitment creating using this method has perfect secrecy if each
210 // g is in <h>, but is binding ONLY if for all g, the committing party does
211 // not know r such that g = h^r mod n, and also the committing party does not
212 // know the factorization of n.
213 //
214 // Additionally, the binding property only holds relative to the order of g.
215 // So if n is a safe prime 2q +1, binding holds only relative to messages < q,
216 // and if n is the product of safe primes, binding holds only relative to
217 // messages less than n/4. The size of messages is not checked in this method.
218 StatusOr<CommitmentAndOpening> Commit(
219 const std::vector<BigNum>& messages) const;
220
221 // Creates a commitment to a vector of messages m, together with the
222 // corresponding opening, using the specified randomness for commitment. If
223 // fewer than gs.size() messages are provided, the remaining messages are
224 // assumed to be 0.
225 //
226 // Returns "INVALID_ARGUMENT" status if any m or r is not >= 0, or if
227 // messages.size() > gs.size().
228 //
229 // This method allows the messages and randomness to be larger than normal,
230 // and is intended to be used only for proofs of knowledge and equality of a
231 // committed message. As part of such a proof, the prover sends large values
232 // r1 and r2 (the prover's responses to the verifier's challenge), such that
233 // the verifier needs to create a commitment to r1 using randomness r2 in
234 // order to check the proof.
235 //
236 // This method should be avoided in all scenarios other than the one
237 // described above, and Pedersen::Commit should be used instead.
238 StatusOr<Commitment> CommitWithRand(const std::vector<BigNum>& messages,
239 const BigNum& rand) const;
240
241 // Homomorphically adds two commitments.
242 // The opening randomness of the resulting commitment is the sum of the
243 // opening randomness of com1 and com2.
244 Commitment Add(const Commitment& com1, const Commitment& com2) const;
245
246 // Homomorphically multiplies a commitment with a given scalar.
247 // The opening randomness of the resulting commitment is scalar times the
248 // opening randomness of com.
249 Commitment Multiply(const Commitment& com, const BigNum& scalar) const;
250
251 // Verifies an opening to a given commitment.
252 //
253 // The commitment binding property only holds relative to the order of each g.
254 // So if n is a safe prime 2q +1, binding holds only relative to messages < q,
255 // and if n is the product of safe primes, binding holds only relative to
256 // messages less than n/4.
257 //
258 // Returns "INVALID_ARGUMENT" status if either of m or r in the opening is
259 // negative.
260 StatusOr<bool> Verify(const Commitment& com,
261 const std::vector<BigNum>& messages,
262 const BigNum& opening) const;
263
264 /////////////////////////////////////////////////////////////////////////////
265 // Static methods to generate fresh parameters, and prove that they were
266 // correctly generated.
267 /////////////////////////////////////////////////////////////////////////////
268
269 // Create parameters for Pedersen's commitment scheme, given a modulus n that
270 // is assumed to be the product of 2 safe primes. num_gs is the number of
271 // "gs" to have in the parameters, which corresponds to the number of messages
272 // that can be simultaneously vector-committed.
273 static Parameters GenerateParameters(Context* ctx, const BigNum& n,
274 int64_t num_gs = 1);
275
276 // Creates a proof that the parameters were correctly generated, namely that
277 // g = h^r mod n for some r. The proof is a set of Schnorr sigma protocols,
278 // repeated in parallel, in the random oracle model, using the Fiat-Shamir
279 // heuristic. It is designed specifically for the case when n is NOT known to
280 // be a safe modulus. To deal with the possible unsafety of the modulus, each
281 // repetition of the sigma protocol has a boolean challenge. This maintains
282 // soundness of the proof even when the modulus is untrusted.
283 // The num_repetitions parameter specifies the number of parallel repetitions
284 // of the sigma protocol to perform. The soundness error of the protocol is
285 // 2^(-num_repetitions). A larger value implies better soundness, but note
286 // that the size of the proof grows linearly with the number of repetitions.
287 // Suggested value is 128 repetitions.
288 // The zk_quality parameter tunes the size of the prover's response in the
289 // sigma protocol. A large zk_quality parameter increases the length of the
290 // response relative to the size of the challenge, which improves how well the
291 // proof hides the secret exponent r. Suggested value is 128 bits.
292 // Returns INVALID_ARGUMENT if the inputs do not correspond to correctly
293 // generated parameters, or if either of num_repetitions or zk_quality are
294 // nonpositive.
295 static StatusOr<ProofOfGen> ProveParametersCorrectlyGenerated(
296 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
297 const BigNum& r, int num_repetitions = 128, int zk_quality = 128);
298
299 // Returns OK if the proof verifies, and a descriptive INVALID_ARGUMENT error
300 // if it doesn't.
301 static Status VerifyParamsProof(Context* ctx, const BigNum& g,
302 const BigNum& h, const BigNum& n,
303 const ProofOfGen& proof);
304
305 // Creates a proof that the parameters were correctly generated, namely that
306 // g = h^r mod n for some r. The proof is a Schnorr sigma protocol in the
307 // random oracle model, using the Fiat-Shamir heuristic. It is designed
308 // specifically for the case when n is known to be a safe modulus.
309 // The zk_quality parameter tunes the size of the prover's response in the
310 // sigma protocol. A large zk_quality parameter increases the length of the
311 // response relative to the size of the challenge, which improves how well the
312 // proof hides the secret exponent r. Suggested value is 128 bits.
313 // Returns INVALID_ARGUMENT if the inputs do not correspond to correctly
314 // generated parameters, or if either of challenge_length or zk_quality are
315 // nonpositive.
316 static StatusOr<ProofOfGenForTrustedModulus>
317 ProveParametersCorrectlyGeneratedForTrustedModulus(
318 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
319 const BigNum& r, int challenge_length = 128, int zk_quality = 128);
320
321 // Returns OK if the proof verifies, and a descriptive INVALID_ARGUMENT
322 // error if it doesn't.
323 static Status VerifyParamsProofForTrustedModulus(
324 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
325 const ProofOfGenForTrustedModulus& proof);
326
327 // Helper method that generates the Fiat-Shamir random oracle challenge
328 // for the proof that the Pedersen parameters were generated correctly, for
329 // the case of a potentially unsafe modulus.
330 // Exposed for testing only.
331 static std::vector<uint8_t> GetGenProofChallenge(
332 Context* ctx, const BigNum& g, const BigNum& h, const BigNum& n,
333 const std::vector<std::unique_ptr<BigNum>>& dummy_gs,
334 int num_repetitions);
335
336 // Helper method that generates the Fiat-Shamir random oracle challenge for
337 // the proof that the Pedersen parameters were generated correctly for the
338 // case of a modulus known to be the product of 2 safe primes.
339 // Exposed for testing only.
340 static BigNum GetTrustedGenProofChallenge(Context* ctx, const BigNum& g,
341 const BigNum& h, const BigNum& n,
342 const BigNum& dummy_g,
343 int challenge_length);
344
345 // Serializes the parameters to a proto (does not serialize rs if provided).
346 static proto::PedersenParameters ParametersToProto(
347 const Parameters& parameters);
348
349 // Serializes the parameters to a proto (does not deserialize rs since these
350 // are not stored).
351 static StatusOr<Parameters> ParseParametersProto(
352 Context* ctx, const proto::PedersenParameters& parameters);
353
354 private:
355 PedersenOverZn(
356 Context* ctx, std::vector<BigNum> gs, const BigNum& h, const BigNum& n,
357 std::unique_ptr<SimultaneousFixedBasesExp<ZnElement, ZnContext>>
358 simultaneous_fixed_bases_exp);
359
360 Context* ctx_;
361 const std::vector<BigNum> gs_;
362 const BigNum h_;
363 const BigNum n_;
364 std::unique_ptr<SimultaneousFixedBasesExp<ZnElement, ZnContext>>
365 simultaneous_fixed_bases_exp_;
366 };
367
368 } // namespace private_join_and_compute
369
370 #endif // PRIVATE_JOIN_AND_COMPUTE_CRYPTO_PEDERSEN_OVER_ZN_H_
371