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/dodis_yampolskiy_prf/bb_oblivious_signature.h"
17
18 #include <stdint.h>
19
20 #include <algorithm>
21 #include <cstddef>
22 #include <iterator>
23 #include <memory>
24 #include <string>
25 #include <tuple>
26 #include <utility>
27 #include <vector>
28
29 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
30 #include "absl/strings/str_cat.h"
31 #include "private_join_and_compute/crypto/big_num.h"
32 #include "private_join_and_compute/crypto/camenisch_shoup.h"
33 #include "private_join_and_compute/crypto/context.h"
34 #include "private_join_and_compute/crypto/dodis_yampolskiy_prf/bb_oblivious_signature.pb.h"
35 #include "private_join_and_compute/crypto/ec_point.h"
36 #include "private_join_and_compute/crypto/pedersen_over_zn.h"
37 #include "private_join_and_compute/crypto/proto/big_num.pb.h"
38 #include "private_join_and_compute/crypto/proto/camenisch_shoup.pb.h"
39 #include "private_join_and_compute/crypto/proto/ec_point.pb.h"
40 #include "private_join_and_compute/crypto/proto/proto_util.h"
41
42 namespace private_join_and_compute {
43
44 namespace {
45
46 // Helper functions to compute batched encryptions of Enc(a*(k + m + yr) + bq)
47 // given masked_messages (= am + bq), as, gammas (= ar), Enc(k), Enc(y), and the
48 // public_camenisch_shoup. Assumes that all sizes have been checked beforehand.
49 //
50 // Can be used for the "real" encryptions, the dummy encryptions, and the masked
51 // dummy encryptions.
52 StatusOr<std::vector<CamenischShoupCiphertext>>
GenerateHomomorphicCsCiphertexts(const std::vector<BigNum> & masked_messages,const std::vector<BigNum> & as,const std::vector<BigNum> & gammas,const std::vector<BigNum> & encryption_randomness,const std::vector<CamenischShoupCiphertext> & parsed_encrypted_k,const std::vector<CamenischShoupCiphertext> & parsed_encrypted_y,PublicCamenischShoup * public_camenisch_shoup)53 GenerateHomomorphicCsCiphertexts(
54 const std::vector<BigNum>& masked_messages, const std::vector<BigNum>& as,
55 const std::vector<BigNum>& gammas,
56 const std::vector<BigNum>& encryption_randomness,
57 const std::vector<CamenischShoupCiphertext>& parsed_encrypted_k,
58 const std::vector<CamenischShoupCiphertext>& parsed_encrypted_y,
59 PublicCamenischShoup* public_camenisch_shoup) {
60 // The messages are encrypted in batches of vector_encryption_length. We
61 // compute the number of Camenisch Shoup ciphertexts needed to cover the
62 // messages.
63 size_t num_camenisch_shoup_ciphertexts =
64 (masked_messages.size() +
65 public_camenisch_shoup->vector_encryption_length() - 1) /
66 public_camenisch_shoup->vector_encryption_length();
67
68 std::vector<CamenischShoupCiphertext> encrypted_masked_messages;
69 encrypted_masked_messages.reserve(num_camenisch_shoup_ciphertexts);
70
71 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
72 size_t batch_start_index =
73 i * public_camenisch_shoup->vector_encryption_length();
74 size_t batch_size = std::min(
75 public_camenisch_shoup->vector_encryption_length(),
76 static_cast<uint64_t>(masked_messages.size() - batch_start_index));
77 size_t batch_end_index = batch_start_index + batch_size;
78 // Determine the messages for the i'th batch.
79 std::vector<BigNum> masked_messages_for_batch_i(
80 masked_messages.begin() + batch_start_index,
81 masked_messages.begin() + batch_end_index);
82 ASSIGN_OR_RETURN(
83 CamenischShoupCiphertext encrypted_masked_message_at_i,
84 public_camenisch_shoup->EncryptWithRand(masked_messages_for_batch_i,
85 encryption_randomness[i]));
86
87 // Homomorphically add the appropriate a*k and a*r*y to the masked_message
88 // in the j'th slot, by using the encryption of k in the j'th slot and y in
89 // the j'th slot respectively (from the BbObliviousSignature public key).
90 for (uint64_t j = 0; j < batch_size; ++j) {
91 encrypted_masked_message_at_i = public_camenisch_shoup->Add(
92 encrypted_masked_message_at_i,
93 public_camenisch_shoup->Multiply(parsed_encrypted_k[j],
94 as[batch_start_index + j]));
95
96 encrypted_masked_message_at_i = public_camenisch_shoup->Add(
97 encrypted_masked_message_at_i,
98 public_camenisch_shoup->Multiply(parsed_encrypted_y[j],
99 gammas[batch_start_index + j]));
100 }
101 encrypted_masked_messages.push_back(
102 std::move(encrypted_masked_message_at_i));
103 }
104 return std::move(encrypted_masked_messages);
105 }
106
107 } // namespace
108
Create(proto::BbObliviousSignatureParameters parameters_proto,Context * ctx,ECGroup * ec_group,PublicCamenischShoup * public_camenisch_shoup,PedersenOverZn * pedersen)109 StatusOr<std::unique_ptr<BbObliviousSignature>> BbObliviousSignature::Create(
110 proto::BbObliviousSignatureParameters parameters_proto, Context* ctx,
111 ECGroup* ec_group, PublicCamenischShoup* public_camenisch_shoup,
112 PedersenOverZn* pedersen) {
113 if (ctx == nullptr) {
114 return absl::InvalidArgumentError(
115 "BbObliviousSignature::Create: The Context object is null.");
116 }
117 if (ec_group == nullptr) {
118 return absl::InvalidArgumentError(
119 "BbObliviousSignature::Create: The ECGroup object is null.");
120 }
121 if (public_camenisch_shoup == nullptr) {
122 return absl::InvalidArgumentError(
123 "BbObliviousSignature::Create: The PublicCamenischShoup object is "
124 "null.");
125 }
126 if (pedersen == nullptr) {
127 return absl::InvalidArgumentError(
128 "BbObliviousSignature::Create: The PedersenOverZn object is null.");
129 }
130 if (parameters_proto.security_parameter() <= 0) {
131 return absl::InvalidArgumentError(
132 "BbObliviousSignature::Create: security_parameter must be positive.");
133 }
134 if (parameters_proto.challenge_length_bits() <= 0) {
135 return absl::InvalidArgumentError(
136 "BbObliviousSignature::Create: challenge_length_bits must be "
137 "positive.");
138 }
139
140 // dummy_masked_betas_bound is the largest value that should be encrypt-able
141 // by the Camenisch-Shoup scheme.
142 BigNum dummy_masked_betas_bound =
143 ctx->One()
144 .Lshift(2 * parameters_proto.challenge_length_bits() +
145 2 * parameters_proto.security_parameter() + 1)
146 .Mul(ec_group->GetOrder())
147 .Mul(ec_group->GetOrder())
148 .Mul(ec_group->GetOrder());
149
150 if (dummy_masked_betas_bound >
151 public_camenisch_shoup->message_upper_bound()) {
152 return absl::InvalidArgumentError(absl::StrCat(
153 "BbObliviousSignature::Create: Camenisch-Shoup encryption scheme is "
154 "not large enough to handle the messages in the proofs. Max message "
155 "size: ",
156 public_camenisch_shoup->message_upper_bound().ToDecimalString(),
157 ", message size needed for proof: ",
158 dummy_masked_betas_bound.ToDecimalString()));
159 }
160 if (dummy_masked_betas_bound > pedersen->n()) {
161 return absl::InvalidArgumentError(absl::StrCat(
162 "BbObliviousSignature::Create: Pedersen Modulus is "
163 "not large enough to handle the messages in the proofs. Max message "
164 "size: ",
165 pedersen->n().ToDecimalString(), ", message size needed for proof: ",
166 dummy_masked_betas_bound.ToDecimalString()));
167 }
168
169 ASSIGN_OR_RETURN(ECPoint base_g,
170 ec_group->CreateECPoint(parameters_proto.base_g()));
171
172 return absl::WrapUnique(new BbObliviousSignature(
173 std::move(parameters_proto), ctx, ec_group, std::move(base_g),
174 public_camenisch_shoup, pedersen));
175 }
176
177 StatusOr<std::tuple<proto::BbObliviousSignaturePublicKey,
178 proto::BbObliviousSignaturePrivateKey>>
GenerateKeys()179 BbObliviousSignature::GenerateKeys() {
180 proto::BbObliviousSignaturePublicKey public_key_proto;
181 proto::BbObliviousSignaturePrivateKey private_key_proto;
182
183 BigNum k = ec_group_->GeneratePrivateKey();
184 BigNum y = ec_group_->GeneratePrivateKey();
185 private_key_proto.set_k(k.ToBytes());
186 private_key_proto.set_y(y.ToBytes());
187
188 public_key_proto.mutable_encrypted_k()->Reserve(
189 public_camenisch_shoup_->vector_encryption_length());
190 public_key_proto.mutable_encrypted_y()->Reserve(
191 public_camenisch_shoup_->vector_encryption_length());
192
193 // The keys k and y should be encrypted vector_encryption_length times,
194 // separately for each slot of the ciphertext.
195 for (uint64_t i = 0; i < public_camenisch_shoup_->vector_encryption_length();
196 ++i) {
197 std::vector<BigNum> messages(
198 public_camenisch_shoup_->vector_encryption_length(), ctx_->Zero());
199 // Encrypt and push back k
200 messages[i] = k;
201 ASSIGN_OR_RETURN(CamenischShoupCiphertext k_ciphertext,
202 public_camenisch_shoup_->Encrypt(messages));
203 *public_key_proto.add_encrypted_k() =
204 CamenischShoupCiphertextToProto(k_ciphertext);
205 // Encrypt and push back y
206 messages[i] = y;
207 ASSIGN_OR_RETURN(CamenischShoupCiphertext y_ciphertext,
208 public_camenisch_shoup_->Encrypt(messages));
209 *public_key_proto.add_encrypted_y() =
210 CamenischShoupCiphertextToProto(y_ciphertext);
211 }
212
213 return std::make_tuple(std::move(public_key_proto),
214 std::move(private_key_proto));
215 }
216
217 StatusOr<std::tuple<proto::BbObliviousSignatureRequest,
218 proto::BbObliviousSignatureRequestProof,
219 proto::BbObliviousSignatureRequestPrivateState>>
GenerateRequestAndProof(const std::vector<BigNum> & messages,const std::vector<BigNum> & rs,const proto::BbObliviousSignaturePublicKey & public_key,const PedersenOverZn::CommitmentAndOpening & commit_and_open_messages,const PedersenOverZn::CommitmentAndOpening & commit_and_open_rs)220 BbObliviousSignature::GenerateRequestAndProof(
221 const std::vector<BigNum>& messages, const std::vector<BigNum>& rs,
222 const proto::BbObliviousSignaturePublicKey& public_key,
223 const PedersenOverZn::CommitmentAndOpening& commit_and_open_messages,
224 const PedersenOverZn::CommitmentAndOpening& commit_and_open_rs) {
225 proto::BbObliviousSignatureRequest request_proto;
226 proto::BbObliviousSignatureRequestProof proof_proto;
227 proto::BbObliviousSignatureRequestPrivateState private_state_proto;
228
229 // Check that sizes are compatible
230 if (messages.size() > pedersen_->gs().size()) {
231 return absl::InvalidArgumentError(absl::StrCat(
232 "BbObliviousSignature::GenerateRequest: messages has size ",
233 messages.size(),
234 " which is larger than the batch size supported by the Pedersen "
235 "commitment scheme (",
236 pedersen_->gs().size(), ")"));
237 }
238 if (rs.size() != messages.size()) {
239 return absl::InvalidArgumentError(absl::StrCat(
240 "BbObliviousSignature::GenerateRequest: rs has size ", messages.size(),
241 " which is different from messages (", messages.size(), ")"));
242 }
243
244 // Generate all "a", "b" and "masked message" values.
245 // Each a is a random exponent in the EC group.
246 // Each b is a random value of size (2^(security_parameter + challenge_length)
247 // * q^2) where lambda is the security parameter, and q is the order of the
248 // ec_group in which we compute the BB Oblivious Signature. Each masked
249 // message is of the form a*m + b*q, which will be homomorphically added to
250 // a*k and ar * y to produce an encryption of a(k+m+yr) + b*q. We also compute
251 // alpha = a*m and gamma = a*r which will be needed for the proof.
252 std::vector<BigNum> as, bs, alphas, gammas, masked_messages;
253 as.reserve(messages.size());
254 bs.reserve(messages.size());
255 alphas.reserve(messages.size());
256 gammas.reserve(messages.size());
257 BigNum bs_bound = (ec_group_->GetOrder() * ec_group_->GetOrder())
258 .Lshift(parameters_proto_.challenge_length_bits() +
259 parameters_proto_.security_parameter());
260 masked_messages.reserve(messages.size());
261 for (size_t i = 0; i < messages.size(); ++i) {
262 as.push_back(ec_group_->GeneratePrivateKey());
263 bs.push_back(ctx_->GenerateRandLessThan(bs_bound));
264 alphas.push_back(messages[i] * as.back());
265 gammas.push_back(rs[i] * as.back());
266 masked_messages.push_back(alphas.back() +
267 (bs.back() * ec_group_->GetOrder()));
268 }
269
270 // Parse the needed components of the public key.
271 std::vector<CamenischShoupCiphertext> parsed_encrypted_k;
272 parsed_encrypted_k.reserve(
273 public_camenisch_shoup_->vector_encryption_length());
274 std::vector<CamenischShoupCiphertext> parsed_encrypted_y;
275 parsed_encrypted_y.reserve(
276 public_camenisch_shoup_->vector_encryption_length());
277
278 for (int i = 0; i < public_camenisch_shoup_->vector_encryption_length();
279 ++i) {
280 ASSIGN_OR_RETURN(CamenischShoupCiphertext cs_encrypt_k_at_i,
281 public_camenisch_shoup_->ParseCiphertextProto(
282 public_key.encrypted_k(i)));
283 parsed_encrypted_k.push_back(std::move(cs_encrypt_k_at_i));
284
285 ASSIGN_OR_RETURN(CamenischShoupCiphertext cs_encrypt_y_at_i,
286 public_camenisch_shoup_->ParseCiphertextProto(
287 public_key.encrypted_y(i)));
288 parsed_encrypted_y.push_back(std::move(cs_encrypt_y_at_i));
289 }
290
291 // The messages are encrypted in batches of vector_encryption_length. We
292 // compute the number of Camenisch Shoup ciphertexts needed to cover the
293 // messages.
294 size_t num_camenisch_shoup_ciphertexts =
295 (messages.size() + public_camenisch_shoup_->vector_encryption_length() -
296 1) /
297 public_camenisch_shoup_->vector_encryption_length();
298
299 // Used for request proof.
300 std::vector<BigNum> encryption_randomness;
301 encryption_randomness.reserve(num_camenisch_shoup_ciphertexts);
302 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
303 encryption_randomness.push_back(
304 ctx_->GenerateRandLessThan(public_camenisch_shoup_->n()));
305 }
306
307 ASSIGN_OR_RETURN(
308 std::vector<CamenischShoupCiphertext> encrypted_masked_messages,
309 GenerateHomomorphicCsCiphertexts(
310 masked_messages, as, gammas, encryption_randomness,
311 parsed_encrypted_k, parsed_encrypted_y, public_camenisch_shoup_));
312
313 request_proto.set_num_messages(messages.size());
314 *private_state_proto.mutable_private_as() = BigNumVectorToProto(as);
315 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
316 *request_proto.add_repeated_encrypted_masked_messages() =
317 CamenischShoupCiphertextToProto(encrypted_masked_messages[i]);
318 }
319
320 // Commit to as, bs.
321 // as must be committed separately in order to be able to homomorphically
322 // generate batch commitments to alphas and gammas. The i'th commitment
323 // contains as[i] in the i'th Pedersen batch-commitment slot, and 0s in all
324 // other slots.
325 std::vector<BigNum> commit_as, open_as;
326 commit_as.reserve(as.size());
327 open_as.reserve(as.size());
328 for (size_t i = 0; i < as.size(); ++i) {
329 std::vector<BigNum> ai_in_ith_position(pedersen_->gs().size(),
330 ctx_->Zero());
331 ai_in_ith_position[i] = as[i];
332 ASSIGN_OR_RETURN(PedersenOverZn::CommitmentAndOpening commit_and_open_ai,
333 pedersen_->Commit(ai_in_ith_position));
334 commit_as.push_back(std::move(commit_and_open_ai.commitment));
335 open_as.push_back(std::move(commit_and_open_ai.opening));
336 }
337
338 ASSIGN_OR_RETURN(PedersenOverZn::CommitmentAndOpening commit_and_open_bs,
339 pedersen_->Commit(bs));
340
341 // Homomorphically generate commitment to alphas, gammas. This
342 // homomorphically generated commitment will be used in 2 parts of the
343 // proof.
344 //
345 // Taking the example of alphas, recall that alphas[i] = as[i] *
346 // messages[i]. We want to show that alphas[i] was (1) properly used in
347 // computing encrypted_masked_messages, and (2) was properly generated as
348 // as[i]*messages[i]. For property (1), we need to show knowledge of
349 // alphas[i] and the randomness used to commit to alphas, and for property
350 // (2), we need to show that the commitment to alphas was homomorphically
351 // generated from Com(as[i]).
352
353 // To support these proofs, we homomorphically generate Com(alpha) as
354 // (Prod_i Com(as[i])^messages[i]) * Com(0), where Com(0) is a fresh
355 // commitment to 0. Since we generated Com(as[i]) with as[i] each in a
356 // different Pedersen vector slot, this will correctly come out to a
357 // commitment of alpha, with overall commitment randomness (Sum_i open_as[i]
358 // * messages[i]) + open_alphas_2, where open_alphas_2 is the randomness
359 // used in the second commitment of 0. We will refer to the overall
360 // commitment randomness as open_alphas_1, and the randomness used to commit
361 // to 0 as open_alphas_2. These will be used in order to prove properties
362 // (1) and (2) respectively.
363 //
364 // We proceed similarly for gammas, where gammas[i] = as[i] * rs[i].
365 std::vector<BigNum> zero_vector(pedersen_->gs().size(), ctx_->Zero());
366 ASSIGN_OR_RETURN(
367 PedersenOverZn::CommitmentAndOpening temp_commit_and_open_alphas,
368 pedersen_->Commit(zero_vector));
369 ASSIGN_OR_RETURN(
370 PedersenOverZn::CommitmentAndOpening temp_commit_and_open_gammas,
371 pedersen_->Commit(zero_vector));
372
373 // commit_alphas and commit_gammas serve as accumulators for the homomorphic
374 // computation. open_alphas_1 and open_gammas_1 will serve as accumulators
375 // for the randomness in these homomorphically generated commitments.
376 // open_alphas_2 and open_gammas_2 will serve to record the randomness used
377 // in the commitments to 0.
378 PedersenOverZn::Commitment commit_alphas =
379 std::move(temp_commit_and_open_alphas.commitment);
380 PedersenOverZn::Commitment commit_gammas =
381 std::move(temp_commit_and_open_gammas.commitment);
382 PedersenOverZn::Opening open_alphas_1 =
383 std::move(temp_commit_and_open_alphas.opening);
384 PedersenOverZn::Opening open_gammas_1 =
385 std::move(temp_commit_and_open_gammas.opening);
386 PedersenOverZn::Opening open_alphas_2 = open_alphas_1;
387 PedersenOverZn::Opening open_gammas_2 = open_gammas_1;
388
389 for (size_t i = 0; i < messages.size(); ++i) {
390 commit_alphas = pedersen_->Add(
391 commit_alphas, pedersen_->Multiply(commit_as[i], messages[i]));
392 commit_gammas =
393 pedersen_->Add(commit_gammas, pedersen_->Multiply(commit_as[i], rs[i]));
394 open_alphas_1 = open_alphas_1 + (open_as[i] * messages[i]);
395 open_gammas_1 = open_gammas_1 + (open_as[i] * rs[i]);
396 }
397
398 // Generate dummy exponents for all values
399 BigNum dummy_messages_bound =
400 ec_group_->GetOrder().Lshift(parameters_proto_.challenge_length_bits() +
401 parameters_proto_.security_parameter());
402 BigNum dummy_rs_bound = dummy_messages_bound;
403 BigNum dummy_as_bound = dummy_messages_bound;
404 BigNum dummy_bs_bound =
405 bs_bound.Lshift(parameters_proto_.challenge_length_bits() +
406 parameters_proto_.security_parameter());
407 BigNum dummy_alphas_bound = dummy_as_bound * ec_group_->GetOrder();
408 BigNum dummy_gammas_bound = dummy_alphas_bound;
409 BigNum dummy_openings_bound =
410 pedersen_->n().Lshift(parameters_proto_.challenge_length_bits() +
411 parameters_proto_.security_parameter());
412
413 // The homomorphically computed openings for Com(alphas) and Com(gammas)
414 // need larger dummy values.
415 BigNum dummy_homomorphically_computed_openings_bound =
416 dummy_openings_bound * ec_group_->GetOrder() *
417 ctx_->CreateBigNum(messages.size() + 1);
418 BigNum dummy_encryption_randomness_bound =
419 public_camenisch_shoup_->n().Lshift(
420 parameters_proto_.challenge_length_bits() +
421 parameters_proto_.security_parameter());
422
423 std::vector<BigNum> dummy_messages;
424 dummy_messages.reserve(messages.size());
425 std::vector<BigNum> dummy_rs;
426 dummy_rs.reserve(messages.size());
427 std::vector<BigNum> dummy_as;
428 dummy_as.reserve(messages.size());
429 std::vector<BigNum> dummy_as_openings;
430 dummy_as_openings.reserve(messages.size());
431 std::vector<BigNum> dummy_bs;
432 dummy_bs.reserve(messages.size());
433 std::vector<BigNum> dummy_alphas;
434 dummy_alphas.reserve(messages.size());
435 std::vector<BigNum> dummy_gammas;
436 dummy_gammas.reserve(messages.size());
437 std::vector<BigNum> dummy_masked_messages;
438 dummy_masked_messages.reserve(messages.size());
439
440 for (size_t i = 0; i < messages.size(); ++i) {
441 dummy_messages.push_back(ctx_->GenerateRandLessThan(dummy_messages_bound));
442 dummy_rs.push_back(ctx_->GenerateRandLessThan(dummy_rs_bound));
443 dummy_as.push_back(ctx_->GenerateRandLessThan(dummy_as_bound));
444 dummy_as_openings.push_back(
445 ctx_->GenerateRandLessThan(dummy_openings_bound));
446 dummy_bs.push_back(ctx_->GenerateRandLessThan(dummy_bs_bound));
447 dummy_alphas.push_back(ctx_->GenerateRandLessThan(dummy_alphas_bound));
448 dummy_gammas.push_back(ctx_->GenerateRandLessThan(dummy_gammas_bound));
449 dummy_masked_messages.push_back(dummy_alphas.back() +
450 (dummy_bs.back() * ec_group_->GetOrder()));
451 }
452 BigNum dummy_messages_opening =
453 ctx_->GenerateRandLessThan(dummy_openings_bound);
454 BigNum dummy_rs_opening = ctx_->GenerateRandLessThan(dummy_openings_bound);
455 BigNum dummy_bs_opening = ctx_->GenerateRandLessThan(dummy_openings_bound);
456 BigNum dummy_alphas_opening_1 =
457 ctx_->GenerateRandLessThan(dummy_homomorphically_computed_openings_bound);
458 BigNum dummy_alphas_opening_2 =
459 ctx_->GenerateRandLessThan(dummy_openings_bound);
460 BigNum dummy_gammas_opening_1 =
461 ctx_->GenerateRandLessThan(dummy_homomorphically_computed_openings_bound);
462 BigNum dummy_gammas_opening_2 =
463 ctx_->GenerateRandLessThan(dummy_openings_bound);
464 std::vector<BigNum> dummy_encryption_randomness;
465 dummy_encryption_randomness.reserve(num_camenisch_shoup_ciphertexts);
466 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
467 dummy_encryption_randomness.push_back(
468 ctx_->GenerateRandLessThan(dummy_encryption_randomness_bound));
469 }
470
471 // Create dummy composites for all values
472 ASSIGN_OR_RETURN(
473 PedersenOverZn::Commitment dummy_commit_messages,
474 pedersen_->CommitWithRand(dummy_messages, dummy_messages_opening));
475 ASSIGN_OR_RETURN(PedersenOverZn::Commitment dummy_commit_rs,
476 pedersen_->CommitWithRand(dummy_rs, dummy_rs_opening));
477 std::vector<PedersenOverZn::Commitment> dummy_commit_as;
478 for (size_t i = 0; i < messages.size(); ++i) {
479 std::vector<BigNum> dummy_as_at_i = zero_vector;
480 dummy_as_at_i[i] = dummy_as[i];
481 ASSIGN_OR_RETURN(
482 PedersenOverZn::Commitment dummy_commit_as_at_i,
483 pedersen_->CommitWithRand(dummy_as_at_i, dummy_as_openings[i]));
484 dummy_commit_as.push_back(std::move(dummy_commit_as_at_i));
485 }
486 ASSIGN_OR_RETURN(PedersenOverZn::Commitment dummy_commit_bs,
487 pedersen_->CommitWithRand(dummy_bs, dummy_bs_opening));
488 ASSIGN_OR_RETURN(
489 PedersenOverZn::Commitment dummy_commit_alphas_1,
490 pedersen_->CommitWithRand(dummy_alphas, dummy_alphas_opening_1));
491 ASSIGN_OR_RETURN(
492 PedersenOverZn::Commitment dummy_commit_gammas_1,
493 pedersen_->CommitWithRand(dummy_gammas, dummy_gammas_opening_1));
494
495 ASSIGN_OR_RETURN(
496 PedersenOverZn::Commitment dummy_commit_alphas_2,
497 pedersen_->CommitWithRand(zero_vector, dummy_alphas_opening_2));
498 ASSIGN_OR_RETURN(
499 PedersenOverZn::Commitment dummy_commit_gammas_2,
500 pedersen_->CommitWithRand(zero_vector, dummy_gammas_opening_2));
501 for (size_t i = 0; i < messages.size(); ++i) {
502 dummy_commit_alphas_2 =
503 pedersen_->Add(dummy_commit_alphas_2,
504 pedersen_->Multiply(commit_as[i], dummy_messages[i]));
505 dummy_commit_gammas_2 = pedersen_->Add(
506 dummy_commit_gammas_2, pedersen_->Multiply(commit_as[i], dummy_rs[i]));
507 }
508
509 // Generate the dummy Camenisch Shoup encryptions.
510 ASSIGN_OR_RETURN(
511 std::vector<CamenischShoupCiphertext> dummy_encrypted_masked_messages,
512 GenerateHomomorphicCsCiphertexts(
513 dummy_masked_messages, dummy_as, dummy_gammas,
514 dummy_encryption_randomness, parsed_encrypted_k, parsed_encrypted_y,
515 public_camenisch_shoup_));
516
517 // Serialize the statement and first message into protos, and generate the
518 // challenge
519 proto::BbObliviousSignatureRequestProof::Statement proof_statement;
520 *proof_statement.mutable_parameters() = parameters_proto_;
521 *proof_statement.mutable_public_key() = public_key;
522 proof_statement.set_commit_messages(
523 commit_and_open_messages.commitment.ToBytes());
524 proof_statement.set_commit_rs(commit_and_open_rs.commitment.ToBytes());
525 *proof_statement.mutable_commit_as() = BigNumVectorToProto(commit_as);
526 proof_statement.set_commit_bs(commit_and_open_bs.commitment.ToBytes());
527 proof_statement.set_commit_alphas(commit_alphas.ToBytes());
528 proof_statement.set_commit_gammas(commit_gammas.ToBytes());
529 *proof_statement.mutable_request() = request_proto;
530
531 proto::BbObliviousSignatureRequestProof::Message1 proof_message_1;
532 proof_message_1.set_dummy_commit_messages(dummy_commit_messages.ToBytes());
533 proof_message_1.set_dummy_commit_rs(dummy_commit_rs.ToBytes());
534 *proof_message_1.mutable_dummy_commit_as() =
535 BigNumVectorToProto(dummy_commit_as);
536 proof_message_1.set_dummy_commit_bs(dummy_commit_bs.ToBytes());
537 proof_message_1.set_dummy_commit_alphas_1(dummy_commit_alphas_1.ToBytes());
538 proof_message_1.set_dummy_commit_alphas_2(dummy_commit_alphas_2.ToBytes());
539 proof_message_1.set_dummy_commit_gammas_1(dummy_commit_gammas_1.ToBytes());
540 proof_message_1.set_dummy_commit_gammas_2(dummy_commit_gammas_2.ToBytes());
541 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
542 *proof_message_1.add_repeated_dummy_encrypted_masked_messages() =
543 CamenischShoupCiphertextToProto(dummy_encrypted_masked_messages[i]);
544 }
545
546 ASSIGN_OR_RETURN(BigNum challenge, GenerateRequestProofChallenge(
547 proof_statement, proof_message_1));
548
549 // Create masked dummy openings
550 std::vector<BigNum> masked_dummy_messages;
551 masked_dummy_messages.reserve(messages.size());
552 std::vector<BigNum> masked_dummy_rs;
553 masked_dummy_rs.reserve(messages.size());
554 std::vector<BigNum> masked_dummy_as;
555 masked_dummy_as.reserve(messages.size());
556 std::vector<BigNum> masked_dummy_as_openings;
557 masked_dummy_as_openings.reserve(messages.size());
558 std::vector<BigNum> masked_dummy_bs;
559 masked_dummy_bs.reserve(messages.size());
560 std::vector<BigNum> masked_dummy_alphas;
561 masked_dummy_alphas.reserve(messages.size());
562 std::vector<BigNum> masked_dummy_gammas;
563 masked_dummy_gammas.reserve(messages.size());
564
565 for (size_t i = 0; i < messages.size(); ++i) {
566 masked_dummy_messages.push_back(dummy_messages[i] +
567 challenge * messages[i]);
568 masked_dummy_rs.push_back(dummy_rs[i] + challenge * rs[i]);
569 masked_dummy_as.push_back(dummy_as[i] + challenge * as[i]);
570 masked_dummy_as_openings.push_back(dummy_as_openings[i] +
571 challenge * open_as[i]);
572 masked_dummy_bs.push_back(dummy_bs[i] + challenge * bs[i]);
573 masked_dummy_alphas.push_back(dummy_alphas[i] + challenge * alphas[i]);
574 masked_dummy_gammas.push_back(dummy_gammas[i] + challenge * gammas[i]);
575 }
576 BigNum masked_dummy_messages_opening =
577 dummy_messages_opening + challenge * commit_and_open_messages.opening;
578 BigNum masked_dummy_rs_opening =
579 dummy_rs_opening + challenge * commit_and_open_rs.opening;
580 BigNum masked_dummy_bs_opening =
581 dummy_bs_opening + challenge * commit_and_open_bs.opening;
582 BigNum masked_dummy_alphas_opening_1 =
583 dummy_alphas_opening_1 + challenge * open_alphas_1;
584 BigNum masked_dummy_alphas_opening_2 =
585 dummy_alphas_opening_2 + challenge * open_alphas_2;
586 BigNum masked_dummy_gammas_opening_1 =
587 dummy_gammas_opening_1 + challenge * open_gammas_1;
588 BigNum masked_dummy_gammas_opening_2 =
589 dummy_gammas_opening_2 + challenge * open_gammas_2;
590 std::vector<BigNum> masked_dummy_encryption_randomness;
591 masked_dummy_encryption_randomness.reserve(num_camenisch_shoup_ciphertexts);
592 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
593 masked_dummy_encryption_randomness.push_back(
594 dummy_encryption_randomness[i] + challenge * encryption_randomness[i]);
595 }
596
597 // Generate proof proto.
598
599 *proof_proto.mutable_commit_as() = BigNumVectorToProto(commit_as);
600 proof_proto.set_commit_bs(commit_and_open_bs.commitment.ToBytes());
601 proof_proto.set_commit_alphas(commit_alphas.ToBytes());
602 proof_proto.set_commit_gammas(commit_gammas.ToBytes());
603 proof_proto.set_challenge(challenge.ToBytes());
604
605 proto::BbObliviousSignatureRequestProof::Message2* proof_proto_message_2 =
606 proof_proto.mutable_message_2();
607 *proof_proto_message_2->mutable_masked_dummy_messages() =
608 BigNumVectorToProto(masked_dummy_messages);
609 proof_proto_message_2->set_masked_dummy_messages_opening(
610 masked_dummy_messages_opening.ToBytes());
611 *proof_proto_message_2->mutable_masked_dummy_rs() =
612 BigNumVectorToProto(masked_dummy_rs);
613 proof_proto_message_2->set_masked_dummy_rs_opening(
614 masked_dummy_rs_opening.ToBytes());
615 *proof_proto_message_2->mutable_masked_dummy_as() =
616 BigNumVectorToProto(masked_dummy_as);
617 *proof_proto_message_2->mutable_masked_dummy_as_opening() =
618 BigNumVectorToProto(masked_dummy_as_openings);
619 *proof_proto_message_2->mutable_masked_dummy_bs() =
620 BigNumVectorToProto(masked_dummy_bs);
621 proof_proto_message_2->set_masked_dummy_bs_opening(
622 masked_dummy_bs_opening.ToBytes());
623 *proof_proto_message_2->mutable_masked_dummy_alphas() =
624 BigNumVectorToProto(masked_dummy_alphas);
625 proof_proto_message_2->set_masked_dummy_alphas_opening_1(
626 masked_dummy_alphas_opening_1.ToBytes());
627 proof_proto_message_2->set_masked_dummy_alphas_opening_2(
628 masked_dummy_alphas_opening_2.ToBytes());
629 *proof_proto_message_2->mutable_masked_dummy_gammas() =
630 BigNumVectorToProto(masked_dummy_gammas);
631 proof_proto_message_2->set_masked_dummy_gammas_opening_1(
632 masked_dummy_gammas_opening_1.ToBytes());
633 proof_proto_message_2->set_masked_dummy_gammas_opening_2(
634 masked_dummy_gammas_opening_2.ToBytes());
635 *proof_proto_message_2
636 ->mutable_masked_dummy_encryption_randomness_per_ciphertext() =
637 BigNumVectorToProto(masked_dummy_encryption_randomness);
638
639 return std::make_tuple(std::move(request_proto), std::move(proof_proto),
640 std::move(private_state_proto));
641 }
642
643 // Verifies a signature request and proof.
VerifyRequest(const proto::BbObliviousSignaturePublicKey & public_key,const proto::BbObliviousSignatureRequest & request,const proto::BbObliviousSignatureRequestProof & request_proof,const PedersenOverZn::Commitment & commit_messages,const PedersenOverZn::Commitment & commit_rs)644 Status BbObliviousSignature::VerifyRequest(
645 const proto::BbObliviousSignaturePublicKey& public_key,
646 const proto::BbObliviousSignatureRequest& request,
647 const proto::BbObliviousSignatureRequestProof& request_proof,
648 const PedersenOverZn::Commitment& commit_messages,
649 const PedersenOverZn::Commitment& commit_rs) {
650 if (request.num_messages() > pedersen_->gs().size()) {
651 return absl::InvalidArgumentError(absl::StrCat(
652 "BbObliviousSignature::VerifyRequest: messages has size ",
653 request.num_messages(),
654 " which is larger than the pedersen batch size in parameters (",
655 pedersen_->gs().size(), ")"));
656 }
657 // Check that all vectors have the correct size.
658 if (request_proof.commit_as().serialized_big_nums_size() !=
659 request.num_messages()) {
660 return absl::InvalidArgumentError(
661 absl::StrCat("BbObliviousSignatures::VerifyRequest: request proof "
662 "has wrong number of commit_as: expected ",
663 request.num_messages(), ", actual ",
664 request_proof.commit_as().serialized_big_nums_size()));
665 }
666 if (request_proof.message_2()
667 .masked_dummy_messages()
668 .serialized_big_nums_size() != request.num_messages()) {
669 return absl::InvalidArgumentError(absl::StrCat(
670 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
671 "number of masked_dummy_messages: expected ",
672 request.num_messages(), ", actual ",
673 request_proof.message_2()
674 .masked_dummy_messages()
675 .serialized_big_nums_size()));
676 }
677 if (request_proof.message_2().masked_dummy_rs().serialized_big_nums_size() !=
678 request.num_messages()) {
679 return absl::InvalidArgumentError(absl::StrCat(
680 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
681 "number of masked_dummy_rs: expected ",
682 request.num_messages(), ", actual ",
683 request_proof.message_2()
684 .masked_dummy_rs()
685 .serialized_big_nums_size()));
686 }
687 if (request_proof.message_2().masked_dummy_as().serialized_big_nums_size() !=
688 request.num_messages()) {
689 return absl::InvalidArgumentError(absl::StrCat(
690 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
691 "number of masked_dummy_as: expected ",
692 request.num_messages(), ", actual ",
693 request_proof.message_2()
694 .masked_dummy_as()
695 .serialized_big_nums_size()));
696 }
697 if (request_proof.message_2().masked_dummy_bs().serialized_big_nums_size() !=
698 request.num_messages()) {
699 return absl::InvalidArgumentError(absl::StrCat(
700 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
701 "number of masked_dummy_bs: expected ",
702 request.num_messages(), ", actual ",
703 request_proof.message_2()
704 .masked_dummy_bs()
705 .serialized_big_nums_size()));
706 }
707 if (request_proof.message_2()
708 .masked_dummy_alphas()
709 .serialized_big_nums_size() != request.num_messages()) {
710 return absl::InvalidArgumentError(absl::StrCat(
711 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
712 "number of masked_dummy_alphas: expected ",
713 request.num_messages(), ", actual ",
714 request_proof.message_2()
715 .masked_dummy_alphas()
716 .serialized_big_nums_size()));
717 }
718 if (request_proof.message_2()
719 .masked_dummy_gammas()
720 .serialized_big_nums_size() != request.num_messages()) {
721 return absl::InvalidArgumentError(absl::StrCat(
722 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
723 "number of masked_dummy_gammas: expected ",
724 request.num_messages(), ", actual ",
725 request_proof.message_2()
726 .masked_dummy_gammas()
727 .serialized_big_nums_size()));
728 }
729
730 // The messages are encrypted in batches of vector_encryption_length. We
731 // compute the number of Camenisch Shoup ciphertexts needed to cover the
732 // messages.
733 size_t num_camenisch_shoup_ciphertexts =
734 (request.num_messages() +
735 public_camenisch_shoup_->vector_encryption_length() - 1) /
736 public_camenisch_shoup_->vector_encryption_length();
737
738 if (request.repeated_encrypted_masked_messages_size() !=
739 num_camenisch_shoup_ciphertexts) {
740 return absl::InvalidArgumentError(
741 absl::StrCat("BbObliviousSignatures::VerifyRequest: request has wrong "
742 "number of ciphertexts: expected ",
743 num_camenisch_shoup_ciphertexts, ", actual ",
744 request.repeated_encrypted_masked_messages_size()));
745 }
746 if (request_proof.message_2()
747 .masked_dummy_encryption_randomness_per_ciphertext()
748 .serialized_big_nums_size() != num_camenisch_shoup_ciphertexts) {
749 return absl::InvalidArgumentError(absl::StrCat(
750 "BbObliviousSignatures::VerifyRequest: request proof has wrong "
751 "number of masked_dummy_encryption_randomness: expected ",
752 num_camenisch_shoup_ciphertexts, ", actual ",
753 request_proof.message_2()
754 .masked_dummy_encryption_randomness_per_ciphertext()
755 .serialized_big_nums_size()));
756 }
757
758 // Create the proof statement
759 proto::BbObliviousSignatureRequestProof::Statement proof_statement;
760 *proof_statement.mutable_parameters() = parameters_proto_;
761 *proof_statement.mutable_public_key() = public_key;
762 proof_statement.set_commit_messages(commit_messages.ToBytes());
763 proof_statement.set_commit_rs(commit_rs.ToBytes());
764 *proof_statement.mutable_commit_as() = request_proof.commit_as();
765 proof_statement.set_commit_bs(request_proof.commit_bs());
766 proof_statement.set_commit_alphas(request_proof.commit_alphas());
767 proof_statement.set_commit_gammas(request_proof.commit_gammas());
768 *proof_statement.mutable_request() = request;
769
770 // Parse the components needed for the proof.
771 std::vector<PedersenOverZn::Commitment> commit_as =
772 ParseBigNumVectorProto(ctx_, request_proof.commit_as());
773 PedersenOverZn::Commitment commit_bs =
774 ctx_->CreateBigNum(request_proof.commit_bs());
775 PedersenOverZn::Commitment commit_alphas =
776 ctx_->CreateBigNum(request_proof.commit_alphas());
777 PedersenOverZn::Commitment commit_gammas =
778 ctx_->CreateBigNum(request_proof.commit_gammas());
779 std::vector<CamenischShoupCiphertext> encrypted_masked_messages;
780 encrypted_masked_messages.reserve(num_camenisch_shoup_ciphertexts);
781 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
782 ASSIGN_OR_RETURN(CamenischShoupCiphertext encrypted_masked_messages_at_i,
783 public_camenisch_shoup_->ParseCiphertextProto(
784 request.repeated_encrypted_masked_messages(i)));
785 encrypted_masked_messages.push_back(
786 std::move(encrypted_masked_messages_at_i));
787 }
788
789 // Parse challenge from the proof.
790 BigNum challenge_from_proof = ctx_->CreateBigNum(request_proof.challenge());
791
792 // Parse the masked dummy values from the proof.
793 std::vector<BigNum> masked_dummy_messages = ParseBigNumVectorProto(
794 ctx_, request_proof.message_2().masked_dummy_messages());
795 BigNum masked_dummy_messages_opening = ctx_->CreateBigNum(
796 request_proof.message_2().masked_dummy_messages_opening());
797 std::vector<BigNum> masked_dummy_rs =
798 ParseBigNumVectorProto(ctx_, request_proof.message_2().masked_dummy_rs());
799 BigNum masked_dummy_rs_opening =
800 ctx_->CreateBigNum(request_proof.message_2().masked_dummy_rs_opening());
801 std::vector<BigNum> masked_dummy_as =
802 ParseBigNumVectorProto(ctx_, request_proof.message_2().masked_dummy_as());
803 std::vector<BigNum> masked_dummy_as_opening = ParseBigNumVectorProto(
804 ctx_, request_proof.message_2().masked_dummy_as_opening());
805 std::vector<BigNum> masked_dummy_bs =
806 ParseBigNumVectorProto(ctx_, request_proof.message_2().masked_dummy_bs());
807 BigNum masked_dummy_bs_opening =
808 ctx_->CreateBigNum(request_proof.message_2().masked_dummy_bs_opening());
809 std::vector<BigNum> masked_dummy_alphas = ParseBigNumVectorProto(
810 ctx_, request_proof.message_2().masked_dummy_alphas());
811 BigNum masked_dummy_alphas_opening_1 = ctx_->CreateBigNum(
812 request_proof.message_2().masked_dummy_alphas_opening_1());
813 BigNum masked_dummy_alphas_opening_2 = ctx_->CreateBigNum(
814 request_proof.message_2().masked_dummy_alphas_opening_2());
815 std::vector<BigNum> masked_dummy_gammas = ParseBigNumVectorProto(
816 ctx_, request_proof.message_2().masked_dummy_gammas());
817 BigNum masked_dummy_gammas_opening_1 = ctx_->CreateBigNum(
818 request_proof.message_2().masked_dummy_gammas_opening_1());
819 BigNum masked_dummy_gammas_opening_2 = ctx_->CreateBigNum(
820 request_proof.message_2().masked_dummy_gammas_opening_2());
821 std::vector<BigNum> masked_dummy_encryption_randomness =
822 ParseBigNumVectorProto(
823 ctx_, request_proof.message_2()
824 .masked_dummy_encryption_randomness_per_ciphertext());
825
826 // Verify bounds.
827 BigNum masked_dummy_messages_bound =
828 ec_group_->GetOrder().Lshift(parameters_proto_.challenge_length_bits() +
829 parameters_proto_.security_parameter() + 1);
830 BigNum masked_dummy_rs_bound = masked_dummy_messages_bound;
831 BigNum masked_dummy_as_bound = masked_dummy_messages_bound;
832 BigNum masked_dummy_bs_bound =
833 (ec_group_->GetOrder() * ec_group_->GetOrder())
834 .Lshift(2 * parameters_proto_.challenge_length_bits() +
835 2 * parameters_proto_.security_parameter() + 1);
836 BigNum masked_dummy_alphas_bound =
837 masked_dummy_as_bound * ec_group_->GetOrder();
838 BigNum masked_dummy_gammas_bound = masked_dummy_alphas_bound;
839
840 for (uint64_t i = 0; i < request.num_messages(); ++i) {
841 if (masked_dummy_messages[i] >= masked_dummy_messages_bound) {
842 return absl::InvalidArgumentError(absl::StrCat(
843 "BbObliviousSignatures::VerifyRequest: The ", i,
844 "th entry of masked_dummy_messages,",
845 masked_dummy_messages[i].ToDecimalString(), " (bit length ",
846 masked_dummy_messages[i].BitLength(), ")",
847 ",is larger than the acceptable bound: ",
848 masked_dummy_messages_bound.ToDecimalString(), " (bit length ",
849 masked_dummy_messages_bound.BitLength(), ")"));
850 }
851 if (masked_dummy_as[i] >= masked_dummy_as_bound) {
852 return absl::InvalidArgumentError(absl::StrCat(
853 "BbObliviousSignatures::VerifyRequest: The ", i,
854 "th entry of masked_dummy_as,", masked_dummy_as[i].ToDecimalString(),
855 " (bit length ", masked_dummy_as[i].BitLength(), ")",
856 ",is larger than the acceptable bound: ",
857 masked_dummy_as_bound.ToDecimalString(), " (bit length ",
858 masked_dummy_as_bound.BitLength(), ")"));
859 }
860 if (masked_dummy_bs[i] >= masked_dummy_bs_bound) {
861 return absl::InvalidArgumentError(absl::StrCat(
862 "BbObliviousSignatures::VerifyRequest: The ", i,
863 "th entry of masked_dummy_bs,", masked_dummy_bs[i].ToDecimalString(),
864 " (bit length ", masked_dummy_bs[i].BitLength(), ")",
865 ",is larger than the acceptable bound: ",
866 masked_dummy_bs_bound.ToDecimalString(), " (bit length ",
867 masked_dummy_bs_bound.BitLength(), ")"));
868 }
869 if (masked_dummy_alphas[i] >= masked_dummy_alphas_bound) {
870 return absl::InvalidArgumentError(absl::StrCat(
871 "BbObliviousSignatures::VerifyRequest: The ", i,
872 "th entry of masked_dummy_alphas,",
873 masked_dummy_alphas[i].ToDecimalString(), " (bit length ",
874 masked_dummy_alphas[i].BitLength(), ")",
875 ",is larger than the acceptable bound: ",
876 masked_dummy_alphas_bound.ToDecimalString(), " (bit length ",
877 masked_dummy_alphas_bound.BitLength(), ")"));
878 }
879 if (masked_dummy_gammas[i] >= masked_dummy_gammas_bound) {
880 return absl::InvalidArgumentError(absl::StrCat(
881 "BbObliviousSignatures::VerifyRequest: The ", i,
882 "th entry of masked_dummy_gammas,",
883 masked_dummy_gammas[i].ToDecimalString(), " (bit length ",
884 masked_dummy_gammas[i].BitLength(), ")",
885 ",is larger than the acceptable bound: ",
886 masked_dummy_gammas_bound.ToDecimalString(), " (bit length ",
887 masked_dummy_gammas_bound.BitLength(), ")"));
888 }
889 }
890
891 // Create masked dummy composite values
892
893 ASSIGN_OR_RETURN(PedersenOverZn::Commitment masked_dummy_commit_messages,
894 pedersen_->CommitWithRand(masked_dummy_messages,
895 masked_dummy_messages_opening));
896 ASSIGN_OR_RETURN(
897 PedersenOverZn::Commitment masked_dummy_commit_rs,
898 pedersen_->CommitWithRand(masked_dummy_rs, masked_dummy_rs_opening));
899
900 std::vector<PedersenOverZn::Commitment> masked_dummy_commit_as;
901 masked_dummy_commit_as.reserve(commit_as.size());
902 std::vector<BigNum> zero_vector(pedersen_->gs().size(), ctx_->Zero());
903 for (size_t i = 0; i < commit_as.size(); ++i) {
904 std::vector<BigNum> masked_dummy_ai_at_i = zero_vector;
905 masked_dummy_ai_at_i[i] = masked_dummy_as[i];
906 ASSIGN_OR_RETURN(PedersenOverZn::Commitment masked_dummy_commit_ai,
907 pedersen_->CommitWithRand(masked_dummy_ai_at_i,
908 masked_dummy_as_opening[i]));
909 masked_dummy_commit_as.push_back(masked_dummy_commit_ai);
910 }
911 ASSIGN_OR_RETURN(
912 PedersenOverZn::Commitment masked_dummy_commit_bs,
913 pedersen_->CommitWithRand(masked_dummy_bs, masked_dummy_bs_opening));
914 ASSIGN_OR_RETURN(PedersenOverZn::Commitment masked_dummy_commit_alphas_1,
915 pedersen_->CommitWithRand(masked_dummy_alphas,
916 masked_dummy_alphas_opening_1));
917 ASSIGN_OR_RETURN(PedersenOverZn::Commitment masked_dummy_commit_gammas_1,
918 pedersen_->CommitWithRand(masked_dummy_gammas,
919 masked_dummy_gammas_opening_1));
920
921 // masked_dummy_alphas_2 and masked_dummy_gammas_2 are homomorphically
922 // computed from commit_as.
923 ASSIGN_OR_RETURN(
924 PedersenOverZn::Commitment masked_dummy_commit_alphas_2,
925 pedersen_->CommitWithRand(zero_vector, masked_dummy_alphas_opening_2));
926 ASSIGN_OR_RETURN(
927 PedersenOverZn::Commitment masked_dummy_commit_gammas_2,
928 pedersen_->CommitWithRand(zero_vector, masked_dummy_gammas_opening_2));
929 for (size_t i = 0; i < commit_as.size(); ++i) {
930 masked_dummy_commit_alphas_2 = pedersen_->Add(
931 pedersen_->Multiply(commit_as[i], masked_dummy_messages[i]),
932 masked_dummy_commit_alphas_2);
933 masked_dummy_commit_gammas_2 =
934 pedersen_->Add(pedersen_->Multiply(commit_as[i], masked_dummy_rs[i]),
935 masked_dummy_commit_gammas_2);
936 }
937
938 // Compute the masked_dummy_encrypted_masked_messages homomorphically.
939 std::vector<BigNum> dummy_masked_encrypted_masked_messages;
940 dummy_masked_encrypted_masked_messages.reserve(masked_dummy_messages.size());
941 for (size_t i = 0; i < masked_dummy_messages.size(); ++i) {
942 dummy_masked_encrypted_masked_messages.push_back(
943 masked_dummy_alphas[i] + masked_dummy_bs[i] * ec_group_->GetOrder());
944 }
945
946 std::vector<CamenischShoupCiphertext> parsed_encrypted_k;
947 parsed_encrypted_k.reserve(
948 public_camenisch_shoup_->vector_encryption_length());
949 std::vector<CamenischShoupCiphertext> parsed_encrypted_y;
950 parsed_encrypted_y.reserve(
951 public_camenisch_shoup_->vector_encryption_length());
952
953 for (size_t i = 0; i < public_camenisch_shoup_->vector_encryption_length();
954 ++i) {
955 ASSIGN_OR_RETURN(CamenischShoupCiphertext cs_encrypt_k_at_i,
956 public_camenisch_shoup_->ParseCiphertextProto(
957 public_key.encrypted_k(i)));
958 parsed_encrypted_k.push_back(std::move(cs_encrypt_k_at_i));
959
960 ASSIGN_OR_RETURN(CamenischShoupCiphertext cs_encrypt_y_at_i,
961 public_camenisch_shoup_->ParseCiphertextProto(
962 public_key.encrypted_y(i)));
963 parsed_encrypted_y.push_back(std::move(cs_encrypt_y_at_i));
964 }
965
966 // Generate the dummy Camenisch Shoup encryptions.
967 ASSIGN_OR_RETURN(
968 std::vector<CamenischShoupCiphertext>
969 masked_dummy_encrypted_masked_messages,
970 GenerateHomomorphicCsCiphertexts(
971 dummy_masked_encrypted_masked_messages, masked_dummy_as,
972 masked_dummy_gammas, masked_dummy_encryption_randomness,
973 parsed_encrypted_k, parsed_encrypted_y, public_camenisch_shoup_));
974
975 // Recreate dummy composites from masked dummy composites (in order to
976 // regenerate Proof Message 1). Each dummy_composite is computed as
977 // masked_dummy_composite / original_value^challenge_in_proof.
978
979 ASSIGN_OR_RETURN(BigNum commit_messages_to_challenge_inverse,
980 pedersen_->Multiply(commit_messages, challenge_from_proof)
981 .ModInverse(pedersen_->n()));
982 PedersenOverZn::Commitment dummy_commit_messages = pedersen_->Add(
983 masked_dummy_commit_messages, commit_messages_to_challenge_inverse);
984
985 ASSIGN_OR_RETURN(BigNum commit_rs_to_challenge_inverse,
986 pedersen_->Multiply(commit_rs, challenge_from_proof)
987 .ModInverse(pedersen_->n()));
988 PedersenOverZn::Commitment dummy_commit_rs =
989 pedersen_->Add(masked_dummy_commit_rs, commit_rs_to_challenge_inverse);
990
991 std::vector<PedersenOverZn::Commitment> dummy_commit_as;
992 dummy_commit_as.reserve(commit_as.size());
993 for (size_t i = 0; i < commit_as.size(); ++i) {
994 ASSIGN_OR_RETURN(BigNum commit_as_to_challenge_inverse,
995 pedersen_->Multiply(commit_as[i], challenge_from_proof)
996 .ModInverse(pedersen_->n()));
997 dummy_commit_as.push_back(pedersen_->Add(masked_dummy_commit_as[i],
998 commit_as_to_challenge_inverse));
999 }
1000
1001 ASSIGN_OR_RETURN(BigNum commit_bs_to_challenge_inverse,
1002 pedersen_->Multiply(commit_bs, challenge_from_proof)
1003 .ModInverse(pedersen_->n()));
1004 PedersenOverZn::Commitment dummy_commit_bs =
1005 pedersen_->Add(masked_dummy_commit_bs, commit_bs_to_challenge_inverse);
1006
1007 ASSIGN_OR_RETURN(BigNum commit_alphas_to_challenge_inverse,
1008 pedersen_->Multiply(commit_alphas, challenge_from_proof)
1009 .ModInverse(pedersen_->n()));
1010 PedersenOverZn::Commitment dummy_commit_alphas_1 = pedersen_->Add(
1011 masked_dummy_commit_alphas_1, commit_alphas_to_challenge_inverse);
1012 PedersenOverZn::Commitment dummy_commit_alphas_2 = pedersen_->Add(
1013 masked_dummy_commit_alphas_2, commit_alphas_to_challenge_inverse);
1014
1015 ASSIGN_OR_RETURN(BigNum commit_gammas_to_challenge_inverse,
1016 pedersen_->Multiply(commit_gammas, challenge_from_proof)
1017 .ModInverse(pedersen_->n()));
1018 PedersenOverZn::Commitment dummy_commit_gammas_1 = pedersen_->Add(
1019 masked_dummy_commit_gammas_1, commit_gammas_to_challenge_inverse);
1020 PedersenOverZn::Commitment dummy_commit_gammas_2 = pedersen_->Add(
1021 masked_dummy_commit_gammas_2, commit_gammas_to_challenge_inverse);
1022
1023 // Package dummy_composites into Proof message_1.
1024 proto::BbObliviousSignatureRequestProof::Message1 message_1;
1025 message_1.set_dummy_commit_messages(dummy_commit_messages.ToBytes());
1026 message_1.set_dummy_commit_rs(dummy_commit_rs.ToBytes());
1027 *message_1.mutable_dummy_commit_as() = BigNumVectorToProto(dummy_commit_as);
1028 message_1.set_dummy_commit_bs(dummy_commit_bs.ToBytes());
1029 message_1.set_dummy_commit_alphas_1(dummy_commit_alphas_1.ToBytes());
1030 message_1.set_dummy_commit_alphas_2(dummy_commit_alphas_2.ToBytes());
1031 message_1.set_dummy_commit_gammas_1(dummy_commit_gammas_1.ToBytes());
1032 message_1.set_dummy_commit_gammas_2(dummy_commit_gammas_2.ToBytes());
1033 // dummy_encrypted_masked_messages are computed below.
1034
1035 // Some extra work is needed for the Camenisch Shoup ciphertext since it
1036 // doesn't natively support inverse.
1037 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
1038 CamenischShoupCiphertext encrypted_masked_messages_to_challenge =
1039 public_camenisch_shoup_->Multiply(encrypted_masked_messages[i],
1040 challenge_from_proof);
1041 ASSIGN_OR_RETURN(BigNum encrypted_masked_messages_to_challenge_u_inverse,
1042 encrypted_masked_messages_to_challenge.u.ModInverse(
1043 public_camenisch_shoup_->modulus()));
1044 std::vector<BigNum> encrypted_masked_messages_to_challenge_es_inverse;
1045 encrypted_masked_messages_to_challenge_es_inverse.reserve(
1046 encrypted_masked_messages_to_challenge.es.size());
1047 for (size_t i = 0; i < encrypted_masked_messages_to_challenge.es.size();
1048 ++i) {
1049 ASSIGN_OR_RETURN(BigNum encrypted_masked_messages_to_challenge_e_inverse,
1050 encrypted_masked_messages_to_challenge.es[i].ModInverse(
1051 public_camenisch_shoup_->modulus()));
1052 encrypted_masked_messages_to_challenge_es_inverse.push_back(
1053 std::move(encrypted_masked_messages_to_challenge_e_inverse));
1054 }
1055 CamenischShoupCiphertext encrypted_masked_messages_to_challenge_inverse{
1056 std::move(encrypted_masked_messages_to_challenge_u_inverse),
1057 std::move(encrypted_masked_messages_to_challenge_es_inverse)};
1058 CamenischShoupCiphertext dummy_encrypted_masked_messages =
1059 public_camenisch_shoup_->Add(
1060 masked_dummy_encrypted_masked_messages[i],
1061 encrypted_masked_messages_to_challenge_inverse);
1062
1063 *message_1.add_repeated_dummy_encrypted_masked_messages() =
1064 CamenischShoupCiphertextToProto(dummy_encrypted_masked_messages);
1065 }
1066
1067 // Reconstruct the challenge and check that it matches the one supplied in
1068 // the proof.
1069 ASSIGN_OR_RETURN(BigNum reconstructed_challenge,
1070 GenerateRequestProofChallenge(proof_statement, message_1));
1071
1072 if (reconstructed_challenge != challenge_from_proof) {
1073 return absl::InvalidArgumentError(
1074 absl::StrCat("BbObliviousSignature::VerifyRequest: Failed to verify "
1075 "request proof. Challenge in proof (",
1076 challenge_from_proof.ToDecimalString(),
1077 ") does not match reconstructed challenge (",
1078 reconstructed_challenge.ToDecimalString(), ")."));
1079 }
1080
1081 return absl::OkStatus();
1082 }
1083
1084 StatusOr<std::tuple<proto::BbObliviousSignatureResponse,
1085 proto::BbObliviousSignatureResponseProof>>
GenerateResponseAndProof(const proto::BbObliviousSignatureRequest & request,const proto::BbObliviousSignaturePublicKey & public_key,const proto::BbObliviousSignaturePrivateKey & private_key,const PedersenOverZn::Commitment & commit_messages,const PedersenOverZn::Commitment & commit_rs,PrivateCamenischShoup * private_camenisch_shoup)1086 BbObliviousSignature::GenerateResponseAndProof(
1087 const proto::BbObliviousSignatureRequest& request,
1088 const proto::BbObliviousSignaturePublicKey& public_key,
1089 const proto::BbObliviousSignaturePrivateKey& private_key,
1090 const PedersenOverZn::Commitment& commit_messages,
1091 const PedersenOverZn::Commitment& commit_rs,
1092 PrivateCamenischShoup* private_camenisch_shoup) {
1093 proto::BbObliviousSignatureResponse response_proto;
1094 proto::BbObliviousSignatureResponseProof response_proof_proto;
1095
1096 if (request.num_messages() > pedersen_->gs().size() ||
1097 request.num_messages() < 0) {
1098 return absl::InvalidArgumentError(
1099 "BbObliviousSignature::GenerateResponse: invalid num_messages in "
1100 "request.");
1101 }
1102
1103 size_t num_camenisch_shoup_ciphertexts =
1104 request.repeated_encrypted_masked_messages_size();
1105
1106 // We will refer to the values decrypted from the CS ciphertexts as betas.
1107 // These betas are implicitly bounded as long as the request proof was
1108 // verified (and the sender generated its parameters correctly).
1109 std::vector<BigNum> betas;
1110 betas.reserve(request.num_messages());
1111 std::vector<CamenischShoupCiphertext> encrypted_masked_messages;
1112 encrypted_masked_messages.reserve(num_camenisch_shoup_ciphertexts);
1113 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
1114 ASSIGN_OR_RETURN(CamenischShoupCiphertext encrypted_masked_messages_at_i,
1115 public_camenisch_shoup_->ParseCiphertextProto(
1116 request.repeated_encrypted_masked_messages(i)));
1117 ASSIGN_OR_RETURN(
1118 std::vector<BigNum> betas_at_i,
1119 private_camenisch_shoup->Decrypt(encrypted_masked_messages_at_i));
1120
1121 encrypted_masked_messages.push_back(
1122 std::move(encrypted_masked_messages_at_i));
1123 betas.insert(betas.end(), std::make_move_iterator(betas_at_i.begin()),
1124 std::make_move_iterator(betas_at_i.end()));
1125 }
1126 // Truncate the last few elements of betas, if it's larger than
1127 // num_messages. (These should be all zeros.)
1128 betas.erase(betas.begin() + request.num_messages(), betas.end());
1129
1130 std::vector<ECPoint> masked_prf_values;
1131 masked_prf_values.reserve(request.num_messages());
1132 for (uint64_t i = 0; i < request.num_messages(); ++i) {
1133 ASSIGN_OR_RETURN(BigNum beta_inverse,
1134 betas[i].ModInverse(ec_group_->GetOrder()));
1135 ASSIGN_OR_RETURN(ECPoint masked_prf_value, base_g_.Mul(beta_inverse));
1136 masked_prf_values.push_back(std::move(masked_prf_value));
1137 }
1138
1139 ASSIGN_OR_RETURN(*response_proto.mutable_masked_signature_values(),
1140 ECPointVectorToProto(masked_prf_values));
1141
1142 // Commit to decrypted_values (aka betas)
1143 ASSIGN_OR_RETURN(auto commit_and_open_betas, pedersen_->Commit(betas));
1144 response_proof_proto.set_commit_betas(
1145 commit_and_open_betas.commitment.ToBytes());
1146
1147 // (1) Generate Proof Message 1
1148
1149 // (1.1) Create dummy_betas, dummy_xs and dummy commitment-opening.
1150 // beta is bounded by 2^(challenge_length + security+parameter) * q^3, so
1151 // dummy_beta is bounded by the beta bound plus an additional
1152 // 2^(challenge_length + security_parameter).
1153 BigNum dummy_betas_bound =
1154 ctx_->One()
1155 .Lshift(2 * parameters_proto_.challenge_length_bits() +
1156 2 * parameters_proto_.security_parameter())
1157 .Mul(ec_group_->GetOrder())
1158 .Mul(ec_group_->GetOrder())
1159 .Mul(ec_group_->GetOrder());
1160 std::vector<BigNum> dummy_betas;
1161 dummy_betas.reserve(request.num_messages());
1162 for (uint64_t i = 0; i < request.num_messages(); ++i) {
1163 dummy_betas.push_back(ctx_->GenerateRandLessThan(dummy_betas_bound));
1164 }
1165
1166 std::vector<BigNum> dummy_xs;
1167 dummy_xs.reserve(public_camenisch_shoup_->vector_encryption_length());
1168 BigNum dummy_xs_bound = public_camenisch_shoup_->n().Lshift(
1169 parameters_proto_.challenge_length_bits() +
1170 parameters_proto_.security_parameter());
1171 for (uint64_t i = 0; i < public_camenisch_shoup_->vector_encryption_length();
1172 ++i) {
1173 dummy_xs.push_back(ctx_->GenerateRandLessThan(dummy_xs_bound));
1174 }
1175
1176 // Dummy opening has the same size as dummy_xs.
1177 BigNum dummy_beta_opening = ctx_->GenerateRandLessThan(dummy_xs_bound);
1178 ASSIGN_OR_RETURN(PedersenOverZn::Commitment dummy_commit_betas,
1179 pedersen_->CommitWithRand(dummy_betas, dummy_beta_opening));
1180
1181 // (1.2) Use the dummy values above to create dummy_cs_ys,
1182 // dummy_commit_betas, and dummy_base_gs and add them to proof message 1.
1183 std::vector<BigNum> dummy_cs_ys;
1184 dummy_cs_ys.reserve(public_camenisch_shoup_->vector_encryption_length());
1185 for (uint64_t i = 0; i < public_camenisch_shoup_->vector_encryption_length();
1186 ++i) {
1187 dummy_cs_ys.push_back(private_camenisch_shoup->g().ModExp(
1188 dummy_xs[i], private_camenisch_shoup->modulus()));
1189 }
1190
1191 std::vector<ECPoint> dummy_base_gs;
1192 dummy_base_gs.reserve(request.num_messages());
1193 for (uint64_t i = 0; i < request.num_messages(); ++i) {
1194 ASSIGN_OR_RETURN(ECPoint dummy_base_g,
1195 masked_prf_values[i].Mul(dummy_betas[i]));
1196 dummy_base_gs.push_back(std::move(dummy_base_g));
1197 }
1198
1199 proto::BbObliviousSignatureResponseProof::Message1 proof_message_1;
1200 *proof_message_1.mutable_dummy_camenisch_shoup_ys() =
1201 BigNumVectorToProto(dummy_cs_ys);
1202 proof_message_1.set_dummy_commit_betas(dummy_commit_betas.ToBytes());
1203 ASSIGN_OR_RETURN(*proof_message_1.mutable_dummy_base_gs(),
1204 ECPointVectorToProto(dummy_base_gs));
1205
1206 // (1.3) dummy_enc_mask_messages_es is more complicated: we need to create one
1207 // entry for each CS ciphertext in the request.
1208 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
1209 size_t batch_start_index =
1210 i * public_camenisch_shoup_->vector_encryption_length();
1211 size_t batch_size =
1212 std::min(public_camenisch_shoup_->vector_encryption_length(),
1213 request.num_messages() - batch_start_index);
1214 size_t batch_end_index = batch_start_index + batch_size;
1215
1216 // determine the dummy_betas that are to be used for this ciphertext.
1217 std::vector<BigNum> dummy_betas_for_batch(
1218 dummy_betas.begin() + batch_start_index,
1219 dummy_betas.begin() + batch_end_index);
1220
1221 // intermediate_es contains
1222 // (1+n)^dummy_betas[i] mod n^(s+1) in the "es" component. This is achieved
1223 // by encrypting dummy_betas with randomness 0.
1224 ASSIGN_OR_RETURN(CamenischShoupCiphertext intermediate_ciphertext,
1225 private_camenisch_shoup->EncryptWithRand(
1226 dummy_betas_for_batch, ctx_->Zero()));
1227 // dummy_enc_mask_messages_es contains u^dummy_xs[j] * (1+n)^dummy_betas[j]
1228 // mod n^(s+1) in the "es" component.
1229 std::vector<BigNum> dummy_enc_mask_messages_es;
1230 dummy_enc_mask_messages_es.reserve(
1231 public_camenisch_shoup_->vector_encryption_length());
1232 for (size_t j = 0; j < batch_size; ++j) {
1233 BigNum dummy_e =
1234 encrypted_masked_messages[i]
1235 .u.ModExp(dummy_xs[j], private_camenisch_shoup->modulus())
1236 .ModMul(intermediate_ciphertext.es[j],
1237 private_camenisch_shoup->modulus());
1238 dummy_enc_mask_messages_es.push_back(std::move(dummy_e));
1239 }
1240
1241 *proof_message_1.add_repeated_dummy_encrypted_masked_messages_es() =
1242 BigNumVectorToProto(dummy_enc_mask_messages_es);
1243 }
1244
1245 // (2) Generate challenge
1246 ASSIGN_OR_RETURN(
1247 BigNum challenge,
1248 GenerateResponseProofChallenge(
1249 public_key, commit_messages, commit_rs, request, response_proto,
1250 commit_and_open_betas.commitment, proof_message_1));
1251 response_proof_proto.set_challenge(challenge.ToBytes());
1252
1253 // (3) Generate Message 2
1254 // Compute all masked dummy values: masked_dummy_betas,
1255 // masked_dummy_xs, masked_dummy_beta_opening
1256 std::vector<BigNum> masked_dummy_betas;
1257 masked_dummy_betas.reserve(request.num_messages());
1258 for (uint64_t i = 0; i < request.num_messages(); ++i) {
1259 masked_dummy_betas.push_back(dummy_betas[i] + betas[i].Mul(challenge));
1260 }
1261 std::vector<BigNum> masked_dummy_xs;
1262 masked_dummy_xs.reserve(public_camenisch_shoup_->vector_encryption_length());
1263 for (uint64_t i = 0; i < public_camenisch_shoup_->vector_encryption_length();
1264 ++i) {
1265 masked_dummy_xs.push_back(
1266 dummy_xs[i] + (private_camenisch_shoup->xs()[i].Mul(challenge)));
1267 }
1268 BigNum masked_dummy_beta_opening =
1269 dummy_beta_opening + commit_and_open_betas.opening.Mul(challenge);
1270
1271 proto::BbObliviousSignatureResponseProof::Message2* response_proof_message_2 =
1272 response_proof_proto.mutable_message_2();
1273 *response_proof_message_2->mutable_masked_dummy_betas() =
1274 BigNumVectorToProto(masked_dummy_betas);
1275 *response_proof_message_2->mutable_masked_dummy_camenisch_shoup_xs() =
1276 BigNumVectorToProto(masked_dummy_xs);
1277 response_proof_message_2->set_masked_dummy_beta_opening(
1278 masked_dummy_beta_opening.ToBytes());
1279
1280 return std::make_tuple(response_proto, response_proof_proto);
1281 }
1282
VerifyResponse(const proto::BbObliviousSignaturePublicKey & public_key,const proto::BbObliviousSignatureResponse & response,const proto::BbObliviousSignatureResponseProof & response_proof,const proto::BbObliviousSignatureRequest & request,const PedersenOverZn::Commitment & commit_messages,const PedersenOverZn::Commitment & commit_rs)1283 Status BbObliviousSignature::VerifyResponse(
1284 const proto::BbObliviousSignaturePublicKey& public_key,
1285 const proto::BbObliviousSignatureResponse& response,
1286 const proto::BbObliviousSignatureResponseProof& response_proof,
1287 const proto::BbObliviousSignatureRequest& request,
1288 const PedersenOverZn::Commitment& commit_messages,
1289 const PedersenOverZn::Commitment& commit_rs) {
1290 if (response.masked_signature_values().serialized_ec_points_size() !=
1291 request.num_messages()) {
1292 return absl::InvalidArgumentError(
1293 "BbObliviousSignature::VerifyResponse: response has a different "
1294 "number "
1295 "of masked_signature_values values than the request");
1296 }
1297
1298 if (response_proof.message_2()
1299 .masked_dummy_camenisch_shoup_xs()
1300 .serialized_big_nums_size() !=
1301 public_camenisch_shoup_->vector_encryption_length()) {
1302 return absl::InvalidArgumentError(
1303 "BbObliviousSignature::VerifyResponse: response proof has wrong "
1304 "number "
1305 "of masked_dummy_camenisch_shoup_xs in message 2.");
1306 }
1307 if (response_proof.message_2()
1308 .masked_dummy_betas()
1309 .serialized_big_nums_size() != request.num_messages()) {
1310 return absl::InvalidArgumentError(
1311 "BbObliviousSignature::VerifyResponse: response proof has wrong "
1312 "number "
1313 "of masked_dummy_betas in message 2.");
1314 }
1315
1316 size_t num_camenisch_shoup_ciphertexts =
1317 request.repeated_encrypted_masked_messages_size();
1318
1319 std::vector<CamenischShoupCiphertext> encrypted_masked_messages;
1320 encrypted_masked_messages.reserve(num_camenisch_shoup_ciphertexts);
1321 // Parse the needed request, response and response proof elements.
1322 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
1323 ASSIGN_OR_RETURN(CamenischShoupCiphertext encrypted_masked_messages_at_i,
1324 public_camenisch_shoup_->ParseCiphertextProto(
1325 request.repeated_encrypted_masked_messages(i)));
1326 encrypted_masked_messages.push_back(
1327 std::move(encrypted_masked_messages_at_i));
1328 }
1329 ASSIGN_OR_RETURN(std::vector<ECPoint> masked_signature_values,
1330 ParseECPointVectorProto(ctx_, ec_group_,
1331 response.masked_signature_values()));
1332 PedersenOverZn::Commitment commit_betas =
1333 ctx_->CreateBigNum(response_proof.commit_betas());
1334 BigNum challenge_from_proof = ctx_->CreateBigNum(response_proof.challenge());
1335 std::vector<BigNum> masked_dummy_camenisch_shoup_xs = ParseBigNumVectorProto(
1336 ctx_, response_proof.message_2().masked_dummy_camenisch_shoup_xs());
1337 std::vector<BigNum> masked_dummy_betas = ParseBigNumVectorProto(
1338 ctx_, response_proof.message_2().masked_dummy_betas());
1339 BigNum masked_dummy_beta_opening = ctx_->CreateBigNum(
1340 response_proof.message_2().masked_dummy_beta_opening());
1341
1342 // Check the lengths of masked dummy betas
1343 BigNum masked_dummy_betas_bound =
1344 ctx_->One().Lshift(2 * parameters_proto_.challenge_length_bits() +
1345 2 * parameters_proto_.security_parameter() + 1) *
1346 ec_group_->GetOrder() * ec_group_->GetOrder() * ec_group_->GetOrder();
1347 for (uint64_t i = 0; i < request.num_messages(); ++i) {
1348 if (masked_dummy_betas[i] >= masked_dummy_betas_bound)
1349 return absl::InvalidArgumentError(absl::StrCat(
1350 "BbObliviousSignature::VerifyResponse: The ", i,
1351 "th entry of masked_dummy_betas,",
1352 masked_dummy_betas[i].ToDecimalString(), " (bit length ",
1353 masked_dummy_betas[i].BitLength(), ")",
1354 ",is larger than the acceptable bound: ",
1355 masked_dummy_betas_bound.ToDecimalString(), " (bit length ",
1356 masked_dummy_betas_bound.BitLength(), ")"));
1357 }
1358
1359 // Reconstruct each element of Proof message 1.
1360 proto::BbObliviousSignatureResponseProof::Message1 reconstructed_message_1;
1361
1362 // Reconstruct dummy_base_gs.
1363 std::vector<ECPoint> dummy_base_gs;
1364 dummy_base_gs.reserve(request.num_messages());
1365 ASSIGN_OR_RETURN(ECPoint base_g_to_challenge,
1366 base_g_.Mul(challenge_from_proof));
1367 ASSIGN_OR_RETURN(ECPoint base_g_to_challenge_inverse,
1368 base_g_to_challenge.Inverse());
1369 for (uint64_t i = 0; i < request.num_messages(); ++i) {
1370 ASSIGN_OR_RETURN(ECPoint masked_dummy_g,
1371 masked_signature_values[i].Mul(masked_dummy_betas[i]));
1372 // Compute dummy_base_g as masked_dummy_g / g^c
1373 ASSIGN_OR_RETURN(ECPoint dummy_base_g,
1374 masked_dummy_g.Add(base_g_to_challenge_inverse));
1375 dummy_base_gs.push_back(std::move(dummy_base_g));
1376 }
1377
1378 ASSIGN_OR_RETURN(*reconstructed_message_1.mutable_dummy_base_gs(),
1379 ECPointVectorToProto(dummy_base_gs));
1380
1381 for (size_t i = 0; i < num_camenisch_shoup_ciphertexts; ++i) {
1382 size_t batch_start_index =
1383 i * public_camenisch_shoup_->vector_encryption_length();
1384 size_t batch_size =
1385 std::min(public_camenisch_shoup_->vector_encryption_length(),
1386 request.num_messages() - batch_start_index);
1387 size_t batch_end_index = batch_start_index + batch_size;
1388 std::vector<BigNum> masked_dummy_betas_for_batch(
1389 masked_dummy_betas.begin() + batch_start_index,
1390 masked_dummy_betas.begin() + batch_end_index);
1391 // Reconstruct dummy_es
1392 // es[i] of intermediate_ciphertext is (1+n)^masked_dummy_betas[i].
1393 ASSIGN_OR_RETURN(CamenischShoupCiphertext intermediate_ciphertext,
1394 public_camenisch_shoup_->EncryptWithRand(
1395 masked_dummy_betas_for_batch, ctx_->Zero()));
1396 std::vector<BigNum> dummy_es;
1397 dummy_es.reserve(batch_size);
1398 for (size_t j = 0; j < batch_size; ++j) {
1399 // masked_dummy_e = (1+n)^masked_dummy_betas_for_batch[j] *
1400 // u^masked_dummy_xs[j]
1401 BigNum masked_dummy_e = intermediate_ciphertext.es[j].ModMul(
1402 encrypted_masked_messages[i].u.ModExp(
1403 masked_dummy_camenisch_shoup_xs[j],
1404 public_camenisch_shoup_->modulus()),
1405 public_camenisch_shoup_->modulus());
1406
1407 ASSIGN_OR_RETURN(
1408 BigNum e_to_challenge_inverse,
1409 encrypted_masked_messages[i]
1410 .es[j]
1411 .ModExp(challenge_from_proof, public_camenisch_shoup_->modulus())
1412 .ModInverse(public_camenisch_shoup_->modulus()));
1413
1414 BigNum dummy_e = masked_dummy_e.ModMul(
1415 e_to_challenge_inverse, public_camenisch_shoup_->modulus());
1416 dummy_es.push_back(std::move(dummy_e));
1417 }
1418 *reconstructed_message_1.add_repeated_dummy_encrypted_masked_messages_es() =
1419 BigNumVectorToProto(dummy_es);
1420 }
1421
1422 // Reconstruct dummy_commit_betas.
1423 ASSIGN_OR_RETURN(
1424 PedersenOverZn::Commitment masked_dummy_commit_betas,
1425 pedersen_->CommitWithRand(masked_dummy_betas, masked_dummy_beta_opening));
1426 ASSIGN_OR_RETURN(BigNum commit_betas_to_challenge_inverse,
1427 pedersen_->Multiply(commit_betas, challenge_from_proof)
1428 .ModInverse(pedersen_->n()));
1429 PedersenOverZn::Commitment dummy_commit_betas = pedersen_->Add(
1430 masked_dummy_commit_betas, commit_betas_to_challenge_inverse);
1431 reconstructed_message_1.set_dummy_commit_betas(dummy_commit_betas.ToBytes());
1432
1433 // Reconstruct dummy_camenisch_shoup_ys
1434 std::vector<BigNum> dummy_camenisch_shoup_ys;
1435 dummy_camenisch_shoup_ys.reserve(
1436 public_camenisch_shoup_->vector_encryption_length());
1437 for (uint64_t i = 0; i < public_camenisch_shoup_->vector_encryption_length();
1438 ++i) {
1439 BigNum masked_dummy_y = public_camenisch_shoup_->g().ModExp(
1440 masked_dummy_camenisch_shoup_xs[i], public_camenisch_shoup_->modulus());
1441 ASSIGN_OR_RETURN(
1442 BigNum y_to_challenge_inverse,
1443 public_camenisch_shoup_->ys()[i]
1444 .ModExp(challenge_from_proof, public_camenisch_shoup_->modulus())
1445 .ModInverse(public_camenisch_shoup_->modulus()));
1446 BigNum dummy_camenisch_shoup_y = masked_dummy_y.ModMul(
1447 y_to_challenge_inverse, public_camenisch_shoup_->modulus());
1448 dummy_camenisch_shoup_ys.push_back(std::move(dummy_camenisch_shoup_y));
1449 }
1450 *reconstructed_message_1.mutable_dummy_camenisch_shoup_ys() =
1451 BigNumVectorToProto(dummy_camenisch_shoup_ys);
1452
1453 // Reconstruct the challenge by applying FiatShamir to the reconstructed
1454 // first message, and ensure it exactly matches the challenge in the proof.
1455 ASSIGN_OR_RETURN(BigNum reconstructed_challenge,
1456 GenerateResponseProofChallenge(
1457 public_key, commit_messages, commit_rs, request,
1458 response, commit_betas, reconstructed_message_1));
1459
1460 if (reconstructed_challenge != challenge_from_proof) {
1461 return absl::InvalidArgumentError(
1462 absl::StrCat("BbObliviousSignature::VerifyResponse: Failed to verify "
1463 "response proof. Challenge in proof (",
1464 challenge_from_proof.ToDecimalString(),
1465 ") does not match reconstructed challenge (",
1466 reconstructed_challenge.ToDecimalString(), ")."));
1467 }
1468 return absl::OkStatus();
1469 }
1470
ExtractResults(const proto::BbObliviousSignatureResponse & response,const proto::BbObliviousSignatureRequest & request,const proto::BbObliviousSignatureRequestPrivateState & request_state)1471 StatusOr<std::vector<ECPoint>> BbObliviousSignature::ExtractResults(
1472 const proto::BbObliviousSignatureResponse& response,
1473 const proto::BbObliviousSignatureRequest& request,
1474 const proto::BbObliviousSignatureRequestPrivateState& request_state) {
1475 // Unmask and extract the signatures.
1476 ASSIGN_OR_RETURN(std::vector<ECPoint> masked_prf_values,
1477 ParseECPointVectorProto(ctx_, ec_group_,
1478 response.masked_signature_values()));
1479 std::vector<BigNum> as =
1480 ParseBigNumVectorProto(ctx_, request_state.private_as());
1481
1482 std::vector<ECPoint> prf_values;
1483 prf_values.reserve(masked_prf_values.size());
1484
1485 for (size_t i = 0; i < masked_prf_values.size(); ++i) {
1486 ASSIGN_OR_RETURN(ECPoint prf_value, masked_prf_values[i].Mul(as[i]));
1487 prf_values.push_back(std::move(prf_value));
1488 }
1489
1490 return std::move(prf_values);
1491 }
1492
1493 // Generates the challenge for the Request proof using the Fiat-Shamir
1494 // heuristic.
GenerateRequestProofChallenge(const proto::BbObliviousSignatureRequestProof::Statement & proof_statement,const proto::BbObliviousSignatureRequestProof::Message1 & proof_message_1)1495 StatusOr<BigNum> BbObliviousSignature::GenerateRequestProofChallenge(
1496 const proto::BbObliviousSignatureRequestProof::Statement& proof_statement,
1497 const proto::BbObliviousSignatureRequestProof::Message1& proof_message_1) {
1498 BigNum challenge_bound =
1499 ctx_->One().Lshift(parameters_proto_.challenge_length_bits());
1500
1501 // Note that the random oracle prefix is implicitly included as part of the
1502 // parameters being serialized in the statement proto. We skip including it
1503 // again here to avoid unnecessary duplication.
1504 std::string challenge_string =
1505 "BbObliviousSignature::GenerateResponseProofChallenge";
1506
1507 auto challenge_sos =
1508 std::make_unique<google::protobuf::io::StringOutputStream>(
1509 &challenge_string);
1510 auto challenge_cos =
1511 std::make_unique<google::protobuf::io::CodedOutputStream>(
1512 challenge_sos.get());
1513 challenge_cos->SetSerializationDeterministic(true);
1514 challenge_cos->WriteVarint64(proof_statement.ByteSizeLong());
1515 challenge_cos->WriteString(SerializeAsStringInOrder(proof_statement));
1516
1517 challenge_cos->WriteVarint64(proof_message_1.ByteSizeLong());
1518 challenge_cos->WriteString(SerializeAsStringInOrder(proof_message_1));
1519
1520 // Delete the CodedOutputStream and StringOutputStream to make sure they are
1521 // cleaned up before hashing.
1522 challenge_cos.reset();
1523 challenge_sos.reset();
1524
1525 return ctx_->RandomOracleSha512(challenge_string, challenge_bound);
1526 }
1527
1528 // Generates the challenge for the Response proof using the Fiat-Shamir
1529 // heuristic.
GenerateResponseProofChallenge(const proto::BbObliviousSignaturePublicKey & public_key,const PedersenOverZn::Commitment & commit_messages,const PedersenOverZn::Commitment & commit_rs,const proto::BbObliviousSignatureRequest & request,const proto::BbObliviousSignatureResponse & response,const PedersenOverZn::Commitment & commit_betas,const proto::BbObliviousSignatureResponseProof::Message1 & proof_message_1)1530 StatusOr<BigNum> BbObliviousSignature::GenerateResponseProofChallenge(
1531 const proto::BbObliviousSignaturePublicKey& public_key,
1532 const PedersenOverZn::Commitment& commit_messages,
1533 const PedersenOverZn::Commitment& commit_rs,
1534 const proto::BbObliviousSignatureRequest& request,
1535 const proto::BbObliviousSignatureResponse& response,
1536 const PedersenOverZn::Commitment& commit_betas,
1537 const proto::BbObliviousSignatureResponseProof::Message1& proof_message_1) {
1538 BigNum challenge_bound =
1539 ctx_->One().Lshift(parameters_proto_.challenge_length_bits());
1540
1541 // Generate the statement
1542 proto::BbObliviousSignatureResponseProof::Statement statement;
1543 *statement.mutable_parameters() = parameters_proto_;
1544 *statement.mutable_public_key() = public_key;
1545 statement.set_commit_messages(commit_messages.ToBytes());
1546 statement.set_commit_rs(commit_rs.ToBytes());
1547 *statement.mutable_request() = request;
1548 *statement.mutable_response() = response;
1549 statement.set_commit_betas(commit_betas.ToBytes());
1550
1551 // Note that the random oracle prefix is implicitly included as part of the
1552 // parameters being serialized in the statement proto. We skip including it
1553 // again here to avoid unnecessary duplication.
1554 std::string challenge_string =
1555 "BbObliviousSignature::GenerateResponseProofChallenge";
1556
1557 auto challenge_sos =
1558 std::make_unique<google::protobuf::io::StringOutputStream>(
1559 &challenge_string);
1560 auto challenge_cos =
1561 std::make_unique<google::protobuf::io::CodedOutputStream>(
1562 challenge_sos.get());
1563 challenge_cos->SetSerializationDeterministic(true);
1564 challenge_cos->WriteVarint64(statement.ByteSizeLong());
1565 challenge_cos->WriteString(SerializeAsStringInOrder(statement));
1566
1567 challenge_cos->WriteVarint64(proof_message_1.ByteSizeLong());
1568 challenge_cos->WriteString(SerializeAsStringInOrder(proof_message_1));
1569
1570 // Delete the CodedOutputStream and StringOutputStream to make sure they are
1571 // cleaned up before hashing.
1572 challenge_cos.reset();
1573 challenge_sos.reset();
1574
1575 return ctx_->RandomOracleSha512(challenge_string, challenge_bound);
1576 }
1577
1578 } // namespace private_join_and_compute
1579