Git Product home page Git Product logo

bls's Introduction

bls Crates.io

Boneh-Lynn-Shacham (BLS) signatures have slow signing, very slow verification, require slow and much less secure pairing friendly curves, and tend towards dangerous malleability. Yet, BLS permits a diverse array of signature aggregation options far beyond any other known signature scheme, which makes BLS a preferred scheme for voting in consensus algorithms and for threshold signatures.

In this crate, we take a largely unified approach to aggregation techniques and verifier optimisations for BLS signature: We support the BLS12-381 and BLS12-377 (Barreto-Lynn-Scott) curves via Arkworks traits, but abstract the pairing so that developers can choose their preferred orientation for BLS signatures. We provide aggregation techniques based on messages being distinct, on proofs-of-possession, and on delinearization, although we do not provide all known optimisations for delinearization.

We provide implementation of generation and verification proof-of-possession based on Schnorr Signature which is faster than using BLS Signature itself for this task.

We cannot claim these abstractions provide miss-use resistance, but they at least structure the problem, provide some guidlines, and maximize the relevance of warnings present in the documentation.

Documentation

You first bring the bls crate into your project just as you normally would.

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) );

In this example, sig is a Signature<ZBLS> which only contains signature. One can use Keypair::signed_message method which returns a SignedMessage struct that contains the message hash, the signer's public key, and of course the signature, but one should usually detach these constituents for wire formats.

Aggregated and blind signatures are almost the only reasons anyone would consider using BLS signatures, so we focus on aggregation here. We assume for brevity that sigs is an array of SignedMessages, as one might construct like

As a rule, aggregation that requires distinct messages still requires one miller loop step per message, so aggregate signatures have rather slow verification times. You can nevertheless achieve quite small signature sizes like

#[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())
}

Anyone who receives the already aggregated signature along with a list of messages and public keys might reconstruct the signature as shown in the above example.

We recommend distinct message aggregation like this primarily for verifying proofs-of-possession, meaning checking the self certificates for numerous keys.

Assuming you already have proofs-of-possession, then you'll want to do aggregation with BitPoPSignedMessage or some variant tuned to your use case. We recommend more care when using SignatureAggregatorAssumingPoP because it provides no mechanism for checking a proof-of-possession table.

The library offers method for generating and verifying proof of positions both based on BLS and Schnorr Signature which is faster to verify than when using BLS signature itself as proof of position. The following example demonstrate how to generate and verify proof of positions and then using SignatureAggregatorAssumingPoP to batch and verify multiple BLS signatures.

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())

If you lack proofs-of-possesion, then delinearized approaches are provided in the delinear module, but such schemes might require a more customised approach. However, note that currently only aggeration assuming proof of possession is maintained and the other strategies are experimental.

Efficient Aggregatable BLS Signatures with Chaum-Pedersen Proofs

The scheme introduced in our recent paper is implemented in chaum_pederson_signature.rs using ChaumPedersonSigner and ChaumPedersonVerifier traits and in pop.rs using add_auxiliary_public_key and verify_using_aggregated_auxiliary_public_keys functions which is demonestrated in the following example:

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"
);

Hash to Curve

In order to sign a message, the library needs to hash the message as a point on the signature curve. While BLSEngine trait is agnostic about MapToSignatureCurve method, our implementation of BLS12-381 (ZBLS) and BLS12-377(BLS377) specifically uses Wahby and Boneh hash to curve method described in Section of 6.6.3 of https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/.

Security Warnings

This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

bls's People

Contributors

burdges avatar drskalman avatar inaoana avatar ivanceras avatar str4d avatar swasilyev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bls's Issues

Use WB hash to curve for BLS Engine

This require in part to replace the dependancy to arkworsk-w3f. This also temprory breaks BLS12-381 signature. Because WB hash has not implement for BLS12-381 yet. But the tests should pass for BLS12-377.

Move TinyBLS to new zexe format

tests are failing because we have not ported TinyBLS implementation and I think now that UsualBLS is ported successfully it should be easy to port TinyBLS as well.

Performance issues

Hi,

After aggregating 500 signatures into one, the performances are dramatically slow, and I noticed it's exponentially slower for every signature I add. Is there a way to improve it without changing crate? I'd like to keep BLS like.

I'm looking basically to decrease the time it takes to verify aggregated signatures.

Thank you so much for this publishing and your work btw!

Radical performance notes

Just a thread for more notes on more extreme performance measures. Anything sane should be done or given its own issue.

Add threshold (multi)signatures

I believe the most advanced threshold signature implementation is https://github.com/poanetwork/threshold_crypto but they do not provide our abstractions, and one should optimize the arithmetic ala poanetwork/threshold_crypto#13 We should do threshold signatures in a way that supports #4 if possible. All this sounds doable if threshold signatures are provided in a way that exploits the current aggregation abstraction, but it's worth considering any parallels with w3f/schnorrkel#11 too.

