mirror of
https://github.com/serai-dex/serai.git
synced 2025-03-12 09:26:51 +00:00
3.3.3 (cont) Add a dedicated Participant type
This commit is contained in:
parent
87dea5e455
commit
2d56d24d9c
18 changed files with 308 additions and 237 deletions
|
@ -23,7 +23,7 @@ use dleq::DLEqProof;
|
|||
use frost::{
|
||||
dkg::lagrange,
|
||||
curve::Ed25519,
|
||||
FrostError, ThresholdKeys, ThresholdView,
|
||||
Participant, FrostError, ThresholdKeys, ThresholdView,
|
||||
algorithm::{WriteAddendum, Algorithm},
|
||||
};
|
||||
|
||||
|
@ -146,8 +146,8 @@ pub(crate) fn add_key_image_share(
|
|||
image: &mut EdwardsPoint,
|
||||
generator: EdwardsPoint,
|
||||
offset: Scalar,
|
||||
included: &[u16],
|
||||
participant: u16,
|
||||
included: &[Participant],
|
||||
participant: Participant,
|
||||
share: EdwardsPoint,
|
||||
) {
|
||||
if image.is_identity() {
|
||||
|
@ -203,7 +203,7 @@ impl Algorithm<Ed25519> for ClsagMultisig {
|
|||
fn process_addendum(
|
||||
&mut self,
|
||||
view: &ThresholdView<Ed25519>,
|
||||
l: u16,
|
||||
l: Participant,
|
||||
addendum: ClsagAddendum,
|
||||
) -> Result<(), FrostError> {
|
||||
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"participant", l.to_be_bytes());
|
||||
self.transcript.append_message(b"participant", l.to_bytes());
|
||||
|
||||
addendum
|
||||
.dleq
|
||||
|
|
|
@ -24,7 +24,10 @@ use crate::{
|
|||
use crate::ringct::clsag::{ClsagDetails, ClsagMultisig};
|
||||
|
||||
#[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 AMOUNT: u64 = 1337;
|
||||
|
@ -93,7 +96,7 @@ fn clsag_multisig() {
|
|||
mask = random_scalar(&mut OsRng);
|
||||
amount = OsRng.next_u64();
|
||||
} else {
|
||||
dest = keys[&1].group_key().0;
|
||||
dest = keys[&Participant::new(1).unwrap()].group_key().0;
|
||||
mask = randomness;
|
||||
amount = AMOUNT;
|
||||
}
|
||||
|
@ -103,7 +106,7 @@ fn clsag_multisig() {
|
|||
let mask_sum = random_scalar(&mut OsRng);
|
||||
let algorithm = ClsagMultisig::new(
|
||||
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(
|
||||
ClsagInput::new(
|
||||
Commitment::new(randomness, AMOUNT),
|
||||
|
|
|
@ -14,7 +14,7 @@ use dalek_ff_group as dfg;
|
|||
use transcript::{Transcript, RecommendedTranscript};
|
||||
use frost::{
|
||||
curve::Ed25519,
|
||||
FrostError, ThresholdKeys,
|
||||
Participant, FrostError, ThresholdKeys,
|
||||
sign::{
|
||||
Writable, Preprocess, CachedPreprocess, SignatureShare, PreprocessMachine, SignMachine,
|
||||
SignatureMachine, AlgorithmMachine, AlgorithmSignMachine, AlgorithmSignatureMachine,
|
||||
|
@ -35,7 +35,7 @@ use crate::{
|
|||
/// FROST signing machine to produce a signed transaction.
|
||||
pub struct TransactionMachine {
|
||||
signable: SignableTransaction,
|
||||
i: u16,
|
||||
i: Participant,
|
||||
transcript: RecommendedTranscript,
|
||||
|
||||
decoys: Vec<Decoys>,
|
||||
|
@ -48,7 +48,7 @@ pub struct TransactionMachine {
|
|||
|
||||
pub struct TransactionSignMachine {
|
||||
signable: SignableTransaction,
|
||||
i: u16,
|
||||
i: Participant,
|
||||
transcript: RecommendedTranscript,
|
||||
|
||||
decoys: Vec<Decoys>,
|
||||
|
@ -236,7 +236,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||
|
||||
fn sign(
|
||||
mut self,
|
||||
mut commitments: HashMap<u16, Self::Preprocess>,
|
||||
mut commitments: HashMap<Participant, Self::Preprocess>,
|
||||
msg: &[u8],
|
||||
) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> {
|
||||
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
|
||||
// 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
|
||||
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 {
|
||||
self.our_preprocess[c].clone()
|
||||
|
@ -389,7 +389,7 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
|||
|
||||
fn complete(
|
||||
mut self,
|
||||
shares: HashMap<u16, Self::SignatureShare>,
|
||||
shares: HashMap<Participant, Self::SignatureShare>,
|
||||
) -> Result<Transaction, FrostError> {
|
||||
let mut tx = self.tx;
|
||||
match tx.rct_signatures.prunable {
|
||||
|
|
|
@ -131,6 +131,7 @@ macro_rules! test {
|
|||
#[cfg(feature = "multisig")]
|
||||
use frost::{
|
||||
curve::Ed25519,
|
||||
Participant,
|
||||
tests::{THRESHOLD, key_gen},
|
||||
};
|
||||
|
||||
|
@ -165,7 +166,7 @@ macro_rules! test {
|
|||
#[cfg(not(feature = "multisig"))]
|
||||
panic!("Multisig branch called without the multisig feature");
|
||||
#[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)));
|
||||
|
@ -211,7 +212,7 @@ macro_rules! test {
|
|||
#[cfg(feature = "multisig")]
|
||||
{
|
||||
let mut machines = HashMap::new();
|
||||
for i in 1 ..= THRESHOLD {
|
||||
for i in (1 ..= THRESHOLD).map(|i| Participant::new(i).unwrap()) {
|
||||
machines.insert(
|
||||
i,
|
||||
tx
|
||||
|
|
|
@ -26,7 +26,7 @@ use multiexp::BatchVerifier;
|
|||
use schnorr::SchnorrSignature;
|
||||
use dleq::DLEqProof;
|
||||
|
||||
use crate::ThresholdParams;
|
||||
use crate::{Participant, ThresholdParams};
|
||||
|
||||
pub trait ReadWrite: Sized {
|
||||
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>(
|
||||
rng: &mut R,
|
||||
dst: &'static [u8],
|
||||
from: u16,
|
||||
from: Participant,
|
||||
to: C::G,
|
||||
mut msg: Zeroizing<E>,
|
||||
) -> EncryptedMessage<C, E> {
|
||||
|
@ -192,7 +192,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
|
|||
}
|
||||
|
||||
#[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
|
||||
// This will cause all initial checks to pass, yet a decrypt to gibberish
|
||||
let key = Zeroizing::new(C::random_nonzero_F(rng));
|
||||
|
@ -213,7 +213,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
|
|||
&mut self,
|
||||
rng: &mut R,
|
||||
dst: &'static [u8],
|
||||
from: u16,
|
||||
from: Participant,
|
||||
to: C::G,
|
||||
) {
|
||||
use group::ff::PrimeField;
|
||||
|
@ -237,7 +237,7 @@ impl<C: Ciphersuite, E: Encryptable> EncryptedMessage<C, E> {
|
|||
&mut self,
|
||||
rng: &mut R,
|
||||
dst: &'static [u8],
|
||||
from: u16,
|
||||
from: Participant,
|
||||
to: C::G,
|
||||
) {
|
||||
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 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.
|
||||
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");
|
||||
transcript.append_message(b"nonce", nonce.to_bytes());
|
||||
transcript.append_message(b"key", key.to_bytes());
|
||||
// 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
|
||||
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
|
||||
|
@ -322,10 +322,10 @@ pub(crate) enum DecryptionError {
|
|||
#[derive(Clone)]
|
||||
pub(crate) struct Encryption<C: Ciphersuite> {
|
||||
dst: &'static [u8],
|
||||
i: u16,
|
||||
i: Participant,
|
||||
enc_key: Zeroizing<C::F>,
|
||||
enc_pub_key: C::G,
|
||||
enc_keys: HashMap<u16, C::G>,
|
||||
enc_keys: HashMap<Participant, C::G>,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> Zeroize for Encryption<C> {
|
||||
|
@ -339,7 +339,11 @@ impl<C: Ciphersuite> Zeroize for 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));
|
||||
Self {
|
||||
dst,
|
||||
|
@ -356,7 +360,7 @@ impl<C: Ciphersuite> Encryption<C> {
|
|||
|
||||
pub(crate) fn register<M: Message>(
|
||||
&mut self,
|
||||
participant: u16,
|
||||
participant: Participant,
|
||||
msg: EncryptionKeyMessage<C, M>,
|
||||
) -> M {
|
||||
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>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
participant: u16,
|
||||
participant: Participant,
|
||||
msg: Zeroizing<E>,
|
||||
) -> EncryptedMessage<C, E> {
|
||||
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
|
||||
// which failed, and therefore to use None for the blame
|
||||
batch_id: I,
|
||||
from: u16,
|
||||
from: Participant,
|
||||
mut msg: EncryptedMessage<C, E>,
|
||||
) -> (Zeroizing<E>, EncryptionKeyProof<C>) {
|
||||
msg.pop.batch_verify(
|
||||
|
@ -413,8 +417,8 @@ impl<C: Ciphersuite> Encryption<C> {
|
|||
// Returns None if the key was wrong.
|
||||
pub(crate) fn decrypt_with_proof<E: Encryptable>(
|
||||
&self,
|
||||
from: u16,
|
||||
decryptor: u16,
|
||||
from: Participant,
|
||||
decryptor: Participant,
|
||||
mut msg: EncryptedMessage<C, E>,
|
||||
// There's no encryption key proof if the accusation is of an invalid signature
|
||||
proof: Option<EncryptionKeyProof<C>>,
|
||||
|
|
|
@ -24,7 +24,7 @@ use multiexp::{multiexp_vartime, BatchVerifier};
|
|||
use schnorr::SchnorrSignature;
|
||||
|
||||
use crate::{
|
||||
DkgError, ThresholdParams, ThresholdCore, validate_map,
|
||||
Participant, DkgError, ThresholdParams, ThresholdCore, validate_map,
|
||||
encryption::{
|
||||
ReadWrite, EncryptionKeyMessage, EncryptedMessage, Encryption, EncryptionKeyProof,
|
||||
DecryptionError,
|
||||
|
@ -34,11 +34,11 @@ use crate::{
|
|||
type FrostError<C> = DkgError<EncryptionKeyProof<C>>;
|
||||
|
||||
#[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");
|
||||
transcript.domain_separate(b"schnorr_proof_of_knowledge");
|
||||
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"commitments", Am);
|
||||
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> {
|
||||
assert!(l != 0, "attempting to evaluate a polynomial with 0");
|
||||
let l = F::from(u64::from(l));
|
||||
fn polynomial<F: PrimeField + Zeroize>(
|
||||
coefficients: &[Zeroizing<F>],
|
||||
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());
|
||||
for (idx, coefficient) in coefficients.iter().rev().enumerate() {
|
||||
*share += coefficient.deref();
|
||||
|
@ -231,11 +235,15 @@ impl<C: Ciphersuite> SecretShareMachine<C> {
|
|||
fn verify_r1<R: RngCore + CryptoRng>(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
mut commitments: HashMap<u16, EncryptionKeyMessage<C, Commitments<C>>>,
|
||||
) -> Result<HashMap<u16, Vec<C::G>>, FrostError<C>> {
|
||||
validate_map(&commitments, &(1 ..= self.params.n()).collect::<Vec<_>>(), self.params.i())?;
|
||||
mut commitments: HashMap<Participant, EncryptionKeyMessage<C, Commitments<C>>>,
|
||||
) -> Result<HashMap<Participant, Vec<C::G>>, FrostError<C>> {
|
||||
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
|
||||
.drain()
|
||||
.map(|(l, msg)| {
|
||||
|
@ -269,14 +277,16 @@ impl<C: Ciphersuite> SecretShareMachine<C> {
|
|||
pub fn generate_secret_shares<R: RngCore + CryptoRng>(
|
||||
mut self,
|
||||
rng: &mut R,
|
||||
commitments: HashMap<u16, EncryptionKeyMessage<C, Commitments<C>>>,
|
||||
) -> Result<(KeyMachine<C>, HashMap<u16, EncryptedMessage<C, SecretShare<C::F>>>), FrostError<C>>
|
||||
{
|
||||
commitments: HashMap<Participant, EncryptionKeyMessage<C, Commitments<C>>>,
|
||||
) -> Result<
|
||||
(KeyMachine<C>, HashMap<Participant, EncryptedMessage<C, SecretShare<C::F>>>),
|
||||
FrostError<C>,
|
||||
> {
|
||||
let commitments = self.verify_r1(&mut *rng, commitments)?;
|
||||
|
||||
// Step 1: Generate secret shares for all other parties
|
||||
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
|
||||
// An app developer could accidentally send it. Best to keep this black boxed
|
||||
if l == self.params.i() {
|
||||
|
@ -308,7 +318,7 @@ impl<C: Ciphersuite> SecretShareMachine<C> {
|
|||
pub struct KeyMachine<C: Ciphersuite> {
|
||||
params: ThresholdParams,
|
||||
secret: Zeroizing<C::F>,
|
||||
commitments: HashMap<u16, Vec<C::G>>,
|
||||
commitments: HashMap<Participant, Vec<C::G>>,
|
||||
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
|
||||
// Initially used with the actual commitments to verify the secret share, later used with
|
||||
// stripes to generate the verification shares
|
||||
fn exponential<C: Ciphersuite>(i: u16, values: &[C::G]) -> Vec<(C::F, C::G)> {
|
||||
let i = C::F::from(i.into());
|
||||
fn exponential<C: Ciphersuite>(i: Participant, values: &[C::G]) -> Vec<(C::F, C::G)> {
|
||||
let i = C::F::from(u16::from(i).into());
|
||||
let mut res = Vec::with_capacity(values.len());
|
||||
(0 .. values.len()).fold(C::F::one(), |exp, 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>(
|
||||
target: u16,
|
||||
target: Participant,
|
||||
commitments: &[C::G],
|
||||
mut share: Zeroizing<C::F>,
|
||||
) -> Vec<(C::F, C::G)> {
|
||||
|
@ -359,8 +369,8 @@ fn share_verification_statements<C: Ciphersuite>(
|
|||
|
||||
#[derive(Clone, Copy, Hash, Debug, Zeroize)]
|
||||
enum BatchId {
|
||||
Decryption(u16),
|
||||
Share(u16),
|
||||
Decryption(Participant),
|
||||
Share(Participant),
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> KeyMachine<C> {
|
||||
|
@ -370,9 +380,13 @@ impl<C: Ciphersuite> KeyMachine<C> {
|
|||
pub fn calculate_share<R: RngCore + CryptoRng>(
|
||||
mut self,
|
||||
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>> {
|
||||
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 blames = HashMap::new();
|
||||
|
@ -412,7 +426,7 @@ impl<C: Ciphersuite> KeyMachine<C> {
|
|||
|
||||
// Calculate each user's verification share
|
||||
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(
|
||||
i,
|
||||
if i == self.params.i() {
|
||||
|
@ -438,7 +452,7 @@ impl<C: Ciphersuite> KeyMachine<C> {
|
|||
}
|
||||
|
||||
pub struct BlameMachine<C: Ciphersuite> {
|
||||
commitments: HashMap<u16, Vec<C::G>>,
|
||||
commitments: HashMap<Participant, Vec<C::G>>,
|
||||
encryption: Encryption<C>,
|
||||
result: ThresholdCore<C>,
|
||||
}
|
||||
|
@ -469,11 +483,11 @@ impl<C: Ciphersuite> BlameMachine<C> {
|
|||
|
||||
fn blame_internal(
|
||||
&self,
|
||||
sender: u16,
|
||||
recipient: u16,
|
||||
sender: Participant,
|
||||
recipient: Participant,
|
||||
msg: EncryptedMessage<C, SecretShare<C::F>>,
|
||||
proof: Option<EncryptionKeyProof<C>>,
|
||||
) -> u16 {
|
||||
) -> Participant {
|
||||
let share_bytes = match self.encryption.decrypt_with_proof(sender, recipient, msg, proof) {
|
||||
Ok(share_bytes) => share_bytes,
|
||||
// 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.
|
||||
pub fn blame(
|
||||
self,
|
||||
sender: u16,
|
||||
recipient: u16,
|
||||
sender: Participant,
|
||||
recipient: Participant,
|
||||
msg: EncryptedMessage<C, SecretShare<C::F>>,
|
||||
proof: Option<EncryptionKeyProof<C>>,
|
||||
) -> (AdditionalBlameMachine<C>, u16) {
|
||||
) -> (AdditionalBlameMachine<C>, Participant) {
|
||||
let faulty = self.blame_internal(sender, recipient, msg, proof);
|
||||
(AdditionalBlameMachine(self), faulty)
|
||||
}
|
||||
|
@ -543,11 +557,11 @@ impl<C: Ciphersuite> AdditionalBlameMachine<C> {
|
|||
/// over a single incident.
|
||||
pub fn blame(
|
||||
self,
|
||||
sender: u16,
|
||||
recipient: u16,
|
||||
sender: Participant,
|
||||
recipient: Participant,
|
||||
msg: EncryptedMessage<C, SecretShare<C::F>>,
|
||||
proof: Option<EncryptionKeyProof<C>>,
|
||||
) -> u16 {
|
||||
) -> Participant {
|
||||
self.0.blame_internal(sender, recipient, msg, proof)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//! provided.
|
||||
|
||||
use core::{
|
||||
fmt::{Debug, Formatter},
|
||||
fmt::{self, Debug},
|
||||
ops::Deref,
|
||||
};
|
||||
use std::{io::Read, sync::Arc, collections::HashMap};
|
||||
|
@ -36,29 +36,59 @@ pub mod promote;
|
|||
#[cfg(any(test, feature = "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.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Error)]
|
||||
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),
|
||||
#[error("invalid amount of required participants (max {1}, got {0})")]
|
||||
InvalidRequiredQuantity(u16, u16),
|
||||
#[error("invalid participant index (0 < index <= {0}, yet index is {1})")]
|
||||
InvalidParticipantIndex(u16, u16),
|
||||
#[error("invalid participant (0 < participant <= {0}, yet participant is {1})")]
|
||||
InvalidParticipant(u16, Participant),
|
||||
|
||||
#[error("invalid signing set")]
|
||||
InvalidSigningSet,
|
||||
#[error("invalid participant quantity (expected {0}, got {1})")]
|
||||
InvalidParticipantQuantity(usize, usize),
|
||||
#[error("duplicated participant index ({0})")]
|
||||
DuplicatedIndex(u16),
|
||||
#[error("duplicated participant ({0})")]
|
||||
DuplicatedParticipant(Participant),
|
||||
#[error("missing participant {0}")]
|
||||
MissingParticipant(u16),
|
||||
MissingParticipant(Participant),
|
||||
|
||||
#[error("invalid proof of knowledge (participant {0})")]
|
||||
InvalidProofOfKnowledge(u16),
|
||||
InvalidProofOfKnowledge(Participant),
|
||||
#[error("invalid share (participant {participant}, blame {blame})")]
|
||||
InvalidShare { participant: u16, blame: Option<B> },
|
||||
InvalidShare { participant: Participant, blame: Option<B> },
|
||||
|
||||
#[error("internal error ({0})")]
|
||||
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
|
||||
pub(crate) fn validate_map<T, B: Clone + PartialEq + Eq + Debug>(
|
||||
map: &HashMap<u16, T>,
|
||||
included: &[u16],
|
||||
ours: u16,
|
||||
map: &HashMap<Participant, T>,
|
||||
included: &[Participant],
|
||||
ours: Participant,
|
||||
) -> Result<(), DkgError<B>> {
|
||||
if (map.len() + 1) != included.len() {
|
||||
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 {
|
||||
if *included == ours {
|
||||
if map.contains_key(included) {
|
||||
Err(DkgError::DuplicatedIndex(*included))?;
|
||||
Err(DkgError::DuplicatedParticipant(*included))?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -99,11 +129,11 @@ pub struct ThresholdParams {
|
|||
/// Amount of participants.
|
||||
n: u16,
|
||||
/// Index of the participant being acted for.
|
||||
i: u16,
|
||||
i: Participant,
|
||||
}
|
||||
|
||||
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) {
|
||||
Err(DkgError::ZeroParameter(t, n))?;
|
||||
}
|
||||
|
@ -113,8 +143,8 @@ impl ThresholdParams {
|
|||
if t > n {
|
||||
Err(DkgError::InvalidRequiredQuantity(t, n))?;
|
||||
}
|
||||
if (i == 0) || (i > n) {
|
||||
Err(DkgError::InvalidParticipantIndex(n, i))?;
|
||||
if u16::from(i) > n {
|
||||
Err(DkgError::InvalidParticipant(n, i))?;
|
||||
}
|
||||
|
||||
Ok(ThresholdParams { t, n, i })
|
||||
|
@ -126,13 +156,15 @@ impl ThresholdParams {
|
|||
pub fn n(&self) -> u16 {
|
||||
self.n
|
||||
}
|
||||
pub fn i(&self) -> u16 {
|
||||
pub fn i(&self) -> Participant {
|
||||
self.i
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 denom = F::one();
|
||||
for l in included {
|
||||
|
@ -140,9 +172,9 @@ pub fn lagrange<F: PrimeField>(i: u16, included: &[u16]) -> F {
|
|||
continue;
|
||||
}
|
||||
|
||||
let share = F::from(u64::from(*l));
|
||||
let share = F::from(u64::from(u16::from(*l)));
|
||||
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
|
||||
|
@ -162,11 +194,11 @@ pub struct ThresholdCore<C: Ciphersuite> {
|
|||
/// Group key.
|
||||
group_key: C::G,
|
||||
/// Verification shares.
|
||||
verification_shares: HashMap<u16, C::G>,
|
||||
verification_shares: HashMap<Participant, C::G>,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> Debug for ThresholdCore<C> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
impl<C: Ciphersuite> fmt::Debug for ThresholdCore<C> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt
|
||||
.debug_struct("ThresholdCore")
|
||||
.field("params", &self.params)
|
||||
|
@ -191,16 +223,9 @@ impl<C: Ciphersuite> ThresholdCore<C> {
|
|||
pub(crate) fn new(
|
||||
params: ThresholdParams,
|
||||
secret_share: Zeroizing<C::F>,
|
||||
verification_shares: HashMap<u16, C::G>,
|
||||
verification_shares: HashMap<Participant, C::G>,
|
||||
) -> ThresholdCore<C> {
|
||||
debug_assert!(validate_map::<_, ()>(
|
||||
&verification_shares,
|
||||
&(0 ..= params.n).collect::<Vec<_>>(),
|
||||
0
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
let t = (1 ..= params.t).collect::<Vec<_>>();
|
||||
let t = (1 ..= params.t).map(Participant).collect::<Vec<_>>();
|
||||
ThresholdCore {
|
||||
params,
|
||||
secret_share,
|
||||
|
@ -220,19 +245,19 @@ impl<C: Ciphersuite> ThresholdCore<C> {
|
|||
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()
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
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(self.params.t.to_be_bytes());
|
||||
serialized.extend(self.params.n.to_be_bytes());
|
||||
serialized.extend(self.params.i.to_be_bytes());
|
||||
serialized.extend(self.params.t.to_le_bytes());
|
||||
serialized.extend(self.params.n.to_le_bytes());
|
||||
serialized.extend(self.params.i.to_bytes());
|
||||
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
|
||||
|
@ -245,7 +270,7 @@ impl<C: Ciphersuite> ThresholdCore<C> {
|
|||
|
||||
let mut id_len = [0; 4];
|
||||
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())?;
|
||||
}
|
||||
|
||||
|
@ -262,9 +287,14 @@ impl<C: Ciphersuite> ThresholdCore<C> {
|
|||
reader
|
||||
.read_exact(&mut value)
|
||||
.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(
|
||||
|
@ -272,7 +302,7 @@ impl<C: Ciphersuite> ThresholdCore<C> {
|
|||
);
|
||||
|
||||
let mut verification_shares = HashMap::new();
|
||||
for l in 1 ..= n {
|
||||
for l in (1 ..= n).map(Participant) {
|
||||
verification_shares.insert(
|
||||
l,
|
||||
<C as Ciphersuite>::read_G(reader)
|
||||
|
@ -306,10 +336,10 @@ pub struct ThresholdKeys<C: Ciphersuite> {
|
|||
pub struct ThresholdView<C: Ciphersuite> {
|
||||
offset: C::F,
|
||||
group_key: C::G,
|
||||
included: Vec<u16>,
|
||||
included: Vec<Participant>,
|
||||
secret_share: Zeroizing<C::F>,
|
||||
original_verification_shares: HashMap<u16, C::G>,
|
||||
verification_shares: HashMap<u16, C::G>,
|
||||
original_verification_shares: HashMap<Participant, C::G>,
|
||||
verification_shares: HashMap<Participant, C::G>,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> Zeroize for ThresholdView<C> {
|
||||
|
@ -363,7 +393,7 @@ impl<C: Ciphersuite> ThresholdKeys<C> {
|
|||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
@ -371,7 +401,7 @@ impl<C: Ciphersuite> ThresholdKeys<C> {
|
|||
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())
|
||||
{
|
||||
Err(DkgError::InvalidSigningSet)?;
|
||||
|
@ -409,7 +439,7 @@ impl<C: Ciphersuite> ThresholdView<C> {
|
|||
self.group_key
|
||||
}
|
||||
|
||||
pub fn included(&self) -> &[u16] {
|
||||
pub fn included(&self) -> &[Participant] {
|
||||
&self.included
|
||||
}
|
||||
|
||||
|
@ -417,11 +447,11 @@ impl<C: Ciphersuite> ThresholdView<C> {
|
|||
&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]
|
||||
}
|
||||
|
||||
pub fn verification_share(&self, l: u16) -> C::G {
|
||||
pub fn verification_share(&self, l: Participant) -> C::G {
|
||||
self.verification_shares[&l]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use ciphersuite::Ciphersuite;
|
|||
use transcript::{Transcript, RecommendedTranscript};
|
||||
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.
|
||||
pub trait CiphersuitePromote<C2: Ciphersuite> {
|
||||
|
@ -27,10 +27,10 @@ pub trait CiphersuitePromote<C2: Ciphersuite> {
|
|||
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");
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -97,10 +97,10 @@ where
|
|||
/// Complete promotion by taking in the proofs from all other participants.
|
||||
pub fn complete(
|
||||
self,
|
||||
proofs: &HashMap<u16, GeneratorProof<C1>>,
|
||||
proofs: &HashMap<Participant, GeneratorProof<C1>>,
|
||||
) -> Result<ThresholdKeys<C2>, DkgError<()>> {
|
||||
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();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use crate::{
|
||||
Ciphersuite, ThresholdParams, ThresholdCore,
|
||||
Ciphersuite, Participant, ThresholdParams, ThresholdCore,
|
||||
frost::{KeyGenMachine, SecretShare, KeyMachine},
|
||||
encryption::{EncryptionKeyMessage, EncryptedMessage},
|
||||
tests::{THRESHOLD, PARTICIPANTS, clone_without},
|
||||
|
@ -11,31 +11,30 @@ use crate::{
|
|||
|
||||
// Needed so rustfmt doesn't fail to format on line length issues
|
||||
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
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>(
|
||||
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 commitments = HashMap::new();
|
||||
let mut enc_keys = HashMap::new();
|
||||
for i in 1 ..= PARTICIPANTS {
|
||||
let machine = KeyGenMachine::<C>::new(
|
||||
ThresholdParams::new(THRESHOLD, PARTICIPANTS, i).unwrap(),
|
||||
"DKG Test Key Generation".to_string(),
|
||||
);
|
||||
for i in (1 ..= PARTICIPANTS).map(Participant) {
|
||||
let params = ThresholdParams::new(THRESHOLD, PARTICIPANTS, i).unwrap();
|
||||
let machine = KeyGenMachine::<C>::new(params, "DKG Test Key Generation".to_string());
|
||||
let (machine, these_commitments) = machine.generate_coefficients(rng);
|
||||
machines.insert(i, machine);
|
||||
|
||||
commitments.insert(
|
||||
i,
|
||||
EncryptionKeyMessage::read::<&[u8]>(
|
||||
&mut these_commitments.serialize().as_ref(),
|
||||
ThresholdParams { t: THRESHOLD, n: PARTICIPANTS, i: 1 },
|
||||
)
|
||||
.unwrap(),
|
||||
EncryptionKeyMessage::read::<&[u8]>(&mut these_commitments.serialize().as_ref(), params)
|
||||
.unwrap(),
|
||||
);
|
||||
enc_keys.insert(i, commitments[&i].enc_key());
|
||||
}
|
||||
|
@ -53,7 +52,8 @@ fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>(
|
|||
l,
|
||||
EncryptedMessage::read::<&[u8]>(
|
||||
&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(),
|
||||
)
|
||||
|
@ -68,8 +68,8 @@ fn commit_enc_keys_and_shares<R: RngCore + CryptoRng, C: Ciphersuite>(
|
|||
}
|
||||
|
||||
fn generate_secret_shares<C: Ciphersuite>(
|
||||
shares: &HashMap<u16, FrostSecretShares<C>>,
|
||||
recipient: u16,
|
||||
shares: &HashMap<Participant, FrostSecretShares<C>>,
|
||||
recipient: Participant,
|
||||
) -> FrostSecretShares<C> {
|
||||
let mut our_secret_shares = HashMap::new();
|
||||
for (i, shares) in shares {
|
||||
|
@ -84,7 +84,7 @@ fn generate_secret_shares<C: Ciphersuite>(
|
|||
/// Fully perform the FROST key generation algorithm.
|
||||
pub fn frost_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
|
||||
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 verification_shares = None;
|
||||
|
@ -122,16 +122,19 @@ mod literal {
|
|||
|
||||
use super::*;
|
||||
|
||||
const ONE: Participant = Participant(1);
|
||||
const TWO: Participant = Participant(2);
|
||||
|
||||
fn test_blame(
|
||||
machines: Vec<BlameMachine<Ristretto>>,
|
||||
msg: FrostEncryptedMessage<Ristretto>,
|
||||
blame: Option<EncryptionKeyProof<Ristretto>>,
|
||||
) {
|
||||
for machine in machines {
|
||||
let (additional, blamed) = machine.blame(1, 2, msg.clone(), blame.clone());
|
||||
assert_eq!(blamed, 1);
|
||||
let (additional, blamed) = machine.blame(ONE, TWO, msg.clone(), blame.clone());
|
||||
assert_eq!(blamed, ONE);
|
||||
// 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);
|
||||
|
||||
// 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 machines = machines
|
||||
|
@ -150,8 +153,8 @@ mod literal {
|
|||
.filter_map(|(i, machine)| {
|
||||
let our_secret_shares = generate_secret_shares(&secret_shares, i);
|
||||
let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
|
||||
if i == 2 {
|
||||
assert_eq!(machine.err(), Some(DkgError::InvalidShare { participant: 1, blame: None }));
|
||||
if i == TWO {
|
||||
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
|
||||
// is self-explainable
|
||||
blame = Some(None);
|
||||
|
@ -162,7 +165,7 @@ mod literal {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
test_blame(machines, secret_shares[&1][&2].clone(), blame.unwrap());
|
||||
test_blame(machines, secret_shares[&ONE][&TWO].clone(), blame.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -176,7 +179,7 @@ mod literal {
|
|||
// 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
|
||||
// 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 machines = machines
|
||||
|
@ -184,9 +187,9 @@ mod literal {
|
|||
.filter_map(|(i, machine)| {
|
||||
let our_secret_shares = generate_secret_shares(&secret_shares, i);
|
||||
let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
|
||||
if i == 1 {
|
||||
if i == ONE {
|
||||
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!(),
|
||||
});
|
||||
None
|
||||
|
@ -197,7 +200,7 @@ mod literal {
|
|||
.collect::<Vec<_>>();
|
||||
|
||||
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
|
||||
|
@ -206,7 +209,7 @@ mod literal {
|
|||
let (mut machines, _, mut secret_shares) =
|
||||
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 machines = machines
|
||||
|
@ -214,9 +217,9 @@ mod literal {
|
|||
.filter_map(|(i, machine)| {
|
||||
let our_secret_shares = generate_secret_shares(&secret_shares, i);
|
||||
let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
|
||||
if i == 1 {
|
||||
if i == ONE {
|
||||
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!(),
|
||||
});
|
||||
None
|
||||
|
@ -227,7 +230,7 @@ mod literal {
|
|||
.collect::<Vec<_>>();
|
||||
|
||||
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]
|
||||
|
@ -235,11 +238,11 @@ mod literal {
|
|||
let (mut machines, enc_keys, mut secret_shares) =
|
||||
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,
|
||||
b"FROST",
|
||||
1,
|
||||
enc_keys[&2],
|
||||
ONE,
|
||||
enc_keys[&TWO],
|
||||
);
|
||||
|
||||
let mut blame = None;
|
||||
|
@ -248,9 +251,9 @@ mod literal {
|
|||
.filter_map(|(i, machine)| {
|
||||
let our_secret_shares = generate_secret_shares(&secret_shares, i);
|
||||
let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
|
||||
if i == 2 {
|
||||
if i == TWO {
|
||||
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!(),
|
||||
});
|
||||
None
|
||||
|
@ -260,7 +263,7 @@ mod literal {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
test_blame(machines, secret_shares[&1][&2].clone(), blame.unwrap());
|
||||
test_blame(machines, secret_shares[&ONE][&TWO].clone(), blame.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -268,11 +271,11 @@ mod literal {
|
|||
let (mut machines, enc_keys, mut secret_shares) =
|
||||
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,
|
||||
b"FROST",
|
||||
1,
|
||||
enc_keys[&2],
|
||||
ONE,
|
||||
enc_keys[&TWO],
|
||||
);
|
||||
|
||||
let mut blame = None;
|
||||
|
@ -281,9 +284,9 @@ mod literal {
|
|||
.filter_map(|(i, machine)| {
|
||||
let our_secret_shares = generate_secret_shares(&secret_shares, i);
|
||||
let machine = machine.calculate_share(&mut OsRng, our_secret_shares);
|
||||
if i == 2 {
|
||||
if i == TWO {
|
||||
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!(),
|
||||
});
|
||||
None
|
||||
|
@ -293,6 +296,6 @@ mod literal {
|
|||
})
|
||||
.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 crate::{ThresholdCore, ThresholdKeys, lagrange};
|
||||
use crate::{Participant, ThresholdCore, ThresholdKeys, lagrange};
|
||||
|
||||
/// FROST generation test.
|
||||
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.
|
||||
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");
|
||||
assert!(keys.len() >= first.params().t().into(), "not enough keys provided");
|
||||
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.
|
||||
pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
|
||||
rng: &mut R,
|
||||
) -> HashMap<u16, ThresholdKeys<C>> {
|
||||
) -> HashMap<Participant, ThresholdKeys<C>> {
|
||||
let res = frost_gen(rng)
|
||||
.drain()
|
||||
.map(|(i, core)| {
|
||||
|
@ -59,7 +59,7 @@ pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
|
|||
(i, ThresholdKeys::new(core))
|
||||
})
|
||||
.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
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use rand_core::{RngCore, CryptoRng};
|
|||
|
||||
use transcript::Transcript;
|
||||
|
||||
use crate::{Curve, FrostError, ThresholdKeys, ThresholdView};
|
||||
use crate::{Curve, Participant, FrostError, ThresholdKeys, ThresholdView};
|
||||
pub use schnorr::SchnorrSignature;
|
||||
|
||||
/// Write an addendum to a writer.
|
||||
|
@ -55,7 +55,7 @@ pub trait Algorithm<C: Curve>: Clone {
|
|||
fn process_addendum(
|
||||
&mut self,
|
||||
params: &ThresholdView<C>,
|
||||
l: u16,
|
||||
l: Participant,
|
||||
reader: Self::Addendum,
|
||||
) -> Result<(), FrostError>;
|
||||
|
||||
|
@ -161,7 +161,12 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn process_addendum(&mut self, _: &ThresholdView<C>, _: u16, _: ()) -> Result<(), FrostError> {
|
||||
fn process_addendum(
|
||||
&mut self,
|
||||
_: &ThresholdView<C>,
|
||||
_: Participant,
|
||||
_: (),
|
||||
) -> Result<(), FrostError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::collections::HashMap;
|
|||
use thiserror::Error;
|
||||
|
||||
/// Distributed key generation protocol.
|
||||
pub use dkg::{self, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView};
|
||||
pub use dkg::{self, Participant, ThresholdParams, ThresholdCore, ThresholdKeys, ThresholdView};
|
||||
|
||||
/// Curve trait and provided curves/HRAMs, forming various ciphersuites.
|
||||
pub mod curve;
|
||||
|
@ -38,21 +38,21 @@ pub mod tests;
|
|||
/// Various errors possible during signing.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
|
||||
pub enum FrostError {
|
||||
#[error("invalid participant index (0 < index <= {0}, yet index is {1})")]
|
||||
InvalidParticipantIndex(u16, u16),
|
||||
#[error("invalid participant (0 < participant <= {0}, yet participant is {1})")]
|
||||
InvalidParticipant(u16, Participant),
|
||||
#[error("invalid signing set ({0})")]
|
||||
InvalidSigningSet(&'static str),
|
||||
#[error("invalid participant quantity (expected {0}, got {1})")]
|
||||
InvalidParticipantQuantity(usize, usize),
|
||||
#[error("duplicated participant index ({0})")]
|
||||
DuplicatedIndex(u16),
|
||||
#[error("duplicated participant ({0})")]
|
||||
DuplicatedParticipant(Participant),
|
||||
#[error("missing participant {0}")]
|
||||
MissingParticipant(u16),
|
||||
MissingParticipant(Participant),
|
||||
|
||||
#[error("invalid preprocess (participant {0})")]
|
||||
InvalidPreprocess(u16),
|
||||
InvalidPreprocess(Participant),
|
||||
#[error("invalid share (participant {0})")]
|
||||
InvalidShare(u16),
|
||||
InvalidShare(Participant),
|
||||
|
||||
#[error("internal error ({0})")]
|
||||
InternalError(&'static str),
|
||||
|
@ -60,9 +60,9 @@ pub enum FrostError {
|
|||
|
||||
// Validate a map of values to have the expected included participants
|
||||
pub fn validate_map<T>(
|
||||
map: &HashMap<u16, T>,
|
||||
included: &[u16],
|
||||
ours: u16,
|
||||
map: &HashMap<Participant, T>,
|
||||
included: &[Participant],
|
||||
ours: Participant,
|
||||
) -> Result<(), FrostError> {
|
||||
if (map.len() + 1) != included.len() {
|
||||
Err(FrostError::InvalidParticipantQuantity(included.len(), map.len() + 1))?;
|
||||
|
@ -71,7 +71,7 @@ pub fn validate_map<T>(
|
|||
for included in included {
|
||||
if *included == ours {
|
||||
if map.contains_key(included) {
|
||||
Err(FrostError::DuplicatedIndex(*included))?;
|
||||
Err(FrostError::DuplicatedParticipant(*included))?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use multiexp::multiexp_vartime;
|
|||
|
||||
use dleq::MultiDLEqProof;
|
||||
|
||||
use crate::curve::Curve;
|
||||
use crate::{curve::Curve, Participant};
|
||||
|
||||
// Transcript used to aggregate binomial nonces for usage within a single DLEq proof.
|
||||
fn aggregation_transcript<T: Transcript>(context: &[u8]) -> T {
|
||||
|
@ -247,17 +247,17 @@ pub(crate) struct IndividualBinding<C: Curve> {
|
|||
binding_factors: Option<Vec<C::F>>,
|
||||
}
|
||||
|
||||
pub(crate) struct BindingFactor<C: Curve>(pub(crate) HashMap<u16, IndividualBinding<C>>);
|
||||
pub(crate) struct BindingFactor<C: Curve>(pub(crate) HashMap<Participant, IndividualBinding<C>>);
|
||||
|
||||
impl<C: Curve> BindingFactor<C> {
|
||||
pub(crate) fn insert(&mut self, i: u16, commitments: Commitments<C>) {
|
||||
pub(crate) fn insert(&mut self, i: Participant, commitments: Commitments<C>) {
|
||||
self.0.insert(i, IndividualBinding { commitments, binding_factors: None });
|
||||
}
|
||||
|
||||
pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) {
|
||||
for (l, binding) in self.0.iter_mut() {
|
||||
let mut transcript = transcript.clone();
|
||||
transcript.append_message(b"participant", C::F::from(u64::from(*l)).to_repr());
|
||||
transcript.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
|
||||
// It *should* be perfectly fine to reuse a binding factor for multiple nonces
|
||||
// This generates a binding factor per nonce just to ensure it never comes up as a question
|
||||
binding.binding_factors = Some(
|
||||
|
@ -268,12 +268,12 @@ impl<C: Curve> BindingFactor<C> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn binding_factors(&self, i: u16) -> &[C::F] {
|
||||
pub(crate) fn binding_factors(&self, i: Participant) -> &[C::F] {
|
||||
self.0[&i].binding_factors.as_ref().unwrap()
|
||||
}
|
||||
|
||||
// Get the bound nonces for a specific party
|
||||
pub(crate) fn bound(&self, l: u16) -> Vec<Vec<C::G>> {
|
||||
pub(crate) fn bound(&self, l: Participant) -> Vec<Vec<C::G>> {
|
||||
let mut res = vec![];
|
||||
for (i, (nonce, rho)) in
|
||||
self.0[&l].commitments.nonces.iter().zip(self.binding_factors(l).iter()).enumerate()
|
||||
|
|
|
@ -19,7 +19,7 @@ use multiexp::BatchVerifier;
|
|||
|
||||
use crate::{
|
||||
curve::Curve,
|
||||
FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
|
||||
Participant, FrostError, ThresholdParams, ThresholdKeys, ThresholdView,
|
||||
algorithm::{WriteAddendum, Addendum, Algorithm},
|
||||
validate_map,
|
||||
};
|
||||
|
@ -239,7 +239,7 @@ pub trait SignMachine<S>: Sized {
|
|||
/// become the signing set for this session.
|
||||
fn sign(
|
||||
self,
|
||||
commitments: HashMap<u16, Self::Preprocess>,
|
||||
commitments: HashMap<Participant, Self::Preprocess>,
|
||||
msg: &[u8],
|
||||
) -> Result<(Self::SignatureMachine, Self::SignatureShare), FrostError>;
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
|||
|
||||
fn sign(
|
||||
mut self,
|
||||
mut preprocesses: HashMap<u16, Preprocess<C, A::Addendum>>,
|
||||
mut preprocesses: HashMap<Participant, Preprocess<C, A::Addendum>>,
|
||||
msg: &[u8],
|
||||
) -> Result<(Self::SignatureMachine, SignatureShare<C>), FrostError> {
|
||||
let multisig_params = self.params.multisig_params();
|
||||
|
@ -307,18 +307,14 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
|||
if included.len() < usize::from(multisig_params.t()) {
|
||||
Err(FrostError::InvalidSigningSet("not enough signers"))?;
|
||||
}
|
||||
// Invalid index
|
||||
if included[0] == 0 {
|
||||
Err(FrostError::InvalidParticipantIndex(included[0], multisig_params.n()))?;
|
||||
}
|
||||
// OOB index
|
||||
if included[included.len() - 1] > multisig_params.n() {
|
||||
Err(FrostError::InvalidParticipantIndex(included[included.len() - 1], multisig_params.n()))?;
|
||||
if u16::from(included[included.len() - 1]) > multisig_params.n() {
|
||||
Err(FrostError::InvalidParticipant(multisig_params.n(), included[included.len() - 1]))?;
|
||||
}
|
||||
// Same signer included multiple times
|
||||
for i in 0 .. (included.len() - 1) {
|
||||
if included[i] == included[i + 1] {
|
||||
Err(FrostError::DuplicatedIndex(included[i]))?;
|
||||
Err(FrostError::DuplicatedParticipant(included[i]))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,7 +328,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
|||
|
||||
let nonces = self.params.algorithm.nonces();
|
||||
#[allow(non_snake_case)]
|
||||
let mut B = BindingFactor(HashMap::<u16, _>::with_capacity(included.len()));
|
||||
let mut B = BindingFactor(HashMap::<Participant, _>::with_capacity(included.len()));
|
||||
{
|
||||
// Parse the preprocesses
|
||||
for l in &included {
|
||||
|
@ -341,7 +337,7 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
|
|||
.params
|
||||
.algorithm
|
||||
.transcript()
|
||||
.append_message(b"participant", C::F::from(u64::from(*l)).to_repr());
|
||||
.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
|
||||
}
|
||||
|
||||
if *l == self.params.keys.params().i() {
|
||||
|
@ -449,7 +445,7 @@ pub trait SignatureMachine<S> {
|
|||
|
||||
/// Complete signing.
|
||||
/// Takes in everyone elses' shares. Returns the signature.
|
||||
fn complete(self, shares: HashMap<u16, Self::SignatureShare>) -> Result<S, FrostError>;
|
||||
fn complete(self, shares: HashMap<Participant, Self::SignatureShare>) -> Result<S, FrostError>;
|
||||
}
|
||||
|
||||
/// Final step of the state machine for the signing process.
|
||||
|
@ -472,7 +468,7 @@ impl<C: Curve, A: Algorithm<C>> SignatureMachine<A::Signature> for AlgorithmSign
|
|||
|
||||
fn complete(
|
||||
self,
|
||||
mut shares: HashMap<u16, SignatureShare<C>>,
|
||||
mut shares: HashMap<Participant, SignatureShare<C>>,
|
||||
) -> Result<A::Signature, FrostError> {
|
||||
let params = self.params.multisig_params();
|
||||
validate_map(&shares, self.view.included(), params.i())?;
|
||||
|
|
|
@ -5,7 +5,7 @@ use rand_core::{RngCore, CryptoRng};
|
|||
pub use dkg::tests::{key_gen, recover_key};
|
||||
|
||||
use crate::{
|
||||
Curve, ThresholdKeys,
|
||||
Curve, Participant, ThresholdKeys,
|
||||
algorithm::Algorithm,
|
||||
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine},
|
||||
};
|
||||
|
@ -36,11 +36,14 @@ pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
|
|||
pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
|
||||
rng: &mut R,
|
||||
algorithm: A,
|
||||
keys: &HashMap<u16, ThresholdKeys<C>>,
|
||||
) -> HashMap<u16, AlgorithmMachine<C, A>> {
|
||||
keys: &HashMap<Participant, ThresholdKeys<C>>,
|
||||
) -> HashMap<Participant, AlgorithmMachine<C, A>> {
|
||||
let mut included = vec![];
|
||||
while included.len() < usize::from(keys[&1].params().t()) {
|
||||
let n = u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap();
|
||||
while included.len() < usize::from(keys[&Participant::new(1).unwrap()].params().t()) {
|
||||
let n = Participant::new(
|
||||
u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
if included.contains(&n) {
|
||||
continue;
|
||||
}
|
||||
|
@ -64,15 +67,15 @@ pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
|
|||
pub(crate) fn commit_and_shares<
|
||||
R: RngCore + CryptoRng,
|
||||
M: PreprocessMachine,
|
||||
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>),
|
||||
F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
|
||||
>(
|
||||
rng: &mut R,
|
||||
mut machines: HashMap<u16, M>,
|
||||
mut machines: HashMap<Participant, M>,
|
||||
mut cache: F,
|
||||
msg: &[u8],
|
||||
) -> (
|
||||
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
|
||||
HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
|
||||
HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
|
||||
HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
|
||||
) {
|
||||
let mut commitments = HashMap::new();
|
||||
let mut machines = machines
|
||||
|
@ -110,10 +113,10 @@ pub(crate) fn commit_and_shares<
|
|||
fn sign_internal<
|
||||
R: RngCore + CryptoRng,
|
||||
M: PreprocessMachine,
|
||||
F: FnMut(&mut R, &mut HashMap<u16, M::SignMachine>),
|
||||
F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
|
||||
>(
|
||||
rng: &mut R,
|
||||
machines: HashMap<u16, M>,
|
||||
machines: HashMap<Participant, M>,
|
||||
cache: F,
|
||||
msg: &[u8],
|
||||
) -> M::Signature {
|
||||
|
@ -135,7 +138,7 @@ fn sign_internal<
|
|||
/// caching.
|
||||
pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
||||
rng: &mut R,
|
||||
machines: HashMap<u16, M>,
|
||||
machines: HashMap<Participant, M>,
|
||||
msg: &[u8],
|
||||
) -> M::Signature {
|
||||
sign_internal(rng, machines, |_, _| {}, msg)
|
||||
|
@ -146,8 +149,8 @@ pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
|||
pub fn sign<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
||||
rng: &mut R,
|
||||
params: <M::SignMachine as SignMachine<M::Signature>>::Params,
|
||||
mut keys: HashMap<u16, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
|
||||
machines: HashMap<u16, M>,
|
||||
mut keys: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
|
||||
machines: HashMap<Participant, M>,
|
||||
msg: &[u8],
|
||||
) -> M::Signature {
|
||||
sign_internal(
|
||||
|
|
|
@ -13,7 +13,7 @@ use dkg::tests::key_gen;
|
|||
|
||||
use crate::{
|
||||
curve::Curve,
|
||||
ThresholdCore, ThresholdKeys, FrostError,
|
||||
Participant, ThresholdCore, ThresholdKeys, FrostError,
|
||||
algorithm::{Schnorr, Hram},
|
||||
sign::{
|
||||
Nonce, GeneratorCommitments, NonceCommitments, Commitments, Writable, Preprocess, SignMachine,
|
||||
|
@ -30,7 +30,7 @@ pub struct Vectors {
|
|||
pub shares: Vec<String>,
|
||||
|
||||
pub msg: String,
|
||||
pub included: Vec<u16>,
|
||||
pub included: Vec<Participant>,
|
||||
pub nonces: Vec<[String; 2]>,
|
||||
|
||||
pub sig_shares: Vec<String>,
|
||||
|
@ -58,8 +58,11 @@ impl From<serde_json::Value> for Vectors {
|
|||
included: to_str(&value["round_one_outputs"]["participant_list"])
|
||||
.split(',')
|
||||
.map(u16::from_str)
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap(),
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|i| Participant::new(*i).unwrap())
|
||||
.collect(),
|
||||
nonces: value["round_one_outputs"]["participants"]
|
||||
.as_object()
|
||||
.unwrap()
|
||||
|
@ -80,7 +83,7 @@ impl From<serde_json::Value> for Vectors {
|
|||
}
|
||||
|
||||
// Load these vectors into ThresholdKeys using a custom serialization it'll deserialize
|
||||
fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, ThresholdKeys<C>> {
|
||||
fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<Participant, ThresholdKeys<C>> {
|
||||
let shares = vectors
|
||||
.shares
|
||||
.iter()
|
||||
|
@ -92,11 +95,11 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
|
|||
for i in 1 ..= u16::try_from(shares.len()).unwrap() {
|
||||
// Manually re-implement the serialization for ThresholdCore to import this data
|
||||
let mut serialized = vec![];
|
||||
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_be_bytes());
|
||||
serialized.extend(u32::try_from(C::ID.len()).unwrap().to_le_bytes());
|
||||
serialized.extend(C::ID);
|
||||
serialized.extend(vectors.threshold.to_be_bytes());
|
||||
serialized.extend(u16::try_from(shares.len()).unwrap().to_be_bytes());
|
||||
serialized.extend(i.to_be_bytes());
|
||||
serialized.extend(vectors.threshold.to_le_bytes());
|
||||
serialized.extend(u16::try_from(shares.len()).unwrap().to_le_bytes());
|
||||
serialized.extend(i.to_le_bytes());
|
||||
serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
|
||||
for share in &verification_shares {
|
||||
serialized.extend(share.to_bytes().as_ref());
|
||||
|
@ -105,10 +108,11 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
|
|||
let these_keys = ThresholdCore::<C>::deserialize::<&[u8]>(&mut serialized.as_ref()).unwrap();
|
||||
assert_eq!(these_keys.params().t(), vectors.threshold);
|
||||
assert_eq!(usize::from(these_keys.params().n()), shares.len());
|
||||
assert_eq!(these_keys.params().i(), i);
|
||||
let participant = Participant::new(i).unwrap();
|
||||
assert_eq!(these_keys.params().i(), participant);
|
||||
assert_eq!(these_keys.secret_share().deref(), &shares[usize::from(i - 1)]);
|
||||
assert_eq!(hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key);
|
||||
keys.insert(i, ThresholdKeys::new(these_keys));
|
||||
keys.insert(participant, ThresholdKeys::new(these_keys));
|
||||
}
|
||||
|
||||
keys
|
||||
|
@ -124,7 +128,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||
let machines = algorithm_machines(&mut *rng, Schnorr::<C, H>::new(), &keys);
|
||||
const MSG: &[u8] = b"Hello, World!";
|
||||
let sig = sign(&mut *rng, Schnorr::<C, H>::new(), keys.clone(), machines, MSG);
|
||||
assert!(sig.verify(keys[&1].group_key(), H::hram(&sig.R, &keys[&1].group_key(), MSG)));
|
||||
let group_key = keys[&Participant::new(1).unwrap()].group_key();
|
||||
assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG)));
|
||||
}
|
||||
|
||||
// Test blame on an invalid Schnorr signature share
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{marker::Send, collections::HashMap};
|
|||
use async_trait::async_trait;
|
||||
use thiserror::Error;
|
||||
|
||||
use frost::{curve::Ciphersuite, FrostError};
|
||||
use frost::{curve::Ciphersuite, Participant, FrostError};
|
||||
|
||||
mod coin;
|
||||
use coin::{CoinError, Coin};
|
||||
|
@ -18,7 +18,7 @@ pub enum NetworkError {}
|
|||
|
||||
#[async_trait]
|
||||
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)]
|
||||
|
|
|
@ -7,6 +7,8 @@ use async_trait::async_trait;
|
|||
|
||||
use rand_core::OsRng;
|
||||
|
||||
use frost::Participant;
|
||||
|
||||
use crate::{
|
||||
NetworkError, Network,
|
||||
coin::{Coin, Monero},
|
||||
|
@ -15,11 +17,11 @@ use crate::{
|
|||
|
||||
#[derive(Clone)]
|
||||
struct LocalNetwork {
|
||||
i: u16,
|
||||
i: Participant,
|
||||
size: u16,
|
||||
round: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
rounds: Arc<RwLock<Vec<HashMap<u16, Vec<u8>>>>>,
|
||||
rounds: Arc<RwLock<Vec<HashMap<Participant, Vec<u8>>>>>,
|
||||
}
|
||||
|
||||
impl LocalNetwork {
|
||||
|
@ -27,7 +29,12 @@ impl LocalNetwork {
|
|||
let rounds = Arc::new(RwLock::new(vec![]));
|
||||
let mut res = vec![];
|
||||
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
|
||||
}
|
||||
|
@ -35,7 +42,7 @@ impl LocalNetwork {
|
|||
|
||||
#[async_trait]
|
||||
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();
|
||||
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 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 wallets = vec![];
|
||||
for i in 1 ..= threshold {
|
||||
let mut wallet = Wallet::new(MemCoinDb::new(), coin.clone());
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue