ferveo_nucypher_tdec_temp6/
lib.rs1#![warn(rust_2018_idioms)]
2use ark_ec::pairing::Pairing;
3
4pub mod ciphertext;
6pub mod combine;
7pub mod context;
8pub mod decryption;
9pub mod hash_to_curve;
10pub mod key_share;
11pub mod secret_box;
12
13pub use ciphertext::*;
23pub use combine::*;
24pub use context::*;
25pub use decryption::*;
26pub use hash_to_curve::*;
27pub use key_share::*;
28pub use secret_box::*;
29
30#[cfg(feature = "api")]
31pub mod api;
32
33#[derive(Debug, thiserror::Error)]
34pub enum Error {
35 #[error("Ciphertext verification failed")]
38 CiphertextVerificationFailed,
39
40 #[error("Decryption share verification failed")]
43 DecryptionShareVerificationFailed,
44
45 #[error("Symmetric encryption failed")]
47 SymmetricEncryptionError(chacha20poly1305::aead::Error),
48
49 #[error(transparent)]
50 BincodeError(#[from] bincode::Error),
51
52 #[error(transparent)]
53 ArkSerializeError(#[from] ark_serialize::SerializationError),
54}
55
56pub type DomainPoint<E> = <E as Pairing>::ScalarField;
57pub type Result<T> = std::result::Result<T, Error>;
58
59#[cfg(any(test, feature = "test-common"))]
61pub mod test_common {
62 use std::ops::Mul;
63
64 pub use ark_bls12_381::Bls12_381 as EllipticCurve;
65 use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
66 pub use ark_ff::UniformRand;
67 use ark_ff::{Field, Zero};
68 use ark_poly::{
69 univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain,
70 Polynomial,
71 };
72 use itertools::izip;
73 use subproductdomain::fast_multiexp;
74
75 pub use super::*;
76
77 pub fn setup_simple<E: Pairing>(
78 shares_num: usize,
79 threshold: usize,
80 rng: &mut impl rand::Rng,
81 ) -> (
82 DkgPublicKey<E>,
83 PrivateKeyShare<E>,
84 Vec<PrivateDecryptionContextSimple<E>>,
85 ) {
86 let g = E::G1Affine::generator();
87 let h = E::G2Affine::generator();
88
89 let threshold_poly =
91 DensePolynomial::<E::ScalarField>::rand(threshold - 1, rng);
92
93 let fft_domain =
95 ark_poly::GeneralEvaluationDomain::<E::ScalarField>::new(
96 shares_num,
97 )
98 .unwrap();
99
100 let domain_points = fft_domain.elements().collect::<Vec<_>>();
102
103 let evals = threshold_poly.evaluate_over_domain_by_ref(fft_domain);
105
106 let share_commitments = fast_multiexp(&evals.evals, g.into_group());
108
109 let privkey_shares = fast_multiexp(&evals.evals, h.into_group());
114
115 let a_0 = threshold_poly.coeffs[0];
117
118 let group_pubkey = g.mul(a_0);
120
121 let group_privkey = h.mul(a_0);
123
124 let secret = threshold_poly.evaluate(&E::ScalarField::zero());
126 debug_assert!(secret == a_0);
127
128 let mut private_contexts = vec![];
129 let mut public_contexts = vec![];
130
131 for (index, (domain_point, share_commit, private_share)) in izip!(
133 domain_points.iter(),
134 share_commitments.iter(),
135 privkey_shares.iter()
136 )
137 .enumerate()
138 {
139 let private_key_share = PrivateKeyShare::<E>(*private_share);
140 let blinding_factor = E::ScalarField::rand(rng);
141
142 let validator_public_key = h.mul(blinding_factor).into_affine();
143 let blinded_key_share = BlindedKeyShare::<E> {
144 validator_public_key,
145 blinded_key_share: private_key_share
146 .0
147 .mul(blinding_factor)
148 .into_affine(),
149 };
150
151 private_contexts.push(PrivateDecryptionContextSimple::<E> {
152 index,
153 setup_params: SetupParams {
154 b: blinding_factor,
155 b_inv: blinding_factor.inverse().unwrap(),
156 g,
157 h_inv: E::G2Prepared::from(-h.into_group()),
158 g_inv: E::G1Prepared::from(-g.into_group()),
159 h,
160 },
161 private_key_share,
162 public_decryption_contexts: vec![],
163 });
164 public_contexts.push(PublicDecryptionContextSimple::<E> {
165 domain: *domain_point,
166 share_commitment: ShareCommitment::<E>(*share_commit),
167 blinded_key_share,
168 validator_public_key: ferveo_common::PublicKey {
169 encryption_key: blinded_key_share.validator_public_key,
170 },
171 });
172 }
173 for private_ctxt in private_contexts.iter_mut() {
174 private_ctxt.public_decryption_contexts = public_contexts.clone();
175 }
176
177 (
178 DkgPublicKey(group_pubkey.into()),
179 PrivateKeyShare(group_privkey.into()), private_contexts,
181 )
182 }
183
184 pub fn setup_precomputed<E: Pairing>(
185 shares_num: usize,
186 threshold: usize,
187 rng: &mut impl rand::Rng,
188 ) -> (
189 DkgPublicKey<E>,
190 PrivateKeyShare<E>,
191 Vec<PrivateDecryptionContextSimple<E>>,
192 ) {
193 setup_simple::<E>(shares_num, threshold, rng)
194 }
195
196 pub fn create_shared_secret_simple<E: Pairing>(
197 pub_contexts: &[PublicDecryptionContextSimple<E>],
198 decryption_shares: &[DecryptionShareSimple<E>],
199 ) -> SharedSecret<E> {
200 let domain = pub_contexts.iter().map(|c| c.domain).collect::<Vec<_>>();
201 let lagrange_coeffs = prepare_combine_simple::<E>(&domain);
202 share_combine_simple::<E>(decryption_shares, &lagrange_coeffs)
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use std::ops::Mul;
209
210 use ark_ec::{pairing::Pairing, CurveGroup};
211 use ark_std::{test_rng, UniformRand};
212 use ferveo_common::{FromBytes, ToBytes};
213 use rand::seq::IteratorRandom;
214
215 use crate::{
216 api::DecryptionSharePrecomputed,
217 test_common::{create_shared_secret_simple, setup_simple, *},
218 };
219
220 type E = ark_bls12_381::Bls12_381;
221 type TargetField = <E as Pairing>::TargetField;
222 type ScalarField = <E as Pairing>::ScalarField;
223
224 #[test]
225 fn ciphertext_serialization() {
226 let rng = &mut test_rng();
227 let shares_num = 16;
228 let threshold = shares_num * 2 / 3;
229 let msg = "my-msg".as_bytes().to_vec();
230 let aad: &[u8] = "my-aad".as_bytes();
231
232 let (pubkey, _, _) = setup_simple::<E>(threshold, shares_num, rng);
233
234 let ciphertext =
235 encrypt::<E>(SecretBox::new(msg), aad, &pubkey, rng).unwrap();
236
237 let serialized = ciphertext.to_bytes().unwrap();
238 let deserialized: Ciphertext<E> =
239 Ciphertext::from_bytes(&serialized).unwrap();
240
241 assert_eq!(serialized, deserialized.to_bytes().unwrap())
242 }
243
244 fn test_ciphertext_validation_fails<E: Pairing>(
245 msg: &[u8],
246 aad: &[u8],
247 ciphertext: &Ciphertext<E>,
248 shared_secret: &SharedSecret<E>,
249 ) {
250 let plaintext =
252 decrypt_with_shared_secret(ciphertext, aad, shared_secret).unwrap();
253 assert_eq!(plaintext, msg);
254
255 let mut ciphertext = ciphertext.clone();
257 ciphertext.ciphertext[0] += 1;
258 assert!(decrypt_with_shared_secret(&ciphertext, aad, shared_secret)
259 .is_err());
260
261 let aad = "bad aad".as_bytes();
263 assert!(decrypt_with_shared_secret(&ciphertext, aad, shared_secret)
264 .is_err());
265 }
266
267 #[test]
268 fn tdec_simple_variant_share_validation() {
269 let rng = &mut test_rng();
270 let shares_num = 16;
271 let threshold = shares_num * 2 / 3;
272 let msg = "my-msg".as_bytes().to_vec();
273 let aad: &[u8] = "my-aad".as_bytes();
274
275 let (pubkey, _, contexts) =
276 setup_simple::<E>(shares_num, threshold, rng);
277 let ciphertext =
278 encrypt::<E>(SecretBox::new(msg), aad, &pubkey, rng).unwrap();
279
280 let bad_aad = "bad aad".as_bytes();
281 assert!(contexts[0]
282 .create_share(&ciphertext.header().unwrap(), bad_aad)
283 .is_err());
284 }
285
286 #[test]
287 fn tdec_simple_variant_e2e() {
288 let mut rng = &mut test_rng();
289 let shares_num = 16;
290 let threshold = shares_num * 2 / 3;
291 let msg = "my-msg".as_bytes().to_vec();
292 let aad: &[u8] = "my-aad".as_bytes();
293
294 let (pubkey, _, contexts) =
295 setup_simple::<E>(shares_num, threshold, &mut rng);
296
297 let ciphertext =
298 encrypt::<E>(SecretBox::new(msg.clone()), aad, &pubkey, rng)
299 .unwrap();
300
301 let decryption_shares: Vec<_> = contexts
303 .iter()
304 .map(|c| {
305 c.create_share(&ciphertext.header().unwrap(), aad).unwrap()
306 })
307 .take(threshold)
308 .collect();
309 let selected_contexts =
310 contexts[0].public_decryption_contexts[..threshold].to_vec();
311 let shared_secret =
312 create_shared_secret_simple(&selected_contexts, &decryption_shares);
313
314 test_ciphertext_validation_fails(
315 &msg,
316 aad,
317 &ciphertext,
318 &shared_secret,
319 );
320
321 let not_enough_dec_shares = decryption_shares[..threshold - 1].to_vec();
323 let not_enough_contexts = selected_contexts[..threshold - 1].to_vec();
324 let bash_shared_secret = create_shared_secret_simple(
325 ¬_enough_contexts,
326 ¬_enough_dec_shares,
327 );
328 let result =
329 decrypt_with_shared_secret(&ciphertext, aad, &bash_shared_secret);
330 assert!(result.is_err());
331 }
332
333 #[test]
334 fn tdec_precomputed_variant_e2e() {
335 let mut rng = &mut test_rng();
336 let shares_num = 16;
337 let threshold = shares_num * 2 / 3;
338 let msg = "my-msg".as_bytes().to_vec();
339 let aad: &[u8] = "my-aad".as_bytes();
340
341 let (pubkey, _, contexts) =
342 setup_precomputed::<E>(shares_num, threshold, &mut rng);
343 let ciphertext =
344 encrypt::<E>(SecretBox::new(msg.clone()), aad, &pubkey, rng)
345 .unwrap();
346
347 let selected_participants =
348 (0..threshold).choose_multiple(rng, threshold);
349 let selected_contexts = contexts
350 .iter()
351 .filter(|c| selected_participants.contains(&c.index))
352 .cloned()
353 .collect::<Vec<_>>();
354
355 let decryption_shares = selected_contexts
356 .iter()
357 .map(|context| {
358 context
359 .create_share_precomputed(
360 &ciphertext.header().unwrap(),
361 aad,
362 &selected_participants,
363 )
364 .unwrap()
365 })
366 .collect::<Vec<DecryptionSharePrecomputed>>();
367
368 let shared_secret = share_combine_precomputed::<E>(&decryption_shares);
369 test_ciphertext_validation_fails(
370 &msg,
371 aad,
372 &ciphertext,
373 &shared_secret,
374 );
375
376 let not_enough_dec_shares = decryption_shares[..threshold - 1].to_vec();
378 let bash_shared_secret =
379 share_combine_precomputed(¬_enough_dec_shares);
380 let result =
381 decrypt_with_shared_secret(&ciphertext, aad, &bash_shared_secret);
382 assert!(result.is_err());
383 }
384
385 #[test]
386 fn tdec_simple_variant_share_verification() {
387 let mut rng = &mut test_rng();
388 let shares_num = 16;
389 let threshold = shares_num * 2 / 3;
390 let msg = "my-msg".as_bytes().to_vec();
391 let aad: &[u8] = "my-aad".as_bytes();
392
393 let (pubkey, _, contexts) =
394 setup_simple::<E>(shares_num, threshold, &mut rng);
395
396 let ciphertext =
397 encrypt::<E>(SecretBox::new(msg), aad, &pubkey, rng).unwrap();
398
399 let decryption_shares: Vec<_> = contexts
400 .iter()
401 .map(|c| {
402 c.create_share(&ciphertext.header().unwrap(), aad).unwrap()
403 })
404 .collect();
405
406 let pub_contexts = &contexts[0].public_decryption_contexts;
413 assert!(verify_decryption_shares_simple(
414 pub_contexts,
415 &ciphertext,
416 &decryption_shares,
417 ));
418
419 let mut has_bad_checksum = decryption_shares[0].clone();
422 has_bad_checksum.validator_checksum.checksum = has_bad_checksum
423 .validator_checksum
424 .checksum
425 .mul(ScalarField::rand(rng))
426 .into_affine();
427
428 assert!(!has_bad_checksum.verify(
429 &pub_contexts[0].blinded_key_share.blinded_key_share,
430 &pub_contexts[0].validator_public_key.encryption_key,
431 &ciphertext,
432 ));
433
434 let mut has_bad_share = decryption_shares[0].clone();
435 has_bad_share.decryption_share =
436 has_bad_share.decryption_share.mul(TargetField::rand(rng));
437
438 assert!(!has_bad_share.verify(
439 &pub_contexts[0].blinded_key_share.blinded_key_share,
440 &pub_contexts[0].validator_public_key.encryption_key,
441 &ciphertext,
442 ));
443 }
444}