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/camenisch_shoup.h"
17 
18 #include <cstdint>
19 #include <map>
20 #include <memory>
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/camenisch_shoup.pb.h"
27 #include "private_join_and_compute/crypto/proto/proto_util.h"
28 #include "private_join_and_compute/util/status.inc"
29 
30 namespace private_join_and_compute {
31 
32 namespace {
33 
34 // Returns a vector of (1 / (i!)) * n^i mod n^(s+1) for i in [0, s]. Modulus
35 // should be n^(s+1).
GetPrecomp(Context * ctx,const BigNum & n,const BigNum & modulus,uint64_t s)36 std::vector<BigNum> GetPrecomp(Context* ctx, const BigNum& n,
37                                const BigNum& modulus, uint64_t s) {
38   std::vector<BigNum> precomp;
39   precomp.push_back(ctx->CreateBigNum(1));
40   for (uint64_t i = 1; i <= s; i++) {
41     BigNum i_inv = ctx->CreateBigNum(i).ModInverse(modulus).value();
42     BigNum i_inv_n = i_inv.ModMul(n, modulus);
43     precomp.push_back(precomp.back().ModMul(i_inv_n, modulus));
44   }
45   return precomp;
46 }
47 
48 // Returns a vector of num^i for i in [0, s + 1].
GetPowers(Context * ctx,const BigNum & num,int s)49 std::vector<BigNum> GetPowers(Context* ctx, const BigNum& num, int s) {
50   std::vector<BigNum> powers;
51   powers.push_back(ctx->CreateBigNum(1));
52   for (int i = 1; i <= s + 1; i++) {
53     powers.push_back(powers.back().Mul(num));
54   }
55   return powers;
56 }
57 
58 // Returns a table of (1 / (k!)) * n^(k - 1) mod n^j for 2 <= k <= j <= s.
59 // Reuses the values from GetPrecomp function output, precomp. The result is a
60 // table that maps (k,j) to the BigNum (1 / (k!)) * n^(k - 1) mod n^j, for all
61 // (k,j) with 2 <= k <= j <= s
GetDecryptPrecomp(Context * ctx,const std::vector<BigNum> & precomp,const std::vector<BigNum> & powers,int s)62 std::map<std::pair<int, int>, BigNum> GetDecryptPrecomp(
63     Context* ctx, const std::vector<BigNum>& precomp,
64     const std::vector<BigNum>& powers, int s) {
65   // The first index is k and the second one is j from the Theorem 1 algorithm
66   // of Damgaard-Jurik-Nielsen paper.
67   // The table indices are [2, s] in each dimension with the following
68   // structure:
69   //     j
70   //  +-----+
71   //   -----|
72   //    ----|  k
73   //     ---|
74   //      --|
75   //       -+
76   std::map<std::pair<int, int>, BigNum> precomp_table;
77   for (int k = 2; k <= s; k++) {
78     BigNum k_inverse = ctx->CreateBigNum(k).ModInverse(powers[s]).value();
79     precomp_table.insert(
80         {std::make_pair(k, s), k_inverse.ModMul(precomp[k - 1], powers[s])});
81     for (int j = s - 1; j >= k; j--) {
82       precomp_table.insert(
83           {std::make_pair(k, j),
84            precomp_table.at(std::make_pair(k, j + 1)).Mod(powers[j])});
85     }
86   }
87   return precomp_table;
88 }
89 
90 // Computes (1 + powers[1])^message via binomial expansion (message=m):
91 // 1 + mn + C(m, 2)n^2 + ... + C(m, s)n^s mod n^(s + 1).
ComputeByBinomialExpansion(Context * ctx,const std::vector<BigNum> & precomp,const std::vector<BigNum> & powers,const BigNum & message)92 BigNum ComputeByBinomialExpansion(Context* ctx,
93                                   const std::vector<BigNum>& precomp,
94                                   const std::vector<BigNum>& powers,
95                                   const BigNum& message) {
96   // Refer to Section 4.2 Optimizations of Encryption from the Damgaard-Jurik
97   // cryptosystem paper.
98   BigNum c = ctx->CreateBigNum(1);
99   BigNum tmp = ctx->CreateBigNum(1);
100   const int s = precomp.size() - 1;
101   BigNum reduced_message = message.Mod(powers[s]);
102   for (int j = 1; j <= s; j++) {
103     const BigNum& j_bn = ctx->CreateBigNum(j);
104     if (reduced_message < j_bn) {
105       break;
106     }
107     tmp = tmp.ModMul(reduced_message - j_bn + ctx->One(), powers[s - j + 1]);
108     c = c + tmp.ModMul(precomp[j], powers[s + 1]);
109   }
110   return c;
111 }
112 
CommonEncryptWithRand(Context * ctx,const std::vector<BigNum> & ms,const BigNum & r,const BigNum & n,const BigNum & n_to_s,const std::vector<BigNum> & precomp,const std::vector<BigNum> & powers,const FixedBaseExp * g_fbe,const std::vector<std::unique_ptr<FixedBaseExp>> & ys_fbe,const BigNum & modulus)113 StatusOr<CamenischShoupCiphertext> CommonEncryptWithRand(
114     Context* ctx, const std::vector<BigNum>& ms, const BigNum& r,
115     const BigNum& n, const BigNum& n_to_s, const std::vector<BigNum>& precomp,
116     const std::vector<BigNum>& powers, const FixedBaseExp* g_fbe,
117     const std::vector<std::unique_ptr<FixedBaseExp>>& ys_fbe,
118     const BigNum& modulus) {
119   if (ms.size() > ys_fbe.size()) {
120     return InvalidArgumentError(absl::StrCat(
121         "CamenischShoup::EncryptWithRand: Too many messages: max = ",
122         ys_fbe.size(), ", given = ", ms.size()));
123   }
124   if (!r.IsNonNegative() || (!r.Gcd(n).IsOne() && !r.IsZero())) {
125     return InvalidArgumentError(
126         "CamenischShoup::EncryptWithRand() - r must be >=0 and "
127         "not share prime factors with n.");
128   }
129 
130   ASSIGN_OR_RETURN(BigNum u, g_fbe->ModExp(r));
131 
132   std::vector<BigNum> es;
133   es.reserve(ys_fbe.size());
134   for (size_t i = 0; i < ys_fbe.size(); i++) {
135     ASSIGN_OR_RETURN(BigNum y_to_r, ys_fbe[i]->ModExp(r));
136     if (i < ms.size()) {
137       BigNum one_plus_n_to_m =
138           ComputeByBinomialExpansion(ctx, precomp, powers, ms[i]);
139       BigNum e = (y_to_r * one_plus_n_to_m).Mod(modulus);
140       es.push_back(e);
141     } else {
142       // Implicitly encrypt 0 if |ms| < |ys|.
143       es.push_back(y_to_r);
144     }
145   }
146   return {{std::move(u), std::move(es)}};
147 }
148 
CommonEncryptAndGetRand(Context * ctx,const std::vector<BigNum> & ms,const BigNum & n,const BigNum & n_to_s,const std::vector<BigNum> & precomp,const std::vector<BigNum> & powers,const FixedBaseExp * g_fbe,const std::vector<std::unique_ptr<FixedBaseExp>> & ys_fbe,const BigNum & modulus)149 StatusOr<CamenischShoupCiphertextWithRand> CommonEncryptAndGetRand(
150     Context* ctx, const std::vector<BigNum>& ms, const BigNum& n,
151     const BigNum& n_to_s, const std::vector<BigNum>& precomp,
152     const std::vector<BigNum>& powers, const FixedBaseExp* g_fbe,
153     const std::vector<std::unique_ptr<FixedBaseExp>>& ys_fbe,
154     const BigNum& modulus) {
155   for (const BigNum& m : ms) {
156     if (!m.IsNonNegative()) {
157       return InvalidArgumentError(
158           "CamenischShoup::EncryptAndGetRand() - Cannot encrypt negative "
159           "number.");
160     }
161   }
162 
163   BigNum r = ctx->RelativelyPrimeRandomLessThan(n);
164   ASSIGN_OR_RETURN(CamenischShoupCiphertext ct,
165                    CommonEncryptWithRand(ctx, ms, r, n, n_to_s, precomp, powers,
166                                          g_fbe, ys_fbe, modulus));
167 
168   return {{std::move(ct), std::move(r)}};
169 }
170 
CommonEncrypt(Context * ctx,const std::vector<BigNum> & ms,const BigNum & n,const BigNum & n_to_s,const std::vector<BigNum> & precomp,const std::vector<BigNum> & powers,const FixedBaseExp * g_fbe,const std::vector<std::unique_ptr<FixedBaseExp>> & ys_fbe,const BigNum & modulus)171 StatusOr<CamenischShoupCiphertext> CommonEncrypt(
172     Context* ctx, const std::vector<BigNum>& ms, const BigNum& n,
173     const BigNum& n_to_s, const std::vector<BigNum>& precomp,
174     const std::vector<BigNum>& powers, const FixedBaseExp* g_fbe,
175     const std::vector<std::unique_ptr<FixedBaseExp>>& ys_fbe,
176     const BigNum& modulus) {
177   ASSIGN_OR_RETURN(auto encryption_and_randomness,
178                    CommonEncryptAndGetRand(ctx, ms, n, n_to_s, precomp, powers,
179                                            g_fbe, ys_fbe, modulus));
180   return {std::move(encryption_and_randomness.ct)};
181 }
182 
183 // A common helper: generates a key with a pre-specified modulus. The fields "p"
184 // and "q" are "0" in the returned key.
GenerateCamenischShoupKeyBase(Context * ctx,const BigNum & n,uint64_t s,uint64_t vector_encryption_length)185 CamenischShoupKey GenerateCamenischShoupKeyBase(
186     Context* ctx, const BigNum& n, uint64_t s,
187     uint64_t vector_encryption_length) {
188   BigNum g = GetGeneratorForCamenischShoup(ctx, n, s);
189 
190   std::vector<BigNum> xs;
191   std::vector<BigNum> ys;
192   xs.reserve(vector_encryption_length);
193   ys.reserve(vector_encryption_length);
194   for (uint64_t i = 0; i < vector_encryption_length; i++) {
195     BigNum x = ctx->RelativelyPrimeRandomLessThan(n);
196     BigNum y = g.ModExp(x, n.Exp(ctx->CreateBigNum(s + 1)));
197     xs.emplace_back(std::move(x));
198     ys.emplace_back(std::move(y));
199   }
200 
201   return CamenischShoupKey{
202       ctx->Zero(),   ctx->Zero(),  n, s, vector_encryption_length, std::move(g),
203       std::move(ys), std::move(xs)};
204 }
205 
CommonParseCiphertextProto(Context * ctx,const BigNum & modulus,uint64_t vector_encryption_length,const proto::CamenischShoupCiphertext & ct_proto)206 StatusOr<CamenischShoupCiphertext> CommonParseCiphertextProto(
207     Context* ctx, const BigNum& modulus, uint64_t vector_encryption_length,
208     const proto::CamenischShoupCiphertext& ct_proto) {
209   BigNum u = ctx->CreateBigNum(ct_proto.u());
210   std::vector<BigNum> es = ParseBigNumVectorProto(ctx, ct_proto.es());
211   if (u >= modulus || !u.IsNonNegative()) {
212     return absl::InvalidArgumentError(
213         "CommonParseCiphertextProto: u must be in [0, modulus).");
214   }
215   if (es.size() > vector_encryption_length) {
216     return absl::InvalidArgumentError(
217         "CommonParseCiphertextProto: es has too many components.");
218   }
219   for (const BigNum& es_component : es) {
220     if (es_component >= modulus || !es_component.IsNonNegative()) {
221       return absl::InvalidArgumentError(
222           "CommonParseCiphertextProto: some element of es is not in [0, "
223           "modulus).");
224     }
225   }
226   return CamenischShoupCiphertext{std::move(u), std::move(es)};
227 }
228 
229 }  // namespace
230 
GetGeneratorForCamenischShoup(Context * ctx,const BigNum & n,uint64_t s)231 BigNum GetGeneratorForCamenischShoup(Context* ctx, const BigNum& n,
232                                      uint64_t s) {
233   BigNum n_to_s = n.Exp(ctx->CreateBigNum(s));
234   BigNum n_to_s_plus_1 = n.Exp(ctx->CreateBigNum(s + 1));
235   BigNum x = ctx->RelativelyPrimeRandomLessThan(n_to_s_plus_1);
236   return x.ModExp((ctx->Two() * n_to_s), n_to_s_plus_1);
237 }
238 
GenerateCamenischShoupKey(Context * ctx,int n_length_bits,uint64_t s,uint64_t vector_encryption_length)239 CamenischShoupKey GenerateCamenischShoupKey(Context* ctx, int n_length_bits,
240                                             uint64_t s,
241                                             uint64_t vector_encryption_length) {
242   BigNum p = ctx->GenerateSafePrime(n_length_bits / 2);
243   BigNum q = ctx->GenerateSafePrime(n_length_bits / 2);
244   while (p == q) {
245     q = ctx->GenerateSafePrime(n_length_bits / 2);
246   }
247   BigNum n = p * q;
248   CamenischShoupKey key =
249       GenerateCamenischShoupKeyBase(ctx, n, s, vector_encryption_length);
250   key.p = std::move(p);
251   key.q = std::move(q);
252   return key;
253 }
254 
255 std::pair<std::unique_ptr<CamenischShoupPublicKey>,
256           std::unique_ptr<CamenischShoupPrivateKey>>
GenerateCamenischShoupKeyPair(Context * ctx,const BigNum & n,uint64_t s,uint64_t vector_encryption_length)257 GenerateCamenischShoupKeyPair(Context* ctx, const BigNum& n, uint64_t s,
258                               uint64_t vector_encryption_length) {
259   CamenischShoupKey cs_key =
260       GenerateCamenischShoupKeyBase(ctx, n, s, vector_encryption_length);
261 
262   auto public_key =
263       std::make_unique<CamenischShoupPublicKey>(CamenischShoupPublicKey{
264           std::move(cs_key.n), cs_key.s, cs_key.vector_encryption_length,
265           std::move(cs_key.g), std::move(cs_key.ys)});
266   auto private_key = std::make_unique<CamenischShoupPrivateKey>(
267       CamenischShoupPrivateKey{std::move(cs_key.xs)});
268 
269   return std::make_pair(std::move(public_key), std::move(private_key));
270 }
271 
272 // Creates a proto from the PublicKey struct.
CamenischShoupPublicKeyToProto(const CamenischShoupPublicKey & public_key)273 proto::CamenischShoupPublicKey CamenischShoupPublicKeyToProto(
274     const CamenischShoupPublicKey& public_key) {
275   proto::CamenischShoupPublicKey public_key_proto;
276   public_key_proto.set_n(public_key.n.ToBytes());
277   public_key_proto.set_g(public_key.g.ToBytes());
278   *public_key_proto.mutable_ys() = BigNumVectorToProto(public_key.ys);
279   public_key_proto.set_s(public_key.s);
280   return public_key_proto;
281 }
282 
ParseCamenischShoupPublicKeyProto(Context * ctx,const proto::CamenischShoupPublicKey & public_key_proto)283 StatusOr<CamenischShoupPublicKey> ParseCamenischShoupPublicKeyProto(
284     Context* ctx, const proto::CamenischShoupPublicKey& public_key_proto) {
285   BigNum n = ctx->CreateBigNum(public_key_proto.n());
286   if (n <= ctx->Zero()) {
287     return absl::InvalidArgumentError(
288         "FromProto: CamenischShoupPublicKey has n that's <= 0");
289   }
290   uint64_t s = public_key_proto.s();
291   if (s == 0) {
292     return absl::InvalidArgumentError(
293         "FromProto: CamenischShoupPublicKey has s = 0");
294   }
295   BigNum modulus = n.Exp(ctx->CreateBigNum(s + 1));
296   BigNum g = ctx->CreateBigNum(public_key_proto.g());
297   if (g <= ctx->Zero() || g >= modulus || g.Gcd(n) != ctx->One()) {
298     return absl::InvalidArgumentError(
299         "FromProto: CamenischShoupPublicKey has invalid g");
300   }
301   std::vector<BigNum> ys = ParseBigNumVectorProto(ctx, public_key_proto.ys());
302   uint64_t vector_encryption_length = ys.size();
303   if (ys.empty()) {
304     return absl::InvalidArgumentError(
305         "FromProto: CamenischShoupPublicKey has empty ys");
306   }
307   for (const BigNum& y : ys) {
308     if (y <= ctx->Zero() || y >= modulus || y.Gcd(n) != ctx->One()) {
309       return absl::InvalidArgumentError(
310           "FromProto: CamenischShoupPublicKey has invalid component in ys");
311     }
312   }
313   return CamenischShoupPublicKey{std::move(n), s, vector_encryption_length,
314                                  std::move(g), std::move(ys)};
315 }
316 
317 // Creates a proto from the PrivateKey struct.
CamenischShoupPrivateKeyToProto(const CamenischShoupPrivateKey & private_key)318 proto::CamenischShoupPrivateKey CamenischShoupPrivateKeyToProto(
319     const CamenischShoupPrivateKey& private_key) {
320   proto::CamenischShoupPrivateKey private_key_proto;
321   *private_key_proto.mutable_xs() = BigNumVectorToProto(private_key.xs);
322   return private_key_proto;
323 }
324 
ParseCamenischShoupPrivateKeyProto(Context * ctx,const proto::CamenischShoupPrivateKey & private_key_proto)325 StatusOr<CamenischShoupPrivateKey> ParseCamenischShoupPrivateKeyProto(
326     Context* ctx, const proto::CamenischShoupPrivateKey& private_key_proto) {
327   std::vector<BigNum> xs = ParseBigNumVectorProto(ctx, private_key_proto.xs());
328   return CamenischShoupPrivateKey{std::move(xs)};
329 }
330 
331 // Creates a proto from the Ciphertext struct.
CamenischShoupCiphertextToProto(const CamenischShoupCiphertext & ciphertext)332 proto::CamenischShoupCiphertext CamenischShoupCiphertextToProto(
333     const CamenischShoupCiphertext& ciphertext) {
334   proto::CamenischShoupCiphertext ciphertext_proto;
335   ciphertext_proto.set_u(ciphertext.u.ToBytes());
336   *ciphertext_proto.mutable_es() = BigNumVectorToProto(ciphertext.es);
337   return ciphertext_proto;
338 }
339 
PublicCamenischShoup(Context * ctx,const BigNum & n,uint64_t s,const BigNum & g,std::vector<BigNum> ys)340 PublicCamenischShoup::PublicCamenischShoup(Context* ctx, const BigNum& n,
341                                            uint64_t s, const BigNum& g,
342                                            std::vector<BigNum> ys)
343     : ctx_(ctx),
344       n_(n),
345       s_(s),
346       vector_encryption_length_(ys.size()),
347       powers_of_n_(GetPowers(ctx, n_, s_)),
348       encryption_precomp_(GetPrecomp(ctx, n_, powers_of_n_[s + 1], s)),
349       n_to_s_(powers_of_n_[s]),
350       modulus_(powers_of_n_[s + 1]),
351       g_(g),
352       ys_(std::move(ys)),
353       g_fbe_(FixedBaseExp::GetFixedBaseExp(ctx_, g_, modulus_)) {
354   ys_fbe_.reserve(ys_.size());
355   for (const BigNum& y : ys_) {
356     ys_fbe_.push_back(FixedBaseExp::GetFixedBaseExp(ctx_, y, modulus_));
357   }
358 }
359 
FromProto(Context * ctx,const proto::CamenischShoupPublicKey & public_key_proto)360 StatusOr<std::unique_ptr<PublicCamenischShoup>> PublicCamenischShoup::FromProto(
361     Context* ctx, const proto::CamenischShoupPublicKey& public_key_proto) {
362   ASSIGN_OR_RETURN(CamenischShoupPublicKey public_key,
363                    ParseCamenischShoupPublicKeyProto(ctx, public_key_proto));
364   return std::make_unique<PublicCamenischShoup>(ctx, public_key.n, public_key.s,
365                                                 public_key.g, public_key.ys);
366 }
367 
Encrypt(const std::vector<BigNum> & ms)368 StatusOr<CamenischShoupCiphertext> PublicCamenischShoup::Encrypt(
369     const std::vector<BigNum>& ms) {
370   return CommonEncrypt(ctx_, ms, n_, n_to_s_, encryption_precomp_, powers_of_n_,
371                        g_fbe_.get(), ys_fbe_, modulus_);
372 }
373 
374 StatusOr<CamenischShoupCiphertextWithRand>
EncryptAndGetRand(const std::vector<BigNum> & ms)375 PublicCamenischShoup::EncryptAndGetRand(const std::vector<BigNum>& ms) {
376   return CommonEncryptAndGetRand(ctx_, ms, n_, n_to_s_, encryption_precomp_,
377                                  powers_of_n_, g_fbe_.get(), ys_fbe_, modulus_);
378 }
379 
EncryptWithRand(const std::vector<BigNum> & ms,const BigNum & r)380 StatusOr<CamenischShoupCiphertext> PublicCamenischShoup::EncryptWithRand(
381     const std::vector<BigNum>& ms, const BigNum& r) {
382   return CommonEncryptWithRand(ctx_, ms, r, n_, n_to_s_, encryption_precomp_,
383                                powers_of_n_, g_fbe_.get(), ys_fbe_, modulus_);
384 }
385 
Add(const CamenischShoupCiphertext & ct1,const CamenischShoupCiphertext & ct2)386 CamenischShoupCiphertext PublicCamenischShoup::Add(
387     const CamenischShoupCiphertext& ct1, const CamenischShoupCiphertext& ct2) {
388   CHECK(ct1.es.size() == ct2.es.size());
389   CHECK(ct1.es.size() == vector_encryption_length_);
390   BigNum u = ct1.u.ModMul(ct2.u, modulus_);
391   std::vector<BigNum> es;
392   es.reserve(vector_encryption_length_);
393   for (uint64_t i = 0; i < vector_encryption_length_; i++) {
394     es.push_back(ct1.es[i].ModMul(ct2.es[i], modulus_));
395   }
396   return {std::move(u), std::move(es)};
397 }
398 
Multiply(const CamenischShoupCiphertext & ct,const BigNum & scalar)399 CamenischShoupCiphertext PublicCamenischShoup::Multiply(
400     const CamenischShoupCiphertext& ct, const BigNum& scalar) {
401   BigNum u = ct.u.ModExp(scalar, modulus_);
402   std::vector<BigNum> es;
403   es.reserve(vector_encryption_length_);
404   for (uint64_t i = 0; i < vector_encryption_length_; i++) {
405     es.push_back(ct.es[i].ModExp(scalar, modulus_));
406   }
407   return {std::move(u), std::move(es)};
408 }
409 
ParseCiphertextProto(const proto::CamenischShoupCiphertext & ciphertext_proto)410 StatusOr<CamenischShoupCiphertext> PublicCamenischShoup::ParseCiphertextProto(
411     const proto::CamenischShoupCiphertext& ciphertext_proto) {
412   return CommonParseCiphertextProto(ctx_, modulus_, vector_encryption_length_,
413                                     ciphertext_proto);
414 }
415 
PrivateCamenischShoup(Context * ctx,const BigNum & n,uint64_t s,const BigNum & g,std::vector<BigNum> ys,std::vector<BigNum> xs)416 PrivateCamenischShoup::PrivateCamenischShoup(Context* ctx, const BigNum& n,
417                                              uint64_t s, const BigNum& g,
418                                              std::vector<BigNum> ys,
419                                              std::vector<BigNum> xs)
420     : ctx_(ctx),
421       n_(n),
422       s_(s),
423       vector_encryption_length_(ys.size()),
424       powers_of_n_(GetPowers(ctx, n_, s_)),
425       encryption_precomp_(GetPrecomp(ctx, n_, powers_of_n_[s + 1], s)),
426       decryption_precomp_(
427           GetDecryptPrecomp(ctx, encryption_precomp_, powers_of_n_, s)),
428       n_to_s_(powers_of_n_[s]),
429       modulus_(powers_of_n_[s + 1]),
430       g_(g),
431       ys_(std::move(ys)),
432       xs_(std::move(xs)),
433       g_fbe_(FixedBaseExp::GetFixedBaseExp(ctx_, g_, modulus_)) {
434   CHECK_EQ(ys_.size(), xs_.size());
435   ys_fbe_.reserve(ys_.size());
436   for (const BigNum& y : ys_) {
437     ys_fbe_.push_back(FixedBaseExp::GetFixedBaseExp(ctx_, y, modulus_));
438   }
439 }
440 
441 StatusOr<std::unique_ptr<PrivateCamenischShoup>>
FromProto(Context * ctx,const proto::CamenischShoupPublicKey & public_key_proto,const proto::CamenischShoupPrivateKey & private_key_proto)442 PrivateCamenischShoup::FromProto(
443     Context* ctx, const proto::CamenischShoupPublicKey& public_key_proto,
444     const proto::CamenischShoupPrivateKey& private_key_proto) {
445   ASSIGN_OR_RETURN(CamenischShoupPublicKey public_key,
446                    ParseCamenischShoupPublicKeyProto(ctx, public_key_proto));
447   ASSIGN_OR_RETURN(CamenischShoupPrivateKey private_key,
448                    ParseCamenischShoupPrivateKeyProto(ctx, private_key_proto));
449   return std::make_unique<PrivateCamenischShoup>(ctx, public_key.n,
450                                                  public_key.s, public_key.g,
451                                                  public_key.ys, private_key.xs);
452 }
453 
Encrypt(const std::vector<BigNum> & ms)454 StatusOr<CamenischShoupCiphertext> PrivateCamenischShoup::Encrypt(
455     const std::vector<BigNum>& ms) {
456   return CommonEncrypt(ctx_, ms, n_, n_to_s_, encryption_precomp_, powers_of_n_,
457                        g_fbe_.get(), ys_fbe_, modulus_);
458 }
459 
460 StatusOr<CamenischShoupCiphertextWithRand>
EncryptAndGetRand(const std::vector<BigNum> & ms)461 PrivateCamenischShoup::EncryptAndGetRand(const std::vector<BigNum>& ms) {
462   return CommonEncryptAndGetRand(ctx_, ms, n_, n_to_s_, encryption_precomp_,
463                                  powers_of_n_, g_fbe_.get(), ys_fbe_, modulus_);
464 }
465 
EncryptWithRand(const std::vector<BigNum> & ms,const BigNum & r)466 StatusOr<CamenischShoupCiphertext> PrivateCamenischShoup::EncryptWithRand(
467     const std::vector<BigNum>& ms, const BigNum& r) {
468   return CommonEncryptWithRand(ctx_, ms, r, n_, n_to_s_, encryption_precomp_,
469                                powers_of_n_, g_fbe_.get(), ys_fbe_, modulus_);
470 }
471 
Decrypt(const CamenischShoupCiphertext & ct)472 StatusOr<std::vector<BigNum>> PrivateCamenischShoup::Decrypt(
473     const CamenischShoupCiphertext& ct) {
474   if (ct.es.size() != vector_encryption_length_) {
475     return InvalidArgumentError(
476         "PrivateCamenischShoup::Decrypt: ciphertext does not contain the "
477         "expected number of components.");
478   }
479 
480   // Theorem 1 algorithm from Damgaard-Jurik-Nielsen paper, but leverages
481   // the fact that lambda = 1. Cancels out the random portion and compute
482   // the L function. Remove the randomizer portion of the ciphertext, and
483   // compute L = (1+n)^m - 1 mod n^(s+1).
484 
485   std::vector<BigNum> ms;
486   ms.reserve(vector_encryption_length_);
487 
488   for (uint64_t i = 0; i < vector_encryption_length_; i++) {
489     ASSIGN_OR_RETURN(BigNum s,
490                      ct.u.ModExp(xs_[i], modulus_).ModInverse(modulus_));
491     BigNum denoised = ct.es[i].ModMul(s, modulus_);
492 
493     // m_j holds m mod n^j at the end of the j'th iteration. At the start of
494     // the loop, it holds m mod 1 = 0, and at the end it will hold m mod n^s,
495     // namely the output.
496     BigNum m_j = ctx_->CreateBigNum(0);  // m_j holds i_j, and i_0 = 0.
497     for (uint64_t j = 1; j <= s_; j++) {
498       BigNum intermediate = denoised.Mod(powers_of_n_[j + 1]) - ctx_->One();
499       if (!intermediate.Mod(n_).IsZero()) {
500         return InvalidArgumentError("Corrupt/invalid ciphertext");
501       }
502       // l_u = ((denoised mod n^(j+1)) - 1)/ n , or L(denoised mod n^(j+1))
503       BigNum l_u = intermediate.Div(n_);
504 
505       BigNum t1 = l_u;  // t1 starts as l_u
506       BigNum t2 = m_j;  // t2 starts as i_(j-1)
507       for (uint64_t k = 2; k <= j; k++) {
508         m_j = m_j - ctx_->One();
509         t2 = t2.ModMul(m_j, powers_of_n_[j]);
510         t1 = t1 - (t2.ModMul(decryption_precomp_.at({k, j}), powers_of_n_[j]));
511       }
512       // t_1 now holds L(denoised mod n^(j+1)) -
513       // ((Sum_{k=2}^s Choose (i_(j-1), k) * n^(k-1)) mod n^j), which is
514       // exactly i_j, which is m mod n^j
515       m_j = std::move(t1);
516     }
517     ms.push_back(m_j.Mod(powers_of_n_[s_]));
518   }
519 
520   return std::move(ms);
521 }
522 
ParseCiphertextProto(const proto::CamenischShoupCiphertext & ciphertext_proto)523 StatusOr<CamenischShoupCiphertext> PrivateCamenischShoup::ParseCiphertextProto(
524     const proto::CamenischShoupCiphertext& ciphertext_proto) {
525   return CommonParseCiphertextProto(ctx_, modulus_, vector_encryption_length_,
526                                     ciphertext_proto);
527 }
528 
529 }  // namespace private_join_and_compute
530