24b9b17e64
* 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
379 lines
13 KiB
Rust
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(¶ms, &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,
|
|
¶ms,
|
|
node_indices[0],
|
|
threshold,
|
|
&receivers,
|
|
None,
|
|
);
|
|
dealing
|
|
.verify(¶ms, 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(¶ms, &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, ¶ms, dealer_index, threshold, &receivers, None).0
|
|
})
|
|
.collect::<Vec<_>>();
|
|
for dealing in dealings.iter() {
|
|
dealing
|
|
.verify(¶ms, 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(¶ms, &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, ¶ms, 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,
|
|
¶ms,
|
|
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(¶ms, 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(¶ms, &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, ¶ms, 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(¶ms, &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,
|
|
¶ms,
|
|
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(¶ms, 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);
|
|
}
|