5个版本

0.1.4 2024年6月2日
0.1.3 2023年5月8日
0.1.2 2023年4月19日
0.1.1 2023年4月19日
0.1.0 2023年4月16日

#37 in 密码学

Download history 27719/week @ 2024-05-03 32742/week @ 2024-05-10 29683/week @ 2024-05-17 41944/week @ 2024-05-24 36762/week @ 2024-05-31 30586/week @ 2024-06-07 30848/week @ 2024-06-14 39921/week @ 2024-06-21 29704/week @ 2024-06-28 32180/week @ 2024-07-05 42643/week @ 2024-07-12 43751/week @ 2024-07-19 41067/week @ 2024-07-26 39987/week @ 2024-08-02 54667/week @ 2024-08-09 33089/week @ 2024-08-16

175,361 每月下载量
339 个crate中使用 (3直接使用)

MIT/Apache

205KB
3K SLoC

bls Crates.io

Boneh-Lynn-Shacham (BLS)签名具有慢速签名、非常慢的验证速度、需要慢速且安全性较低的配对友好曲线,并且容易受到危险的模态影响。然而,BLS允许比任何其他已知签名方案更多的签名聚合选项,这使得BLS成为共识算法中投票和门限签名的首选方案。

在这个crate中,我们采用了对BLS签名聚合技术和验证器优化的大致统一方法:我们通过Arkworks traits支持BLS12-381BLS12-377(Barreto-Lynn-Scott)曲线,但抽象化配对,以便开发者可以选择他们喜欢的BLS签名方向。我们提供了基于消息唯一性、占位证明和反线化的聚合技术,尽管我们并没有提供所有已知反线化的优化。

我们提供了基于Schnorr签名生成和验证占位证明的实现,这比使用BLS签名本身进行此任务更快。

我们无法宣称这些抽象提供了误用抵抗,但它们至少结构化了问题,提供了一些指导,并最大化了文档中存在的警告的相关性。

文档

您首先像平常一样将bls crate引入到您的项目中。

use w3f_bls::{Keypair,ZBLS,Message,Signed};

let mut keypair = Keypair::<ZBLS>::generate(::rand::thread_rng());
let message = Message::new(b"Some context",b"Some message");
let sig = keypair.sign(&message);
assert!( sig.verify(&message,&keypair.public) );

在这个例子中,sig是一个只包含签名的Signature<ZBLS>,可以使用Keypair::signed_message方法,该方法返回一个包含消息哈希、签名者的公钥以及当然签名的SignedMessage结构,但通常应该将这些组成部分解耦以适应网络格式。

聚合签名和盲签名几乎是人们考虑使用BLS签名的主要原因,因此我们在此重点关注聚合签名。为了简化,我们假设sigs是一个SignedMessage数组的实例,就像人们可能构建的那样:

通常,需要不同消息的聚合仍然需要每个消息一个Miller循环步骤,因此聚合签名的验证时间相对较慢。但您仍然可以实现非常小的签名大小,例如:

#[cfg(feature = "experimental")]
use w3f_bls::{distinct::DistinctMessages, Keypair, Message, Signed, ZBLS};

#[cfg(feature = "experimental")]
{
	let mut keypairs = [
		Keypair::<ZBLS>::generate(::rand::thread_rng()),
		Keypair::<ZBLS>::generate(::rand::thread_rng()),
	];
	let msgs = [
		"The ships",
		"hung in the sky",
		"in much the same way",
		"that bricks don’t.",
	]
	.iter()
	.map(|m| Message::new(b"Some context", m.as_bytes()))
	.collect::<Vec<_>>();
	let sigs = msgs
		.iter()
		.zip(keypairs.iter_mut())
		.map(|(m, k)| k.signed_message(m))
		.collect::<Vec<_>>();

		let dms = sigs
		.iter()
		.try_fold(DistinctMessages::<ZBLS>::new(), |dm, sig| dm.add(sig))
		.unwrap();
	let signature = <&DistinctMessages<ZBLS> as Signed>::signature(&&dms);

		let publickeys = keypairs.iter().map(|k| k.public).collect::<Vec<_>>();
	let mut dms = msgs
		.into_iter()
		.zip(publickeys)
		.try_fold(
			DistinctMessages::<ZBLS>::new(),
			|dm, (message, publickey)| dm.add_message_n_publickey(message, publickey),
		)
		.unwrap();
	dms.add_signature(&signature);
	assert!(dms.verify())
}

任何收到已聚合签名、消息列表和公钥的人都可以像上面示例中那样重建签名。

我们主要推荐使用这种不同消息聚合方法来验证持有证明,这意味着检查大量密钥的自我证书。

假设您已经拥有持有证明,那么您将希望使用BitPoPSignedMessage或针对您的用例调整的变体进行聚合。我们建议在使用SignatureAggregatorAssumingPoP时要更加小心,因为它不提供检查持有证明表的机制。

该库提供了基于BLS和Schnorr签名生成和验证持有证明的方法,后者比使用BLS签名本身作为持有证明更快。以下示例演示了如何生成和验证持有证明,然后使用SignatureAggregatorAssumingPoP批量验证多个BLS签名。

