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