Expand and correct documentation

This commit is contained in:
Luke Parker
2022-09-29 05:25:29 -04:00
parent 19cd609cba
commit ca091a5f04
18 changed files with 137 additions and 118 deletions

View File

@@ -167,7 +167,7 @@ macro_rules! from_uint {
};
}
/// Wrapper around the dalek Scalar type
/// Wrapper around the dalek Scalar type.
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
pub struct Scalar(pub DScalar);
deref_borrow!(Scalar, DScalar);
@@ -176,12 +176,12 @@ math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul);
from_uint!(Scalar, DScalar);
impl Scalar {
/// Perform wide reduction on a 64-byte array to create a Scalar without bias
/// Perform wide reduction on a 64-byte array to create a Scalar without bias.
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
Self(DScalar::from_bytes_mod_order_wide(bytes))
}
/// Derive a Scalar without bias from a digest via wide reduction
/// Derive a Scalar without bias from a digest via wide reduction.
pub fn from_hash<D: Digest<OutputSize = U64>>(hash: D) -> Scalar {
let mut output = [0u8; 64];
output.copy_from_slice(&hash.finalize());
@@ -287,7 +287,7 @@ macro_rules! dalek_group {
$BASEPOINT_POINT: ident,
$BASEPOINT_TABLE: ident
) => {
/// Wrapper around the dalek Point type. For Ed25519, this is restricted to the prime subgroup
/// Wrapper around the dalek Point type. For Ed25519, this is restricted to the prime subgroup.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct $Point(pub $DPoint);
deref_borrow!($Point, $DPoint);
@@ -355,7 +355,7 @@ macro_rules! dalek_group {
impl PrimeGroup for $Point {}
/// Wrapper around the dalek Table type, offering efficient multiplication against the
/// basepoint
/// basepoint.
pub struct $Table(pub $DTable);
deref_borrow!($Table, $DTable);
pub const $BASEPOINT_TABLE: $Table = $Table(constants::$BASEPOINT_TABLE);

View File

@@ -277,11 +277,11 @@ where
/// Prove the cross-Group Discrete Log Equality for the points derived from the scalar created as
/// the output of the passed in Digest. Given the non-standard requirements to achieve
/// uniformity, needing to be < 2^x instead of less than a prime moduli, this is the simplest way
/// to safely and securely generate a Scalar, without risk of failure, nor bias
/// to safely and securely generate a Scalar, without risk of failure, nor bias.
/// It also ensures a lack of determinable relation between keys, guaranteeing security in the
/// currently expected use case for this, atomic swaps, where each swap leaks the key. Knowing
/// the relationship between keys would allow breaking all swaps after just one
pub fn prove<R: RngCore + CryptoRng, T: Clone + Transcript, D: Digest>(
/// the relationship between keys would allow breaking all swaps after just one.
rng: &mut R,
transcript: &mut T,
generators: (Generators<G0>, Generators<G1>),
@@ -297,7 +297,7 @@ where
/// Prove the cross-Group Discrete Log Equality for the points derived from the scalar passed in,
/// failing if it's not mutually valid. This allows for rejection sampling externally derived
/// scalars until they're safely usable, as needed
/// scalars until they're safely usable, as needed.
pub fn prove_without_bias<R: RngCore + CryptoRng, T: Clone + Transcript>(
rng: &mut R,
transcript: &mut T,
@@ -307,7 +307,7 @@ where
scalar_convert(f0).map(|f1| Self::prove_internal(rng, transcript, generators, (f0, f1)))
}
/// Verify a cross-Group Discrete Log Equality statement, returning the points proven for
/// Verify a cross-Group Discrete Log Equality statement, returning the points proven for.
pub fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
&self,
rng: &mut R,

View File

@@ -2,7 +2,7 @@ use ff::PrimeFieldBits;
use zeroize::Zeroize;
/// Convert a uniform scalar into one usable on both fields, clearing the top bits as needed
/// Convert a uniform scalar into one usable on both fields, clearing the top bits as needed.
pub fn scalar_normalize<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
mut scalar: F0,
) -> (F0, F1) {
@@ -45,7 +45,7 @@ pub fn scalar_normalize<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
(res1, res2)
}
/// Helper to convert a scalar between fields. Returns None if the scalar isn't mutually valid
/// Helper to convert a scalar between fields. Returns None if the scalar isn't mutually valid.
pub fn scalar_convert<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
mut scalar: F0,
) -> Option<F1> {
@@ -56,7 +56,7 @@ pub fn scalar_convert<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
res
}
/// Create a mutually valid scalar from bytes via bit truncation to not introduce bias
/// Create a mutually valid scalar from bytes via bit truncation to not introduce bias.
pub fn mutual_scalar_from_bytes<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
bytes: &[u8],
) -> (F0, F1) {

View File

@@ -8,27 +8,27 @@ use transcript::Transcript;
use crate::{Curve, FrostError, FrostView, schnorr};
pub use schnorr::SchnorrSignature;
/// Algorithm to use FROST with
/// Algorithm to use FROST with.
pub trait Algorithm<C: Curve>: Clone {
type Transcript: Transcript + Clone + Debug;
/// The resulting type of the signatures this algorithm will produce
/// The resulting type of the signatures this algorithm will produce.
type Signature: Clone + PartialEq + Debug;
/// Obtain a mutable borrow of the underlying transcript
/// Obtain a mutable borrow of the underlying transcript.
fn transcript(&mut self) -> &mut Self::Transcript;
/// Obtain the list of nonces to generate, as specified by the basepoints to create commitments
/// against per-nonce. These are not committed to by FROST on the underlying transcript
/// Obtain the list of nonces to generate, as specified by the basepoints to create commitments.
/// against per-nonce. These are not committed to by FROST on the underlying transcript.
fn nonces(&self) -> Vec<Vec<C::G>>;
/// Generate an addendum to FROST"s preprocessing stage
/// Generate an addendum to FROST"s preprocessing stage.
fn preprocess_addendum<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R,
params: &FrostView<C>,
) -> Vec<u8>;
/// Proccess the addendum for the specified participant. Guaranteed to be ordered
/// Proccess the addendum for the specified participant. Guaranteed to be ordered.
fn process_addendum<Re: Read>(
&mut self,
params: &FrostView<C>,
@@ -36,10 +36,10 @@ pub trait Algorithm<C: Curve>: Clone {
reader: &mut Re,
) -> Result<(), FrostError>;
/// Sign a share with the given secret/nonce
/// Sign a share with the given secret/nonce.
/// The secret will already have been its lagrange coefficient applied so it is the necessary
/// key share
/// The nonce will already have been processed into the combined form d + (e * p)
/// key share.
/// The nonce will already have been processed into the combined form d + (e * p).
fn sign_share(
&mut self,
params: &FrostView<C>,
@@ -48,12 +48,12 @@ pub trait Algorithm<C: Curve>: Clone {
msg: &[u8],
) -> C::F;
/// Verify a signature
/// Verify a signature.
#[must_use]
fn verify(&self, group_key: C::G, nonces: &[Vec<C::G>], sum: C::F) -> Option<Self::Signature>;
/// Verify a specific share given as a response. Used to determine blame if signature
/// verification fails
/// verification fails.
#[must_use]
fn verify_share(&self, verification_share: C::G, nonces: &[Vec<C::G>], share: C::F) -> bool;
}
@@ -84,8 +84,8 @@ impl Transcript for IetfTranscript {
}
pub trait Hram<C: Curve>: Clone {
/// HRAM function to generate a challenge
/// H2 from the IETF draft despite having a different argument set (not pre-formatted)
/// HRAM function to generate a challenge.
/// H2 from the IETF draft, despite having a different argument set (not being pre-formatted).
#[allow(non_snake_case)]
fn hram(R: &C::G, A: &C::G, m: &[u8]) -> C::F;
}
@@ -109,7 +109,7 @@ impl<C: Curve, H: Hram<C>> Schnorr<C, H> {
}
}
/// Implementation of Schnorr signatures for use with FROST
/// Implementation of Schnorr signatures for use with FROST.
impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
type Transcript = IetfTranscript;
type Signature = SchnorrSignature<C>;

View File

@@ -29,7 +29,7 @@ mod ed448;
#[cfg(feature = "ed448")]
pub use ed448::{Ed448, Ietf8032Ed448Hram, NonIetfEd448Hram};
/// Set of errors for curve-related operations, namely encoding and decoding
/// Set of errors for curve-related operations, namely encoding and decoding.
#[derive(Clone, Error, Debug)]
pub enum CurveError {
#[error("invalid scalar")]
@@ -38,23 +38,23 @@ pub enum CurveError {
InvalidPoint,
}
/// Unified trait to manage a field/group
/// Unified trait to manage an elliptic curve.
// This should be moved into its own crate if the need for generic cryptography over ff/group
// continues, which is the exact reason ff/group exists (to provide a generic interface)
// elliptic-curve exists, yet it doesn't really serve the same role, nor does it use &[u8]/Vec<u8>
// It uses GenericArray which will hopefully be deprecated as Rust evolves and doesn't offer enough
// advantages in the modern day to be worth the hassle -- Kayaba
pub trait Curve: Clone + Copy + PartialEq + Eq + Debug + Zeroize {
/// Scalar field element type
/// Scalar field element type.
// This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses
type F: PrimeField + PrimeFieldBits + Zeroize;
/// Group element type
/// Group element type.
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize;
/// ID for this curve
/// ID for this curve.
const ID: &'static [u8];
/// Generator for the group
/// Generator for the group.
// While group does provide this in its API, privacy coins may want to use a custom basepoint
fn generator() -> Self::G;
@@ -66,22 +66,22 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug + Zeroize {
#[allow(non_snake_case)]
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F;
/// Hash the message for the binding factor. H4 from the IETF draft
/// Hash the message for the binding factor. H4 from the IETF draft.
fn hash_msg(msg: &[u8]) -> Vec<u8> {
Self::hash_to_vec(b"msg", msg)
}
/// Hash the commitments for the binding factor. H5 from the IETF draft
/// Hash the commitments for the binding factor. H5 from the IETF draft.
fn hash_commitments(commitments: &[u8]) -> Vec<u8> {
Self::hash_to_vec(b"com", commitments)
}
/// Hash the commitments and message to calculate the binding factor. H1 from the IETF draft
/// Hash the commitments and message to calculate the binding factor. H1 from the IETF draft.
fn hash_binding_factor(binding: &[u8]) -> Self::F {
Self::hash_to_F(b"rho", binding)
}
/// Securely generate a random nonce. H3 from the IETF draft
/// 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);

View File

@@ -178,10 +178,8 @@ fn generate_key_r2<Re: Read, R: RngCore + CryptoRng, C: Curve>(
}
/// Finishes round 2 and returns both the secret share and the serialized public key.
/// This key is not usable until all parties confirm they have completed the protocol without
/// issue, yet simply confirming protocol completion without issue is enough to confirm the same
/// key was generated as long as a lack of duplicated commitments was also confirmed when they were
/// broadcasted initially
/// This key MUST NOT be considered usable until all parties confirm they have completed the
/// protocol without issue.
fn complete_r2<Re: Read, R: RngCore + CryptoRng, C: Curve>(
rng: &mut R,
params: FrostParams,
@@ -291,15 +289,16 @@ impl<C: Curve> Drop for KeyMachine<C> {
impl<C: Curve> ZeroizeOnDrop for KeyMachine<C> {}
impl<C: Curve> KeyGenMachine<C> {
/// Creates a new machine to generate a key for the specified curve in the specified multisig
// The context string must be unique among multisigs
/// Creates a new machine to generate a key for the specified curve in the specified multisig.
// The context string should be unique among multisigs.
pub fn new(params: FrostParams, context: String) -> KeyGenMachine<C> {
KeyGenMachine { params, context, _curve: PhantomData }
}
/// Start generating a key according to the FROST DKG spec
/// Start generating a key according to the FROST DKG spec.
/// Returns a serialized list of commitments to be sent to all parties over an authenticated
/// channel. If any party submits multiple sets of commitments, they MUST be treated as malicious
/// channel. If any party submits multiple sets of commitments, they MUST be treated as
/// malicious.
pub fn generate_coefficients<R: RngCore + CryptoRng>(
self,
rng: &mut R,
@@ -320,11 +319,9 @@ impl<C: Curve> KeyGenMachine<C> {
}
impl<C: Curve> SecretShareMachine<C> {
/// Continue generating a key
/// Takes in everyone else's commitments, which are expected to be in a Vec where participant
/// index = Vec index. An empty vector is expected at index 0 to allow for this. An empty vector
/// is also expected at index i which is locally handled. Returns a byte vector representing a
/// secret share for each other participant which should be encrypted before sending
/// Continue generating a key.
/// Takes in everyone else's commitments. Returns a HashMap of byte vectors representing secret
/// shares. These MUST be encrypted and only then sent to their respective participants.
pub fn generate_secret_shares<Re: Read, R: RngCore + CryptoRng>(
mut self,
rng: &mut R,
@@ -343,12 +340,10 @@ impl<C: Curve> SecretShareMachine<C> {
}
impl<C: Curve> KeyMachine<C> {
/// Complete key generation
/// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index =
/// Vec index with an empty vector at index 0 and index i. Returns a byte vector representing the
/// group's public key, while setting a valid secret share inside the machine. > t participants
/// must report completion without issue before this key can be considered usable, yet you should
/// wait for all participants to report as such
/// Complete key generation.
/// Takes in everyone elses' shares submitted to us. Returns a FrostCore object representing the
/// generated keys. Successful protocol completion MUST be confirmed by all parties before these
/// keys may be safely used.
pub fn complete<Re: Read, R: RngCore + CryptoRng>(
mut self,
rng: &mut R,

View File

@@ -51,15 +51,15 @@ pub(crate) fn validate_map<T>(
Ok(())
}
/// Parameters for a multisig
/// Parameters for a multisig.
// These fields can not be made public as they should be static
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct FrostParams {
/// Participants needed to sign on behalf of the group
/// Participants needed to sign on behalf of the group.
t: u16,
/// Amount of participants
/// Amount of participants.
n: u16,
/// Index of the participant being acted for
/// Index of the participant being acted for.
i: u16,
}
@@ -122,7 +122,7 @@ pub enum FrostError {
InternalError(&'static str),
}
/// 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 {
let mut num = F::one();
let mut denom = F::one();
@@ -141,18 +141,18 @@ pub fn lagrange<F: PrimeField>(i: u16, included: &[u16]) -> F {
num * denom.invert().unwrap()
}
/// Core keys generated by performing a FROST keygen protocol
/// Core keys generated by performing a FROST keygen protocol.
#[derive(Clone, PartialEq, Eq, Zeroize)]
pub struct FrostCore<C: Curve> {
/// FROST Parameters
/// FROST Parameters.
#[zeroize(skip)]
params: FrostParams,
/// Secret share key
/// Secret share key.
secret_share: C::F,
/// Group key
/// Group key.
group_key: C::G,
/// Verification shares
/// Verification shares.
#[zeroize(skip)]
verification_shares: HashMap<u16, C::G>,
}
@@ -273,14 +273,14 @@ impl<C: Curve> FrostCore<C> {
}
}
/// FROST keys usable for signing
/// FROST keys usable for signing.
#[derive(Clone, Debug, Zeroize)]
pub struct FrostKeys<C: Curve> {
/// Core keys
/// Core keys.
#[zeroize(skip)]
core: Arc<FrostCore<C>>,
/// Offset applied to these keys
/// Offset applied to these keys.
pub(crate) offset: Option<C::F>,
}
@@ -315,10 +315,10 @@ impl<C: Curve> FrostKeys<C> {
FrostKeys { core: Arc::new(core), offset: None }
}
/// Offset the keys by a given scalar to allow for account and privacy schemes
/// This offset is ephemeral and will not be included when these keys are serialized
/// Keys offset multiple times will form a new offset of their sum
/// Not IETF compliant
/// Offset the keys by a given scalar to allow for account and privacy schemes.
/// This offset is ephemeral and will not be included when these keys are serialized.
/// Keys offset multiple times will form a new offset of their sum.
/// Not IETF compliant.
pub fn offset(&self, offset: C::F) -> FrostKeys<C> {
let mut res = self.clone();
// Carry any existing offset
@@ -336,12 +336,12 @@ impl<C: Curve> FrostKeys<C> {
self.core.secret_share
}
/// Returns the group key with any offset applied
/// Returns the group key with any offset applied.
pub fn group_key(&self) -> C::G {
self.core.group_key + (C::generator() * self.offset.unwrap_or_else(C::F::zero))
}
/// 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> {
self.core.verification_shares()
}

View File

@@ -17,7 +17,7 @@ use crate::{
FrostError, FrostCore, FrostKeys, validate_map,
};
/// Promote a set of keys to another Curve definition
/// Promote a set of keys to another Curve definition.
pub trait CurvePromote<C2: Curve> {
#[doc(hidden)]
#[allow(non_snake_case)]
@@ -86,7 +86,7 @@ pub struct GeneratorPromotion<C1: Curve, C2: Curve> {
_c2: PhantomData<C2>,
}
/// Promote a set of keys from one generator to another
/// Promote a set of keys from one generator to another.
// The linear DLEq proofs are much more efficient than an exponential key gen
impl<C1: Curve, C2: Curve> GeneratorPromotion<C1, C2>
where

View File

@@ -22,7 +22,7 @@ use crate::{
curve::Curve, FrostError, FrostParams, FrostKeys, FrostView, algorithm::Algorithm, validate_map,
};
/// Pairing of an Algorithm with a FrostKeys instance and this specific signing set
/// Pairing of an Algorithm with a FrostKeys instance and this specific signing set.
#[derive(Clone)]
pub struct Params<C: Curve, A: Algorithm<C>> {
algorithm: A,
@@ -369,20 +369,17 @@ pub trait PreprocessMachine {
type Signature: Clone + PartialEq + fmt::Debug;
type SignMachine: SignMachine<Self::Signature>;
/// Perform the preprocessing round required in order to sign
/// Returns a byte vector which must be transmitted to all parties selected for this signing
/// process, over an authenticated channel
/// Perform the preprocessing round required in order to sign.
/// Returns a byte vector to be broadcast to all participants, over an authenticated channel.
fn preprocess<R: RngCore + CryptoRng>(self, rng: &mut R) -> (Self::SignMachine, Vec<u8>);
}
pub trait SignMachine<S> {
type SignatureMachine: SignatureMachine<S>;
/// Sign a message
/// Takes in the participant's commitments, which are expected to be in a Vec where participant
/// index = Vec index. None is expected at index 0 to allow for this. None is also expected at
/// index i which is locally handled. Returns a byte vector representing a share of the signature
/// for every other participant to receive, over an authenticated channel
/// Sign a message.
/// Takes in the participants' preprocesses. Returns a byte vector representing a signature share
/// to be broadcast to all participants, over an authenticated channel.
fn sign<Re: Read>(
self,
commitments: HashMap<u16, Re>,
@@ -391,14 +388,12 @@ pub trait SignMachine<S> {
}
pub trait SignatureMachine<S> {
/// Complete signing
/// Takes in everyone elses' shares submitted to us as a Vec, expecting participant index =
/// Vec index with None at index 0 and index i. Returns a byte vector representing the serialized
/// signature
/// Complete signing.
/// Takes in everyone elses' shares. Returns the signature.
fn complete<Re: Read>(self, shares: HashMap<u16, Re>) -> Result<S, FrostError>;
}
/// State machine which manages signing for an arbitrary signature algorithm
/// State machine which manages signing for an arbitrary signature algorithm.
pub struct AlgorithmMachine<C: Curve, A: Algorithm<C>> {
params: Params<C, A>,
}
@@ -414,7 +409,7 @@ pub struct AlgorithmSignatureMachine<C: Curve, A: Algorithm<C>> {
}
impl<C: Curve, A: Algorithm<C>> AlgorithmMachine<C, A> {
/// Creates a new machine to generate a key for the specified curve in the specified multisig
/// Creates a new machine to generate a signature with the specified keys.
pub fn new(
algorithm: A,
keys: FrostKeys<C>,

View File

@@ -11,22 +11,22 @@ use digest::{typenum::type_operators::IsGreaterOrEqual, consts::U256, Digest, Ou
pub trait Transcript {
type Challenge: Clone + Send + Sync + AsRef<[u8]>;
/// Create a new transcript with the specified name
/// Create a new transcript with the specified name.
fn new(name: &'static [u8]) -> Self;
/// Apply a domain separator to the transcript
/// Apply a domain separator to the transcript.
fn domain_separate(&mut self, label: &'static [u8]);
/// Append a message to the transcript
/// Append a message to the transcript.
fn append_message(&mut self, label: &'static [u8], message: &[u8]);
/// Produce a challenge. This MUST update the transcript as it does so, preventing the same
/// challenge from being generated multiple times
/// challenge from being generated multiple times.
fn challenge(&mut self, label: &'static [u8]) -> Self::Challenge;
/// Produce a RNG seed. Helper function for parties needing to generate random data from an
/// agreed upon state. Internally calls the challenge function for the needed bytes, converting
/// them to the seed format rand_core expects
/// them to the seed format rand_core expects.
fn rng_seed(&mut self, label: &'static [u8]) -> [u8; 32];
}
@@ -50,12 +50,12 @@ impl DigestTranscriptMember {
}
}
/// A trait defining Digests with at least a 256-byte output size, assuming at least a 128-bit
/// level of security accordingly
pub trait SecureDigest: Clone + Digest {}
impl<D: Clone + Digest> SecureDigest for D where D::OutputSize: IsGreaterOrEqual<U256> {}
/// A trait defining cryptographic Digests with at least a 256-byte output size, assuming at least
/// a 128-bit level of security accordingly.
/// A simple transcript format constructed around the specified hash algorithm
/// A simple transcript format constructed around the specified hash algorithm.
#[derive(Clone, Debug)]
pub struct DigestTranscript<D: SecureDigest>(D);