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