Files
nym/common/dkg/tests/integration.rs
T
Jon Häggblad 24b9b17e64 Add a few more nym- crate prefixes (#3158)
* Add nym- to socks5-prefixes crate

* Update imports

* rustfmt

* Add nym-socks5 prefix to proxy-helpers crate

* rustfmt

* Add nym prefix to ordered-buffer crate

* rustfmt

* Add nym prefix to service-providers-common crate

* rustfmt

* Add nym prefix to dkg crate

* Add nym prefix to credentials crate

* rustfmt

* fix build fail in tests
2023-03-08 11:56:29 +01:00

379 lines
13 KiB
Rust

// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bls12_381::{G2Projective, Scalar};
use nym_dkg::bte::{decrypt_share, keygen, setup};
use nym_dkg::dealing::RecoveredVerificationKeys;
use nym_dkg::interpolation::perform_lagrangian_interpolation_at_origin;
use nym_dkg::{combine_shares, try_recover_verification_keys, Dealing};
use rand_core::SeedableRng;
use std::collections::BTreeMap;
#[test]
#[ignore] // expensive test
fn single_sender() {
// makes it easier to understand than `full_threshold_secret_sharing`
// and is a good stepping stone, because its everything each node will have to perform (from one point of view)
let dummy_seed = [42u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let params = setup();
// the simplest possible case
let threshold = 2;
// the indices are going to get assigned externally, so for test sake, use non-consecutive ones
let node_indices = vec![15u64, 248, 33521];
let mut receivers = BTreeMap::new();
let mut full_keys = Vec::new();
for index in &node_indices {
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(*index, *pk.public_key());
full_keys.push((dk, pk))
}
// TODO: HERE BE SERIALIZATION / DESERIALIZATION THAT'S NOT IMPLEMENTED YET
// verify remote proofs of key possession
for key in full_keys.iter() {
assert!(key.1.verify());
}
let (dealing, dealer_share) = Dealing::create(
&mut rng,
&params,
node_indices[0],
threshold,
&receivers,
None,
);
dealing
.verify(&params, threshold, &receivers, None)
.unwrap();
// make sure each share is actually decryptable (even though proofs say they must be, perform this sanity check)
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let _recovered = decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap();
}
// and for good measure, check that the dealer's share matches decryption result
let recovered_dealer = decrypt_share(&full_keys[0].0, 0, &dealing.ciphertexts, None).unwrap();
assert_eq!(recovered_dealer, dealer_share.unwrap());
}
#[test]
#[ignore] // expensive test
fn full_threshold_secret_sharing() {
let dummy_seed = [42u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let params = setup();
// the simplest possible case
let threshold = 2;
// the indices are going to get assigned externally, so for test sake, use non-consecutive ones
let node_indices = vec![15u64, 248, 33521];
let mut receivers = BTreeMap::new();
let mut full_keys = Vec::new();
for index in &node_indices {
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(*index, *pk.public_key());
full_keys.push((dk, pk))
}
// TODO: HERE BE SERIALIZATION / DESERIALIZATION THAT'S NOT IMPLEMENTED YET
// verify remote proofs of key possession
for key in full_keys.iter() {
assert!(key.1.verify());
}
let dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
})
.collect::<Vec<_>>();
for dealing in dealings.iter() {
dealing
.verify(&params, threshold, &receivers, None)
.unwrap();
}
// recover verification keys
let RecoveredVerificationKeys {
recovered_master,
recovered_partials,
} = try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
let g2 = G2Projective::generator();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
// we know dealer_share matches, but it would be inconvenient to try to put them in here,
// so for ease of use (IN A TEST SETTING), just decrypt one's own share
let recovered_secret =
combine_shares(shares, &receivers.keys().copied().collect::<Vec<_>>()).unwrap();
// make sure it matches the associated vk
assert_eq!(recovered_partials[i], g2 * recovered_secret);
derived_secrets.push(recovered_secret)
}
// sanity check that the shares were combined correctly and if we take threshold number of them,
// we end up with the same master secret, note: those are NEVER explicitly recovered in actual system
// (remember threshold was 2)
let master1 = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), derived_secrets[0]),
(Scalar::from(node_indices[1]), derived_secrets[1]),
])
.unwrap();
let master2 = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[1]), derived_secrets[1]),
(Scalar::from(node_indices[2]), derived_secrets[2]),
])
.unwrap();
assert_eq!(master1, master2);
assert_eq!(recovered_master, g2 * master1);
}
#[test]
#[ignore] // expensive test
fn full_threshold_secret_resharing() {
let dummy_seed = [42u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let params = setup();
// the simplest possible case
let threshold = 2;
// the indices are going to get assigned externally, so for test sake, use non-consecutive ones
let node_indices = vec![15u64, 248, 33521];
let mut receivers = BTreeMap::new();
let mut full_keys = Vec::new();
for index in &node_indices {
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(*index, *pk.public_key());
full_keys.push((dk, pk))
}
let first_dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
})
.collect::<Vec<_>>();
// recover verification keys
let RecoveredVerificationKeys {
recovered_master: public_original_master,
recovered_partials,
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = first_dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret =
combine_shares(shares, &receivers.keys().copied().collect::<Vec<_>>()).unwrap();
derived_secrets.push(recovered_secret)
}
let original_master = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), derived_secrets[0]),
(Scalar::from(node_indices[1]), derived_secrets[1]),
])
.unwrap();
// attempt to create resharing dealings!
let resharing_dealings = node_indices
.iter()
.zip(derived_secrets.iter())
.map(|(&dealer_index, prior_secret)| {
Dealing::create(
&mut rng,
&params,
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
)
.0
})
.collect::<Vec<_>>();
for (reshared_dealing, prior_vk) in resharing_dealings.iter().zip(recovered_partials.iter()) {
reshared_dealing
.verify(&params, threshold, &receivers, Some(*prior_vk))
.unwrap();
}
// recover verification keys
let RecoveredVerificationKeys {
recovered_master: public_reshared_master,
recovered_partials: reshared_partials,
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
let mut reshared_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = resharing_dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret =
combine_shares(shares, &receivers.keys().copied().collect::<Vec<_>>()).unwrap();
reshared_secrets.push(recovered_secret)
}
let reshared_master = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), reshared_secrets[0]),
(Scalar::from(node_indices[1]), reshared_secrets[1]),
])
.unwrap();
// the master secret and public values didn't change
assert_eq!(original_master, reshared_master);
assert_eq!(public_original_master, public_reshared_master);
// but partials did
assert_ne!(derived_secrets, reshared_secrets);
assert_ne!(recovered_partials, reshared_partials);
}
#[test]
#[ignore] // expensive test
fn full_threshold_secret_resharing_left_party() {
let dummy_seed = [42u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let params = setup();
// the simplest possible case
let threshold = 2;
// the indices are going to get assigned externally, so for test sake, use non-consecutive ones
let mut node_indices = vec![15u64, 248, 33521];
let mut receivers = BTreeMap::new();
let mut full_keys = Vec::new();
for index in &node_indices {
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(*index, *pk.public_key());
full_keys.push((dk, pk))
}
let first_dealings = node_indices
.iter()
.map(|&dealer_index| {
Dealing::create(&mut rng, &params, dealer_index, threshold, &receivers, None).0
})
.collect::<Vec<_>>();
// recover verification keys
let RecoveredVerificationKeys {
recovered_master: public_original_master,
recovered_partials,
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
let mut derived_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = first_dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret =
combine_shares(shares, &receivers.keys().copied().collect::<Vec<_>>()).unwrap();
derived_secrets.push(recovered_secret)
}
let original_master = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), derived_secrets[0]),
(Scalar::from(node_indices[1]), derived_secrets[1]),
])
.unwrap();
// one party leaves the process
let left_party_index = node_indices.pop().unwrap();
receivers.remove(&left_party_index);
full_keys.pop();
// and another one joins, but we're still over the threshold value of initial parties
let join_party_index = 100000;
let (dk, pk) = keygen(&params, &mut rng);
receivers.insert(join_party_index, *pk.public_key());
full_keys.push((dk, pk));
// only initial parties attempt to create resharing dealings!
let resharing_dealings = node_indices
.iter()
.zip(derived_secrets.iter().take(2))
.map(|(&dealer_index, prior_secret)| {
Dealing::create(
&mut rng,
&params,
dealer_index,
threshold,
&receivers,
Some(*prior_secret),
)
.0
})
.collect::<Vec<_>>();
for (reshared_dealing, prior_vk) in resharing_dealings
.iter()
.zip(recovered_partials.iter().take(2))
{
reshared_dealing
.verify(&params, threshold, &receivers, Some(*prior_vk))
.unwrap();
}
// recover verification keys
let RecoveredVerificationKeys {
recovered_master: public_reshared_master,
recovered_partials: reshared_partials,
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
let mut reshared_secrets = Vec::new();
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
let shares = resharing_dealings
.iter()
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
.collect();
let recovered_secret = combine_shares(shares, &node_indices).unwrap();
reshared_secrets.push(recovered_secret)
}
let reshared_master = perform_lagrangian_interpolation_at_origin(&[
(Scalar::from(node_indices[0]), reshared_secrets[0]),
(Scalar::from(join_party_index), reshared_secrets[2]),
])
.unwrap();
// the master secret and public values didn't change
assert_eq!(original_master, reshared_master);
assert_eq!(public_original_master, public_reshared_master);
// but partials did
assert_ne!(derived_secrets, reshared_secrets);
assert_ne!(recovered_partials, reshared_partials);
}