Compact Multi-Signatures for Smaller Blockchains代碼解析

1. 引言

Boneh等人2018年論文《Compact Multi-Signatures for Smaller Blockchains》,論文解讀參見博客 Compact Multi-Signatures for Smaller Blockchains學習筆記
其中第5章爲Discrete-Logarithm based multi-signature scheme MSDL,算法細節與Musig方案類似,詳細可參見博客 Musig方案代碼解析。【針對Schnorr signature。】

在本博客中主要解析的是https://github.com/lovesh/signature-schemes/bls倉庫中基於pairing的multi-signature MSP/AMSP/ASM/PoP等算法實現。【針對BLS signature。】

2. 主要庫依賴及features

2.1 主要庫依賴

https://github.com/lovesh/signature-schemes/tree/master/bls/Cargo.toml中內容:

[dependencies.amcl_wrapper]
version = "0.2.1"
default-features = false
features = ["bls381"]

2.2 features——公鑰和簽名group選擇

Bilinear group通常爲非對稱的,其中的1個group具有a more compact(緊湊的) representation。 pairing-based MSP/AMSP均要求public key和signature在不同的groups(即live in different groups)。

  • 對於標準簽名來說,一個公鑰會對很多消息簽名,通常會選擇用更緊湊的group來表示signatures。["SignatureG2"]
  • 而在本論文中,由於需要同時支持signature aggregation和public key aggregation,則要根據實際應用來確定group選擇。
[features]
default = ["SignatureG2"]
SignatureG1 = []    # signature and message are in G1, verification key in G2
SignatureG2 = []    # signature and message are in G2, verification key in G1
  • ["SignatureG2"]:signature和message均在G2G_2,verification key 在G1G_1,對應的是驗籤很快,簽名略慢。默認爲["SignatureG2"]以保證驗籤速度。
  • ["SignatureG1"]:signature和message均在G1G_1,verification key 在G2G_2,對應的是簽名很快,驗籤略慢。

2.3 總體代碼結構

總體代碼結構爲:

  • lib.rs:引用的amcl_wrapper庫針對不同group選擇所對應的pairing函數接口調用。
  • common.rs:簽名key/驗籤key的結構體和初始化實現。
pub struct Keypair {
    pub sig_key: SigKey, //私鑰
    pub ver_key: VerKey, //公鑰
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VerKey {
    pub point: VerkeyGroup,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SigKey {
    pub x: FieldElement,
}
  • multi_sig_fast.rs:需要通過proof of possession (PoP) 來抵抗rogue key attack。【需要naive方案+PoP,實際test函數有待參考博客 Compact Multi-Signatures for Smaller Blockchains學習筆記中第4節內容進一步改進。】
  • multi_sig_slow.rs:藉助hash函數生成係數aia_i,可抵抗rogue key attack。簽名和驗籤速度都要比multi_sig_fast.rs慢。對應爲博客 Compact Multi-Signatures for Smaller Blockchains學習筆記中第2節內容。
  • simple.rs:基礎Signature new/verify/batch_verify(不要求消息互不相同)和batch_verify_distinct_msgs(要求消息互不相同)算法實現。
  • threshold_sig.rs:基於Accountable-Subgroup Multi-signatures (ASM)的門限簽名實現。

3. 主要算法實現

3.1 Parameters Generation

在這裏插入圖片描述

let params = Params::new("test".as_bytes());
pub struct Params {
    pub g: VerkeyGroup,
}

impl Params {
    pub fn new(label: &[u8]) -> Self {
        // NUMS
        let g = VerkeyGroup::from_msg_hash(label);
        Params { g }
    }
}

3.2 Key Generation

在這裏插入圖片描述

let mut rng = thread_rng();
let keypair = Keypair::new(&mut rng, &params);
let sk = keypair.sig_key;
let vk = keypair.ver_key;
pub struct Keypair {
    pub sig_key: SigKey,
    pub ver_key: VerKey,
}

impl Keypair {
    pub fn new<R: Rng + CryptoRng>(rng: &mut R, params: &Params) -> Self {
        let sk = SigKey::new(rng);
        let vk = VerKey::from_sigkey(&sk, params);
        Keypair {
            sig_key: sk,
            ver_key: vk,
        }
    }
}

3.3 Key Aggregation

在這裏插入圖片描述

let mut sigs_and_ver_keys: Vec<(Signature, VerKey)> = Vec::new();
let mut avk = AggregatedVerKey::from_verkeys(&sigs_and_ver_keys);
 sigs_and_ver_keys.push((sig, v));
// This is a newer but SLOWER way of doing BLS signature aggregation. This is NOT VULNERABLE to
// rogue public key attack so does not need proof of possession.

pub struct AggregatedVerKey {}

impl AggregatedVerKey {
    /// Hashes a verkey with all other verkeys using a Hash function `H:{0, 1}* -> Z_q`
    /// Takes a verkey `vk_i` and all verkeys `vk_1, vk_2,...vk_n` (including `vk_i`) and calculates
    /// `H(vk_i||vk_1||vk_2...||vk_i||...vk_n)`
    pub(crate) fn hashed_verkey_for_aggregation<'a>(
        ver_key: &VerKey,
        all_ver_key_bytes: impl IntoIterator<Item = &'a (impl AsRef<[u8]> + 'a)>,
    ) -> FieldElement {
        // TODO: Sort the verkeys in some order to avoid accidentally passing wrong order of keys
        let mut res_vec: Vec<u8> = Vec::new();

        res_vec.extend_from_slice(&ver_key.to_bytes());

        for vk_bytes in all_ver_key_bytes.into_iter() {
            res_vec.extend_from_slice(vk_bytes.as_ref());
        }
        Self::hash_verkeys(res_vec.as_slice())
    }

