Luke Parker
2022-11-10 22:35:09 -05:00
parent d714f2202d
commit 84de427d72
32 changed files with 313 additions and 278 deletions

View File

@@ -1,6 +1,7 @@
use core::{marker::PhantomData, fmt::Debug};
use std::io::{self, Read, Write};
use zeroize::Zeroizing;
use rand_core::{RngCore, CryptoRng};
use transcript::Transcript;
@@ -66,7 +67,7 @@ pub trait Algorithm<C: Curve>: Clone {
&mut self,
params: &ThresholdView<C>,
nonce_sums: &[Vec<C::G>],
nonces: &[C::F],
nonces: Vec<Zeroizing<C::F>>,
msg: &[u8],
) -> C::F;
@@ -161,12 +162,12 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
&mut self,
params: &ThresholdView<C>,
nonce_sums: &[Vec<C::G>],
nonces: &[C::F],
mut nonces: Vec<Zeroizing<C::F>>,
msg: &[u8],
) -> C::F {
let c = H::hram(&nonce_sums[0][0], &params.group_key(), msg);
self.c = Some(c);
SchnorrSignature::<C>::sign(params.secret_share(), nonces[0], c).s
SchnorrSignature::<C>::sign(params.secret_share(), nonces.swap_remove(0), c).s
}
#[must_use]

View File

@@ -1,8 +1,9 @@
use core::ops::Deref;
use std::io::{self, Read};
use rand_core::{RngCore, CryptoRng};
use zeroize::Zeroize;
use zeroize::{Zeroize, Zeroizing};
use subtle::ConstantTimeEq;
use digest::Digest;
@@ -67,26 +68,29 @@ pub trait Curve: Ciphersuite {
}
/// Securely generate a random nonce. H3 from the IETF draft.
fn random_nonce<R: RngCore + CryptoRng>(mut secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed);
fn random_nonce<R: RngCore + CryptoRng>(
secret: &Zeroizing<Self::F>,
rng: &mut R,
) -> Zeroizing<Self::F> {
let mut seed = Zeroizing::new(vec![0; 32]);
rng.fill_bytes(seed.as_mut());
let mut repr = secret.to_repr();
secret.zeroize();
let mut res;
while {
seed.extend(repr.as_ref());
res = <Self as Curve>::hash_to_F(b"nonce", &seed);
res = Zeroizing::new(<Self as Curve>::hash_to_F(b"nonce", seed.deref()));
res.ct_eq(&Self::F::zero()).into()
} {
seed = Zeroizing::new(vec![0; 32]);
rng.fill_bytes(&mut seed);
}
for i in repr.as_mut() {
i.zeroize();
}
seed.zeroize();
res
}

View File

