Musig方案代碼解析

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:每個簽名者生成公私鑰對(x,X=gx)(x,X=g^x)
  • Sign:待簽名消息mm,所有簽名者公鑰L={X1,,Xn}L=\{X_1,\cdots,X_n\},對於i{1,,n}i\in\{1,\cdots,n\},簽名者計算ai=Hagg(L,Xi)a_i=H_{agg}(L,X_i),然後計算aggregated public key X~=i=1nXiai\tilde{X}=\prod_{i=1}^{n}X_i^{a_i};選擇隨機數r1Zqr_1\leftarrow \mathbb{Z}_q,計算R1=Gr1,t1=Hcom(R1)R_1=G^{r_1},t_1=H_{com}(R_1),將t1t_1發送給所有其它簽名者;當收齊其它簽名者發來的t2,,tnt_2,\cdots,t_n時,將R1R_1發送給所有其它簽名者;當收到其它簽名者發來的R2,,RnR_2,\cdots,R_n時,驗證ti=Hcom(Ri))t_i=H_{com}(R_i))是否成立(for all i{2,,n}i\in\{2,\cdots,n\}),若不成立則停止,否則繼續計算R=i=1nRi,c=Hsig(X~,R,m),s1=r1+ca1x1mod  pR=\prod_{i=1}^{n}R_i,c=H_{sig}(\tilde{X},R,m),s_1=r_1+ca_1x_1\mod p,將s1s_1發送給其它所有簽名者;當收到了所有的簽名信息s2,,sns_2,\cdots,s_n時,計算s=i=1nsimod  ps=\sum_{i=1}^{n}s_i\mod p,最終的簽名信息爲σ=(R,s)\sigma=(R,s)。【各簽名者之間需要交互interactive。】
  • Ver:Given L={X1,,Xn},m,σ=(R,s)L=\{X_1,\cdots,X_n\},m,\sigma=(R,s),驗籤者計算ai=Hagg(L,Xi),X~=i=1nXiaic=Hsig(X~,R,m)a_i=H_{agg}(L,X_i),\tilde{X}=\prod_{i=1}^{n}X_i^{a_i},c=H_{sig}(\tilde{X},R,m),驗證gs=Ri=1nXiaic=RX~cg^s=R\prod_{i=1}^{n}X_i^{a_ic}=R\tilde{X}^c是否成立,若成立則簽名驗證成功。

2. lovesh/signature-schemes

https://github.com/lovesh/signature-schemes/musig中爲Musig方案的代碼實現。

2.1 主要庫依賴

https://github.com/lovesh/signature-schemes/musig/Cargo.toml中內容:

[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)第一階段:每個簽名者選擇隨機數riZqr_i\leftarrow \mathbb{Z}_q,計算相應的commitment Ri=Gri,ti=Hcom(Ri)R_i=G^{r_i},t_i=H_{com}(R_i),將tit_i發送給所有其它簽名者。必須保證收齊了所有其它人的tit_i值(可調用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值tit_i後,簽名者會將相應的commitment RiR_i發送給所有其它簽名者。當簽名者收齊(調用got_commitment,用於驗證ti=Hcom(Ri)t_i=H_{com}(R_i)是否成立。)了所有其他人的RiR_i值之後(可調用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 RiR_i後,各個簽名者分別用自己的私鑰對同一消息進行簽名。

	 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。主要支持的曲線有Secp256k1Ed25519JubjubRistrettoBLS12-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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章