use w3f_bls::{Keypair,PublicKey,ZBLS,Message,Signed, ProofOfPossessionGenerator, ProofOfPossession, schnorr_pop::{SchnorrPoP}, multi_pop_aggregator::MultiMessageSignatureAggregatorAssumingPoP};
use sha2::Sha256;

let mut keypairs = [Keypair::<ZBLS>::generate(::rand::thread_rng()), Keypair::<ZBLS>::generate(::rand::thread_rng())];
let msgs = ["The ships", "hung in the sky", "in much the same way", "that bricks don’t."].iter().map(|m| Message::new(b"Some context", m.as_bytes())).collect::<Vec<_>>();
let sigs = msgs.iter().zip(keypairs.iter_mut()).map(|(m,k)| k.sign(m)).collect::<Vec<_>>();

let publickeys = keypairs.iter().map(|k|k.public.clone()).collect::<Vec<_>>();
let pops = keypairs.iter_mut().map(|k|(ProofOfPossessionGenerator::<ZBLS, Sha256, PublicKey<ZBLS>, SchnorrPoP<ZBLS>>::generate_pok(k))).collect::<Vec<_>>();

//first make sure public keys have valid pop
let publickeys = publickeys.iter().zip(pops.iter()).map(|(publickey, pop) | {assert!(ProofOfPossession::<ZBLS, Sha256, PublicKey<ZBLS>>::verify(pop,publickey)); publickey}).collect::<Vec<_>>();

let batch_poped = msgs.iter().zip(publickeys).zip(sigs).fold(
    MultiMessageSignatureAggregatorAssumingPoP::<ZBLS>::new(),
    |mut bpop,((message, publickey),sig)| { bpop.add_message_n_publickey(message, &publickey); bpop.add_signature(&sig); bpop }
);
assert!(batch_poped.verify())

如果您缺乏持有证明,则可以在delinear模块中提供非线化方法,但这些方案可能需要更定制化的方法。但是请注意,目前只维护基于持有证明的聚合,其他策略是实验性的。

基于Chaum-Pedersen证明的高效可聚合BLS签名

我们在我们最近的论文中介绍的计划在chaum_pederson_signature.rs中实现,使用ChaumPedersonSignerChaumPedersonVerifier特质,并在pop.rs中使用add_auxiliary_public_keyverify_using_aggregated_auxiliary_public_keys函数进行演示。

use sha2::Sha256;
use ark_bls12_377::Bls12_377;
use ark_ff::Zero;
use rand::thread_rng;

use w3f_bls::{
    single_pop_aggregator::SignatureAggregatorAssumingPoP, DoublePublicKeyScheme, EngineBLS, Keypair, Message, PublicKey, PublicKeyInSignatureGroup, Signed, TinyBLS, TinyBLS377,
};


let message = Message::new(b"ctx", b"I'd far rather be happy than right any day.");
let mut keypairs: Vec<_> = (0..3)
    .into_iter()
    .map(|_| Keypair::<TinyBLS<Bls12_377, ark_bls12_377::Config>>::generate(thread_rng()))
    .collect();
let pub_keys_in_sig_grp: Vec<PublicKeyInSignatureGroup<TinyBLS377>> = keypairs
    .iter()
    .map(|k| k.into_public_key_in_signature_group())
    .collect();

let mut prover_aggregator =
    SignatureAggregatorAssumingPoP::<TinyBLS377>::new(message.clone());
let mut aggregated_public_key =
    PublicKey::<TinyBLS377>(<TinyBLS377 as EngineBLS>::PublicKeyGroup::zero());

//sign and aggegate
let _ = keypairs
    .iter_mut()
    .map(|k| {
        prover_aggregator.add_signature(&k.sign(&message));
        aggregated_public_key.0 += k.public.0;
    })
    .count();

let mut verifier_aggregator = SignatureAggregatorAssumingPoP::<TinyBLS377>::new(message);

verifier_aggregator.add_signature(&(&prover_aggregator).signature());

//aggregate public keys in signature group
verifier_aggregator.add_publickey(&aggregated_public_key);

pub_keys_in_sig_grp.iter().for_each(|pk| {verifier_aggregator.add_auxiliary_public_key(pk);});

assert!(
    verifier_aggregator.verify_using_aggregated_auxiliary_public_keys::<Sha256>(),
    "verifying with honest auxilary public key should pass"
);

散列到曲线

为了对消息进行签名,库需要将消息散列为签名曲线上的一个点。尽管BLSEngine特质对MapToSignatureCurve方法不敏感,但我们BLS12-381(ZBLS)和BLS12-377(BLS377)的实现专门使用Wahby和Boneh散列到曲线方法,该方法在https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/的第6.6.3节中描述。

安全警告

此库不对恒等时间操作、内存访问模式或对旁路攻击的抵抗力做出任何保证。

许可

根据您的选择,许可如下:

任选其一。

贡献

除非您明确声明,否则根据Apache-2.0许可证定义的,您有意提交以包含在您的工作中的任何贡献,均应如上双许可,而不附加任何额外条款或条件。

依赖

~7.5MB
~125K SLoC