Update BLS library to Pairing and Serialization changes

There are quite few changes in Arkworks Algebra which need to be reflected in the BLS library. including the renaming of the Projective and Affine Curve, removal of to_bytes and from_bytes interfaces and changes in pairing engine. BLS library need to compile and pass the tests again.

Reverse beefy messages to only support unaggregated BLS signatures in Beefy

APK proof can only be applied to bitfield (disjoint) aggregated BLS signature (in oppose with counted aggregation). This makes it impossible for validator to apply disjoint aggregation to aggregated BLS signatures they receive in a disorganized gossip. As such till we have a more intelligent gossip topolgy it is impossible for validators to aggregate BLS signatures before gossiping them again. As such they should sent the full list of unaggregated signatures and the prover will aggregate them before producing the proof.

Delinearized SignerTable

We need some mechanism for creating deliniarized SignerTables as an alternative to proofs-of-possession.

Enable BEEFY Keystore to produce BLS signature

Currently happening at https://github.com/drskalman/substrate/blob/skalman-bls-beefy/client/beefy/src/keystore.rs#L77

It only support signing ecdsa signature. However, in the case, where beefy message is also going to contain a single aggregated signature, this need to be change. The easiest path to this is to have AggregatableBeefyKeyStore trait which will generate and aggregate BLS signature. One could implemenet this trait for MMRed BEEFY signature and do nothing in this way the beefyclient code does not need to be changed wether we are dealing with MMRed Aggregated or BLS aggregated signature.

Make ValidatorSet generic over public key type

ValidatorSet is a set of authority ids, of type of primitive::beefy::crypto:Public which is currently hard wired to ECDSA public key. We need to change primitives::beefy::crypto:Public to primitives::beefy::crypto:ECDSAPublic and then make primitives::beefy::crypto:Public generic to be either ECDSA or ECDSA,BLS pair. that way the whole code of beefy validator can be generic over type of the public key.

Implement bls signature related functions for (substrate) host API

#47 depends on application crypto which on its turn depends on host api providing the following functions

		sp_io::crypto::{
        bls_public_keys(key_type),
        bls_generate(key_type, seed)
        bls_sign(key_type, self, msg.as_ref())
		bls_verify(&signature, msg.as_ref(), self)}

which this issue requires to implement.

Decompression extremely slow

Hi!

Thank you for this amazing crate, It seems like it's the best one available for Rust now.
However I'm having one quite annoying issue with it, it seems that the decompression function is extremely slow, at least too slow for my use case.

I've seen online there is 2 methods of compression, one patented and another one not, which one do you use?

Do you know anyway of making this function faster?
pub fn from_bytes(bytes: [u8; 96]) -> Result<Self, GroupDecodingError>

https://docs.rs/bls-like/0.1.0/bls_like/single/struct.Signature.html#method.decompress

Have a nice day!

Make PoK works for both G1 and G2

We should maybe make this PoK do both G1 and G2 if we're going that route, no? I guess that's a seperate thing that depends upon only Engine.

Implement Pixel

We should implement Pixel because doing so appears fairly straightforward and somewhat orthogonal. I believe the primary hurdle would be abstracting the verification equation, but if done properly then all the underlying optimizations work.

I think EngineBLS could act as both the curve and orientation like now, while some SignatureScheme extension trait provides the verification equation. We'd have BLS<E: EngineBLS> and Pixel<E: EngineBLS> ZSTs that satisfy SignatureScheme.

In this, Pixel's associated type SignatureGroup becomes the cartesian product of both groups, which incidentally makes us more dependent upon our patched version of pairing for batch_normalization.

Use a stronger key splitting?

Any BLS signature library needs key splitting since afaik no constant-time pairing libraries exist, well not everyone believes the amcl claims. We do not care about the pairing itself being constant time of course, but we do signatures on the curve over an extension field, and the extension field arithmetic is not constant time.

What should our key splitting look like?

In this crate, I've used the simple aH(m) + bH(m) with a + b = s rerandomized before each signature in https://github.com/w3f/bls/blob/master/src/single.rs#L158-L189

Yet, much stronger approaches exist like a*(H(m)-X) + b*(H(m)-Y) + (aX+bY) with the (aX+bY) precomputed in the previous signature, and X, Y, and a+b=s rerandomized. In this variant, the attacker knows literally nothing about any of the inputs to the dangerous * operations, not even the point. So

