2022-11-10 22:35:09 -05:00
|
|
|
use core::ops::Deref;
|
|
|
|
|
|
2022-10-25 23:17:25 -05:00
|
|
|
use std::collections::HashMap;
|
2022-10-13 00:38:36 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
|
use std::str::FromStr;
|
2022-06-03 01:25:46 -04:00
|
|
|
|
2022-11-10 22:35:09 -05:00
|
|
|
use zeroize::Zeroizing;
|
2022-12-13 20:25:32 -05:00
|
|
|
use rand_core::{RngCore, CryptoRng};
|
2022-06-06 04:22:49 -04:00
|
|
|
|
2022-06-28 01:25:26 -04:00
|
|
|
use group::{ff::PrimeField, GroupEncoding};
|
|
|
|
|
|
2022-12-24 17:08:22 -05:00
|
|
|
use dkg::tests::key_gen;
|
2022-10-29 03:54:42 -05:00
|
|
|
|
2022-06-03 01:25:46 -04:00
|
|
|
use crate::{
|
2022-07-15 01:26:07 -04:00
|
|
|
curve::Curve,
|
2023-01-01 01:54:18 -05:00
|
|
|
ThresholdCore, ThresholdKeys, FrostError,
|
2022-06-03 01:25:46 -04:00
|
|
|
algorithm::{Schnorr, Hram},
|
2022-10-25 23:17:25 -05:00
|
|
|
sign::{
|
2022-10-29 05:10:07 -04:00
|
|
|
Nonce, GeneratorCommitments, NonceCommitments, Commitments, Writable, Preprocess, SignMachine,
|
|
|
|
|
SignatureMachine, AlgorithmMachine,
|
2022-10-25 23:17:25 -05:00
|
|
|
},
|
2023-01-01 01:54:18 -05:00
|
|
|
tests::{clone_without, recover_key, algorithm_machines, commit_and_shares, sign},
|
2022-06-03 01:25:46 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub struct Vectors {
|
|
|
|
|
pub threshold: u16,
|
2022-10-13 00:38:36 -04:00
|
|
|
|
|
|
|
|
pub group_secret: String,
|
|
|
|
|
pub group_key: String,
|
|
|
|
|
pub shares: Vec<String>,
|
|
|
|
|
|
|
|
|
|
pub msg: String,
|
|
|
|
|
pub included: Vec<u16>,
|
|
|
|
|
pub nonces: Vec<[String; 2]>,
|
|
|
|
|
|
|
|
|
|
pub sig_shares: Vec<String>,
|
|
|
|
|
|
2022-07-15 01:26:07 -04:00
|
|
|
pub sig: String,
|
2022-06-03 01:25:46 -04:00
|
|
|
}
|
|
|
|
|
|
2022-10-13 00:38:36 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
|
impl From<serde_json::Value> for Vectors {
|
|
|
|
|
fn from(value: serde_json::Value) -> Vectors {
|
2022-10-15 23:46:22 -04:00
|
|
|
let to_str = |value: &serde_json::Value| value.as_str().unwrap().to_string();
|
2022-10-13 00:38:36 -04:00
|
|
|
Vectors {
|
|
|
|
|
threshold: u16::from_str(value["config"]["NUM_PARTICIPANTS"].as_str().unwrap()).unwrap(),
|
|
|
|
|
|
|
|
|
|
group_secret: to_str(&value["inputs"]["group_secret_key"]),
|
|
|
|
|
group_key: to_str(&value["inputs"]["group_public_key"]),
|
|
|
|
|
shares: value["inputs"]["participants"]
|
|
|
|
|
.as_object()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.values()
|
|
|
|
|
.map(|share| to_str(&share["participant_share"]))
|
|
|
|
|
.collect(),
|
|
|
|
|
|
|
|
|
|
msg: to_str(&value["inputs"]["message"]),
|
|
|
|
|
included: to_str(&value["round_one_outputs"]["participant_list"])
|
2023-01-01 04:18:23 -05:00
|
|
|
.split(',')
|
2022-10-13 00:38:36 -04:00
|
|
|
.map(u16::from_str)
|
|
|
|
|
.collect::<Result<_, _>>()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
nonces: value["round_one_outputs"]["participants"]
|
|
|
|
|
.as_object()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.values()
|
|
|
|
|
.map(|value| [to_str(&value["hiding_nonce"]), to_str(&value["binding_nonce"])])
|
|
|
|
|
.collect(),
|
|
|
|
|
|
|
|
|
|
sig_shares: value["round_two_outputs"]["participants"]
|
|
|
|
|
.as_object()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.values()
|
|
|
|
|
.map(|value| to_str(&value["sig_share"]))
|
|
|
|
|
.collect(),
|
|
|
|
|
|
|
|
|
|
sig: to_str(&value["final_output"]["sig"]),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-29 03:54:42 -05:00
|
|
|
// Load these vectors into ThresholdKeys using a custom serialization it'll deserialize
|
|
|
|
|
fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, ThresholdKeys<C>> {
|
2022-07-15 01:26:07 -04:00
|
|
|
let shares = vectors
|
|
|
|
|
.shares
|
|
|
|
|
.iter()
|
2022-10-25 23:17:25 -05:00
|
|
|
.map(|secret| C::read_F::<&[u8]>(&mut hex::decode(secret).unwrap().as_ref()).unwrap())
|
2022-07-15 01:26:07 -04:00
|
|
|
.collect::<Vec<_>>();
|
2022-08-13 05:07:07 -04:00
|
|
|
let verification_shares = shares.iter().map(|secret| C::generator() * secret).collect::<Vec<_>>();
|
2022-06-03 01:25:46 -04:00
|
|
|
|
|
|
|
|
let mut keys = HashMap::new();
|
|
|
|
|
for i in 1 ..= u16::try_from(shares.len()).unwrap() {
|
2022-10-29 03:54:42 -05:00
|
|
|
// Manually re-implement the serialization for ThresholdCore to import this data
|
2022-06-03 01:25:46 -04:00
|
|
|
let mut serialized = vec![];
|
2022-07-13 02:38:29 -04:00
|
|
|
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_be_bytes());
|
2022-06-03 23:22:08 -04:00
|
|
|
serialized.extend(C::ID);
|
2022-06-03 01:25:46 -04:00
|
|
|
serialized.extend(vectors.threshold.to_be_bytes());
|
|
|
|
|
serialized.extend(u16::try_from(shares.len()).unwrap().to_be_bytes());
|
|
|
|
|
serialized.extend(i.to_be_bytes());
|
2022-06-28 01:25:26 -04:00
|
|
|
serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
|
2022-06-03 01:25:46 -04:00
|
|
|
for share in &verification_shares {
|
2022-06-28 01:25:26 -04:00
|
|
|
serialized.extend(share.to_bytes().as_ref());
|
2022-06-03 01:25:46 -04:00
|
|
|
}
|
|
|
|
|
|
2022-10-29 03:54:42 -05:00
|
|
|
let these_keys = ThresholdCore::<C>::deserialize::<&[u8]>(&mut serialized.as_ref()).unwrap();
|
2022-06-03 01:25:46 -04:00
|
|
|
assert_eq!(these_keys.params().t(), vectors.threshold);
|
|
|
|
|
assert_eq!(usize::from(these_keys.params().n()), shares.len());
|
|
|
|
|
assert_eq!(these_keys.params().i(), i);
|
2022-11-10 22:35:09 -05:00
|
|
|
assert_eq!(these_keys.secret_share().deref(), &shares[usize::from(i - 1)]);
|
2022-10-13 00:38:36 -04:00
|
|
|
assert_eq!(hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key);
|
2022-10-29 03:54:42 -05:00
|
|
|
keys.insert(i, ThresholdKeys::new(these_keys));
|
2022-06-03 01:25:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keys
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-15 01:26:07 -04:00
|
|
|
pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|
|
|
|
rng: &mut R,
|
|
|
|
|
vectors: Vectors,
|
|
|
|
|
) {
|
2022-12-08 19:04:35 -05:00
|
|
|
// Test a basic Schnorr signature
|
|
|
|
|
{
|
|
|
|
|
let keys = key_gen(&mut *rng);
|
|
|
|
|
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
|
|
|
|
const MSG: &[u8] = b"Hello, World!";
|
|
|
|
|
let sig = sign(&mut *rng, Schnorr::<C, H>::new(), keys.clone(), machines, MSG);
|
|
|
|
|
assert!(sig.verify(keys[&1].group_key(), H::hram(&sig.R, &keys[&1].group_key(), MSG)));
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-01 01:54:18 -05:00
|
|
|
// Test blame on an invalid Schnorr signature share
|
|
|
|
|
{
|
|
|
|
|
let keys = key_gen(&mut *rng);
|
|
|
|
|
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
|
|
|
|
const MSG: &[u8] = b"Hello, World!";
|
|
|
|
|
|
|
|
|
|
let (mut machines, mut shares) = commit_and_shares(&mut *rng, machines, |_, _| {}, MSG);
|
2023-01-01 04:18:23 -05:00
|
|
|
let faulty = *shares.keys().next().unwrap();
|
2023-01-01 01:54:18 -05:00
|
|
|
shares.get_mut(&faulty).unwrap().invalidate();
|
|
|
|
|
|
|
|
|
|
for (i, machine) in machines.drain() {
|
|
|
|
|
if i == faulty {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
assert_eq!(
|
|
|
|
|
machine.complete(clone_without(&shares, &i)).err(),
|
|
|
|
|
Some(FrostError::InvalidShare(faulty))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-06 04:22:49 -04:00
|
|
|
// Test against the vectors
|
2022-06-03 01:25:46 -04:00
|
|
|
let keys = vectors_to_multisig_keys::<C>(&vectors);
|
2022-10-25 23:17:25 -05:00
|
|
|
let group_key =
|
2022-10-29 03:54:42 -05:00
|
|
|
<C as Curve>::read_G::<&[u8]>(&mut hex::decode(&vectors.group_key).unwrap().as_ref()).unwrap();
|
2022-10-25 23:17:25 -05:00
|
|
|
let secret =
|
|
|
|
|
C::read_F::<&[u8]>(&mut hex::decode(&vectors.group_secret).unwrap().as_ref()).unwrap();
|
2022-10-13 00:38:36 -04:00
|
|
|
assert_eq!(C::generator() * secret, group_key);
|
2022-10-29 03:54:42 -05:00
|
|
|
assert_eq!(recover_key(&keys), secret);
|
2022-06-03 01:25:46 -04:00
|
|
|
|
|
|
|
|
let mut machines = vec![];
|
2022-10-13 00:38:36 -04:00
|
|
|
for i in &vectors.included {
|
2022-12-08 19:04:35 -05:00
|
|
|
machines.push((i, AlgorithmMachine::new(Schnorr::<C, H>::new(), keys[i].clone()).unwrap()));
|
2022-06-03 01:25:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut commitments = HashMap::new();
|
|
|
|
|
let mut c = 0;
|
2022-07-15 01:26:07 -04:00
|
|
|
let mut machines = machines
|
|
|
|
|
.drain(..)
|
|
|
|
|
.map(|(i, machine)| {
|
2022-11-10 22:35:09 -05:00
|
|
|
let nonce = |i| {
|
|
|
|
|
Zeroizing::new(
|
|
|
|
|
C::read_F::<&[u8]>(&mut hex::decode(&vectors.nonces[c][i]).unwrap().as_ref()).unwrap(),
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let nonces = [nonce(0), nonce(1)];
|
2022-07-15 01:26:07 -04:00
|
|
|
c += 1;
|
2022-11-10 22:35:09 -05:00
|
|
|
let these_commitments =
|
|
|
|
|
[C::generator() * nonces[0].deref(), C::generator() * nonces[1].deref()];
|
2022-10-29 05:10:07 -04:00
|
|
|
let machine = machine.unsafe_override_preprocess(
|
|
|
|
|
vec![Nonce(nonces)],
|
|
|
|
|
Preprocess {
|
2022-10-25 23:17:25 -05:00
|
|
|
commitments: Commitments {
|
|
|
|
|
nonces: vec![NonceCommitments {
|
|
|
|
|
generators: vec![GeneratorCommitments(these_commitments)],
|
|
|
|
|
}],
|
2023-01-01 09:16:09 -05:00
|
|
|
dleq: None,
|
2022-10-25 23:17:25 -05:00
|
|
|
},
|
|
|
|
|
addendum: (),
|
|
|
|
|
},
|
2022-10-29 05:10:07 -04:00
|
|
|
);
|
2022-07-15 01:26:07 -04:00
|
|
|
|
|
|
|
|
commitments.insert(
|
2022-10-13 00:38:36 -04:00
|
|
|
*i,
|
2022-10-25 23:17:25 -05:00
|
|
|
machine
|
|
|
|
|
.read_preprocess::<&[u8]>(
|
|
|
|
|
&mut [
|
|
|
|
|
these_commitments[0].to_bytes().as_ref(),
|
|
|
|
|
these_commitments[1].to_bytes().as_ref(),
|
|
|
|
|
]
|
|
|
|
|
.concat()
|
|
|
|
|
.as_ref(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap(),
|
2022-07-15 01:26:07 -04:00
|
|
|
);
|
|
|
|
|
(i, machine)
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
2022-06-03 01:25:46 -04:00
|
|
|
|
|
|
|
|
let mut shares = HashMap::new();
|
|
|
|
|
c = 0;
|
2022-07-15 01:26:07 -04:00
|
|
|
let mut machines = machines
|
|
|
|
|
.drain(..)
|
|
|
|
|
.map(|(i, machine)| {
|
|
|
|
|
let (machine, share) =
|
2022-10-13 00:38:36 -04:00
|
|
|
machine.sign(clone_without(&commitments, i), &hex::decode(&vectors.msg).unwrap()).unwrap();
|
2022-07-15 01:26:07 -04:00
|
|
|
|
2022-10-25 23:17:25 -05:00
|
|
|
let share = {
|
|
|
|
|
let mut buf = vec![];
|
|
|
|
|
share.write(&mut buf).unwrap();
|
|
|
|
|
buf
|
|
|
|
|
};
|
2022-10-13 00:38:36 -04:00
|
|
|
assert_eq!(share, hex::decode(&vectors.sig_shares[c]).unwrap());
|
2022-07-15 01:26:07 -04:00
|
|
|
c += 1;
|
|
|
|
|
|
2022-10-25 23:17:25 -05:00
|
|
|
shares.insert(*i, machine.read_share::<&[u8]>(&mut share.as_ref()).unwrap());
|
2022-07-15 01:26:07 -04:00
|
|
|
(i, machine)
|
|
|
|
|
})
|
|
|
|
|
.collect::<HashMap<_, _>>();
|
2022-06-24 08:40:14 -04:00
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
for (i, machine) in machines.drain() {
|
2022-10-13 00:38:36 -04:00
|
|
|
let sig = machine.complete(clone_without(&shares, i)).unwrap();
|
2022-06-28 01:25:26 -04:00
|
|
|
let mut serialized = sig.R.to_bytes().as_ref().to_vec();
|
|
|
|
|
serialized.extend(sig.s.to_repr().as_ref());
|
2022-06-03 01:25:46 -04:00
|
|
|
assert_eq!(hex::encode(serialized), vectors.sig);
|
|
|
|
|
}
|
|
|
|
|
}
|