@@ -8,6 +8,7 @@
// Each nonce remains of the form (d, e) and made into a proper nonce with d + (e * b)
// When multiple D, E pairs are provided, a DLEq proof is also provided to confirm their integrity
use core::ops::Deref;
use std::{
io::{self, Read, Write},
collections::HashMap,
@@ -15,7 +16,7 @@ use std::{
use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, ZeroizeOnDrop};
use zeroize::{Zeroize, Zeroizing};
use transcript::Transcript;
@@ -33,13 +34,7 @@ fn dleq_transcript<T: Transcript>() -> T {
// Each nonce is actually a pair of random scalars, notated as d, e under the FROST paper
// This is considered a single nonce as r = d + be
#[derive(Clone, Zeroize)]
pub(crate) struct Nonce<C: Curve>(pub(crate) [C::F; 2]);
impl<C: Curve> Drop for Nonce<C> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<C: Curve> ZeroizeOnDrop for Nonce<C> {}
pub(crate) struct Nonce<C: Curve>(pub(crate) [Zeroizing<C::F>; 2]);
// Commitments to a specific generator for this nonce
#[derive(Copy, Clone, PartialEq, Eq)]
@@ -70,16 +65,20 @@ pub(crate) struct NonceCommitments<C: Curve> {
impl<C: Curve> NonceCommitments<C> {
pub(crate) fn new<R: RngCore + CryptoRng, T: Transcript>(
rng: &mut R,
mut secret_share: C::F,
secret_share: &Zeroizing<C::F>,
generators: &[C::G],
) -> (Nonce<C>, NonceCommitments<C>) {
let nonce =
Nonce([C::random_nonce(secret_share, &mut *rng), C::random_nonce(secret_share, &mut *rng)]);
secret_share.zeroize();
let nonce = Nonce::<C>([
C::random_nonce(secret_share, &mut *rng),
C::random_nonce(secret_share, &mut *rng),
]);
let mut commitments = Vec::with_capacity(generators.len());
for generator in generators {
commitments.push(GeneratorCommitments([*generator * nonce.0[0], *generator * nonce.0[1]]));
commitments.push(GeneratorCommitments([
*generator * nonce.0[0].deref(),
*generator * nonce.0[1].deref(),
]));
}
let mut dleqs = None;
@@ -91,7 +90,7 @@ impl<C: Curve> NonceCommitments<C> {
// TODO: At least include a challenge from the existing transcript
DLEqProof::prove(&mut *rng, &mut dleq_transcript::<T>(), generators, nonce)
};
dleqs = Some([dleq(nonce.0[0]), dleq(nonce.0[1])]);
dleqs = Some([dleq(&nonce.0[0]), dleq(&nonce.0[1])]);
}
(nonce, NonceCommitments { generators: commitments, dleqs })
@@ -145,7 +144,7 @@ pub(crate) struct Commitments<C: Curve> {
impl<C: Curve> Commitments<C> {
pub(crate) fn new<R: RngCore + CryptoRng, T: Transcript>(
rng: &mut R,
secret_share: C::F,
secret_share: &Zeroizing<C::F>,
planned_nonces: &[Vec<C::G>],
) -> (Vec<Nonce<C>>, Commitments<C>) {
let mut nonces = vec![];

View File

@@ -1,4 +1,4 @@
use core::fmt;
use core::{ops::Deref, fmt::Debug};
use std::{
io::{self, Read, Write},
collections::HashMap,
@@ -6,7 +6,7 @@ use std::{
use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, ZeroizeOnDrop};
use zeroize::Zeroize;
use transcript::Transcript;
@@ -49,12 +49,6 @@ pub struct Params<C: Curve, A: Algorithm<C>> {
keys: ThresholdKeys<C>,
view: ThresholdView<C>,
}
impl<C: Curve, A: Algorithm<C>> Drop for Params<C, A> {
fn drop(&mut self) {
self.zeroize()
}
}
impl<C: Curve, A: Algorithm<C>> ZeroizeOnDrop for Params<C, A> {}
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
pub fn new(
@@ -122,7 +116,7 @@ pub trait PreprocessMachine {
/// Preprocess message for this machine.
type Preprocess: Clone + PartialEq + Writable;
/// Signature produced by this machine.
type Signature: Clone + PartialEq + fmt::Debug;
type Signature: Clone + PartialEq + Debug;
/// SignMachine this PreprocessMachine turns into.
type SignMachine: SignMachine<Self::Signature, Preprocess = Self::Preprocess>;
@@ -213,22 +207,13 @@ pub trait SignMachine<S> {
}
/// Next step of the state machine for the signing process.
#[derive(Zeroize)]
pub struct AlgorithmSignMachine<C: Curve, A: Algorithm<C>> {
params: Params<C, A>,
pub(crate) nonces: Vec<Nonce<C>>,
#[zeroize(skip)]
pub(crate) preprocess: Preprocess<C, A::Addendum>,
}
impl<C: Curve, A: Algorithm<C>> Zeroize for AlgorithmSignMachine<C, A> {
fn zeroize(&mut self) {
self.nonces.zeroize()
}
}
impl<C: Curve, A: Algorithm<C>> Drop for AlgorithmSignMachine<C, A> {
fn drop(&mut self) {
self.zeroize()
}
}
impl<C: Curve, A: Algorithm<C>> ZeroizeOnDrop for AlgorithmSignMachine<C, A> {}
impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachine<C, A> {
type Preprocess = Preprocess<C, A::Addendum>;
@@ -336,16 +321,19 @@ impl<C: Curve, A: Algorithm<C>> SignMachine<A::Signature> for AlgorithmSignMachi
let Rs = B.nonces(&nonces);
let our_binding_factors = B.binding_factors(multisig_params.i());
let mut nonces = self
let nonces = self
.nonces
.iter()
.drain(..)
.enumerate()
.map(|(n, nonces)| nonces.0[0] + (nonces.0[1] * our_binding_factors[n]))
.map(|(n, nonces)| {
let [base, mut actual] = nonces.0;
*actual *= our_binding_factors[n];
*actual += base.deref();
actual
})
.collect::<Vec<_>>();
self.nonces.zeroize();
let share = self.params.algorithm.sign_share(&self.params.view, &Rs, &nonces, msg);
nonces.zeroize();
let share = self.params.algorithm.sign_share(&self.params.view, &Rs, nonces, msg);
Ok((
AlgorithmSignatureMachine { params: self.params.clone(), B, Rs, share },

View File

@@ -1,7 +1,10 @@
use core::ops::Deref;
use std::collections::HashMap;
#[cfg(test)]
use std::str::FromStr;
use zeroize::Zeroizing;
use rand_core::{RngCore, CryptoRng};
use group::{ff::PrimeField, GroupEncoding};
@@ -103,7 +106,7 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Thresho
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);
assert_eq!(these_keys.secret_share(), 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);
keys.insert(i, ThresholdKeys::new(these_keys));
}
@@ -148,12 +151,15 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
let mut machines = machines
.drain(..)
.map(|(i, machine)| {
let nonces = [
C::read_F::<&[u8]>(&mut hex::decode(&vectors.nonces[c][0]).unwrap().as_ref()).unwrap(),
C::read_F::<&[u8]>(&mut hex::decode(&vectors.nonces[c][1]).unwrap().as_ref()).unwrap(),
];
let nonce = |i| {
Zeroizing::new(
C::read_F::<&[u8]>(&mut hex::decode(&vectors.nonces[c][i]).unwrap().as_ref()).unwrap(),
)
};
let nonces = [nonce(0), nonce(1)];
c += 1;
let these_commitments = [C::generator() * nonces[0], C::generator() * nonces[1]];
let these_commitments =
[C::generator() * nonces[0].deref(), C::generator() * nonces[1].deref()];
let machine = machine.unsafe_override_preprocess(
vec![Nonce(nonces)],
Preprocess {