3.3.3 (cont) Add a dedicated Participant type

This commit is contained in:
Luke Parker
2023-02-23 06:50:45 -05:00
parent 87dea5e455
commit 2d56d24d9c
18 changed files with 308 additions and 237 deletions

View File

@@ -6,7 +6,7 @@ use rand_core::{RngCore, CryptoRng};
use transcript::Transcript;
use crate::{Curve, FrostError, ThresholdKeys, ThresholdView};
use crate::{Curve, Participant, FrostError, ThresholdKeys, ThresholdView};
pub use schnorr::SchnorrSignature;
/// Write an addendum to a writer.
@@ -55,7 +55,7 @@ pub trait Algorithm<C: Curve>: Clone {
fn process_addendum(
&mut self,
params: &ThresholdView<C>,
l: u16,
l: Participant,
reader: Self::Addendum,
) -> Result<(), FrostError>;
@@ -161,7 +161,12 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
Ok(())
}
fn process_addendum(&mut self, _: &ThresholdView<C>, _: u16, _: ()) -> Result<(), FrostError> {
fn process_addendum(
&mut self,
_: &ThresholdView<C>,
_: Participant,
_: (),
) -> Result<(), FrostError> {
Ok(())
}

View File

@@ -19,7 +19,7 @@ use std::collections::HashMap;
use thiserror::Error;
/// Distributed key generation protocol.
pub use dkg::{self, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView};
pub use dkg::{self, Participant, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView};
/// Curve trait and provided curves/HRAMs, forming various ciphersuites.
pub mod curve;
@@ -38,21 +38,21 @@ pub mod tests;
/// Various errors possible during signing.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
pub enum FrostError {
#[error("invalid participant index (0 < index <= {0}, yet index is {1})")]
InvalidParticipantIndex(u16, u16),
#[error("invalid participant (0 < participant <= {0}, yet participant is {1})")]
InvalidParticipant(u16, Participant),
#[error("invalid signing set ({0})")]
InvalidSigningSet(&'static str),
#[error("invalid participant quantity (expected {0}, got {1})")]
InvalidParticipantQuantity(usize, usize),
#[error("duplicated participant index ({0})")]
DuplicatedIndex(u16),
#[error("duplicated participant ({0})")]
DuplicatedParticipant(Participant),
#[error("missing participant {0}")]
MissingParticipant(u16),
MissingParticipant(Participant),
#[error("invalid preprocess (participant {0})")]
InvalidPreprocess(u16),
InvalidPreprocess(Participant),
#[error("invalid share (participant {0})")]
InvalidShare(u16),
InvalidShare(Participant),
#[error("internal error ({0})")]
InternalError(&'static str),
@@ -60,9 +60,9 @@ pub enum FrostError {
// Validate a map of values to have the expected included participants
pub fn validate_map<T>(
map: &HashMap<u16, T>,
included: &[u16],
ours: u16,
map: &HashMap<Participant, T>,
included: &[Participant],
ours: Participant,
) -> Result<(), FrostError> {
if (map.len() + 1) != included.len() {
Err(FrostError::InvalidParticipantQuantity(included.len(), map.len() + 1))?;
@@ -71,7 +71,7 @@ pub fn validate_map<T>(
for included in included {
if *included == ours {
if map.contains_key(included) {
Err(FrostError::DuplicatedIndex(*included))?;
Err(FrostError::DuplicatedParticipant(*included))?;
}
continue;
}

View File

@@ -25,7 +25,7 @@ use multiexp::multiexp_vartime;
use dleq::MultiDLEqProof;
use crate::curve::Curve;
use crate::{curve::Curve, Participant};
// Transcript used to aggregate binomial nonces for usage within a single DLEq proof.
fn aggregation_transcript<T: Transcript>(context: &[u8]) -> T {
@@ -247,17 +247,17 @@ pub(crate) struct IndividualBinding<C: Curve> {
binding_factors: Option<Vec<C::F>>,
}
pub(crate) struct BindingFactor<C: Curve>(pub(crate) HashMap<u16, IndividualBinding<C>>);
pub(crate) struct BindingFactor<C: Curve>(pub(crate) HashMap<Participant, IndividualBinding<C>>);
impl<C: Curve> BindingFactor<C> {
pub(crate) fn insert(&mut self, i: u16, commitments: Commitments<C>) {
pub(crate) fn insert(&mut self, i: Participant, commitments: Commitments<C>) {
self.0.insert(i, IndividualBinding { commitments, binding_factors: None });
}
pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) {
for (l, binding) in self.0.iter_mut() {
let mut transcript = transcript.clone();
transcript.append_message(b"participant", C::F::from(u64::from(*l)).to_repr());
transcript.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
// It *should* be perfectly fine to reuse a binding factor for multiple nonces
// This generates a binding factor per nonce just to ensure it never comes up as a question
binding.binding_factors = Some(
@@ -268,12 +268,12 @@ impl<C: Curve> BindingFactor<C> {
}
}
pub(crate) fn binding_factors(&self, i: u16) -> &[C::F] {
pub(crate) fn binding_factors(&self, i: Participant) -> &[C::F] {
self.0[&i].binding_factors.as_ref().unwrap()
}
// Get the bound nonces for a specific party
pub(crate) fn bound(&self, l: u16) -> Vec<Vec<C::G>> {
pub(crate) fn bound(&self, l: Participant) -> Vec<Vec<C::G>> {
let mut res = vec![];
for (i, (nonce, rho)) in
self.0[&l].commitments.nonces.iter().zip(self.binding_factors(l).iter()).enumerate()

View File

@@ -19,7 +19,7 @@ use multiexp::BatchVerifier;
use crate::{
curve::Curve,
FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
Participant, FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
algorithm::{WriteAddendum, Addendum, Algorithm},
validate_map,
};
@@ -239,7 +239,7 @@ pub trait SignMachine<S>: Sized {
/// become the signing set for this session.
fn sign(
self,
commitments: HashMap<u16, Self::Preprocess>,
commitments: HashMap<Participant, Self::Preprocess>,
msg: &[u8],
) -> Result<(Self::SignatureMachine, Self::SignatureShare), FrostError>;
}
@@ -291,7 +291,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
fn sign(
mut self,
mut preprocesses: HashMap<u16, Preprocess<C, A::Addendum>>,
mut preprocesses: HashMap<Participant, Preprocess<C, A::Addendum>>,
msg: &[u8],
) -> Result<(Self::SignatureMachine, SignatureShare<C>), FrostError> {
let multisig_params = self.params.multisig_params();
@@ -307,18 +307,14 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
if included.len() < usize::from(multisig_params.t()) {
Err(FrostError::InvalidSigningSet("not enough signers"))?;
}
// Invalid index
if included[0] == 0 {
Err(FrostError::InvalidParticipantIndex(included[0], multisig_params.n()))?;
}
// OOB index
if included[included.len() - 1] > multisig_params.n() {
Err(FrostError::InvalidParticipantIndex(included[included.len() - 1], multisig_params.n()))?;
if u16::from(included[included.len() - 1]) > multisig_params.n() {
Err(FrostError::InvalidParticipant(multisig_params.n(), included[included.len() - 1]))?;
}
// Same signer included multiple times
for i in 0 .. (included.len() - 1) {
if included[i] == included[i + 1] {
Err(FrostError::DuplicatedIndex(included[i]))?;
Err(FrostError::DuplicatedParticipant(included[i]))?;
}
}
@@ -332,7 +328,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
let nonces = self.params.algorithm.nonces();
#[allow(non_snake_case)]
let mut B = BindingFactor(HashMap::<u16, _>::with_capacity(included.len()));
let mut B = BindingFactor(HashMap::<Participant, _>::with_capacity(included.len()));
{
// Parse the preprocesses
for l in &included {
@@ -341,7 +337,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
.params
.algorithm
.transcript()
.append_message(b"participant", C::F::from(u64::from(*l)).to_repr());
.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
}
if *l == self.params.keys.params().i() {
@@ -449,7 +445,7 @@ pub trait SignatureMachine<S> {
/// Complete signing.
/// Takes in everyone elses' shares. Returns the signature.
fn complete(self, shares: HashMap<u16, Self::SignatureShare>) -> Result<S, FrostError>;
fn complete(self, shares: HashMap<Participant, Self::SignatureShare>) -> Result<S, FrostError>;
}
/// Final step of the state machine for the signing process.
@@ -472,7 +468,7 @@ impl<C: Curve, A: Algorithm<C>> SignatureMachine<A::Signature> for AlgorithmSign
fn complete(
self,
mut shares: HashMap<u16, SignatureShare<C>>,
mut shares: HashMap<Participant, SignatureShare<C>>,
) -> Result<A::Signature, FrostError> {
let params = self.params.multisig_params();
validate_map(&shares, self.view.included(), params.i())?;

View File

@@ -5,7 +5,7 @@ use rand_core::{RngCore, CryptoRng};
pub use dkg::tests::{key_gen, recover_key};
use crate::{
Curve, ThresholdKeys,
Curve, Participant, ThresholdKeys,
algorithm::Algorithm,
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine},
};
@@ -36,11 +36,14 @@ pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
rng: &mut R,
algorithm: A,
keys: &HashMap<u16, ThresholdKeys<C>>,
) -> HashMap<u16, AlgorithmMachine<C, A>> {
keys: &HashMap<Participant, ThresholdKeys<C>>,
) -> HashMap<Participant, AlgorithmMachine<C, A>> {
let mut included = vec![];
while included.len() < usize::from(keys[&1].params().t()) {
let n = u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap();
while included.len() < usize::from(keys[&Participant::new(1).unwrap()].params().t()) {
let n = Participant::new(
u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap(),
)
.unwrap();
if included.contains(&n) {
continue;
}
@@ -64,15 +67,15 @@ pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
pub(crate) fn commit_and_shares<
R: RngCore + CryptoRng,
M: PreprocessMachine,
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>),
F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
>(
rng: &mut R,
mut machines: HashMap<u16, M>,
mut machines: HashMap<Participant, M>,
mut cache: F,
msg: &[u8],
) -> (
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
) {
let mut commitments = HashMap::new();
let mut machines = machines
@@ -110,10 +113,10 @@ pub(crate) fn commit_and_shares<
fn sign_internal<
R: RngCore + CryptoRng,
M: PreprocessMachine,
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>),
F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
>(
rng: &mut R,
machines: HashMap<u16, M>,
machines: HashMap<Participant, M>,
cache: F,
msg: &[u8],
) -> M::Signature {
@@ -135,7 +138,7 @@ fn sign_internal<
/// caching.
pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
rng: &mut R,
machines: HashMap<u16, M>,
machines: HashMap<Participant, M>,
msg: &[u8],
) -> M::Signature {
sign_internal(rng, machines, |_, _| {}, msg)
@@ -146,8 +149,8 @@ pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
pub fn sign<R: RngCore + CryptoRng, M: PreprocessMachine>(
rng: &mut R,
params: <M::SignMachine as SignMachine<M::Signature>>::Params,
mut keys: HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
machines: HashMap<u16, M>,
mut keys: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
machines: HashMap<Participant, M>,
msg: &[u8],
) -> M::Signature {
sign_internal(

View File

@@ -13,7 +13,7 @@ use dkg::tests::key_gen;
use crate::{
curve::Curve,
ThresholdCore, ThresholdKeys, FrostError,
Participant, ThresholdCore, ThresholdKeys, FrostError,
algorithm::{Schnorr, Hram},
sign::{
Nonce, GeneratorCommitments, NonceCommitments, Commitments, Writable, Preprocess, SignMachine,
@@ -30,7 +30,7 @@ pub struct Vectors {
pub shares: Vec<String>,
pub msg: String,
pub included: Vec<u16>,
pub included: Vec<Participant>,
pub nonces: Vec<[String; 2]>,
pub sig_shares: Vec<String>,
@@ -58,8 +58,11 @@ impl From<serde_json::Value> for Vectors {
included: to_str(&value["round_one_outputs"]["participant_list"])
.split(',')
.map(u16::from_str)
.collect::<Result<_, _>>()
.unwrap(),
.collect::<Result<Vec<_>, _>>()
.unwrap()
.iter()
.map(|i| Participant::new(*i).unwrap())
.collect(),
nonces: value["round_one_outputs"]["participants"]
.as_object()
.unwrap()
@@ -80,7 +83,7 @@ impl From<serde_json::Value> for Vectors {
}
// 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>> {
fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<Participant, ThresholdKeys<C>> {
let shares = vectors
.shares
.iter()
@@ -92,11 +95,11 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
for i in 1 ..= u16::try_from(shares.len()).unwrap() {
// Manually re-implement the serialization for ThresholdCore to import this data
let mut serialized = vec![];
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_be_bytes());
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_le_bytes());
serialized.extend(C::ID);
serialized.extend(vectors.threshold.to_be_bytes());
serialized.extend(u16::try_from(shares.len()).unwrap().to_be_bytes());
serialized.extend(i.to_be_bytes());
serialized.extend(vectors.threshold.to_le_bytes());
serialized.extend(u16::try_from(shares.len()).unwrap().to_le_bytes());
serialized.extend(i.to_le_bytes());
serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
for share in &verification_shares {
serialized.extend(share.to_bytes().as_ref());
@@ -105,10 +108,11 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
let these_keys = ThresholdCore::<C>::deserialize::<&[u8]>(&mut serialized.as_ref()).unwrap();
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);
let participant = Participant::new(i).unwrap();
assert_eq!(these_keys.params().i(), participant);
assert_eq!(these_keys.secret_share().deref(), &shares[usize::from(i - 1)]);
assert_eq!(hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key);
keys.insert(i, ThresholdKeys::new(these_keys));
keys.insert(participant, ThresholdKeys::new(these_keys));
}
keys
@@ -124,7 +128,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
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)));
let group_key = keys[&Participant::new(1).unwrap()].group_key();
assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG)));
}
// Test blame on an invalid Schnorr signature share