Transcript crate with both a merlin backend and a basic label len value backend

Moves binding factor/seeded RNGs over to the transcripts.
This commit is contained in:
Luke Parker
2022-05-03 07:20:24 -04:00
parent 87f38cafe4
commit bf257b3a1f
19 changed files with 282 additions and 129 deletions

View File

@@ -4,16 +4,16 @@ use rand_core::{RngCore, CryptoRng};
use group::Group;
use transcript::{Transcript, DigestTranscript};
use crate::{Curve, FrostError, MultisigView};
/// 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
type Signature: Clone + Debug;
/// The amount of bytes from each participant's addendum to commit to
fn addendum_commit_len() -> usize;
/// Generate an addendum to FROST"s preprocessing stage
fn preprocess_addendum<R: RngCore + CryptoRng>(
rng: &mut R,
@@ -30,8 +30,8 @@ pub trait Algorithm<C: Curve>: Clone {
serialized: &[u8],
) -> Result<(), FrostError>;
/// Context for this algorithm to be hashed into b, and therefore committed to
fn context(&self) -> Vec<u8>;
/// Transcript for this algorithm to be used to create the binding factor
fn transcript(&self) -> Option<Self::Transcript>;
/// Sign a share with the given secret/nonce
/// The secret will already have been its lagrange coefficient applied so it is the necessary
@@ -90,12 +90,11 @@ pub struct SchnorrSignature<C: Curve> {
/// Implementation of Schnorr signatures for use with FROST
impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
// Specify a firm type which either won't matter as it won't be used or will be used (offset) and
// is accordingly solid
type Transcript = DigestTranscript::<blake2::Blake2b512>;
type Signature = SchnorrSignature<C>;
fn addendum_commit_len() -> usize {
0
}
fn preprocess_addendum<R: RngCore + CryptoRng>(
_: &mut R,
_: &MultisigView<C>,
@@ -114,8 +113,8 @@ impl<C: Curve, H: Hram<C>> Algorithm<C> for Schnorr<C, H> {
Ok(())
}
fn context(&self) -> Vec<u8> {
vec![]
fn transcript(&self) -> Option<DigestTranscript::<blake2::Blake2b512>> {
None
}
fn sign_share(

View File

@@ -1,10 +1,10 @@
use core::{ops::Mul, fmt::Debug};
use thiserror::Error;
use ff::{Field, PrimeField};
use group::{Group, GroupOps, ScalarMul};
use thiserror::Error;
pub mod key_gen;
pub mod algorithm;
pub mod sign;

View File

@@ -6,6 +6,8 @@ use rand_core::{RngCore, CryptoRng};
use ff::{Field, PrimeField};
use group::Group;
use transcript::Transcript;
use crate::{Curve, FrostError, MultisigParams, MultisigKeys, MultisigView, algorithm::Algorithm};
/// Calculate the lagrange coefficient
@@ -142,17 +144,13 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
Err(FrostError::NonEmptyParticipantZero)?;
}
let commitments_len = C::G_len() * 2;
// Allow algorithms to commit to more data than just the included nonces
// Not IETF draft compliant yet it doesn't prevent a compliant Schnorr algorithm from being used
// with this library, which does ship one
let commit_len = commitments_len + A::addendum_commit_len();
#[allow(non_snake_case)]
let mut B = Vec::with_capacity(multisig_params.n + 1);
B.push(None);
// Commitments + a presumed 32-byte hash of the message
let mut b: Vec<u8> = Vec::with_capacity((multisig_params.t * 2 * C::G_len()) + 32);
let commitments_len = 2 * C::G_len();
let mut b: Vec<u8> = Vec::with_capacity((multisig_params.t * commitments_len) + 32);
// Parse the commitments and prepare the binding factor
for l in 1 ..= multisig_params.n {
@@ -163,7 +161,7 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
B.push(Some(our_preprocess.commitments));
b.extend(&u16::try_from(l).unwrap().to_le_bytes());
b.extend(&our_preprocess.serialized[0 .. commit_len]);
b.extend(&our_preprocess.serialized[0 .. (C::G_len() * 2)]);
continue;
}
@@ -193,7 +191,7 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
.map_err(|_| FrostError::InvalidCommitment(l))?;
B.push(Some([D, E]));
b.extend(&u16::try_from(l).unwrap().to_le_bytes());
b.extend(&commitments[0 .. commit_len]);
b.extend(&commitments[0 .. commitments_len]);
}
// Process the commitments and addendums
@@ -216,26 +214,27 @@ fn sign_with_share<C: Curve, A: Algorithm<C>>(
// Finish the binding factor
b.extend(&C::hash_msg(&msg));
// If the following are used with certain lengths, it is possible to craft distinct
// commitments/messages/contexts with the same binding factor. While we can't length prefix the
// commitments, unfortunately, we can tag and length prefix the following
// Let the algorithm provide a transcript of its variables
// While Merlin, which may or may not be the transcript used here, wants application level
// transcripts passed around to proof systems, this maintains a desired level of abstraction and
// works without issue
let mut transcript = params.algorithm.transcript();
// If the offset functionality provided by this library is in use, include it in the binding
// factor. Not compliant with the IETF spec which doesn't have a concept of offsets
// If the offset functionality provided by this library is in use, include it in the transcript.
// Not compliant with the IETF spec which doesn't have a concept of offsets, nor does it use
// transcripts
if params.keys.offset.is_some() {
b.extend(b"offset");
b.extend(u64::try_from(C::F_len()).unwrap().to_le_bytes());
b.extend(&C::F_to_le_bytes(&params.keys.offset.unwrap()));
let mut offset_transcript = transcript.unwrap_or(A::Transcript::new(b"FROST_offset"));
offset_transcript.append_message(b"offset", &C::F_to_le_bytes(&params.keys.offset.unwrap()));
transcript = Some(offset_transcript);
}
// Also include any context the algorithm may want to specify. Again not compliant with the IETF
// spec which doesn't considered there may be signatures other than Schnorr being generated with
// FROST
let context = params.algorithm.context();
if context.len() != 0 {
b.extend(b"context");
b.extend(u64::try_from(context.len()).unwrap().to_le_bytes());
b.extend(&context);
// If a transcript was defined, move the commitments used for the binding factor into it
// Then, obtain its sum and use that as the binding factor
if transcript.is_some() {
let mut transcript = transcript.unwrap();
transcript.append_message(b"commitments", &b);
b = transcript.challenge(b"binding", 64);
}
let b = C::hash_to_F(&b);