1#![warn(rust_2018_idioms)]
2
3#[cfg(feature = "bindings-wasm")]
4extern crate alloc;
5
6use ark_ec::pairing::Pairing;
7use group_threshold_cryptography as tpke;
8use itertools::zip_eq;
9
10#[cfg(feature = "bindings-python")]
11pub mod bindings_python;
12
13#[cfg(feature = "bindings-wasm")]
14pub mod bindings_wasm;
15
16pub mod api;
17pub mod dkg;
18pub mod primitives;
19pub mod pvss;
20pub mod validator;
21
22mod utils;
23
24pub use dkg::*;
25pub use primitives::*;
26pub use pvss::*;
27pub use validator::*;
28
29#[derive(Debug, thiserror::Error)]
30pub enum Error {
31 #[error(transparent)]
32 ThresholdEncryptionError(#[from] tpke::Error),
33
34 #[error("Invalid DKG state to deal PVSS shares")]
36 InvalidDkgStateToDeal,
37
38 #[error("Invalid DKG state to aggregate PVSS transcripts")]
40 InvalidDkgStateToAggregate,
41
42 #[error("Invalid DKG state to verify PVSS transcripts")]
44 InvalidDkgStateToVerify,
45
46 #[error("Invalid DKG state to ingest PVSS transcripts")]
48 InvalidDkgStateToIngest,
49
50 #[error("Expected validator to be a part of the DKG validator set: {0}")]
52 DealerNotInValidatorSet(EthereumAddress),
53
54 #[error("DKG received an unknown dealer: {0}")]
56 UnknownDealer(EthereumAddress),
57
58 #[error("DKG received a PVSS transcript from a dealer that has already been dealt: {0}")]
60 DuplicateDealer(EthereumAddress),
61
62 #[error("DKG received an invalid transcript")]
64 InvalidPvssTranscript,
65
66 #[error(
68 "Insufficient transcripts for aggregation (expected {0}, got {1})"
69 )]
70 InsufficientTranscriptsForAggregate(u32, u32),
71
72 #[error("Failed to derive a valid final key for the DKG")]
74 InvalidDkgPublicKey,
75
76 #[error("Not enough validators (expected {0}, got {1})")]
78 InsufficientValidators(u32, u32),
79
80 #[error("Transcript aggregate doesn't match the received PVSS instances")]
82 InvalidTranscriptAggregate,
83
84 #[error("DKG validators not sorted")]
86 ValidatorsNotSorted,
87
88 #[error("Validator public key mismatch")]
90 ValidatorPublicKeyMismatch,
91
92 #[error(transparent)]
93 BincodeError(#[from] bincode::Error),
94
95 #[error(transparent)]
96 ArkSerializeError(#[from] ark_serialize::SerializationError),
97
98 #[error("Invalid byte length. Expected {0}, got {1}")]
99 InvalidByteLength(usize, usize),
100
101 #[error("Invalid variant: {0}")]
102 InvalidVariant(String),
103}
104
105pub type Result<T> = std::result::Result<T, Error>;
106
107pub fn make_pvss_map<E: Pairing>(
108 transcripts: &[PubliclyVerifiableSS<E>],
109 validators: &[Validator<E>],
110) -> PVSSMap<E> {
111 let mut pvss_map: PVSSMap<E> = PVSSMap::new();
112 zip_eq(transcripts, validators).for_each(|(transcript, validator)| {
113 pvss_map.insert(validator.address.clone(), transcript.clone());
114 });
115 pvss_map
116}
117
118#[cfg(test)]
119mod test_dkg_full {
120 use std::collections::HashMap;
121
122 use ark_bls12_381::{Bls12_381 as E, Fr, G1Affine};
123 use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup};
124 use ark_ff::{UniformRand, Zero};
125 use ark_poly::EvaluationDomain;
126 use ark_std::test_rng;
127 use ferveo_common::Keypair;
128 use group_threshold_cryptography as tpke;
129 use group_threshold_cryptography::{
130 DecryptionSharePrecomputed, DecryptionShareSimple, SecretBox,
131 SharedSecret,
132 };
133 use itertools::izip;
134
135 use super::*;
136 use crate::dkg::test_common::*;
137
138 type TargetField = <E as Pairing>::TargetField;
139
140 fn make_shared_secret_simple_tdec(
141 dkg: &PubliclyVerifiableDkg<E>,
142 aad: &[u8],
143 ciphertext_header: &tpke::CiphertextHeader<E>,
144 validator_keypairs: &[Keypair<E>],
145 ) -> (
146 PubliclyVerifiableSS<E, Aggregated>,
147 Vec<DecryptionShareSimple<E>>,
148 SharedSecret<E>,
149 ) {
150 let pvss_aggregated = aggregate(&dkg.vss);
151 assert!(pvss_aggregated.verify_aggregation(dkg).is_ok());
152
153 let decryption_shares: Vec<DecryptionShareSimple<E>> =
154 validator_keypairs
155 .iter()
156 .map(|validator_keypair| {
157 let validator = dkg
158 .get_validator(&validator_keypair.public_key())
159 .unwrap();
160 pvss_aggregated
161 .make_decryption_share_simple(
162 ciphertext_header,
163 aad,
164 &validator_keypair.decryption_key,
165 validator.share_index,
166 &dkg.pvss_params.g_inv(),
167 )
168 .unwrap()
169 })
170 .collect();
171
172 let domain_points = &dkg
173 .domain
174 .elements()
175 .take(decryption_shares.len())
176 .collect::<Vec<_>>();
177 assert_eq!(domain_points.len(), decryption_shares.len());
178
179 let lagrange_coeffs = tpke::prepare_combine_simple::<E>(domain_points);
183 let shared_secret = tpke::share_combine_simple::<E>(
184 &decryption_shares,
185 &lagrange_coeffs,
186 );
187
188 (pvss_aggregated, decryption_shares, shared_secret)
189 }
190
191 #[test]
192 fn test_dkg_simple_tdec() {
193 let rng = &mut test_rng();
194
195 for shares_num in [4, 7] {
197 let threshold = shares_num / 2 + 1;
198 let (dkg, validator_keypairs) =
199 setup_dealt_dkg_with_n_validators(threshold, shares_num);
200 let msg = "my-msg".as_bytes().to_vec();
201 let aad: &[u8] = "my-aad".as_bytes();
202 let public_key = dkg.public_key();
203 let ciphertext = tpke::encrypt::<E>(
204 SecretBox::new(msg.clone()),
205 aad,
206 &public_key,
207 rng,
208 )
209 .unwrap();
210
211 let (_, _, shared_secret) = make_shared_secret_simple_tdec(
212 &dkg,
213 aad,
214 &ciphertext.header().unwrap(),
215 &validator_keypairs,
216 );
217
218 let plaintext = tpke::decrypt_with_shared_secret(
219 &ciphertext,
220 aad,
221 &shared_secret,
222 &dkg.pvss_params.g_inv(),
223 )
224 .unwrap();
225 assert_eq!(plaintext, msg);
226 }
227 }
228
229 #[test]
230 fn test_dkg_simple_tdec_precomputed() {
231 let rng = &mut test_rng();
232
233 for shares_num in [4, 7] {
235 let threshold = shares_num;
237 let (dkg, validator_keypairs) =
238 setup_dealt_dkg_with_n_validators(threshold, shares_num);
239 let msg = "my-msg".as_bytes().to_vec();
240 let aad: &[u8] = "my-aad".as_bytes();
241 let public_key = dkg.public_key();
242 let ciphertext = tpke::encrypt::<E>(
243 SecretBox::new(msg.clone()),
244 aad,
245 &public_key,
246 rng,
247 )
248 .unwrap();
249
250 let pvss_aggregated = aggregate(&dkg.vss);
251 pvss_aggregated.verify_aggregation(&dkg).unwrap();
252 let domain_points = dkg
253 .domain
254 .elements()
255 .take(validator_keypairs.len())
256 .collect::<Vec<_>>();
257
258 let decryption_shares: Vec<DecryptionSharePrecomputed<E>> =
259 validator_keypairs
260 .iter()
261 .map(|validator_keypair| {
262 let validator = dkg
263 .get_validator(&validator_keypair.public_key())
264 .unwrap();
265 pvss_aggregated
266 .make_decryption_share_simple_precomputed(
267 &ciphertext.header().unwrap(),
268 aad,
269 &validator_keypair.decryption_key,
270 validator.share_index,
271 &domain_points,
272 &dkg.pvss_params.g_inv(),
273 )
274 .unwrap()
275 })
276 .collect();
277 assert_eq!(domain_points.len(), decryption_shares.len());
278
279 let shared_secret =
280 tpke::share_combine_precomputed::<E>(&decryption_shares);
281
282 let plaintext = tpke::decrypt_with_shared_secret(
284 &ciphertext,
285 aad,
286 &shared_secret,
287 &dkg.pvss_params.g_inv(),
288 )
289 .unwrap();
290 assert_eq!(plaintext, msg);
291 }
292 }
293
294 #[test]
295 fn test_dkg_simple_tdec_share_verification() {
296 let rng = &mut test_rng();
297
298 let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
299 let msg = "my-msg".as_bytes().to_vec();
300 let aad: &[u8] = "my-aad".as_bytes();
301 let public_key = dkg.public_key();
302 let ciphertext =
303 tpke::encrypt::<E>(SecretBox::new(msg), aad, &public_key, rng)
304 .unwrap();
305
306 let (pvss_aggregated, decryption_shares, _) =
307 make_shared_secret_simple_tdec(
308 &dkg,
309 aad,
310 &ciphertext.header().unwrap(),
311 &validator_keypairs,
312 );
313
314 izip!(
315 &pvss_aggregated.shares,
316 &validator_keypairs,
317 &decryption_shares,
318 )
319 .for_each(
320 |(aggregated_share, validator_keypair, decryption_share)| {
321 assert!(decryption_share.verify(
322 aggregated_share,
323 &validator_keypair.public_key().encryption_key,
324 &dkg.pvss_params.h,
325 &ciphertext,
326 ));
327 },
328 );
329
330 let decryption_share = decryption_shares[0].clone();
332
333 let mut with_bad_decryption_share = decryption_share.clone();
335 with_bad_decryption_share.decryption_share = TargetField::zero();
336 assert!(!with_bad_decryption_share.verify(
337 &pvss_aggregated.shares[0],
338 &validator_keypairs[0].public_key().encryption_key,
339 &dkg.pvss_params.h,
340 &ciphertext,
341 ));
342
343 let mut with_bad_checksum = decryption_share;
345 with_bad_checksum.validator_checksum.checksum = G1Affine::zero();
346 assert!(!with_bad_checksum.verify(
347 &pvss_aggregated.shares[0],
348 &validator_keypairs[0].public_key().encryption_key,
349 &dkg.pvss_params.h,
350 &ciphertext,
351 ));
352 }
353
354 #[test]
355 fn test_dkg_simple_tdec_share_recovery() {
356 let rng = &mut test_rng();
357
358 let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
359 let msg = "my-msg".as_bytes().to_vec();
360 let aad: &[u8] = "my-aad".as_bytes();
361 let public_key = &dkg.public_key();
362 let ciphertext =
363 tpke::encrypt::<E>(SecretBox::new(msg), aad, public_key, rng)
364 .unwrap();
365
366 let (_, _, old_shared_secret) = make_shared_secret_simple_tdec(
368 &dkg,
369 aad,
370 &ciphertext.header().unwrap(),
371 &validator_keypairs,
372 );
373
374 let x_r = Fr::rand(rng);
378
379 let removed_validator_addr =
381 dkg.validators.keys().last().unwrap().clone();
382 let mut remaining_validators = dkg.validators.clone();
383 remaining_validators.remove(&removed_validator_addr);
384
385 let mut domain_points = dkg.domain.elements().collect::<Vec<_>>();
387 domain_points.pop().unwrap();
388
389 let share_updates = remaining_validators
391 .keys()
392 .map(|v_addr| {
393 let deltas_i = tpke::prepare_share_updates_for_recovery::<E>(
394 &domain_points,
395 &dkg.pvss_params.h.into_affine(),
396 &x_r,
397 dkg.dkg_params.security_threshold as usize,
398 rng,
399 );
400 (v_addr.clone(), deltas_i)
401 })
402 .collect::<HashMap<_, _>>();
403
404 let pvss_aggregated = aggregate(&dkg.vss);
406
407 let updated_shares: Vec<_> = remaining_validators
409 .iter()
410 .map(|(validator_address, validator)| {
411 let updates_for_participant =
413 share_updates.get(validator_address).unwrap();
414
415 let decryption_key = validator_keypairs
417 .get(validator.share_index)
418 .unwrap()
419 .decryption_key;
420
421 pvss_aggregated.update_private_key_share_for_recovery(
423 &decryption_key,
424 validator.share_index,
425 updates_for_participant,
426 )
427 })
428 .collect();
429
430 let new_private_key_share =
432 tpke::recover_share_from_updated_private_shares(
433 &x_r,
434 &domain_points,
435 &updated_shares,
436 );
437
438 let mut decryption_shares: Vec<DecryptionShareSimple<E>> =
440 validator_keypairs
441 .iter()
442 .enumerate()
443 .map(|(share_index, validator_keypair)| {
444 pvss_aggregated
445 .make_decryption_share_simple(
446 &ciphertext.header().unwrap(),
447 aad,
448 &validator_keypair.decryption_key,
449 share_index,
450 &dkg.pvss_params.g_inv(),
451 )
452 .unwrap()
453 })
454 .collect();
455
456 let new_validator_decryption_key = Fr::rand(rng);
458 decryption_shares.push(
459 DecryptionShareSimple::create(
460 &new_validator_decryption_key,
461 &new_private_key_share,
462 &ciphertext.header().unwrap(),
463 aad,
464 &dkg.pvss_params.g_inv(),
465 )
466 .unwrap(),
467 );
468
469 let lagrange = tpke::prepare_combine_simple::<E>(&domain_points);
470 let new_shared_secret =
471 tpke::share_combine_simple::<E>(&decryption_shares, &lagrange);
472
473 assert_eq!(old_shared_secret, new_shared_secret);
474 }
475
476 #[test]
477 fn simple_tdec_share_refreshing() {
478 let rng = &mut test_rng();
479 let (dkg, validator_keypairs) = setup_dealt_dkg_with_n_validators(3, 4);
480
481 let msg = "my-msg".as_bytes().to_vec();
482 let aad: &[u8] = "my-aad".as_bytes();
483 let public_key = dkg.public_key();
484 let ciphertext =
485 tpke::encrypt::<E>(SecretBox::new(msg), aad, &public_key, rng)
486 .unwrap();
487
488 let pvss_aggregated = aggregate(&dkg.vss);
489
490 let (_, _, old_shared_secret) = make_shared_secret_simple_tdec(
492 &dkg,
493 aad,
494 &ciphertext.header().unwrap(),
495 &validator_keypairs,
496 );
497
498 let polynomial = tpke::make_random_polynomial_at::<E>(
502 dkg.dkg_params.security_threshold as usize,
503 &Fr::zero(),
504 rng,
505 );
506
507 let new_decryption_shares: Vec<DecryptionShareSimple<E>> =
511 validator_keypairs
512 .iter()
513 .enumerate()
514 .map(|(validator_address, validator_keypair)| {
515 pvss_aggregated
516 .refresh_decryption_share(
517 &ciphertext.header().unwrap(),
518 aad,
519 &validator_keypair.decryption_key,
520 validator_address,
521 &polynomial,
522 &dkg,
523 )
524 .unwrap()
525 })
526 .collect();
527
528 let domain = &dkg.domain.elements().collect::<Vec<_>>();
530 let lagrange_coeffs = tpke::prepare_combine_simple::<E>(domain);
533 let new_shared_secret = tpke::share_combine_simple::<E>(
534 &new_decryption_shares,
535 &lagrange_coeffs,
536 );
537
538 assert_eq!(old_shared_secret, new_shared_secret);
539 }
540}