Skip to content

Commit

Permalink
for taproot merkle root is optional but tweaks are not
Browse files Browse the repository at this point in the history
  • Loading branch information
xoloki committed Aug 15, 2023
1 parent 9f004bc commit 90f48ca
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 200 deletions.
8 changes: 5 additions & 3 deletions src/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,19 @@ pub fn tagged_hash(tag: &str) -> Sha256 {
}

/// Create a BIP341 compliant taproot tweak from a public key and merkle root
pub fn tweak(public_key: &Point, merkle_root: &[u8]) -> Scalar {
pub fn tweak(public_key: &Point, merkle_root: Option<[u8; 32]>) -> Scalar {
let mut hasher = tagged_hash("TapTweak");

hasher.update(public_key.x().to_bytes());
hasher.update(merkle_root);
if let Some(root) = merkle_root {
hasher.update(root);
}

hash_to_scalar(&mut hasher)
}

/// Create a BIP341 compliant taproot tweak from a public key and merkle root
pub fn tweaked_public_key(public_key: &Point, merkle_root: &[u8]) -> Point {
pub fn tweaked_public_key(public_key: &Point, merkle_root: Option<[u8; 32]>) -> Point {
public_key + tweak(public_key, merkle_root) * G
}

Expand Down
160 changes: 17 additions & 143 deletions src/taproot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,8 @@ pub mod test_helpers {
loop {
let group_key = A.iter().fold(Point::new(), |s, a| s + a.A[0]);
if group_key.has_even_y() {
if let Some(root) = merkle_root {
let tweaked = compute::tweaked_public_key(&group_key, &root);
if tweaked.has_even_y() {
break;
}
} else {
let tweaked = compute::tweaked_public_key(&group_key, merkle_root);
if tweaked.has_even_y() {
break;
}
}
Expand Down Expand Up @@ -178,28 +174,12 @@ pub mod test_helpers {
msg: &[u8],
signers: &mut [Signer],
rng: &mut RNG,
merkle_root: Option<[u8; 32]>,
) -> (Vec<PublicNonce>, Vec<SignatureShare>) {
let (signer_ids, key_ids, nonces) = sign_params(msg, signers, rng);
let shares = signers
.iter()
.flat_map(|s| s.sign(msg, &signer_ids, &key_ids, &nonces))
.collect();

(nonces, shares)
}

/// Run a tweaked signing round for the passed `msg`
#[allow(non_snake_case)]
pub fn sign_tweaked<RNG: RngCore + CryptoRng, Signer: traits::Signer>(
msg: &[u8],
signers: &mut [Signer],
rng: &mut RNG,
tweaked_public_key: &Point,
) -> (Vec<PublicNonce>, Vec<SignatureShare>) {
let (signer_ids, key_ids, nonces) = sign_params(msg, signers, rng);
let shares = signers
.iter()
.flat_map(|s| s.sign_tweaked(msg, &signer_ids, &key_ids, &nonces, tweaked_public_key))
.flat_map(|s| s.sign_taproot(msg, &signer_ids, &key_ids, &nonces, merkle_root))
.collect();

(nonces, shares)
Expand All @@ -213,112 +193,6 @@ mod test {
use crate::{compute, traits::Signer, v1, v2};
use rand_core::OsRng;

#[test]
#[allow(non_snake_case)]
fn test_schnorr_sign_verify_v1() {
let mut rng = OsRng::default();

// First create and verify a frost signature
let msg = "It was many and many a year ago".as_bytes();
let N: u32 = 10;
let T: u32 = 7;
let signer_ids: Vec<Vec<u32>> = [
[0, 1, 2].to_vec(),
[3, 4].to_vec(),
[5, 6, 7].to_vec(),
[8, 9].to_vec(),
]
.to_vec();
let mut signers: Vec<v1::Signer> = signer_ids
.iter()
.enumerate()
.map(|(id, ids)| v1::Signer::new(id.try_into().unwrap(), ids, N, T, &mut rng))
.collect();

let A = match test_helpers::dkg(&mut signers, &mut rng, None) {
Ok(A) => A,
Err(secret_errors) => {
panic!("Got secret errors from DKG: {:?}", secret_errors);
}
};

let mut S = [signers[0].clone(), signers[1].clone(), signers[3].clone()].to_vec();
let mut sig_agg =
v1::SignatureAggregator::new(N, T, A.clone()).expect("aggregator ctor failed");

let (nonces, sig_shares) = test_helpers::sign(&msg, &mut S, &mut rng);
let sig = match sig_agg.sign(&msg, &nonces, &sig_shares) {
Err(e) => panic!("Aggregator sign failed: {:?}", e),
Ok(sig) => sig,
};

// now create a SchnorrProof from the frost signature
let proof = SchnorrProof::new(&sig).unwrap();

assert!(proof.verify(&sig_agg.poly[0].x(), msg));

// now ser/de the proof
let proof_bytes = proof.to_bytes();
let proof_deser = SchnorrProof::from(proof_bytes);

assert_eq!(proof, proof_deser);
assert!(proof_deser.verify(&sig_agg.poly[0].x(), msg));
}

#[test]
#[allow(non_snake_case)]
fn test_schnorr_sign_verify_v2() {
let mut rng = OsRng::default();

// First create and verify a frost signature
let msg = "It was many and many a year ago".as_bytes();
let Nk: u32 = 10;
let Np: u32 = 4;
let T: u32 = 7;
let signer_ids: Vec<Vec<u32>> = [
[0, 1, 2].to_vec(),
[3, 4].to_vec(),
[5, 6, 7].to_vec(),
[8, 9].to_vec(),
]
.to_vec();
let mut signers: Vec<v2::Signer> = signer_ids
.iter()
.enumerate()
.map(|(id, ids)| v2::Signer::new(id.try_into().unwrap(), ids, Np, Nk, T, &mut rng))
.collect();

let A = match test_helpers::dkg(&mut signers, &mut rng, None) {
Ok(A) => A,
Err(secret_errors) => {
panic!("Got secret errors from DKG: {:?}", secret_errors);
}
};

let mut S = [signers[0].clone(), signers[1].clone(), signers[3].clone()].to_vec();
let key_ids = S.iter().flat_map(|s| s.get_key_ids()).collect::<Vec<u32>>();
let mut sig_agg =
v2::SignatureAggregator::new(Nk, T, A.clone()).expect("aggregator ctor failed");

let (nonces, sig_shares) = test_helpers::sign(&msg, &mut S, &mut rng);
let sig = match sig_agg.sign(&msg, &nonces, &sig_shares, &key_ids) {
Err(e) => panic!("Aggregator sign failed: {:?}", e),
Ok(sig) => sig,
};

// now create a SchnorrProof from the frost signature
let proof = SchnorrProof::new(&sig).unwrap();

assert!(proof.verify(&sig_agg.poly[0].x(), msg));

// now ser/de the proof
let proof_bytes = proof.to_bytes();
let proof_deser = SchnorrProof::from(proof_bytes);

assert_eq!(proof, proof_deser);
assert!(proof_deser.verify(&sig_agg.poly[0].x(), msg));
}

#[test]
#[allow(non_snake_case)]
fn test_taproot_sign_verify_v1() {
Expand Down Expand Up @@ -354,11 +228,11 @@ mod test {
let mut sig_agg =
v1::SignatureAggregator::new(N, T, A.clone()).expect("aggregator ctor failed");
let aggregate_public_key = sig_agg.poly[0];
let tweaked_public_key = compute::tweaked_public_key(&aggregate_public_key, &merkle_root);
let tweaked_public_key =
compute::tweaked_public_key(&aggregate_public_key, Some(merkle_root));

let (nonces, sig_shares) =
test_helpers::sign_tweaked(&msg, &mut S, &mut rng, &tweaked_public_key);
let sig = match sig_agg.sign_merkle_root(&msg, &nonces, &sig_shares, &merkle_root) {
let (nonces, sig_shares) = test_helpers::sign(&msg, &mut S, &mut rng, Some(merkle_root));
let sig = match sig_agg.sign_taproot(&msg, &nonces, &sig_shares, Some(merkle_root)) {
Err(e) => panic!("Aggregator sign failed: {:?}", e),
Ok(sig) => sig,
};
Expand Down Expand Up @@ -413,15 +287,15 @@ mod test {
let mut sig_agg =
v2::SignatureAggregator::new(Nk, T, A.clone()).expect("aggregator ctor failed");
let aggregate_public_key = sig_agg.poly[0];
let tweaked_public_key = compute::tweaked_public_key(&aggregate_public_key, &merkle_root);

let (nonces, sig_shares) =
test_helpers::sign_tweaked(&msg, &mut S, &mut rng, &tweaked_public_key);
let sig = match sig_agg.sign_merkle_root(&msg, &nonces, &sig_shares, &key_ids, &merkle_root)
{
Err(e) => panic!("Aggregator sign failed: {:?}", e),
Ok(sig) => sig,
};
let tweaked_public_key =
compute::tweaked_public_key(&aggregate_public_key, Some(merkle_root));

let (nonces, sig_shares) = test_helpers::sign(&msg, &mut S, &mut rng, Some(merkle_root));
let sig =
match sig_agg.sign_taproot(&msg, &nonces, &sig_shares, &key_ids, Some(merkle_root)) {
Err(e) => panic!("Aggregator sign failed: {:?}", e),
Ok(sig) => sig,
};

// now create a SchnorrProof from the frost signature
let proof = SchnorrProof::new(&sig).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ pub trait Signer {
) -> Vec<SignatureShare>;

/// Sign `msg` using all this signer's keys and a tweaked public key
fn sign_tweaked(
fn sign_taproot(
&self,
msg: &[u8],
signer_ids: &[u32],
key_ids: &[u32],
nonces: &[PublicNonce],
tweaked_public_key: &Point,
merkle_root: Option<[u8; 32]>,
) -> Vec<SignatureShare>;
}
40 changes: 12 additions & 28 deletions src/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,37 +189,28 @@ impl Party {
}
}

/// Sign `msg` with this party's share of the group private key, using the set of `sigers` and corresponding `nonces` with a precomputed `aggregate_nonce`
/// Sign `msg` with this party's share of the group private key, using the set of `sigers` and corresponding `nonces` with a precomputed `aggregate_nonce` and an optional tweak to the public key
pub fn sign_precomputed(
&self,
msg: &[u8],
signers: &[u32],
nonces: &[PublicNonce],
aggregate_nonce: &Point,
) -> SignatureShare {
let mut z = &self.nonce.d + &self.nonce.e * compute::binding(&self.id(), nonces, msg);
z += compute::challenge(&self.group_key, aggregate_nonce, msg)
* &self.private_key
* compute::lambda(self.id, signers);

SignatureShare {
id: self.id,
z_i: z,
key_ids: vec![self.id],
}
self.sign_precomputed_with_tweak(msg, signers, nonces, aggregate_nonce, &Scalar::from(0))
}

/// Sign `msg` with this party's share of the group private key, using the set of `sigers` and corresponding `nonces` with a precomputed `aggregate_nonce` and a tweaked public key
pub fn sign_precomputed_tweaked(
/// Sign `msg` with this party's share of the group private key, using the set of `sigers` and corresponding `nonces` with a precomputed `aggregate_nonce` and an optional tweak to the public key
pub fn sign_precomputed_with_tweak(
&self,
msg: &[u8],
signers: &[u32],
nonces: &[PublicNonce],
aggregate_nonce: &Point,
tweaked_public_key: &Point,
tweak: &Scalar,
) -> SignatureShare {
let mut z = &self.nonce.d + &self.nonce.e * compute::binding(&self.id(), nonces, msg);
z += compute::challenge(tweaked_public_key, aggregate_nonce, msg)
z += compute::challenge(&(self.group_key + tweak * G), aggregate_nonce, msg)
* &self.private_key
* compute::lambda(self.id, signers);

Expand Down Expand Up @@ -285,12 +276,12 @@ impl SignatureAggregator {
}

/// Check and aggregate the party signatures using a merke root to make a tweak
pub fn sign_merkle_root(
pub fn sign_taproot(
&mut self,
msg: &[u8],
nonces: &[PublicNonce],
sig_shares: &[SignatureShare],
merkle_root: &[u8],
merkle_root: Option<[u8; 32]>,
) -> Result<Signature, AggregatorError> {
let tweak = compute::tweak(&self.poly[0], merkle_root);
self.sign_with_tweak(msg, nonces, sig_shares, &tweak)
Expand Down Expand Up @@ -516,26 +507,19 @@ impl crate::traits::Signer for Signer {
.collect()
}

fn sign_tweaked(
fn sign_taproot(
&self,
msg: &[u8],
_signer_ids: &[u32],
key_ids: &[u32],
nonces: &[PublicNonce],
tweaked_public_key: &Point,
merkle_root: Option<[u8; 32]>,
) -> Vec<SignatureShare> {
let aggregate_nonce = compute::aggregate_nonce(msg, key_ids, nonces).unwrap();
let tweak = compute::tweak(&self.parties[0].group_key, merkle_root);
self.parties
.iter()
.map(|p| {
p.sign_precomputed_tweaked(
msg,
key_ids,
nonces,
&aggregate_nonce,
tweaked_public_key,
)
})
.map(|p| p.sign_precomputed_with_tweak(msg, key_ids, nonces, &aggregate_nonce, &tweak))
.collect()
}
}
Expand Down
Loading

0 comments on commit 90f48ca

Please sign in to comment.