mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
3.3.3 (cont) Add a dedicated Participant type
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>>,
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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())?;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user