    /// Calculates the aggregated verkey
    /// For each `v_i` of the verkeys `vk_1, vk_2,...vk_n` calculate
    /// `a_i = vk_i * hashed_verkey_for_aggregation(vk_i, [vk_1, vk_2,...vk_n])`
    /// Add all `a_i`
    pub fn from_verkeys<'a, T, V>(ver_keys: T) -> VerKey
    where
        T: IntoIterator<Item = &'a V>,
        T::IntoIter: Clone,
        V: AsRef<VerKey> + 'a
    {
        let ver_keys = ver_keys.into_iter();
        // TODO: Sort the verkeys in some order to avoid accidentally passing wrong order of keys
        let vk_bytes: Vec<_> = ver_keys.clone().map(|x| x.as_ref().to_bytes()).collect();

        let (hs, vks): (Vec<_>, Vec<_>) = ver_keys
            .map(|vk| (
                AggregatedVerKey::hashed_verkey_for_aggregation(vk.as_ref(), &vk_bytes),
                vk.as_ref().point.clone(),
            ))
            .unzip();

        let avk = VerkeyGroupVec::from(vks)
            .multi_scalar_mul_var_time(&hs.into())
            .unwrap();
        VerKey { point: avk }
    }

    /// Hash verkey bytes to field element. H_1 from the paper.
    pub(crate) fn hash_verkeys(verkey_bytes: &[u8]) -> FieldElement {
        FieldElement::from_msg_hash(&[&VERKEY_DOMAIN_PREFIX, verkey_bytes].concat())
    }
}

3.4 Signing

在這裏插入圖片描述

 let mut asig = MultiSignature::from_sigs(&sigs_and_ver_keys);
/// The aggregator needs to know of all the signer before it can generate the aggregate signature.
    /// Takes individual signatures from each of the signers and their verkey and aggregates the
    /// signatures. For each signature `s_i` from signer with verkey `v_i` calculate
    /// `a_i = hashed_verkey_for_aggregation(vk_i, [vk_1, vk_2,...vk_n])`
    /// `a_si = s_i * a_i`
    /// Add all `a_si`.
    /// An alternate construction is (as described in the paper) to let signer compute `s_i * a_i` and
    /// the aggregator simply adds each signer's output. In that model, signer does more work but in the
    /// implemented model, aggregator does more work and the same signer implementation can be used by
    /// signers of "slow" and "fast" implementation.
    pub fn from_sigs<'a, T, I>(sigs_and_ver_keys: T) -> Signature
    where
        T: IntoIterator<Item = &'a I>,
        T::IntoIter: Clone,
        I: AsRef<VerKey> + AsRef<Signature> + 'a

    {
        let sigs_and_ver_keys = sigs_and_ver_keys.into_iter();
        // TODO: Sort the verkeys in some order to avoid accidentally passing wrong order of keys

        let all_ver_key_bytes: Vec<_> = sigs_and_ver_keys
            .clone()
            .map(|x| AsRef::<VerKey>::as_ref(x).to_bytes())
            .collect();

        let (hs, sigs): (Vec<_>, Vec<_>) = sigs_and_ver_keys
            .map(|x| (
                AggregatedVerKey::hashed_verkey_for_aggregation(x.as_ref(), &all_ver_key_bytes),
                AsRef::<Signature>::as_ref(x).point.clone(),
            ))
            .unzip();

        let asig = SignatureGroupVec::from(sigs)
            .multi_scalar_mul_var_time(&hs.into())
            .unwrap();

        Signature { point: asig }
    }

3.5 Multi-Signature Verification

在這裏插入圖片描述

assert!(MultiSignature::verify(&asig, &b, &sigs_and_ver_keys, &params));
或者
assert!(asig.verify(&b, &avk, &params));
/// An aggregate VerKey is created from `ver_keys`. When verifying signature using the same
    /// set of keys frequently generate a verkey once and then use `Signature::verify`
    pub fn verify<'a, T, K>(sig: &Signature, msg: &[u8], ver_keys: T, params: &Params) -> bool
    where
        T: IntoIterator<Item = &'a K>,
        T::IntoIter: Clone,
        K: AsRef<VerKey> + 'a
    {
        let avk = AggregatedVerKey::from_verkeys(ver_keys);
        sig.verify(msg, &avk, params)
    }

