Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

MuSig: add counterparties #229

Merged
merged 15 commits into from
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions zkvm/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ pub enum VMError {
#[fail(display = "Deferred point operations failed")]
PointOperationsFailed,

/// This error occurs when a signature share fails to verify
#[fail(display = "Share #{:?} failed to verify correctly", index)]
ShareError {
/// The index of the share that failed fo verify correctly
index: usize,
/// This error occurs when a MuSig signature share fails to verify
#[fail(display = "Share #{:?} failed to verify correctly", pubkey)]
MuSigShareError {
/// The pubkey corresponding to the MuSig share that failed fo verify correctly
pubkey: [u8; 32],
},

/// This error occurs when R1CS proof verification failed.
Expand Down
112 changes: 112 additions & 0 deletions zkvm/src/signature/counterparty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use super::multikey::Multikey;
use super::VerificationKey;
use crate::errors::VMError;
use crate::transcript::TranscriptProtocol;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use merlin::Transcript;
use subtle::ConstantTimeEq;

#[derive(Copy, Clone)]
pub struct NoncePrecommitment([u8; 32]);

#[derive(Copy, Clone, Debug)]
pub struct NonceCommitment(RistrettoPoint);

impl NonceCommitment {
pub(super) fn new(commitment: RistrettoPoint) -> Self {
NonceCommitment(commitment)
}

pub(super) fn precommit(&self) -> NoncePrecommitment {
let mut h = Transcript::new(b"MuSig.nonce-precommit");
h.commit_point(b"R", &self.0.compress());
let mut precommitment = [0u8; 32];
h.challenge_bytes(b"precommitment", &mut precommitment);
NoncePrecommitment(precommitment)
}

pub(super) fn sum(commitments: &Vec<Self>) -> RistrettoPoint {
commitments.iter().map(|R_i| R_i.0).sum()
}
}

pub struct Counterparty {
pubkey: VerificationKey,
}

pub struct CounterpartyPrecommitted {
precommitment: NoncePrecommitment,
pubkey: VerificationKey,
}

pub struct CounterpartyCommitted {
commitment: NonceCommitment,
pubkey: VerificationKey,
}

impl Counterparty {
pub(super) fn new(pubkey: VerificationKey) -> Self {
Counterparty { pubkey }
}

pub(super) fn precommit_nonce(
self,
precommitment: NoncePrecommitment,
) -> CounterpartyPrecommitted {
CounterpartyPrecommitted {
precommitment,
pubkey: self.pubkey,
}
}
}

impl CounterpartyPrecommitted {
pub(super) fn commit_nonce(
self,
commitment: NonceCommitment,
) -> Result<CounterpartyCommitted, VMError> {
// Check H(commitment) =? precommitment
let received_precommitment = commitment.precommit();
let equal = self.precommitment.0.ct_eq(&received_precommitment.0);
if equal.unwrap_u8() == 0 {
return Err(VMError::MuSigShareError {
pubkey: self.pubkey.0.to_bytes(),
});
}

Ok(CounterpartyCommitted {
commitment: commitment,
pubkey: self.pubkey,
})
}
}

impl CounterpartyCommitted {
pub(super) fn sign(
self,
share: Scalar,
challenge: Scalar,
multikey: &Multikey,
) -> Result<Scalar, VMError> {
// Check if s_i * G == R_i + c * a_i * X_i.
// s_i = share
// G = RISTRETTO_BASEPOINT_POINT
// R_i = self.commitment
// c = challenge
// a_i = multikey.factor_for_key(self.pubkey)
// X_i = self.pubkey
let S_i = share * RISTRETTO_BASEPOINT_POINT;
let a_i = multikey.factor_for_key(&self.pubkey);
let X_i = self.pubkey.0.decompress().ok_or(VMError::InvalidPoint)?;

if S_i != self.commitment.0 + challenge * a_i * X_i {
return Err(VMError::MuSigShareError {
pubkey: self.pubkey.0.to_bytes(),
});
}

Ok(share)
}
}
3 changes: 2 additions & 1 deletion zkvm/src/signature/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use crate::errors::VMError;
use crate::point_ops::PointOp;
use crate::transcript::TranscriptProtocol;

mod counterparty;
mod multikey;
mod musig;
mod prover;
mod signer;

/// Verification key (aka "pubkey") is a wrapper type around a Ristretto point
/// that lets the verifier to check the signature.
Expand Down
6 changes: 3 additions & 3 deletions zkvm/src/signature/multikey.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::signature::VerificationKey;
use super::VerificationKey;
use crate::transcript::TranscriptProtocol;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
Expand All @@ -13,13 +13,13 @@ pub struct Multikey {
impl Multikey {
pub fn new(pubkeys: Vec<VerificationKey>) -> Option<Self> {
// Create transcript for Multikey
let mut transcript = Transcript::new(b"ZkVM.aggregated-key");
let mut transcript = Transcript::new(b"MuSig.aggregated-key");
transcript.commit_u64(b"n", pubkeys.len() as u64);

// Commit pubkeys into the transcript
// <L> = H(X_1 || X_2 || ... || X_n)
for X in &pubkeys {
transcript.commit_point(b"P", &X.0);
transcript.commit_point(b"X", &X.0);
}

// aggregated_key = sum_i ( a_i * X_i )
Expand Down
45 changes: 22 additions & 23 deletions zkvm/src/signature/musig.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
use super::VerificationKey;
use crate::errors::VMError;
use crate::signature::VerificationKey;
use crate::transcript::TranscriptProtocol;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::ristretto::CompressedRistretto;
use curve25519_dalek::scalar::Scalar;
use merlin::Transcript;

#[derive(Debug, Clone)]
pub struct Signature {
pub s: Scalar,
pub R: RistrettoPoint,
pub R: CompressedRistretto,
}

impl Signature {
pub fn verify(&self, transcript: &Transcript, P: VerificationKey) -> Result<(), VMError> {
pub fn verify(&self, transcript: &Transcript, X: VerificationKey) -> Result<(), VMError> {
let G = RISTRETTO_BASEPOINT_POINT;
let mut transcript = transcript.clone();

// Make c = H(X, R, m)
// The message `m` should already have been fed into the transcript
// The message `m` has already been fed into the transcript
let c = {
transcript.commit_point(b"P", &P.0);
transcript.commit_point(b"R", &self.R.compress());
transcript.commit_point(b"X", &X.0);
transcript.commit_point(b"R", &self.R);
transcript.challenge_scalar(b"c")
};

let P = P.0.decompress().ok_or(VMError::InvalidPoint)?;
let X = X.0.decompress().ok_or(VMError::InvalidPoint)?;
let R = self.R.decompress().ok_or(VMError::InvalidPoint)?;

// Check sG = R + c * aggregated_key
if self.s * G == self.R + c * P {
// Check sG = R + c * X
if self.s * G == R + c * X {
Ok(())
} else {
Err(VMError::PointOperationsFailed)
Expand All @@ -41,7 +42,7 @@ mod tests {

use super::*;
use crate::errors::VMError;
use crate::signature::prover::*;
use crate::signature::signer::*;
use crate::signature::{multikey::Multikey, VerificationKey};
use curve25519_dalek::ristretto::CompressedRistretto;

Expand All @@ -57,8 +58,8 @@ mod tests {
let multikey = multikey_helper(&priv_keys).unwrap();

let expected_pubkey = CompressedRistretto::from_slice(&[
212, 211, 54, 88, 245, 166, 107, 207, 28, 70, 247, 28, 5, 233, 67, 112, 196, 30, 35,
136, 160, 232, 167, 109, 47, 88, 194, 207, 227, 71, 222, 102,
56, 92, 251, 79, 34, 221, 181, 222, 11, 112, 55, 45, 154, 242, 40, 250, 247, 1, 109,
126, 150, 210, 181, 6, 117, 95, 44, 102, 38, 28, 144, 49,
]);

assert_eq!(expected_pubkey, multikey.aggregated_key().0);
Expand Down Expand Up @@ -90,17 +91,21 @@ mod tests {
}

fn sign_helper(
priv_keys: Vec<Scalar>,
privkeys: Vec<Scalar>,
multikey: Multikey,
m: Vec<u8>,
) -> Result<Signature, VMError> {
let mut transcript = Transcript::new(b"signing test");
transcript.commit_bytes(b"message", &m);
let pubkeys: Vec<_> = privkeys
.iter()
.map(|privkey| VerificationKey((privkey * RISTRETTO_BASEPOINT_POINT).compress()))
.collect();

let (parties, precomms): (Vec<_>, Vec<_>) = priv_keys
let (parties, precomms): (Vec<_>, Vec<_>) = privkeys
.clone()
.into_iter()
.map(|x_i| Party::new(&transcript.clone(), x_i, multikey.clone()))
.map(|x_i| Party::new(&transcript.clone(), x_i, multikey.clone(), pubkeys.clone()))
.unzip();

let (parties, comms): (Vec<_>, Vec<_>) = parties
Expand All @@ -113,15 +118,9 @@ mod tests {
.map(|p| p.receive_commitments(comms.clone()).unwrap())
.unzip();

let pub_keys: Vec<_> = priv_keys
.iter()
.map(|priv_key| VerificationKey((priv_key * RISTRETTO_BASEPOINT_POINT).compress()))
.collect();
let signatures: Vec<_> = parties
.into_iter()
.map(|p: PartyAwaitingShares| {
p.receive_shares(shares.clone(), pub_keys.clone()).unwrap()
})
.map(|p: PartyAwaitingShares| p.receive_shares(shares.clone()).unwrap())
.collect();

// Check that signatures from all parties are the same
Expand Down
Loading