/// We sign using the formula a*(H(m)-X) + b*(H(m)-Y) + (a*X+b*Y) where
/// a, b, X, Y are chosen randomly subject to a + b being our actual secret key,
/// and (a*X+b*Y) being precomputed.
pub struct SecretKey<E: EngineBLS> {
    /// [a, b]
    secrets: [E::Scalar; 2],
    /// [X, Y]
    points: [E::SignatureGroup; 2],
    /// (a*X+b*Y)
    previous: E::SignatureGroup,
}

In between, I suppose a*(H(m) - X) + b*(H(m)+X) + (a-b)*X sounds quite reasonable, so

pub struct SecretKey<E: EngineBLS> {
    a: E::Scalar,
    b: E::Scalar,
    x: E::SignatureGroup,
    y: Option<E::SignatureGroup>, // (a-b)*x
}

There are two extra scalar multiplications in the first, but only one in the second, but the second ties this scalar multiplication to the current values of a and b while the first permits leaving either a and b or X and Y fixed for longer periods.

Importantly, these extra scalar multiplications could be done in another thread, so the second requires resplit to clear y, but now resplit should be called after sign_once and another function that precomputes y should be called in between resplit and the next sign_once.

Adopt the IRTF CFRG draft standard

There is a new hash to curve function implemented in the fork https://github.com/kwantam/pairing/ via zkcrypto/pairing#56 (comment) which should be evaluated.

There is also a draft BLS standard by the IETF at https://datatracker.ietf.org/doc/draft-boneh-bls-signature/ but they only hash-to-G1, which makes verification slow after extensive aggregation. In fact, you'd only ever use BLS with extensive aggregation, so this sounds silly, but perhaps hash-to-G2 is so slow that more extensive aggregation is required before hash-to-G2 wins.

Also, the standard recommends hashing-to-G1 with try-and-increment, which sound archaic. We currently use Michael Orru's adaptation of Fouque-Tibouchi as implemented in zkcrypto/pairing#30 but Wahby-Boneh sounds interesting too.

Clean up BLS PoP code and prepare it for review

Module names are dragging from the time library were coming from file coin. Also there is a lot of commented out code to be dealt with. This ticket for one last review of the code before submission for the first review.

Review, verify, clean up and update README.md

Check if the fact mentioned in the README.md of the crate is still relevant and if the README code compiles. Additionally:

  • Emphasize on simple cases so we do not scare people out: One messaged singed by two party according to IETF proposal.
  • Add explanation for the hash to curve because the library is opinionated about it.
  • Explain Schnorr pop.

How is `context` meant to be used when creating a new `Message`?

Most code from this library uses the text "ctx" although the README uses "Some context" and the From impl for Message uses a blank string.

It seems like the string plus length of message gets prepended to the message itself, implying any signatures derived from this Message will be affected by the choice of context.

What is it for, and what is best practice for its usage?

modify `client/beefy/src/worker.rs` to use either ECDSA or BLSnECDSA keystores

worker.rs currently only using BeefyKeystore to carry out crypto tasks, however in the new structure BeefyKeystore is only a trait now and the worker either need to instiate BeefyECDSAKeystor or BeefyBLSnECDSAKeystore depending if they want to certify their BEEFY messages using Merkle tree of ECDSA signature or an aggregated BLS signature.

Select between ZEXE and ZCash crates

We should select between using Zexe's algebra and algebra-core vs Zcash's new crates, so I'll outline the trade offs:

I think both curve abstraction traits hierarchies differ only somewhat, like both use core::ops traits similarly.

  • Pro-Zcash: Zcash's traits looks slightly more detailed, so maybe Zexe would adopt Zcash's traits eventually. Zexe would surely accept pulls that made these traits more similar. Also Toni Aceri uses Zcash's traits some places. Zcash's traits look stable.
  • Pro-Zexe: Zexe's maybe handled serialization nicer, but not sure. It's a smaller migration, so less work now, but much less stable.

Zexe support's bls12_377 which gives us Plonk proofs eventually, although only groth16 works right now probably. At first, I imagined this gave Zexe a definitive win, but not really: We suspect altering the bls12_381 crate to handle bls12_377 might not be that hard, and @drskalman might enjoy doing so. If so, a Zcash based signer could supply signatures for a Zexe based prover, although not sure how muc extra effort this requires. If annoying, then Zcash removed their prover abstraction trait JubjubEngine, which makes Zcash even more annoying.

Zcash support gives us constant time bls12_381, which becomes extremely important for bls. Zexe maybe added constant time, or will do so. Zcash folks were much more careful about this however. Zcash code was written to be production and they'd spend vastly more on auditing. Zcash could abandon pairing entirely if halo2 works out, but that's at least 5 years away.

All this gives us three questions:

  1. Is it easier to migrate to Zexe traits than to Zcash traits?
  2. Is it trivial to modify Zcash's bls12_381 crate into bls12_377?
  3. Is either or their forks tracking the IRTF hash-to-curve spec closer?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.