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均在 G 2 G_2 ,verification key 在 G 1 G_1 ,对应的是验签很快,签名略慢。默认为["SignatureG2"]以保证验签速度。
  • ["SignatureG1"]:signature和message均在 G 1 G_1 ,verification key 在 G 2 G_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函数生成系数 a i a_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_verifiy中用 ( r , r 2 , , r b ) (r,r^2,\cdots,r^b) (:new_vandermonde_vector函数)来代替 ( ρ 1 , , ρ b ) (\rho_1,\cdots,\rho_b)

	/// 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,
        )
    }
}

3.8 基于Accountable-Subgroup Multi-signatures (ASM)的门限签名

其中的Parameter generation, key generation, and key aggregation 分别与3.1/3.2/3.3节的算法一致。不同之处在于, key generation之前需增加Group Setup算法 ,相应的SigningVerification算法也有所不同。

3.8.1 Parameter generation

let params = Params::new("test".as_bytes());

3.8.2 Group Setup & Key generation

ASM的整个签名过程为非交互的。但是初始的key generation 需要在所有 n n 个签名者之间运行a simple one-round protocol,使得每个签名者都可obtain a group-specific membership key m k mk that it needs to sign messages for the group P K \mathcal{PK}
在这里插入图片描述
ASM中所有签名者需要有统一全局的index,整个过程可由可信第三方执行Shamir secret sharing来构建,而不再使用论文中的hash函数 H 0 , H 1 , H 2 H_0,H_1,H_2 。(具体可参见博客 verifiable secret sharing可验证的秘密共享【借用的思想为:aggregate secret key对应为aggregate public key。以下secret_x为有毒垃圾,应丢弃。threshold_vk即为aggregate public key。】
在这里插入图片描述

pub struct Signer {
    pub id: usize,
    pub sigkey: SigKey,
    pub verkey: VerKey,
}
		let threshold = 3;
        let total = 6;
		let (secret_x, signers) = trusted_party_SSS_keygen(threshold, total, &params);
		 
		 
		let mut keys = vec![];
        keys.push((signers[0].id, &signers[0].verkey));
        keys.push((signers[2].id, &signers[2].verkey));
        keys.push((signers[4].id, &signers[4].verkey));

        check_threshold_key_gen_gaps_in_ids(threshold, secret_x, keys, &params);//验证aggregate secret key对应为aggregate public key。

/// Keygen done by trusted party using Shamir secret sharing. Creates signing and verification
/// keys for each signer. The trusted party will know every signer's secret keys and the
/// aggregate secret keys and can create signatures.
/// Outputs 2 items, first is the shared secret and should be destroyed.
/// The second contains the keys, 1 item corresponding to each signer.
pub fn trusted_party_SSS_keygen(
    threshold: usize,
    total: usize,
    params: &Params,
) -> (FieldElement, Vec<Signer>) {
    let (secret_x, x_shares) = get_shared_secret(threshold, total);
    (secret_x, keygen_from_shares(total, x_shares, params)) 
}

/// Takes shares for x and generate signing and verification keys
fn keygen_from_shares(
    num_signers: usize,
    mut x_shares: HashMap<usize, FieldElement>,
    params: &Params,
) -> Vec<Signer> {
    let mut signers = vec![];
    for i in 0..num_signers {
        let id = i + 1;
        let x_i = x_shares.remove(&id).unwrap();
        let g_x_i = &params.g * &x_i;

        signers.push(Signer {
            id,
            sigkey: SigKey { x: x_i },
            verkey: VerKey { point: g_x_i },
        })
    }
    signers
}

fn check_threshold_key_gen_gaps_in_ids(
        threshold: usize,
        secret_x: FieldElement,
        keys_to_aggr: Vec<(usize, &VerKey)>,
        params: &Params,
    ) {
        let threshold_vk = ThresholdScheme::aggregate_vk(threshold, keys_to_aggr);

        let expected_vk = &params.g * &secret_x;
        assert_eq!(expected_vk, threshold_vk.point);
    }

pub fn aggregate_vk(threshold: usize, keys: Vec<(usize, &VerKey)>) -> VerKey {
        assert!(keys.len() >= threshold);

        let mut vk_bases = VerkeyGroupVec::with_capacity(threshold);
        let mut vk_exps = FieldElementVector::with_capacity(threshold);

        let signer_ids = keys
            .iter()
            .take(threshold)
            .map(|(i, _)| *i)
            .collect::<HashSet<usize>>();
        for (id, vk) in keys.into_iter().take(threshold) {
            let l = Polynomial::lagrange_basis_at_0(signer_ids.clone(), id);
            vk_bases.push(vk.point.clone());
            vk_exps.push(l.clone());
        }

        // threshold verkey = vk_1^l_1 * vk_2^l_2 * ... vk_i^l_i for i in threshold

        VerKey {
            point: vk_bases.multi_scalar_mul_var_time(&vk_exps).unwrap(),
        }
    }

/// Generate a random polynomial with the secret at the polynomial evaluation at 0.
pub fn get_shared_secret_with_polynomial(
    threshold: usize,
    total: usize,
) -> (FieldElement, HashMap<usize, FieldElement>, Polynomial) {
    let random_poly = Polynomial::random(threshold - 1);
    let secret = random_poly.eval(&FieldElement::zero());
    let shares = (1..=total)
        .map(|x| (x, random_poly.eval(&FieldElement::from(x as u64))))
        .collect::<HashMap<usize, FieldElement>>();
    (secret, shares, random_poly)
}

/// Generate a secret with its shares according to Shamir secret sharing.
/// Returns the secret and a map of share_id -> share
pub fn get_shared_secret(
    threshold: usize,
    total: usize,
) -> (FieldElement, HashMap<usize, FieldElement>) {
    let (secret, shares, _) = get_shared_secret_with_polynomial(threshold, total);
    (secret, shares)
}

    /// Return the Lagrange basis polynomial at x = 0 given the x coordinates
    pub fn lagrange_basis_at_0(x_coords: HashSet<usize>, i: usize) -> FieldElement {
        let mut numerator = FieldElement::one();
        let mut denominator = FieldElement::one();
        let i_as_field_elem = FieldElement::from(i as u64);
        let neg_i = -i_as_field_elem; // -i
        for x in x_coords {
            if x == i {
                continue;
            }
            // numerator = numerator * x
            let x_as_field_elem = FieldElement::from(x as u64);
            numerator = &numerator * &x_as_field_elem;
            let x_minus_i = &x_as_field_elem + &neg_i;
            // denominator = denominator * (x - i)
            denominator = &denominator * &x_minus_i;
        }
        denominator.inverse_mut();
        // (x_coords[0]) * (x_coords[1]) * ... / ((x_coords[0] - i) * (x_coords[1] - i) * ...)
        numerator * denominator
    }

3.8.3 Key aggregation

		let mut keys = vec![];
        keys.push((signers[1].id, &signers[1].verkey)); // signer id is 2
        keys.push((signers[3].id, &signers[3].verkey)); // signer id is 4
        keys.push((signers[5].id, &signers[5].verkey)); // signer id is 6

        let threshold_vk = ThresholdScheme::aggregate_vk(threshold, keys);

3.8.4 门限签名Signing

在这里插入图片描述

		// Signers from which signature will be requested.
        let mut signer_ids = HashSet::new();
        signer_ids.insert(1);
        signer_ids.insert(3);
        signer_ids.insert(5);

        let mut sigs = vec![];
        for i in &signer_ids {
            let sig = Signature::new(&msg, &signers[*i].sigkey);
            assert!(sig.verify(&msg, &signers[*i].verkey, &params));
            sigs.push((signers[*i].id, sig));
        }

        let threshold_sig = ThresholdScheme::aggregate_sigs(threshold, sigs);
pub fn aggregate_sigs(threshold: usize, sigs: Vec<(usize, Signature)>) -> Signature {
        assert!(sigs.len() >= threshold);
        let mut s_bases = SignatureGroupVec::with_capacity(threshold);
        let mut s_exps = FieldElementVector::with_capacity(threshold);

        let signer_ids = sigs
            .iter()
            .take(threshold)
            .map(|(i, _)| *i)
            .collect::<HashSet<usize>>();
        for (id, sig) in sigs.into_iter().take(threshold) {
            let l = Polynomial::lagrange_basis_at_0(signer_ids.clone(), id);
            s_bases.push(sig.point.clone());
            s_exps.push(l);
        }
        // theshold signature = sig[i]^l for all i
        Signature {
            point: s_bases.multi_scalar_mul_const_time(&s_exps).unwrap(),
        }
    }

3.8.5 门限签名Verification

在这里插入图片描述

assert!(threshold_sig.verify(&msg, &threshold_vk, &params));

可以像普通signature一样用aggregate public key进行直接验签。

猜你喜欢

转载自blog.csdn.net/mutourend/article/details/107020751