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

@@ -23,7 +23,7 @@ use dleq::DLEqProof;
use frost::{ use frost::{
dkg::lagrange, dkg::lagrange,
curve::Ed25519, curve::Ed25519,
FrostError, ThresholdKeys, ThresholdView, Participant, FrostError, ThresholdKeys, ThresholdView,
algorithm::{WriteAddendum, Algorithm}, algorithm::{WriteAddendum, Algorithm},
}; };
@@ -146,8 +146,8 @@ pub(crate) fn add_key_image_share(
image: &mut EdwardsPoint, image: &mut EdwardsPoint,
generator: EdwardsPoint, generator: EdwardsPoint,
offset: Scalar, offset: Scalar,
included: &[u16], included: &[Participant],
participant: u16, participant: Participant,
share: EdwardsPoint, share: EdwardsPoint,
) { ) {
if image.is_identity() { if image.is_identity() {
@@ -203,7 +203,7 @@ impl Algorithm<Ed25519> for ClsagMultisig {
fn process_addendum( fn process_addendum(
&mut self, &mut self,
view: &ThresholdView<Ed25519>, view: &ThresholdView<Ed25519>,
l: u16, l: Participant,
addendum: ClsagAddendum, addendum: ClsagAddendum,
) -> Result<(), FrostError> { ) -> Result<(), FrostError> {
if self.image.is_identity() { if self.image.is_identity() {
@@ -212,7 +212,7 @@ impl Algorithm<Ed25519> for ClsagMultisig {
self.transcript.append_message(b"mask", self.mask().to_bytes()); self.transcript.append_message(b"mask", self.mask().to_bytes());
} }
self.transcript.append_message(b"participant", l.to_be_bytes()); self.transcript.append_message(b"participant", l.to_bytes());
addendum addendum
.dleq .dleq

View File

@@ -24,7 +24,10 @@ use crate::{
use crate::ringct::clsag::{ClsagDetails, ClsagMultisig}; use crate::ringct::clsag::{ClsagDetails, ClsagMultisig};
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
use frost::tests::{key_gen, algorithm_machines, sign}; use frost::{
Participant,
tests::{key_gen, algorithm_machines, sign},
};
const RING_LEN: u64 = 11; const RING_LEN: u64 = 11;
const AMOUNT: u64 = 1337; const AMOUNT: u64 = 1337;
@@ -93,7 +96,7 @@ fn clsag_multisig() {
mask = random_scalar(&mut OsRng); mask = random_scalar(&mut OsRng);
amount = OsRng.next_u64(); amount = OsRng.next_u64();
} else { } else {
dest = keys[&1].group_key().0; dest = keys[&Participant::new(1).unwrap()].group_key().0;
mask = randomness; mask = randomness;
amount = AMOUNT; amount = AMOUNT;
} }
@@ -103,7 +106,7 @@ fn clsag_multisig() {
let mask_sum = random_scalar(&mut OsRng); let mask_sum = random_scalar(&mut OsRng);
let algorithm = ClsagMultisig::new( let algorithm = ClsagMultisig::new(
RecommendedTranscript::new(b"Monero Serai CLSAG Test"), RecommendedTranscript::new(b"Monero Serai CLSAG Test"),
keys[&1].group_key().0, keys[&Participant::new(1).unwrap()].group_key().0,
Arc::new(RwLock::new(Some(ClsagDetails::new( Arc::new(RwLock::new(Some(ClsagDetails::new(
ClsagInput::new( ClsagInput::new(
Commitment::new(randomness, AMOUNT), Commitment::new(randomness, AMOUNT),

View File

@@ -14,7 +14,7 @@ use dalek_ff_group as dfg;
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, RecommendedTranscript};
use frost::{ use frost::{
curve::Ed25519, curve::Ed25519,
FrostError, ThresholdKeys, Participant, FrostError, ThresholdKeys,
sign::{ sign::{
Writable, Preprocess, CachedPreprocess, SignatureShare, PreprocessMachine, SignMachine, Writable, Preprocess, CachedPreprocess, SignatureShare, PreprocessMachine, SignMachine,
SignatureMachine, AlgorithmMachine, AlgorithmSignMachine, AlgorithmSignatureMachine, SignatureMachine, AlgorithmMachine, AlgorithmSignMachine, AlgorithmSignatureMachine,
@@ -35,7 +35,7 @@ use crate::{
/// FROST signing machine to produce a signed transaction. /// FROST signing machine to produce a signed transaction.
pub struct TransactionMachine { pub struct TransactionMachine {
signable: SignableTransaction, signable: SignableTransaction,
i: u16, i: Participant,
transcript: RecommendedTranscript, transcript: RecommendedTranscript,
decoys: Vec<Decoys>, decoys: Vec<Decoys>,
@@ -48,7 +48,7 @@ pub struct TransactionMachine {
pub struct TransactionSignMachine { pub struct TransactionSignMachine {
signable: SignableTransaction, signable: SignableTransaction,
i: u16, i: Participant,
transcript: RecommendedTranscript, transcript: RecommendedTranscript,
decoys: Vec<Decoys>, decoys: Vec<Decoys>,
@@ -236,7 +236,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
fn sign( fn sign(
mut self, mut self,
mut commitments: HashMap<u16, Self::Preprocess>, mut commitments: HashMap<Participant, Self::Preprocess>,
msg: &[u8], msg: &[u8],
) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> { ) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> {
if !msg.is_empty() { if !msg.is_empty() {
@@ -263,7 +263,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
// While each CLSAG will do this as they need to for security, they have their own // While each CLSAG will do this as they need to for security, they have their own
// transcripts cloned from this TX's initial premise's transcript. For our TX // transcripts cloned from this TX's initial premise's transcript. For our TX
// transcript to have the CLSAG data for entropy, it'll have to be added ourselves here // transcript to have the CLSAG data for entropy, it'll have to be added ourselves here
self.transcript.append_message(b"participant", (*l).to_be_bytes()); self.transcript.append_message(b"participant", (*l).to_bytes());
let preprocess = if *l == self.i { let preprocess = if *l == self.i {
self.our_preprocess[c].clone() self.our_preprocess[c].clone()
@@ -389,7 +389,7 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
fn complete( fn complete(
mut self, mut self,
shares: HashMap<u16, Self::SignatureShare>, shares: HashMap<Participant, Self::SignatureShare>,
) -> Result<Transaction, FrostError> { ) -> Result<Transaction, FrostError> {
let mut tx = self.tx; let mut tx = self.tx;
match tx.rct_signatures.prunable { match tx.rct_signatures.prunable {

View File

@@ -131,6 +131,7 @@ macro_rules! test {
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
use frost::{ use frost::{
curve::Ed25519, curve::Ed25519,
Participant,
tests::{THRESHOLD, key_gen}, tests::{THRESHOLD, key_gen},
}; };
@@ -165,7 +166,7 @@ macro_rules! test {
#[cfg(not(feature = "multisig"))] #[cfg(not(feature = "multisig"))]
panic!("Multisig branch called without the multisig feature"); panic!("Multisig branch called without the multisig feature");
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
keys[&1].group_key().0 keys[&Participant::new(1).unwrap()].group_key().0
}; };
let view = ViewPair::new(spend_pub, Zeroizing::new(random_scalar(&mut OsRng))); let view = ViewPair::new(spend_pub, Zeroizing::new(random_scalar(&mut OsRng)));
@@ -211,7 +212,7 @@ macro_rules! test {
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
{ {
let mut machines = HashMap::new(); let mut machines = HashMap::new();
for i in 1 ..= THRESHOLD { for i in (1 ..= THRESHOLD).map(|i| Participant::new(i).unwrap()) {
machines.insert( machines.insert(
i, i,
tx tx

View File

@@ -26,7 +26,7 @@ use multiexp::BatchVerifier;
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
use dleq::DLEqProof; use dleq::DLEqProof;
use crate::ThresholdParams; use crate::{Participant, ThresholdParams};
pub trait ReadWrite: Sized { pub trait ReadWrite: Sized {
fn read<R: Read>(reader: &mut R, params: ThresholdParams) -> io::Result<Self>; fn read<R: Read>(reader: &mut R, params: ThresholdParams) -> io::Result<Self>;
@@ -133,7 +133,7 @@ fn cipher<C: Ciphersuite>(dst: &'static [u8], ecdh: &Zeroizing<C::G>) -> ChaCha2
fn encrypt<R: RngCore + CryptoRng, C: Ciphersuite, E: Encryptable>( fn encrypt<R: RngCore + CryptoRng, C: Ciphersuite, E: Encryptable>(
rng: &mut R, rng: &mut R,
dst: &'static [u8], dst: &'static [u8],
from: u16, from: Participant,
to: C::G, to: C::G,
mut msg: Zeroizing<E>, mut msg: Zeroizing<E>,
) -> EncryptedMessage<C, E> { ) -> EncryptedMessage<C, E> {
@@ -192,7 +192,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
} }
#[cfg(test)] #[cfg(test)]
pub(crate) fn invalidate_msg<R: RngCore + CryptoRng>(&mut self, rng: &mut R, from: u16) { pub(crate) fn invalidate_msg<R: RngCore + CryptoRng>(&mut self, rng: &mut R, from: Participant) {
// Invalidate the message by specifying a new key/Schnorr PoP // Invalidate the message by specifying a new key/Schnorr PoP
// This will cause all initial checks to pass, yet a decrypt to gibberish // This will cause all initial checks to pass, yet a decrypt to gibberish
let key = Zeroizing::new(C::random_nonzero_F(rng)); let key = Zeroizing::new(C::random_nonzero_F(rng));
@@ -213,7 +213,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
&mut self, &mut self,
rng: &mut R, rng: &mut R,
dst: &'static [u8], dst: &'static [u8],
from: u16, from: Participant,
to: C::G, to: C::G,
) { ) {
use group::ff::PrimeField; use group::ff::PrimeField;
@@ -237,7 +237,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
&mut self, &mut self,
rng: &mut R, rng: &mut R,
dst: &'static [u8], dst: &'static [u8],
from: u16, from: Participant,
to: C::G, to: C::G,
) { ) {
use group::ff::PrimeField; use group::ff::PrimeField;
@@ -292,12 +292,12 @@ impl<C: Ciphersuite> EncryptionKeyProof<C> {
// This doesn't need to take the msg. It just doesn't hurt as an extra layer. // This doesn't need to take the msg. It just doesn't hurt as an extra layer.
// This still doesn't mean the DKG offers an authenticated channel. The per-message keys have no // This still doesn't mean the DKG offers an authenticated channel. The per-message keys have no
// root of trust other than their existence in the assumed-to-exist external authenticated channel. // root of trust other than their existence in the assumed-to-exist external authenticated channel.
fn pop_challenge<C: Ciphersuite>(nonce: C::G, key: C::G, sender: u16, msg: &[u8]) -> C::F { fn pop_challenge<C: Ciphersuite>(nonce: C::G, key: C::G, sender: Participant, msg: &[u8]) -> C::F {
let mut transcript = RecommendedTranscript::new(b"DKG Encryption Key Proof of Possession v0.2"); let mut transcript = RecommendedTranscript::new(b"DKG Encryption Key Proof of Possession v0.2");
transcript.append_message(b"nonce", nonce.to_bytes()); transcript.append_message(b"nonce", nonce.to_bytes());
transcript.append_message(b"key", key.to_bytes()); transcript.append_message(b"key", key.to_bytes());
// This is sufficient to prevent the attack this is meant to stop // This is sufficient to prevent the attack this is meant to stop
transcript.append_message(b"sender", sender.to_le_bytes()); transcript.append_message(b"sender", sender.to_bytes());
// This, as written above, doesn't hurt // This, as written above, doesn't hurt
transcript.append_message(b"message", msg); transcript.append_message(b"message", msg);
// While this is a PoK and a PoP, it's called a PoP here since the important part is its owner // While this is a PoK and a PoP, it's called a PoP here since the important part is its owner
@@ -322,10 +322,10 @@ pub(crate) enum DecryptionError {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct Encryption<C: Ciphersuite> { pub(crate) struct Encryption<C: Ciphersuite> {
dst: &'static [u8], dst: &'static [u8],
i: u16, i: Participant,
enc_key: Zeroizing<C::F>, enc_key: Zeroizing<C::F>,
enc_pub_key: C::G, enc_pub_key: C::G,
enc_keys: HashMap<u16, C::G>, enc_keys: HashMap<Participant, C::G>,
} }
impl<C: Ciphersuite> Zeroize for Encryption<C> { impl<C: Ciphersuite> Zeroize for Encryption<C> {
@@ -339,7 +339,11 @@ impl<C: Ciphersuite> Zeroize for Encryption<C> {
} }
impl<C: Ciphersuite> Encryption<C> { impl<C: Ciphersuite> Encryption<C> {
pub(crate) fn new<R: RngCore + CryptoRng>(dst: &'static [u8], i: u16, rng: &mut R) -> Self { pub(crate) fn new<R: RngCore + CryptoRng>(
dst: &'static [u8],
i: Participant,
rng: &mut R,
) -> Self {
let enc_key = Zeroizing::new(C::random_nonzero_F(rng)); let enc_key = Zeroizing::new(C::random_nonzero_F(rng));
Self { Self {
dst, dst,
@@ -356,7 +360,7 @@ impl<C: Ciphersuite> Encryption<C> {
pub(crate) fn register<M: Message>( pub(crate) fn register<M: Message>(
&mut self, &mut self,
participant: u16, participant: Participant,
msg: EncryptionKeyMessage<C, M>, msg: EncryptionKeyMessage<C, M>,
) -> M { ) -> M {
if self.enc_keys.contains_key(&participant) { if self.enc_keys.contains_key(&participant) {
@@ -369,7 +373,7 @@ impl<C: Ciphersuite> Encryption<C> {
pub(crate) fn encrypt<R: RngCore + CryptoRng, E: Encryptable>( pub(crate) fn encrypt<R: RngCore + CryptoRng, E: Encryptable>(
&self, &self,
rng: &mut R, rng: &mut R,
participant: u16, participant: Participant,
msg: Zeroizing<E>, msg: Zeroizing<E>,
) -> EncryptedMessage<C, E> { ) -> EncryptedMessage<C, E> {
encrypt(rng, self.dst, self.i, self.enc_keys[&participant], msg) encrypt(rng, self.dst, self.i, self.enc_keys[&participant], msg)
@@ -382,7 +386,7 @@ impl<C: Ciphersuite> Encryption<C> {
// Uses a distinct batch ID so if this batch verifier is reused, we know its the PoP aspect // Uses a distinct batch ID so if this batch verifier is reused, we know its the PoP aspect
// which failed, and therefore to use None for the blame // which failed, and therefore to use None for the blame
batch_id: I, batch_id: I,
from: u16, from: Participant,
mut msg: EncryptedMessage<C, E>, mut msg: EncryptedMessage<C, E>,
) -> (Zeroizing<E>, EncryptionKeyProof<C>) { ) -> (Zeroizing<E>, EncryptionKeyProof<C>) {
msg.pop.batch_verify( msg.pop.batch_verify(
@@ -413,8 +417,8 @@ impl<C: Ciphersuite> Encryption<C> {
// Returns None if the key was wrong. // Returns None if the key was wrong.
pub(crate) fn decrypt_with_proof<E: Encryptable>( pub(crate) fn decrypt_with_proof<E: Encryptable>(
&self, &self,
from: u16, from: Participant,
decryptor: u16, decryptor: Participant,
mut msg: EncryptedMessage<C, E>, mut msg: EncryptedMessage<C, E>,
// There's no encryption key proof if the accusation is of an invalid signature // There's no encryption key proof if the accusation is of an invalid signature
proof: Option<EncryptionKeyProof<C>>, proof: Option<EncryptionKeyProof<C>>,

View File

@@ -24,7 +24,7 @@ use multiexp::{multiexp_vartime, BatchVerifier};
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
use crate::{ use crate::{
DkgError, ThresholdParams, ThresholdCore, validate_map, Participant, DkgError, ThresholdParams, ThresholdCore, validate_map,
encryption::{ encryption::{
ReadWrite, EncryptionKeyMessage, EncryptedMessage, Encryption, EncryptionKeyProof, ReadWrite, EncryptionKeyMessage, EncryptedMessage, Encryption, EncryptionKeyProof,
DecryptionError, DecryptionError,
@@ -34,11 +34,11 @@ use crate::{
type FrostError<C> = DkgError<EncryptionKeyProof<C>>; type FrostError<C> = DkgError<EncryptionKeyProof<C>>;
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn challenge<C: Ciphersuite>(context: &str, l: u16, R: &[u8], Am: &[u8]) -> C::F { fn challenge<C: Ciphersuite>(context: &str, l: Participant, R: &[u8], Am: &[u8]) -> C::F {
let mut transcript = RecommendedTranscript::new(b"DKG FROST v0.2"); let mut transcript = RecommendedTranscript::new(b"DKG FROST v0.2");
transcript.domain_separate(b"schnorr_proof_of_knowledge"); transcript.domain_separate(b"schnorr_proof_of_knowledge");
transcript.append_message(b"context", context.as_bytes()); transcript.append_message(b"context", context.as_bytes());
transcript.append_message(b"participant", l.to_le_bytes()); transcript.append_message(b"participant", l.to_bytes());
transcript.append_message(b"nonce", R); transcript.append_message(b"nonce", R);
transcript.append_message(b"commitments", Am); transcript.append_message(b"commitments", Am);
C::hash_to_F(b"DKG-FROST-proof_of_knowledge-0", &transcript.challenge(b"schnorr")) C::hash_to_F(b"DKG-FROST-proof_of_knowledge-0", &transcript.challenge(b"schnorr"))
@@ -150,9 +150,13 @@ impl<C: Ciphersuite> KeyGenMachine<C> {
} }
} }
fn polynomial<F: PrimeField + Zeroize>(coefficients: &[Zeroizing<F>], l: u16) -> Zeroizing<F> { fn polynomial<F: PrimeField + Zeroize>(
assert!(l != 0, "attempting to evaluate a polynomial with 0"); coefficients: &[Zeroizing<F>],
let l = F::from(u64::from(l)); l: Participant,
) -> Zeroizing<F> {
let l = F::from(u64::from(u16::from(l)));
// This should never be reached since Participant is explicitly non-zero
assert!(l != F::zero(), "zero participant passed to polynomial");
let mut share = Zeroizing::new(F::zero()); let mut share = Zeroizing::new(F::zero());
for (idx, coefficient) in coefficients.iter().rev().enumerate() { for (idx, coefficient) in coefficients.iter().rev().enumerate() {
*share += coefficient.deref(); *share += coefficient.deref();
@@ -231,11 +235,15 @@ impl<C: Ciphersuite> SecretShareMachine<C> {
fn verify_r1<R: RngCore + CryptoRng>( fn verify_r1<R: RngCore + CryptoRng>(
&mut self, &mut self,
rng: &mut R, rng: &mut R,
mut commitments: HashMap<u16, EncryptionKeyMessage<C, Commitments<C>>>, mut commitments: HashMap<Participant, EncryptionKeyMessage<C, Commitments<C>>>,
) -> Result<HashMap<u16, Vec<C::G>>, FrostError<C>> { ) -> Result<HashMap<Participant, Vec<C::G>>, FrostError<C>> {
validate_map(&commitments, &(1 ..= self.params.n()).collect::<Vec<_>>(), self.params.i())?; validate_map(
&commitments,
&(1 ..= self.params.n()).map(Participant).collect::<Vec<_>>(),
self.params.i(),
)?;
let mut batch = BatchVerifier::<u16, C::G>::new(commitments.len()); let mut batch = BatchVerifier::<Participant, C::G>::new(commitments.len());
let mut commitments = commitments let mut commitments = commitments
.drain() .drain()
.map(|(l, msg)| { .map(|(l, msg)| {
@@ -269,14 +277,16 @@ impl<C: Ciphersuite> SecretShareMachine<C> {
pub fn generate_secret_shares<R: RngCore + CryptoRng>( pub fn generate_secret_shares<R: RngCore + CryptoRng>(
mut self, mut self,
rng: &mut R, rng: &mut R,
commitments: HashMap<u16, EncryptionKeyMessage<C, Commitments<C>>>, commitments: HashMap<Participant, EncryptionKeyMessage<C, Commitments<C>>>,
) -> Result<(KeyMachine<C>, HashMap<u16, EncryptedMessage<C, SecretShare<C::F>>>), FrostError<C>> ) -> Result<
{ (KeyMachine<C>, HashMap<Participant, EncryptedMessage<C, SecretShare<C::F>>>),
FrostError<C>,
> {
let commitments = self.verify_r1(&mut *rng, commitments)?; let commitments = self.verify_r1(&mut *rng, commitments)?;
// Step 1: Generate secret shares for all other parties // Step 1: Generate secret shares for all other parties
let mut res = HashMap::new(); let mut res = HashMap::new();
for l in 1 ..= self.params.n() { for l in (1 ..= self.params.n()).map(Participant) {
// Don't insert our own shares to the byte buffer which is meant to be sent around // Don't insert our own shares to the byte buffer which is meant to be sent around
// An app developer could accidentally send it. Best to keep this black boxed // An app developer could accidentally send it. Best to keep this black boxed
if l == self.params.i() { if l == self.params.i() {
@@ -308,7 +318,7 @@ impl<C: Ciphersuite> SecretShareMachine<C> {
pub struct KeyMachine<C: Ciphersuite> { pub struct KeyMachine<C: Ciphersuite> {
params: ThresholdParams, params: ThresholdParams,
secret: Zeroizing<C::F>, secret: Zeroizing<C::F>,
commitments: HashMap<u16, Vec<C::G>>, commitments: HashMap<Participant, Vec<C::G>>,
encryption: Encryption<C>, encryption: Encryption<C>,
} }
@@ -326,8 +336,8 @@ impl<C: Ciphersuite> Zeroize for KeyMachine<C> {
// Calculate the exponent for a given participant and apply it to a series of commitments // Calculate the exponent for a given participant and apply it to a series of commitments
// Initially used with the actual commitments to verify the secret share, later used with // Initially used with the actual commitments to verify the secret share, later used with
// stripes to generate the verification shares // stripes to generate the verification shares
fn exponential<C: Ciphersuite>(i: u16, values: &[C::G]) -> Vec<(C::F, C::G)> { fn exponential<C: Ciphersuite>(i: Participant, values: &[C::G]) -> Vec<(C::F, C::G)> {
let i = C::F::from(i.into()); let i = C::F::from(u16::from(i).into());
let mut res = Vec::with_capacity(values.len()); let mut res = Vec::with_capacity(values.len());
(0 .. values.len()).fold(C::F::one(), |exp, l| { (0 .. values.len()).fold(C::F::one(), |exp, l| {
res.push((exp, values[l])); res.push((exp, values[l]));
@@ -337,7 +347,7 @@ fn exponential<C: Ciphersuite>(i: u16, values: &[C::G]) -> Vec<(C::F, C::G)> {
} }
fn share_verification_statements<C: Ciphersuite>( fn share_verification_statements<C: Ciphersuite>(
target: u16, target: Participant,
commitments: &[C::G], commitments: &[C::G],
mut share: Zeroizing<C::F>, mut share: Zeroizing<C::F>,
) -> Vec<(C::F, C::G)> { ) -> Vec<(C::F, C::G)> {
@@ -359,8 +369,8 @@ fn share_verification_statements<C: Ciphersuite>(
#[derive(Clone, Copy, Hash, Debug, Zeroize)] #[derive(Clone, Copy, Hash, Debug, Zeroize)]
enum BatchId { enum BatchId {
Decryption(u16), Decryption(Participant),
Share(u16), Share(Participant),
} }
impl<C: Ciphersuite> KeyMachine<C> { impl<C: Ciphersuite> KeyMachine<C> {
@@ -370,9 +380,13 @@ impl<C: Ciphersuite> KeyMachine<C> {
pub fn calculate_share<R: RngCore + CryptoRng>( pub fn calculate_share<R: RngCore + CryptoRng>(
mut self, mut self,
rng: &mut R, rng: &mut R,
mut shares: HashMap<u16, EncryptedMessage<C, SecretShare<C::F>>>, mut shares: HashMap<Participant, EncryptedMessage<C, SecretShare<C::F>>>,
) -> Result<BlameMachine<C>, FrostError<C>> { ) -> Result<BlameMachine<C>, FrostError<C>> {
validate_map(&shares, &(1 ..= self.params.n()).collect::<Vec<_>>(), self.params.i())?; validate_map(
&shares,
&(1 ..= self.params.n()).map(Participant).collect::<Vec<_>>(),
self.params.i(),
)?;
let mut batch = BatchVerifier::new(shares.len()); let mut batch = BatchVerifier::new(shares.len());
let mut blames = HashMap::new(); let mut blames = HashMap::new();
@@ -412,7 +426,7 @@ impl<C: Ciphersuite> KeyMachine<C> {
// Calculate each user's verification share // Calculate each user's verification share
let mut verification_shares = HashMap::new(); let mut verification_shares = HashMap::new();
for i in 1 ..= self.params.n() { for i in (1 ..= self.params.n()).map(Participant) {
verification_shares.insert( verification_shares.insert(
i, i,
if i == self.params.i() { if i == self.params.i() {
@@ -438,7 +452,7 @@ impl<C: Ciphersuite> KeyMachine<C> {
} }
pub struct BlameMachine<C: Ciphersuite> { pub struct BlameMachine<C: Ciphersuite> {
commitments: HashMap<u16, Vec<C::G>>, commitments: HashMap<Participant, Vec<C::G>>,
encryption: Encryption<C>, encryption: Encryption<C>,
result: ThresholdCore<C>, result: ThresholdCore<C>,
} }
@@ -469,11 +483,11 @@ impl<C: Ciphersuite> BlameMachine<C> {
fn blame_internal( fn blame_internal(
&self, &self,
sender: u16, sender: Participant,
recipient: u16, recipient: Participant,
msg: EncryptedMessage<C, SecretShare<C::F>>, msg: EncryptedMessage<C, SecretShare<C::F>>,
proof: Option<EncryptionKeyProof<C>>, proof: Option<EncryptionKeyProof<C>>,
) -> u16 { ) -> Participant {
let share_bytes = match self.encryption.decrypt_with_proof(sender, recipient, msg, proof) { let share_bytes = match self.encryption.decrypt_with_proof(sender, recipient, msg, proof) {
Ok(share_bytes) => share_bytes, Ok(share_bytes) => share_bytes,
// If there's an invalid signature, the sender did not send a properly formed message // If there's an invalid signature, the sender did not send a properly formed message
@@ -518,11 +532,11 @@ impl<C: Ciphersuite> BlameMachine<C> {
/// order to prevent multiple instances of blame over a single incident. /// order to prevent multiple instances of blame over a single incident.
pub fn blame( pub fn blame(
self, self,
sender: u16, sender: Participant,
recipient: u16, recipient: Participant,
msg: EncryptedMessage<C, SecretShare<C::F>>, msg: EncryptedMessage<C, SecretShare<C::F>>,
proof: Option<EncryptionKeyProof<C>>, proof: Option<EncryptionKeyProof<C>>,
) -> (AdditionalBlameMachine<C>, u16) { ) -> (AdditionalBlameMachine<C>, Participant) {
let faulty = self.blame_internal(sender, recipient, msg, proof); let faulty = self.blame_internal(sender, recipient, msg, proof);
(AdditionalBlameMachine(self), faulty) (AdditionalBlameMachine(self), faulty)
} }
@@ -543,11 +557,11 @@ impl<C: Ciphersuite> AdditionalBlameMachine<C> {
/// over a single incident. /// over a single incident.
pub fn blame( pub fn blame(
self, self,
sender: u16, sender: Participant,
recipient: u16, recipient: Participant,
msg: EncryptedMessage<C, SecretShare<C::F>>, msg: EncryptedMessage<C, SecretShare<C::F>>,
proof: Option<EncryptionKeyProof<C>>, proof: Option<EncryptionKeyProof<C>>,
) -> u16 { ) -> Participant {
self.0.blame_internal(sender, recipient, msg, proof) self.0.blame_internal(sender, recipient, msg, proof)
} }
} }

View File

@@ -7,7 +7,7 @@
//! provided. //! provided.
use core::{ use core::{
fmt::{Debug, Formatter}, fmt::{self, Debug},
ops::Deref, ops::Deref,
}; };
use std::{io::Read, sync::Arc, collections::HashMap}; use std::{io::Read, sync::Arc, collections::HashMap};
@@ -36,29 +36,59 @@ pub mod promote;
#[cfg(any(test, feature = "tests"))] #[cfg(any(test, feature = "tests"))]
pub mod tests; pub mod tests;
/// The ID of a participant, defined as a non-zero u16.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Zeroize)]
pub struct Participant(pub(crate) u16);
impl Participant {
pub fn new(i: u16) -> Option<Participant> {
if i == 0 {
None
} else {
Some(Participant(i))
}
}
#[allow(clippy::wrong_self_convention)]
pub fn to_bytes(&self) -> [u8; 2] {
self.0.to_le_bytes()
}
}
impl From<Participant> for u16 {
fn from(participant: Participant) -> u16 {
participant.0
}
}
impl fmt::Display for Participant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// Various errors possible during key generation/signing. /// Various errors possible during key generation/signing.
#[derive(Clone, PartialEq, Eq, Debug, Error)] #[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum DkgError<B: Clone + PartialEq + Eq + Debug> { pub enum DkgError<B: Clone + PartialEq + Eq + Debug> {
#[error("a parameter was 0 (required {0}, participants {1})")] #[error("a parameter was 0 (threshold {0}, participants {1})")]
ZeroParameter(u16, u16), ZeroParameter(u16, u16),
#[error("invalid amount of required participants (max {1}, got {0})")] #[error("invalid amount of required participants (max {1}, got {0})")]
InvalidRequiredQuantity(u16, u16), InvalidRequiredQuantity(u16, u16),
#[error("invalid participant index (0 < index <= {0}, yet index is {1})")] #[error("invalid participant (0 < participant <= {0}, yet participant is {1})")]
InvalidParticipantIndex(u16, u16), InvalidParticipant(u16, Participant),
#[error("invalid signing set")] #[error("invalid signing set")]
InvalidSigningSet, InvalidSigningSet,
#[error("invalid participant quantity (expected {0}, got {1})")] #[error("invalid participant quantity (expected {0}, got {1})")]
InvalidParticipantQuantity(usize, usize), InvalidParticipantQuantity(usize, usize),
#[error("duplicated participant index ({0})")] #[error("duplicated participant ({0})")]
DuplicatedIndex(u16), DuplicatedParticipant(Participant),
#[error("missing participant {0}")] #[error("missing participant {0}")]
MissingParticipant(u16), MissingParticipant(Participant),
#[error("invalid proof of knowledge (participant {0})")] #[error("invalid proof of knowledge (participant {0})")]
InvalidProofOfKnowledge(u16), InvalidProofOfKnowledge(Participant),
#[error("invalid share (participant {participant}, blame {blame})")] #[error("invalid share (participant {participant}, blame {blame})")]
InvalidShare { participant: u16, blame: Option<B> }, InvalidShare { participant: Participant, blame: Option<B> },
#[error("internal error ({0})")] #[error("internal error ({0})")]
InternalError(&'static str), InternalError(&'static str),
@@ -66,9 +96,9 @@ pub enum DkgError<B: Clone + PartialEq + Eq + Debug> {
// Validate a map of values to have the expected included participants // Validate a map of values to have the expected included participants
pub(crate) fn validate_map<T, B: Clone + PartialEq + Eq + Debug>( pub(crate) fn validate_map<T, B: Clone + PartialEq + Eq + Debug>(
map: &HashMap<u16, T>, map: &HashMap<Participant, T>,
included: &[u16], included: &[Participant],
ours: u16, ours: Participant,
) -> Result<(), DkgError<B>> { ) -> Result<(), DkgError<B>> {
if (map.len() + 1) != included.len() { if (map.len() + 1) != included.len() {
Err(DkgError::InvalidParticipantQuantity(included.len(), map.len() + 1))?; Err(DkgError::InvalidParticipantQuantity(included.len(), map.len() + 1))?;
@@ -77,7 +107,7 @@ pub(crate) fn validate_map<T, B: Clone + PartialEq + Eq + Debug>(
for included in included { for included in included {
if *included == ours { if *included == ours {
if map.contains_key(included) { if map.contains_key(included) {
Err(DkgError::DuplicatedIndex(*included))?; Err(DkgError::DuplicatedParticipant(*included))?;
} }
continue; continue;
} }
@@ -99,11 +129,11 @@ pub struct ThresholdParams {
/// Amount of participants. /// Amount of participants.
n: u16, n: u16,
/// Index of the participant being acted for. /// Index of the participant being acted for.
i: u16, i: Participant,
} }
impl ThresholdParams { impl ThresholdParams {
pub fn new(t: u16, n: u16, i: u16) -> Result<ThresholdParams, DkgError<()>> { pub fn new(t: u16, n: u16, i: Participant) -> Result<ThresholdParams, DkgError<()>> {
if (t == 0) || (n == 0) { if (t == 0) || (n == 0) {
Err(DkgError::ZeroParameter(t, n))?; Err(DkgError::ZeroParameter(t, n))?;
} }
@@ -113,8 +143,8 @@ impl ThresholdParams {
if t > n { if t > n {
Err(DkgError::InvalidRequiredQuantity(t, n))?; Err(DkgError::InvalidRequiredQuantity(t, n))?;
} }
if (i == 0) || (i > n) { if u16::from(i) > n {
Err(DkgError::InvalidParticipantIndex(n, i))?; Err(DkgError::InvalidParticipant(n, i))?;
} }
Ok(ThresholdParams { t, n, i }) Ok(ThresholdParams { t, n, i })
@@ -126,13 +156,15 @@ impl ThresholdParams {
pub fn n(&self) -> u16 { pub fn n(&self) -> u16 {
self.n self.n
} }
pub fn i(&self) -> u16 { pub fn i(&self) -> Participant {
self.i self.i
} }
} }
/// Calculate the lagrange coefficient for a signing set. /// Calculate the lagrange coefficient for a signing set.
pub fn lagrange<F: PrimeField>(i: u16, included: &[u16]) -> F { pub fn lagrange<F: PrimeField>(i: Participant, included: &[Participant]) -> F {
let i_f = F::from(u64::from(u16::from(i)));
let mut num = F::one(); let mut num = F::one();
let mut denom = F::one(); let mut denom = F::one();
for l in included { for l in included {
@@ -140,9 +172,9 @@ pub fn lagrange<F: PrimeField>(i: u16, included: &[u16]) -> F {
continue; continue;
} }
let share = F::from(u64::from(*l)); let share = F::from(u64::from(u16::from(*l)));
num *= share; num *= share;
denom *= share - F::from(u64::from(i)); denom *= share - i_f;
} }
// Safe as this will only be 0 if we're part of the above loop // Safe as this will only be 0 if we're part of the above loop
@@ -162,11 +194,11 @@ pub struct ThresholdCore<C: Ciphersuite> {
/// Group key. /// Group key.
group_key: C::G, group_key: C::G,
/// Verification shares. /// Verification shares.
verification_shares: HashMap<u16, C::G>, verification_shares: HashMap<Participant, C::G>,
} }
impl<C: Ciphersuite> Debug for ThresholdCore<C> { impl<C: Ciphersuite> fmt::Debug for ThresholdCore<C> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt fmt
.debug_struct("ThresholdCore") .debug_struct("ThresholdCore")
.field("params", &self.params) .field("params", &self.params)
@@ -191,16 +223,9 @@ impl<C: Ciphersuite> ThresholdCore<C> {
pub(crate) fn new( pub(crate) fn new(
params: ThresholdParams, params: ThresholdParams,
secret_share: Zeroizing<C::F>, secret_share: Zeroizing<C::F>,
verification_shares: HashMap<u16, C::G>, verification_shares: HashMap<Participant, C::G>,
) -> ThresholdCore<C> { ) -> ThresholdCore<C> {
debug_assert!(validate_map::<_, ()>( let t = (1 ..= params.t).map(Participant).collect::<Vec<_>>();
&verification_shares,
&(0 ..= params.n).collect::<Vec<_>>(),
0
)
.is_ok());
let t = (1 ..= params.t).collect::<Vec<_>>();
ThresholdCore { ThresholdCore {
params, params,
secret_share, secret_share,
@@ -220,19 +245,19 @@ impl<C: Ciphersuite> ThresholdCore<C> {
self.group_key self.group_key
} }
pub(crate) fn verification_shares(&self) -> HashMap<u16, C::G> { pub(crate) fn verification_shares(&self) -> HashMap<Participant, C::G> {
self.verification_shares.clone() self.verification_shares.clone()
} }
pub fn serialize(&self) -> Vec<u8> { pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![]; 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(C::ID);
serialized.extend(self.params.t.to_be_bytes()); serialized.extend(self.params.t.to_le_bytes());
serialized.extend(self.params.n.to_be_bytes()); serialized.extend(self.params.n.to_le_bytes());
serialized.extend(self.params.i.to_be_bytes()); serialized.extend(self.params.i.to_bytes());
serialized.extend(self.secret_share.to_repr().as_ref()); serialized.extend(self.secret_share.to_repr().as_ref());
for l in 1 ..= self.params.n { for l in (1 ..= self.params.n).map(Participant) {
serialized.extend(self.verification_shares[&l].to_bytes().as_ref()); serialized.extend(self.verification_shares[&l].to_bytes().as_ref());
} }
serialized serialized
@@ -245,7 +270,7 @@ impl<C: Ciphersuite> ThresholdCore<C> {
let mut id_len = [0; 4]; let mut id_len = [0; 4];
reader.read_exact(&mut id_len).map_err(|_| missing.clone())?; reader.read_exact(&mut id_len).map_err(|_| missing.clone())?;
if u32::try_from(C::ID.len()).unwrap().to_be_bytes() != id_len { if u32::try_from(C::ID.len()).unwrap().to_le_bytes() != id_len {
Err(different.clone())?; Err(different.clone())?;
} }
@@ -262,9 +287,14 @@ impl<C: Ciphersuite> ThresholdCore<C> {
reader reader
.read_exact(&mut value) .read_exact(&mut value)
.map_err(|_| DkgError::InternalError("missing participant quantities"))?; .map_err(|_| DkgError::InternalError("missing participant quantities"))?;
Ok(u16::from_be_bytes(value)) Ok(u16::from_le_bytes(value))
}; };
(read_u16()?, read_u16()?, read_u16()?) (
read_u16()?,
read_u16()?,
Participant::new(read_u16()?)
.ok_or(DkgError::InternalError("invalid participant index"))?,
)
}; };
let secret_share = Zeroizing::new( let secret_share = Zeroizing::new(
@@ -272,7 +302,7 @@ impl<C: Ciphersuite> ThresholdCore<C> {
); );
let mut verification_shares = HashMap::new(); let mut verification_shares = HashMap::new();
for l in 1 ..= n { for l in (1 ..= n).map(Participant) {
verification_shares.insert( verification_shares.insert(
l, l,
<C as Ciphersuite>::read_G(reader) <C as Ciphersuite>::read_G(reader)
@@ -306,10 +336,10 @@ pub struct ThresholdKeys<C: Ciphersuite> {
pub struct ThresholdView<C: Ciphersuite> { pub struct ThresholdView<C: Ciphersuite> {
offset: C::F, offset: C::F,
group_key: C::G, group_key: C::G,
included: Vec<u16>, included: Vec<Participant>,
secret_share: Zeroizing<C::F>, secret_share: Zeroizing<C::F>,
original_verification_shares: HashMap<u16, C::G>, original_verification_shares: HashMap<Participant, C::G>,
verification_shares: HashMap<u16, C::G>, verification_shares: HashMap<Participant, C::G>,
} }
impl<C: Ciphersuite> Zeroize for ThresholdView<C> { impl<C: Ciphersuite> Zeroize for ThresholdView<C> {
@@ -363,7 +393,7 @@ impl<C: Ciphersuite> ThresholdKeys<C> {
} }
/// Returns all participants' verification shares without any offsetting. /// Returns all participants' verification shares without any offsetting.
pub(crate) fn verification_shares(&self) -> HashMap<u16, C::G> { pub(crate) fn verification_shares(&self) -> HashMap<Participant, C::G> {
self.core.verification_shares() self.core.verification_shares()
} }
@@ -371,7 +401,7 @@ impl<C: Ciphersuite> ThresholdKeys<C> {
self.core.serialize() self.core.serialize()
} }
pub fn view(&self, included: &[u16]) -> Result<ThresholdView<C>, DkgError<()>> { pub fn view(&self, included: &[Participant]) -> Result<ThresholdView<C>, DkgError<()>> {
if (included.len() < self.params().t.into()) || (usize::from(self.params().n) < included.len()) if (included.len() < self.params().t.into()) || (usize::from(self.params().n) < included.len())
{ {
Err(DkgError::InvalidSigningSet)?; Err(DkgError::InvalidSigningSet)?;
@@ -409,7 +439,7 @@ impl<C: Ciphersuite> ThresholdView<C> {
self.group_key self.group_key
} }
pub fn included(&self) -> &[u16] { pub fn included(&self) -> &[Participant] {
&self.included &self.included
} }
@@ -417,11 +447,11 @@ impl<C: Ciphersuite> ThresholdView<C> {
&self.secret_share &self.secret_share
} }
pub fn original_verification_share(&self, l: u16) -> C::G { pub fn original_verification_share(&self, l: Participant) -> C::G {
self.original_verification_shares[&l] self.original_verification_shares[&l]
} }
pub fn verification_share(&self, l: u16) -> C::G { pub fn verification_share(&self, l: Participant) -> C::G {
self.verification_shares[&l] self.verification_shares[&l]
} }
} }

View File

@@ -14,7 +14,7 @@ use ciphersuite::Ciphersuite;
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, RecommendedTranscript};
use dleq::DLEqProof; use dleq::DLEqProof;
use crate::{DkgError, ThresholdCore, ThresholdKeys, validate_map}; use crate::{Participant, DkgError, ThresholdCore, ThresholdKeys, validate_map};
/// Promote a set of keys to another Ciphersuite definition. /// Promote a set of keys to another Ciphersuite definition.
pub trait CiphersuitePromote<C2: Ciphersuite> { pub trait CiphersuitePromote<C2: Ciphersuite> {
@@ -27,10 +27,10 @@ pub trait CiphersuitePromote<C2: Ciphersuite> {
fn promote(self) -> ThresholdKeys<C2>; fn promote(self) -> ThresholdKeys<C2>;
} }
fn transcript<G: GroupEncoding>(key: G, i: u16) -> RecommendedTranscript { fn transcript<G: GroupEncoding>(key: G, i: Participant) -> RecommendedTranscript {
let mut transcript = RecommendedTranscript::new(b"DKG Generator Promotion v0.2"); let mut transcript = RecommendedTranscript::new(b"DKG Generator Promotion v0.2");
transcript.append_message(b"group_key", key.to_bytes()); transcript.append_message(b"group_key", key.to_bytes());
transcript.append_message(b"participant", i.to_be_bytes()); transcript.append_message(b"participant", i.to_bytes());
transcript transcript
} }
@@ -97,10 +97,10 @@ where
/// Complete promotion by taking in the proofs from all other participants. /// Complete promotion by taking in the proofs from all other participants.
pub fn complete( pub fn complete(
self, self,
proofs: &HashMap<u16, GeneratorProof<C1>>, proofs: &HashMap<Participant, GeneratorProof<C1>>,
) -> Result<ThresholdKeys<C2>, DkgError<()>> { ) -> Result<ThresholdKeys<C2>, DkgError<()>> {
let params = self.base.params(); let params = self.base.params();
validate_map(proofs, &(1 ..= params.n).collect::<Vec<_>>(), params.i)?; validate_map(proofs, &(1 ..= params.n).map(Participant).collect::<Vec<_>>(), params.i)?;
let original_shares = self.base.verification_shares(); let original_shares = self.base.verification_shares();

View File

@@ -3,7 +3,7 @@ use std::collections::HashMap;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use crate::{ use crate::{
Ciphersuite, ThresholdParams, ThresholdCore, Ciphersuite, Participant, ThresholdParams, ThresholdCore,
frost::{KeyGenMachine, SecretShare, KeyMachine}, frost::{KeyGenMachine, SecretShare, KeyMachine},
encryption::{EncryptionKeyMessage, EncryptedMessage}, encryption::{EncryptionKeyMessage, EncryptedMessage},
tests::{THRESHOLD, PARTICIPANTS, clone_without}, tests::{THRESHOLD, PARTICIPANTS, clone_without},
@@ -11,30 +11,29 @@ use crate::{
// Needed so rustfmt doesn't fail to format on line length issues // Needed so rustfmt doesn't fail to format on line length issues
type FrostEncryptedMessage<C> = EncryptedMessage<C, SecretShare<<C as Ciphersuite>::F>>; type FrostEncryptedMessage<C> = EncryptedMessage<C, SecretShare<<C as Ciphersuite>::F>>;
type FrostSecretShares<C> = HashMap<u16, FrostEncryptedMessage<C>>; type FrostSecretShares<C> = HashMap<Participant, FrostEncryptedMessage<C>>;
// Commit, then return enc key and shares // Commit, then return enc key and shares
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>( fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>(
rng: &mut R, rng: &mut R,
) -> (HashMap<u16, KeyMachine<C>>, HashMap<u16, C::G>, HashMap<u16, FrostSecretShares<C>>) { ) -> (
HashMap<Participant, KeyMachine<C>>,
HashMap<Participant, C::G>,
HashMap<Participant, FrostSecretShares<C>>,
) {
let mut machines = HashMap::new(); let mut machines = HashMap::new();
let mut commitments = HashMap::new(); let mut commitments = HashMap::new();
let mut enc_keys = HashMap::new(); let mut enc_keys = HashMap::new();
for i in 1 ..= PARTICIPANTS { for i in (1 ..= PARTICIPANTS).map(Participant) {
let machine = KeyGenMachine::<C>::new( let params = ThresholdParams::new(THRESHOLD, PARTICIPANTS, i).unwrap();
ThresholdParams::new(THRESHOLD, PARTICIPANTS, i).unwrap(), let machine = KeyGenMachine::<C>::new(params, "DKG Test Key Generation".to_string());
"DKG Test Key Generation".to_string(),
);
let (machine, these_commitments) = machine.generate_coefficients(rng); let (machine, these_commitments) = machine.generate_coefficients(rng);
machines.insert(i, machine); machines.insert(i, machine);
commitments.insert( commitments.insert(
i, i,
EncryptionKeyMessage::read::<&[u8]>( EncryptionKeyMessage::read::<&[u8]>(&mut these_commitments.serialize().as_ref(), params)
&mut these_commitments.serialize().as_ref(),
ThresholdParams { t: THRESHOLD, n: PARTICIPANTS, i: 1 },
)
.unwrap(), .unwrap(),
); );
enc_keys.insert(i, commitments[&i].enc_key()); enc_keys.insert(i, commitments[&i].enc_key());
@@ -53,7 +52,8 @@ fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>(
l, l,
EncryptedMessage::read::<&[u8]>( EncryptedMessage::read::<&[u8]>(
&mut share.serialize().as_ref(), &mut share.serialize().as_ref(),
ThresholdParams { t: THRESHOLD, n: PARTICIPANTS, i: 1 }, // Only t/n actually matters, so hardcode i to 1 here
ThresholdParams { t: THRESHOLD, n: PARTICIPANTS, i: Participant(1) },
) )
.unwrap(), .unwrap(),
) )
@@ -68,8 +68,8 @@ fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>(
} }
fn generate_secret_shares<C: Ciphersuite>( fn generate_secret_shares<C: Ciphersuite>(
shares: &HashMap<u16, FrostSecretShares<C>>, shares: &HashMap<Participant, FrostSecretShares<C>>,
recipient: u16, recipient: Participant,
) -> FrostSecretShares<C> { ) -> FrostSecretShares<C> {
let mut our_secret_shares = HashMap::new(); let mut our_secret_shares = HashMap::new();
for (i, shares) in shares { for (i, shares) in shares {
@@ -84,7 +84,7 @@ fn generate_secret_shares<C: Ciphersuite>(
/// Fully perform the FROST key generation algorithm. /// Fully perform the FROST key generation algorithm.
pub fn frost_gen<R: RngCore + CryptoRng, C: Ciphersuite>( pub fn frost_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
rng: &mut R, rng: &mut R,
) -> HashMap<u16, ThresholdCore<C>> { ) -> HashMap<Participant, ThresholdCore<C>> {
let (mut machines, _, secret_shares) = commit_enc_keys_and_shares::<_, C>(rng); let (mut machines, _, secret_shares) = commit_enc_keys_and_shares::<_, C>(rng);
let mut verification_shares = None; let mut verification_shares = None;
@@ -122,16 +122,19 @@ mod literal {
use super::*; use super::*;
const ONE: Participant = Participant(1);
const TWO: Participant = Participant(2);
fn test_blame( fn test_blame(
machines: Vec<BlameMachine<Ristretto>>, machines: Vec<BlameMachine<Ristretto>>,
msg: FrostEncryptedMessage<Ristretto>, msg: FrostEncryptedMessage<Ristretto>,
blame: Option<EncryptionKeyProof<Ristretto>>, blame: Option<EncryptionKeyProof<Ristretto>>,
) { ) {
for machine in machines { for machine in machines {
let (additional, blamed) = machine.blame(1, 2, msg.clone(), blame.clone()); let (additional, blamed) = machine.blame(ONE, TWO, msg.clone(), blame.clone());
assert_eq!(blamed, 1); assert_eq!(blamed, ONE);
// Verify additional blame also works // Verify additional blame also works
assert_eq!(additional.blame(1, 2, msg.clone(), blame.clone()), 1); assert_eq!(additional.blame(ONE, TWO, msg.clone(), blame.clone()), ONE);
} }
} }
@@ -142,7 +145,7 @@ mod literal {
commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng); commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng);
// Mutate the PoP of the encrypted message from 1 to 2 // Mutate the PoP of the encrypted message from 1 to 2
secret_shares.get_mut(&1).unwrap().get_mut(&2).unwrap().invalidate_pop(); secret_shares.get_mut(&ONE).unwrap().get_mut(&TWO).unwrap().invalidate_pop();
let mut blame = None; let mut blame = None;
let machines = machines let machines = machines
@@ -150,8 +153,8 @@ mod literal {
.filter_map(|(i, machine)| { .filter_map(|(i, machine)| {
let our_secret_shares = generate_secret_shares(&secret_shares, i); let our_secret_shares = generate_secret_shares(&secret_shares, i);
let machine = machine.calculate_share(&mut OsRng, our_secret_shares); let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
if i == 2 { if i == TWO {
assert_eq!(machine.err(), Some(DkgError::InvalidShare { participant: 1, blame: None })); assert_eq!(machine.err(), Some(DkgError::InvalidShare { participant: ONE, blame: None }));
// Explicitly declare we have a blame object, which happens to be None since invalid PoP // Explicitly declare we have a blame object, which happens to be None since invalid PoP
// is self-explainable // is self-explainable
blame = Some(None); blame = Some(None);
@@ -162,7 +165,7 @@ mod literal {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
test_blame(machines, secret_shares[&1][&2].clone(), blame.unwrap()); test_blame(machines, secret_shares[&ONE][&TWO].clone(), blame.unwrap());
} }
#[test] #[test]
@@ -176,7 +179,7 @@ mod literal {
// We then malleate 1's blame proof, so 1 ends up malicious // We then malleate 1's blame proof, so 1 ends up malicious
// Doesn't simply invalidate the PoP as that won't have a blame statement // Doesn't simply invalidate the PoP as that won't have a blame statement
// By mutating the encrypted data, we do ensure a blame statement is created // By mutating the encrypted data, we do ensure a blame statement is created
secret_shares.get_mut(&2).unwrap().get_mut(&1).unwrap().invalidate_msg(&mut OsRng, 2); secret_shares.get_mut(&TWO).unwrap().get_mut(&ONE).unwrap().invalidate_msg(&mut OsRng, TWO);
let mut blame = None; let mut blame = None;
let machines = machines let machines = machines
@@ -184,9 +187,9 @@ mod literal {
.filter_map(|(i, machine)| { .filter_map(|(i, machine)| {
let our_secret_shares = generate_secret_shares(&secret_shares, i); let our_secret_shares = generate_secret_shares(&secret_shares, i);
let machine = machine.calculate_share(&mut OsRng, our_secret_shares); let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
if i == 1 { if i == ONE {
blame = Some(match machine.err() { blame = Some(match machine.err() {
Some(DkgError::InvalidShare { participant: 2, blame: Some(blame) }) => Some(blame), Some(DkgError::InvalidShare { participant: TWO, blame: Some(blame) }) => Some(blame),
_ => panic!(), _ => panic!(),
}); });
None None
@@ -197,7 +200,7 @@ mod literal {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
blame.as_mut().unwrap().as_mut().unwrap().invalidate_key(); blame.as_mut().unwrap().as_mut().unwrap().invalidate_key();
test_blame(machines, secret_shares[&2][&1].clone(), blame.unwrap()); test_blame(machines, secret_shares[&TWO][&ONE].clone(), blame.unwrap());
} }
// This should be largely equivalent to the prior test // This should be largely equivalent to the prior test
@@ -206,7 +209,7 @@ mod literal {
let (mut machines, _, mut secret_shares) = let (mut machines, _, mut secret_shares) =
commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng); commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng);
secret_shares.get_mut(&2).unwrap().get_mut(&1).unwrap().invalidate_msg(&mut OsRng, 2); secret_shares.get_mut(&TWO).unwrap().get_mut(&ONE).unwrap().invalidate_msg(&mut OsRng, TWO);
let mut blame = None; let mut blame = None;
let machines = machines let machines = machines
@@ -214,9 +217,9 @@ mod literal {
.filter_map(|(i, machine)| { .filter_map(|(i, machine)| {
let our_secret_shares = generate_secret_shares(&secret_shares, i); let our_secret_shares = generate_secret_shares(&secret_shares, i);
let machine = machine.calculate_share(&mut OsRng, our_secret_shares); let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
if i == 1 { if i == ONE {
blame = Some(match machine.err() { blame = Some(match machine.err() {
Some(DkgError::InvalidShare { participant: 2, blame: Some(blame) }) => Some(blame), Some(DkgError::InvalidShare { participant: TWO, blame: Some(blame) }) => Some(blame),
_ => panic!(), _ => panic!(),
}); });
None None
@@ -227,7 +230,7 @@ mod literal {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
blame.as_mut().unwrap().as_mut().unwrap().invalidate_dleq(); blame.as_mut().unwrap().as_mut().unwrap().invalidate_dleq();
test_blame(machines, secret_shares[&2][&1].clone(), blame.unwrap()); test_blame(machines, secret_shares[&TWO][&ONE].clone(), blame.unwrap());
} }
#[test] #[test]
@@ -235,11 +238,11 @@ mod literal {
let (mut machines, enc_keys, mut secret_shares) = let (mut machines, enc_keys, mut secret_shares) =
commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng); commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng);
secret_shares.get_mut(&1).unwrap().get_mut(&2).unwrap().invalidate_share_serialization( secret_shares.get_mut(&ONE).unwrap().get_mut(&TWO).unwrap().invalidate_share_serialization(
&mut OsRng, &mut OsRng,
b"FROST", b"FROST",
1, ONE,
enc_keys[&2], enc_keys[&TWO],
); );
let mut blame = None; let mut blame = None;
@@ -248,9 +251,9 @@ mod literal {
.filter_map(|(i, machine)| { .filter_map(|(i, machine)| {
let our_secret_shares = generate_secret_shares(&secret_shares, i); let our_secret_shares = generate_secret_shares(&secret_shares, i);
let machine = machine.calculate_share(&mut OsRng, our_secret_shares); let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
if i == 2 { if i == TWO {
blame = Some(match machine.err() { blame = Some(match machine.err() {
Some(DkgError::InvalidShare { participant: 1, blame: Some(blame) }) => Some(blame), Some(DkgError::InvalidShare { participant: ONE, blame: Some(blame) }) => Some(blame),
_ => panic!(), _ => panic!(),
}); });
None None
@@ -260,7 +263,7 @@ mod literal {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
test_blame(machines, secret_shares[&1][&2].clone(), blame.unwrap()); test_blame(machines, secret_shares[&ONE][&TWO].clone(), blame.unwrap());
} }
#[test] #[test]
@@ -268,11 +271,11 @@ mod literal {
let (mut machines, enc_keys, mut secret_shares) = let (mut machines, enc_keys, mut secret_shares) =
commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng); commit_enc_keys_and_shares::<_, Ristretto>(&mut OsRng);
secret_shares.get_mut(&1).unwrap().get_mut(&2).unwrap().invalidate_share_value( secret_shares.get_mut(&ONE).unwrap().get_mut(&TWO).unwrap().invalidate_share_value(
&mut OsRng, &mut OsRng,
b"FROST", b"FROST",
1, ONE,
enc_keys[&2], enc_keys[&TWO],
); );
let mut blame = None; let mut blame = None;
@@ -281,9 +284,9 @@ mod literal {
.filter_map(|(i, machine)| { .filter_map(|(i, machine)| {
let our_secret_shares = generate_secret_shares(&secret_shares, i); let our_secret_shares = generate_secret_shares(&secret_shares, i);
let machine = machine.calculate_share(&mut OsRng, our_secret_shares); let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
if i == 2 { if i == TWO {
blame = Some(match machine.err() { blame = Some(match machine.err() {
Some(DkgError::InvalidShare { participant: 1, blame: Some(blame) }) => Some(blame), Some(DkgError::InvalidShare { participant: ONE, blame: Some(blame) }) => Some(blame),
_ => panic!(), _ => panic!(),
}); });
None None
@@ -293,6 +296,6 @@ mod literal {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
test_blame(machines, secret_shares[&1][&2].clone(), blame.unwrap()); test_blame(machines, secret_shares[&ONE][&TWO].clone(), blame.unwrap());
} }
} }

View File

@@ -7,7 +7,7 @@ use group::ff::Field;
use ciphersuite::Ciphersuite; use ciphersuite::Ciphersuite;
use crate::{ThresholdCore, ThresholdKeys, lagrange}; use crate::{Participant, ThresholdCore, ThresholdKeys, lagrange};
/// FROST generation test. /// FROST generation test.
pub mod frost; pub mod frost;
@@ -33,7 +33,7 @@ pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
} }
/// Recover the secret from a collection of keys. /// Recover the secret from a collection of keys.
pub fn recover_key<C: Ciphersuite>(keys: &HashMap<u16, ThresholdKeys<C>>) -> C::F { pub fn recover_key<C: Ciphersuite>(keys: &HashMap<Participant, ThresholdKeys<C>>) -> C::F {
let first = keys.values().next().expect("no keys provided"); let first = keys.values().next().expect("no keys provided");
assert!(keys.len() >= first.params().t().into(), "not enough keys provided"); assert!(keys.len() >= first.params().t().into(), "not enough keys provided");
let included = keys.keys().cloned().collect::<Vec<_>>(); let included = keys.keys().cloned().collect::<Vec<_>>();
@@ -48,7 +48,7 @@ pub fn recover_key<C: Ciphersuite>(keys: &HashMap<u16, ThresholdKeys<C>>) -> C::
/// Generate threshold keys for tests. /// Generate threshold keys for tests.
pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>( pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
rng: &mut R, rng: &mut R,
) -> HashMap<u16, ThresholdKeys<C>> { ) -> HashMap<Participant, ThresholdKeys<C>> {
let res = frost_gen(rng) let res = frost_gen(rng)
.drain() .drain()
.map(|(i, core)| { .map(|(i, core)| {
@@ -59,7 +59,7 @@ pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
(i, ThresholdKeys::new(core)) (i, ThresholdKeys::new(core))
}) })
.collect(); .collect();
assert_eq!(C::generator() * recover_key(&res), res[&1].group_key()); assert_eq!(C::generator() * recover_key(&res), res[&Participant(1)].group_key());
res res
} }

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ use multiexp::multiexp_vartime;
use dleq::MultiDLEqProof; 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. // Transcript used to aggregate binomial nonces for usage within a single DLEq proof.
fn aggregation_transcript<T: Transcript>(context: &[u8]) -> T { fn aggregation_transcript<T: Transcript>(context: &[u8]) -> T {
@@ -247,17 +247,17 @@ pub(crate) struct IndividualBinding<C: Curve> {
binding_factors: Option<Vec<C::F>>, 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> { 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 }); self.0.insert(i, IndividualBinding { commitments, binding_factors: None });
} }
pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) { pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) {
for (l, binding) in self.0.iter_mut() { for (l, binding) in self.0.iter_mut() {
let mut transcript = transcript.clone(); 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 // 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 // This generates a binding factor per nonce just to ensure it never comes up as a question
binding.binding_factors = Some( 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() self.0[&i].binding_factors.as_ref().unwrap()
} }
// Get the bound nonces for a specific party // 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![]; let mut res = vec![];
for (i, (nonce, rho)) in for (i, (nonce, rho)) in
self.0[&l].commitments.nonces.iter().zip(self.binding_factors(l).iter()).enumerate() self.0[&l].commitments.nonces.iter().zip(self.binding_factors(l).iter()).enumerate()

View File

@@ -19,7 +19,7 @@ use multiexp::BatchVerifier;
use crate::{ use crate::{
curve::Curve, curve::Curve,
FrostError, ThresholdParams, ThresholdKeys, ThresholdView, Participant, FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
algorithm::{WriteAddendum, Addendum, Algorithm}, algorithm::{WriteAddendum, Addendum, Algorithm},
validate_map, validate_map,
}; };
@@ -239,7 +239,7 @@ pub trait SignMachine<S>: Sized {
/// become the signing set for this session. /// become the signing set for this session.
fn sign( fn sign(
self, self,
commitments: HashMap<u16, Self::Preprocess>, commitments: HashMap<Participant, Self::Preprocess>,
msg: &[u8], msg: &[u8],
) -> Result<(Self::SignatureMachine, Self::SignatureShare), FrostError>; ) -> Result<(Self::SignatureMachine, Self::SignatureShare), FrostError>;
} }
@@ -291,7 +291,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
fn sign( fn sign(
mut self, mut self,
mut preprocesses: HashMap<u16, Preprocess<C, A::Addendum>>, mut preprocesses: HashMap<Participant, Preprocess<C, A::Addendum>>,
msg: &[u8], msg: &[u8],
) -> Result<(Self::SignatureMachine, SignatureShare<C>), FrostError> { ) -> Result<(Self::SignatureMachine, SignatureShare<C>), FrostError> {
let multisig_params = self.params.multisig_params(); 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()) { if included.len() < usize::from(multisig_params.t()) {
Err(FrostError::InvalidSigningSet("not enough signers"))?; Err(FrostError::InvalidSigningSet("not enough signers"))?;
} }
// Invalid index
if included[0] == 0 {
Err(FrostError::InvalidParticipantIndex(included[0], multisig_params.n()))?;
}
// OOB index // OOB index
if included[included.len() - 1] > multisig_params.n() { if u16::from(included[included.len() - 1]) > multisig_params.n() {
Err(FrostError::InvalidParticipantIndex(included[included.len() - 1], multisig_params.n()))?; Err(FrostError::InvalidParticipant(multisig_params.n(), included[included.len() - 1]))?;
} }
// Same signer included multiple times // Same signer included multiple times
for i in 0 .. (included.len() - 1) { for i in 0 .. (included.len() - 1) {
if included[i] == included[i + 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(); let nonces = self.params.algorithm.nonces();
#[allow(non_snake_case)] #[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 // Parse the preprocesses
for l in &included { for l in &included {
@@ -341,7 +337,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
.params .params
.algorithm .algorithm
.transcript() .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() { if *l == self.params.keys.params().i() {
@@ -449,7 +445,7 @@ pub trait SignatureMachine<S> {
/// Complete signing. /// Complete signing.
/// Takes in everyone elses' shares. Returns the signature. /// 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. /// 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( fn complete(
self, self,
mut shares: HashMap<u16, SignatureShare<C>>, mut shares: HashMap<Participant, SignatureShare<C>>,
) -> Result<A::Signature, FrostError> { ) -> Result<A::Signature, FrostError> {
let params = self.params.multisig_params(); let params = self.params.multisig_params();
validate_map(&shares, self.view.included(), params.i())?; 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}; pub use dkg::tests::{key_gen, recover_key};
use crate::{ use crate::{
Curve, ThresholdKeys, Curve, Participant, ThresholdKeys,
algorithm::Algorithm, algorithm::Algorithm,
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine}, 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>>( pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
rng: &mut R, rng: &mut R,
algorithm: A, algorithm: A,
keys: &HashMap<u16, ThresholdKeys<C>>, keys: &HashMap<Participant, ThresholdKeys<C>>,
) -> HashMap<u16, AlgorithmMachine<C, A>> { ) -> HashMap<Participant, AlgorithmMachine<C, A>> {
let mut included = vec![]; let mut included = vec![];
while included.len() < usize::from(keys[&1].params().t()) { while included.len() < usize::from(keys[&Participant::new(1).unwrap()].params().t()) {
let n = u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap(); let n = Participant::new(
u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap(),
)
.unwrap();
if included.contains(&n) { if included.contains(&n) {
continue; continue;
} }
@@ -64,15 +67,15 @@ pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
pub(crate) fn commit_and_shares< pub(crate) fn commit_and_shares<
R: RngCore + CryptoRng, R: RngCore + CryptoRng,
M: PreprocessMachine, M: PreprocessMachine,
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>), F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
>( >(
rng: &mut R, rng: &mut R,
mut machines: HashMap<u16, M>, mut machines: HashMap<Participant, M>,
mut cache: F, mut cache: F,
msg: &[u8], msg: &[u8],
) -> ( ) -> (
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>, HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>, HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
) { ) {
let mut commitments = HashMap::new(); let mut commitments = HashMap::new();
let mut machines = machines let mut machines = machines
@@ -110,10 +113,10 @@ pub(crate) fn commit_and_shares<
fn sign_internal< fn sign_internal<
R: RngCore + CryptoRng, R: RngCore + CryptoRng,
M: PreprocessMachine, M: PreprocessMachine,
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>), F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
>( >(
rng: &mut R, rng: &mut R,
machines: HashMap<u16, M>, machines: HashMap<Participant, M>,
cache: F, cache: F,
msg: &[u8], msg: &[u8],
) -> M::Signature { ) -> M::Signature {
@@ -135,7 +138,7 @@ fn sign_internal<
/// caching. /// caching.
pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>( pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
rng: &mut R, rng: &mut R,
machines: HashMap<u16, M>, machines: HashMap<Participant, M>,
msg: &[u8], msg: &[u8],
) -> M::Signature { ) -> M::Signature {
sign_internal(rng, machines, |_, _| {}, msg) 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>( pub fn sign<R: RngCore + CryptoRng, M: PreprocessMachine>(
rng: &mut R, rng: &mut R,
params: <M::SignMachine as SignMachine<M::Signature>>::Params, params: <M::SignMachine as SignMachine<M::Signature>>::Params,
mut keys: HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::Keys>, mut keys: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
machines: HashMap<u16, M>, machines: HashMap<Participant, M>,
msg: &[u8], msg: &[u8],
) -> M::Signature { ) -> M::Signature {
sign_internal( sign_internal(

View File

@@ -13,7 +13,7 @@ use dkg::tests::key_gen;
use crate::{ use crate::{
curve::Curve, curve::Curve,
ThresholdCore, ThresholdKeys, FrostError, Participant, ThresholdCore, ThresholdKeys, FrostError,
algorithm::{Schnorr, Hram}, algorithm::{Schnorr, Hram},
sign::{ sign::{
Nonce, GeneratorCommitments, NonceCommitments, Commitments, Writable, Preprocess, SignMachine, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Writable, Preprocess, SignMachine,
@@ -30,7 +30,7 @@ pub struct Vectors {
pub shares: Vec<String>, pub shares: Vec<String>,
pub msg: String, pub msg: String,
pub included: Vec<u16>, pub included: Vec<Participant>,
pub nonces: Vec<[String; 2]>, pub nonces: Vec<[String; 2]>,
pub sig_shares: Vec<String>, 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"]) included: to_str(&value["round_one_outputs"]["participant_list"])
.split(',') .split(',')
.map(u16::from_str) .map(u16::from_str)
.collect::<Result<_, _>>() .collect::<Result<Vec<_>, _>>()
.unwrap(), .unwrap()
.iter()
.map(|i| Participant::new(*i).unwrap())
.collect(),
nonces: value["round_one_outputs"]["participants"] nonces: value["round_one_outputs"]["participants"]
.as_object() .as_object()
.unwrap() .unwrap()
@@ -80,7 +83,7 @@ impl From<serde_json::Value> for Vectors {
} }
// Load these vectors into ThresholdKeys using a custom serialization it'll deserialize // 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 let shares = vectors
.shares .shares
.iter() .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() { for i in 1 ..= u16::try_from(shares.len()).unwrap() {
// Manually re-implement the serialization for ThresholdCore to import this data // Manually re-implement the serialization for ThresholdCore to import this data
let mut serialized = vec![]; 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(C::ID);
serialized.extend(vectors.threshold.to_be_bytes()); serialized.extend(vectors.threshold.to_le_bytes());
serialized.extend(u16::try_from(shares.len()).unwrap().to_be_bytes()); serialized.extend(u16::try_from(shares.len()).unwrap().to_le_bytes());
serialized.extend(i.to_be_bytes()); serialized.extend(i.to_le_bytes());
serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref()); serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
for share in &verification_shares { for share in &verification_shares {
serialized.extend(share.to_bytes().as_ref()); 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(); let these_keys = ThresholdCore::<C>::deserialize::<&[u8]>(&mut serialized.as_ref()).unwrap();
assert_eq!(these_keys.params().t(), vectors.threshold); assert_eq!(these_keys.params().t(), vectors.threshold);
assert_eq!(usize::from(these_keys.params().n()), shares.len()); 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!(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); 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 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); let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
const MSG: &[u8] = b"Hello, World!"; const MSG: &[u8] = b"Hello, World!";
let sig = sign(&mut *rng, Schnorr::<C, H>::new(), keys.clone(), machines, MSG); 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 // Test blame on an invalid Schnorr signature share

View File

@@ -3,7 +3,7 @@ use std::{marker::Send, collections::HashMap};
use async_trait::async_trait; use async_trait::async_trait;
use thiserror::Error; use thiserror::Error;
use frost::{curve::Ciphersuite, FrostError}; use frost::{curve::Ciphersuite, Participant, FrostError};
mod coin; mod coin;
use coin::{CoinError, Coin}; use coin::{CoinError, Coin};
@@ -18,7 +18,7 @@ pub enum NetworkError {}
#[async_trait] #[async_trait]
pub trait Network: Send { pub trait Network: Send {
async fn round(&mut self, data: Vec<u8>) -> Result<HashMap<u16, Vec<u8>>, NetworkError>; async fn round(&mut self, data: Vec<u8>) -> Result<HashMap<Participant, Vec<u8>>, NetworkError>;
} }
#[derive(Clone, Error, Debug)] #[derive(Clone, Error, Debug)]

View File

@@ -7,6 +7,8 @@ use async_trait::async_trait;
use rand_core::OsRng; use rand_core::OsRng;
use frost::Participant;
use crate::{ use crate::{
NetworkError, Network, NetworkError, Network,
coin::{Coin, Monero}, coin::{Coin, Monero},
@@ -15,11 +17,11 @@ use crate::{
#[derive(Clone)] #[derive(Clone)]
struct LocalNetwork { struct LocalNetwork {
i: u16, i: Participant,
size: u16, size: u16,
round: usize, round: usize,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
rounds: Arc<RwLock<Vec<HashMap<u16, Vec<u8>>>>>, rounds: Arc<RwLock<Vec<HashMap<Participant, Vec<u8>>>>>,
} }
impl LocalNetwork { impl LocalNetwork {
@@ -27,7 +29,12 @@ impl LocalNetwork {
let rounds = Arc::new(RwLock::new(vec![])); let rounds = Arc::new(RwLock::new(vec![]));
let mut res = vec![]; let mut res = vec![];
for i in 1 ..= size { for i in 1 ..= size {
res.push(LocalNetwork { i, size, round: 0, rounds: rounds.clone() }); res.push(LocalNetwork {
i: Participant::new(i).unwrap(),
size,
round: 0,
rounds: rounds.clone(),
});
} }
res res
} }
@@ -35,7 +42,7 @@ impl LocalNetwork {
#[async_trait] #[async_trait]
impl Network for LocalNetwork { impl Network for LocalNetwork {
async fn round(&mut self, data: Vec<u8>) -> Result<HashMap<u16, Vec<u8>>, NetworkError> { async fn round(&mut self, data: Vec<u8>) -> Result<HashMap<Participant, Vec<u8>>, NetworkError> {
{ {
let mut rounds = self.rounds.write().unwrap(); let mut rounds = self.rounds.write().unwrap();
if rounds.len() == self.round { if rounds.len() == self.round {
@@ -64,14 +71,14 @@ async fn test_send<C: Coin + Clone>(coin: C, fee: C::Fee) {
let latest = coin.get_latest_block_number().await.unwrap(); let latest = coin.get_latest_block_number().await.unwrap();
let mut keys = frost::tests::key_gen::<_, C::Curve>(&mut OsRng); let mut keys = frost::tests::key_gen::<_, C::Curve>(&mut OsRng);
let threshold = keys[&1].params().t(); let threshold = keys[&Participant::new(1).unwrap()].params().t();
let mut networks = LocalNetwork::new(threshold); let mut networks = LocalNetwork::new(threshold);
let mut wallets = vec![]; let mut wallets = vec![];
for i in 1 ..= threshold { for i in 1 ..= threshold {
let mut wallet = Wallet::new(MemCoinDb::new(), coin.clone()); let mut wallet = Wallet::new(MemCoinDb::new(), coin.clone());
wallet.acknowledge_block(0, latest); wallet.acknowledge_block(0, latest);
wallet.add_keys(&WalletKeys::new(keys.remove(&i).unwrap(), 0)); wallet.add_keys(&WalletKeys::new(keys.remove(&Participant::new(i).unwrap()).unwrap(), 0));
wallets.push(wallet); wallets.push(wallet);
} }