pub fn verify(&self, msg: &[u8], ver_key: &VerKey, params: &Params) -> bool {
        // TODO: Check if point exists on curve, maybe use `ECP::new_big` and x cord of verkey
        if self.point.is_identity() {
            println!("Signature point at infinity");
            return false;
        }
        let msg_hash_point = Self::hash_message(msg);
        // e(self.point, params.g) == e(msg_hash_point, ver_key.point) =>
        // e(msg_hash_point, ver_key.point) * e(self.point, params.g)^-1 == 1 =>
        // e(msg_hash_point, ver_key.point) * e(self.point, params.g^-1) == 1
        ate_2_pairing(
            &msg_hash_point,
            &ver_key.point,
            &self.point,
            &params.g.negation(),
        )
        .is_one()
    }

3.6 Batch verification

在這裏插入圖片描述

	/// Batch verification of signatures. Takes a vector of 3-tuple where each tuple has a message,
    /// signature and public key. Messages can be same or different
    pub fn batch_verify(msgs_sigs: Vec<(&[u8], &Signature, &VerKey)>, params: &Params) -> bool {
        let r = FieldElement::random();
        let r_vec = FieldElementVector::new_vandermonde_vector(&r, msgs_sigs.len());
        let mut sigs = SignatureGroupVec::with_capacity(msgs_sigs.len());
        let mut hs = vec![];
        let mut vs = vec![];
        for (i, (msg, sig, vk)) in msgs_sigs.iter().enumerate() {
            sigs.push(sig.point.clone());
            hs.push(Signature::hash_message(msg));
            // The multiplication with &r_vec[i] can be moved to message instead of verkey but
            // since verkey is in group G1 by default and operations in G1 are cheaper.
            // A better way would be to have code conditional on features such that
            // multiplication is moved to message when messages are in G1 and verkey in G2.
            vs.push(&vk.point * &r_vec[i]);
        }
        let aggr_sig = sigs.multi_scalar_mul_var_time(&r_vec).unwrap();
        let mut pairings = hs
            .iter()
            .zip(vs.iter())
            .map(|(h, v)| (h, v))
            .collect::<Vec<(&SignatureGroup, &VerkeyGroup)>>();
        let neg_g = params.g.negation();
        pairings.push((&aggr_sig, &neg_g));
        ate_multi_pairing(pairings).is_one()
    }

    /// Batch verification of signatures. Takes a vector of 3-tuple where each tuple has a message,
    /// signature and public key. Assumes all messages to be distinct
    pub fn batch_verify_distinct_msgs(
        msgs_sigs: Vec<(&[u8], &Signature, &VerKey)>,
        params: &Params,
    ) -> bool {
        let mut aggr_sig = SignatureGroup::new();
        let mut hs = vec![];
        let mut vs = vec![];
        for (msg, sig, vk) in msgs_sigs {
            aggr_sig += &sig.point;
            hs.push(Signature::hash_message(msg));
            vs.push(vk);
        }
        let mut pairings = hs
            .iter()
            .zip(vs)
            .map(|(h, v)| (h, &v.point))
            .collect::<Vec<(&SignatureGroup, &VerkeyGroup)>>();
        let neg_g = params.g.negation();
        pairings.push((&aggr_sig, &neg_g));
        ate_multi_pairing(pairings).is_one()
    }

3.7 Proof of Possession

即相當於用私鑰對公鑰進行簽名。

	#[test]
    fn proof_of_possession() {
        let mut rng = thread_rng();
        let params = Params::new("test".as_bytes());
        let keypair = Keypair::new(&mut rng, &params);
        let sk = keypair.sig_key;
        let vk = keypair.ver_key;

        let proof = ProofOfPossession::generate(&vk, &sk); 
        assert!(ProofOfPossession::verify(&proof, &vk, &params));
    }
pub struct ProofOfPossession {}
impl ProofOfPossession {
    // Used for domain separation while creating Proof of Possession
    const PoP_DOMAIN_PREFIX: [u8; 2] = [2, 2];

    pub fn generate(verkey: &VerKey, sigkey: &SigKey) -> Signature {
        Signature::new(
            &[&Self::PoP_DOMAIN_PREFIX, verkey.to_bytes().as_slice()].concat(),
            &sigkey,
        )
    }

    pub fn verify(proof: &Signature, verkey: &VerKey, params: &Params) -> bool {
        proof.verify(
            &[&Self::PoP_DOMAIN_PREFIX, verkey.to_bytes().as_slice()].concat(),
            verkey,
            params,
        )
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章