1. 引言
Musig方案由Blockstream團隊2018年論文《Simple Schnorr Multi-Signatures with Applications to Bitcoin》中提出。
對應的代碼實現:
- https://github.com/lovesh/signature-schemes (所用的amcl庫有點bug,詳見 issue。)
- https://github.com/KZen-networks/multi-party-schnorr
參見博客 Simple Schnorr Multi-Signatures with Applications to Bitcoin 學習筆記。
Musig方案基本流程爲:(即一羣簽名者對同一消息進行聯合簽名)
- KeyGen:每個簽名者生成公私鑰對。
- Sign:待簽名消息,所有簽名者公鑰,對於,簽名者計算,然後計算aggregated public key ;選擇隨機數,計算,將發送給所有其它簽名者;當收齊其它簽名者發來的時,將發送給所有其它簽名者;當收到其它簽名者發來的時,驗證是否成立(for all ),若不成立則停止,否則繼續計算,將發送給其它所有簽名者;當收到了所有的簽名信息時,計算,最終的簽名信息爲。【各簽名者之間需要交互interactive。】
- Ver:Given ,驗籤者計算,驗證是否成立,若成立則簽名驗證成功。
2. lovesh/signature-schemes
https://github.com/lovesh/signature-schemes/musig中爲Musig方案的代碼實現。
2.1 主要庫依賴
看https://github.com/lovesh/signature-schemes/musig/Cargo.toml
中內容:
- https://github.com/lovesh/amcl_rust_wrapper:主要做了基礎運算的constant time 和variable time function封裝。支持的曲線主要有"bls381", “bn254”, “secp256k1”, “ed25519”。(具體可參見博客 Polynomial Commitments代碼實現【2】——lovesh/kzg-poly-commit 第1節內容。)
[dependencies.amcl_wrapper]
git = "https://github.com/lovesh/amcl_rust_wrapper"
rev = "4ea40f758e9d676937ea3e609d63b37fe9e1df7f"
features = ["secp256k1"]
- rand:隨機數生成器,Musig方案中的隨機數要求爲strong random number。
- lazy_static:lazy_static是在第一次調用時(運行時)進行初始化。而非編譯時,而static是在編譯時進行初始化,運行階段分配內存空間。(實際未使用,可去除。)
- log:提供日誌接口。(實際未使用,可去除。)
2.2 代碼實現
- KeyGen:
let keypair = Keypair::new(None);
OR
let rng = EntropyRng::new();
let keypair = Keypair::new(Some(rng));
let my_sk = keypair.sig_key;
let my_vk = keypair.ver_key;
- Sign:簽名過程主要分爲3個interactive階段。
– 1)第一階段:每個簽名者選擇隨機數,計算相應的commitment ,將發送給所有其它簽名者。必須保證收齊了所有其它人的值(可調用is_phase_1_complete
來判斷),才進入第二階段。
let signer = Signer::new(num_cosigners); // num_cosigners is the total number of signers including the current signer
signer.init_phase_1();
/// Signer creates his r, R and t
pub fn init_phase_1(&mut self) {
let r = FieldElement::random();
let R = G1::generator() * r;
// TODO: Need domain separation for H_com
let t = FieldElement::from_msg_hash(&R.to_bytes());
self.r = r;
self.R[0] = R;
self.t[0] = t;
}
Each signer gives a numeric reference starting from 1 to other signers. These references are local to the signer. 每個簽名者的編號都是本地的,本人的編號總是從0開始。
On receiving hash h
from signer referred by j
, it calls got_hash
.
/// `t`, `R` are local (to the current signer) references to the cosigners.
/// The current signer always references himself by index 0.
pub struct Signer {
pub r: FieldElement,
pub R: Vec<G1>,
pub t: Vec<FieldElement>,
}
// Do phase 1. Each cosigner generates r, t, R and shares t with others.
let ts: Vec<FieldElement> = (0..num_cosigners)
.map(|i| signers[i].t[0].clone())
.collect();
for i in 0..num_cosigners {
let signer = &mut signers[i];
let mut k = 1;
for j in 0..num_cosigners {
if i == j {
continue;
}
signer.got_hash(ts[j], k).unwrap();
k += 1;
}
}
for i in 0..num_cosigners {
assert!(signers[i].is_phase_1_complete());
}
/// Process the received `t` from other cosigners
pub fn got_hash(&mut self, t: FieldElement, cosigner_ref: usize) -> Result<(), MuSigError> {
self.validate_cosigner_ref(cosigner_ref)?;
self.t[cosigner_ref] = t;
Ok(())
}
fn validate_cosigner_ref(&self, cosigner_ref: usize) -> Result<(), MuSigError> {
if cosigner_ref == 0 {
// Since 0 always references the current signer
return Err(MuSigError::IncorrectCosignerRef(cosigner_ref));
}
// Does not matter if `self.R.len` is used or `self.t.len` as they have same length
if cosigner_ref >= self.t.len() {
return Err(MuSigError::UnknownCosignerRef(cosigner_ref));
}
Ok(())
}
– 2)第二階段:收齊了所有的commitment hash值後,簽名者會將相應的commitment 發送給所有其它簽名者。當簽名者收齊(調用got_commitment
,用於驗證是否成立。)了所有其他人的值之後(可調用is_phase_2_complete
來判斷),才進入第三階段。
// Do phase 2. Each cosigner shares R with others
let Rs: Vec<G1> = (0..num_cosigners)
.map(|i| signers[i].R[0].clone())
.collect();
for i in 0..num_cosigners {
let signer = &mut signers[i];
let mut k = 1;
for j in 0..num_cosigners {
if i == j {
continue;
}
signer.got_commitment(Rs[j], k).unwrap();
k += 1;
}
}
for i in 0..num_cosigners {
assert!(signers[i].is_phase_2_complete());
}
–3)第三階段:當收齊了所有其他簽名者的commitment 後,各個簽名者分別用自己的私鑰對同一消息進行簽名。
let mut signatures: Vec<Signature> = vec![];
let msg_b = msg.as_bytes();
for i in 0..num_cosigners {
let signer = &signers[i];
let keypair = &keypairs[i];
let sig = signer
.generate_sig(msg_b, &keypair.sig_key, &keypair.ver_key, &verkeys)
.unwrap();
signatures.push(sig);
}
收集所有簽名者的簽名信息,進行aggregate:
let aggr_sig = AggregatedSignature::new(&signatures).unwrap();
assert!(aggr_sig.verify(msg_b, &verkeys));
Musig方案支持Key aggregation:
let verkeys = keypairs.iter().map(|k| k.ver_key.clone()).collect();
let L = HashedVerKeys::new(&verkeys);
let mut avk = AggregatedVerKey::new(&verkeys);
當需要同一簽名羣體對不同的消息多次簽名時,可採用aggregated key avk
來進行簽名和驗籤操作,減少重複計算量。
let verkeys = keypairs.iter().map(|k| k.ver_key.clone()).collect();
let L = HashedVerKeys::new(&verkeys);
let mut avk = AggregatedVerKey::new(&verkeys); //aggregated key
let mut signatures: Vec<Signature> = vec![];
let R = Signer::compute_aggregated_nonce(&signers[0].R);
for i in 0..num_cosigners {
let keypair = &keypairs[i];
let a = L.hash_with_verkey(&keypair.ver_key);
let sig = Signer::generate_sig_using_aggregated_objs(
msg_b,
&keypair.sig_key,
&signers[i].r,
&keypair.ver_key,
R,
&a,
&avk, //aggregated key
);
signatures.push(sig);
}
let aggr_sig = AggregatedSignature::new(&signatures).unwrap();
assert!(aggr_sig.verify_using_aggregated_verkey(msg_b, &avk)); //aggregated key
- Ver:驗籤。
assert!(aggr_sig.verify(msg_b, &verkeys));
OR
assert!(aggr_sig.verify_using_aggregated_verkey(msg_b, &avk)); //aggregated key
3. KZen-networks/multi-party-schnorr
https://github.com/KZen-networks/multi-party-schnorr/tree/master/src/protocols/aggsig 爲Musig方案和Boneh等人2018年論文《Compact Multi-Signatures for Smaller Blockchains》第5章內容的聯合實現。
Aggragated Signatures: {n,n} scheme based on simple_schnorr_multi_signatures_with_applications_to_bitcoin and the scheme for discrete-logs (section 5) from compact_multi_signatures_for_smaller_blockchains。
3.1 主要庫依賴
- serde:A generic serialization/deserialization framework。
- serde_derive:Macros 1.1 implementation of #[derive(Serialize, Deserialize)]。
- hex:Encoding and decoding data into/from hexadecimal representation.
- https://github.com/KZen-networks/curv:提供了support for some useful operations/primitives such as verifiable secret sharing, commitment schemes, zero knowledge proofs, and simple two party protocols such as ECDH and coin flip。主要支持的曲線有
Secp256k1
、Ed25519
、Jubjub
、Ristretto
和BLS12-381
。
curv = { git = "https://github.com/KZen-networks/curv" , tag = "v0.2.4", features = ["ec_secp256k1","merkle"]}
- https://github.com/KZen-networks/centipede:A scheme for instantiating KMS’s with recovery。
3.2 代碼實現
- KeyGen:
pub struct KeyPair {
pub public_key: GE,
private_key: FE,
}
// round 0: generate signing keys
let party1_key = KeyPair::create();
let party2_key = KeyPair::create();
OR
let private_key_raw = "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF";
let party1_key = KeyPair::create_from_private_key(
&BigInt::from_str_radix(&private_key_raw, 16).unwrap(),
);
- Sign:簽名過程主要分爲3個interactive階段。
// round 1: send commitments to ephemeral public keys
let party1_ephemeral_key = EphemeralKey::create();
let party2_ephemeral_key = EphemeralKey::create();
let party1_commitment = &party1_ephemeral_key.commitment;
let party2_commitment = &party2_ephemeral_key.commitment;
/ round 2: send ephemeral public keys and check commitments
// p1 release R1' and p2 test com(R1') = com(R1):
assert!(EphemeralKey::test_com(
&party2_ephemeral_key.keypair.public_key,
&party2_ephemeral_key.blind_factor,
party2_commitment
));
// p2 release R2' and p1 test com(R2') = com(R2):
assert!(EphemeralKey::test_com(
&party1_ephemeral_key.keypair.public_key,
&party1_ephemeral_key.blind_factor,
party1_commitment
));
//round 3:簽名
// compute apk:
let mut pks: Vec<GE> = Vec::new();
pks.push(party1_key.public_key.clone());
pks.push(party2_key.public_key.clone());
let party1_key_agg = KeyAgg::key_aggregation_n(&pks, 0);
let party2_key_agg = KeyAgg::key_aggregation_n(&pks, 1);
assert_eq!(party1_key_agg.apk, party2_key_agg.apk);
// compute R' = R1+R2:
let party1_r_tag = EphemeralKey::add_ephemeral_pub_keys(
&party1_ephemeral_key.keypair.public_key,
&party2_ephemeral_key.keypair.public_key,
);
let party2_r_tag = EphemeralKey::add_ephemeral_pub_keys(
&party1_ephemeral_key.keypair.public_key,
&party2_ephemeral_key.keypair.public_key,
);
assert_eq!(party1_r_tag, party2_r_tag);
// compute c = H0(Rtag || apk || message)
let party1_h_0 =
EphemeralKey::hash_0(&party1_r_tag, &party1_key_agg.apk, &message, is_musig);
let party2_h_0 =
EphemeralKey::hash_0(&party2_r_tag, &party2_key_agg.apk, &message, is_musig);
assert_eq!(party1_h_0, party2_h_0);
// compute partial signature s_i and send to the other party:
let s1 = EphemeralKey::sign(
&party1_ephemeral_key,
&party1_h_0,
&party1_key,
&party1_key_agg.hash,
);
let s2 = EphemeralKey::sign(
&party2_ephemeral_key,
&party2_h_0,
&party2_key,
&party2_key_agg.hash,
);
let r = party1_ephemeral_key.keypair.public_key.x_coor().unwrap();
assert!(verify_partial(
&ECScalar::from(&s1),
&r,
&ECScalar::from(&party1_h_0),
&ECScalar::from(&party1_key_agg.hash),
&party1_key.public_key
)
.is_ok());
// signature s:
let (r, s) = EphemeralKey::add_signature_parts(s1, &s2, &party1_r_tag);
- Ver:
assert!(verify(&s, &r, &party1_key_agg.apk, &message, is_musig,).is_ok())
參考資料:
[1] https://github.com/lovesh/signature-schemes
[2] https://github.com/KZen-networks/multi-party-schnorr
[3] 博客 rust crate: lazy_static