zkcrypto / pairing Goto Github PK
View Code? Open in Web Editor NEWPairing-friendly elliptic curve library.
License: Other
Pairing-friendly elliptic curve library.
License: Other
Depends on #1 probably.
Requires the guarantee that all prime field moduli > 2^64...
cargo test --release --no-default-features
should work on stable.
cargo bench --no-run
This is a clippy lint that I'm disabling temporarily until the next Rust stable release which automatically implements clone for [T; N]
(where T: Copy). The explicit Clone
impls can be replaced with derives without affecting the API.
For some applications it might be necessary to define an order on the elliptic curve points.
To give you an example, I am currently building an application using BLS signature built on top of this pairing library that needs to order the public keys by some deterministic ordering.
Currently, the only way to do that is using the encoded forms. And that leaves us with three options:
None of these options is really attractive.
To mitigate this, there would be multiple options available:
Ord
on curve pointsOrd::cmp
Since the ordering is not really well defined, I am not a big fan of option 1.
I also don't like exposing the internal coordinates too much (could also be done for the specific curve implementations only), although I would see it as a valid option.
Option 2 currently seems to be the cleanest to me.
I'd like to hear other opinions as well.
Edit: I saw that Fq/Fq2 implement Ord
as well and define lexicographic ordering, so option 1. might still be in.
An example of how to implement a lexicographic ordering can be found in my fork.
The function miller_loop
takes an IntoIterator<Item = &'a (&'a _, &' _)>
which agrees with the IntoIterator
for a &Vec<(&_,&_)>
, but basically nothing else.
There is no way to construct the outer &'a
from iterator adapters, requiring a fresh allocation. Yet, you could easily make any collection work without the outer &'a
using .into_iter().map(|t| *t)
or |(x,y)| (x,y)
or similar. I'd think the G2 prepared points are commonly ephemeral, so this requires two ephemeral data structures, one containing the prepared points, and one containing the references to them, while both sound superfluous.
We cannot wrap the current miller_loop
in an interface that takes borrows, but maybe it'd work if miller_loop
took them directly, so maybe try
fn miller_loop<I,P1,P2>(i: I) -> Self::Fqk
where
I: IntoIterator<Item=(P1,P2)>,
P1: Borrow<<Self::G1Affine as CurveAffine>::Prepared>,
P2: Borrow<<Self::G2Affine as CurveAffine>::Prepared>,
You could make Item: Borrow<(P1,P2)>
too if you wanted backwards compatibility with the current interface, but I'd make callers use .into_iter().map(|t| *t)
here myself.
In fact, you allocate internally anyways, so maybe one should favor &[P1,P2]
over the iterator anyways, not sure.
Hello!
I'm trying to get Fq::pow
to work, but it seems I can't get it right… is this me or is it really a bug?
This is what I can get to do with sage:
sage: def h(x): return hex(int(x))[:-1] # this is only to get a confrontable hex representation
sage: q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
sage: Fq = GF(q)
sage: h(q)
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
sage: a = Fq(945268695322731484897977891916087812827587793626675745004848628347665446205888379878456193299686130571287070888667)
sage: h(a)
'0x6243b9d40ce4d8c1d86a603d9ce33eb76736fc8900b1e8da6de01cbdb27177539b472f83366efa4ed5bf6438391d2db'
sage: a**9
1183374762412757565163659156070127511863449313069977006820839763739586492957873774713939591053070945417445562515423
sage: h(a**9)
'0x7b04438ade27f8f2b916ebad4ad86d3941f87b5bbb022113edcf19384b6892c9e5fa522f8d60ab97d5c4ef522adffdf'
but if I attempt to add the following test case to fq.rs
:
#[test]
fn test_pow_bug() {
let e = Fq(FqRepr([0xd5bf6438391d2db, 0x9b472f83366efa4e, 0x6de01cbdb2717753,
0x6736fc8900b1e8da, 0xd86a603d9ce33eb7, 0x6243b9d40ce4d8c1]));
let expected = Fq(FqRepr([0xd5c4ef522adffdf, 0xe5fa522f8d60ab97, 0xedcf19384b6892c9,
0x41f87b5bbb022113, 0xb916ebad4ad86d39, 0x7b04438ade27f8f2]));
let got = e.pow([0x9, 0x0, 0x0, 0x0, 0x0, 0x0]);
assert_eq!(got, expected)
the test fails:
---- bls12_381::fq::test_pow_bug stdout ----
thread 'bls12_381::fq::test_pow_bug' panicked at 'assertion failed: `(left == right)`
left: `Fq(FqRepr([15168336788228049517, 6104148727050941414, 11744620724444176788, 291052430401786830, 12974234119515072997, 720310162563104718]))`,
right: `Fq(FqRepr([962731235106226143, 16571648143052024727, 17135942836742034121, 4753685040599277843, 13337106476021869881, 8864284230283688178]))`', src/bls12_381/fq.rs:1817:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.
am I doing something wrong here?
This is currently hidden but could be useful in fast circuit synthesis contexts.
One of which is that this library does not provide any constant-time guarantees or guarantees against side-channel attacks.
Would it make sense to overload the arithmetic operators (std::ops
) for everything that has an add
, add_assign
, mul
or mul_assign
method? I would love being able to write x += y;
instead of x.add_assign(y)
, but I know that there are arguments against it, too. It certainly would make some heavy computation "feel" more lightweight than it is.
What's your opinion on this? If you are fine with it, I'd be happy to try and make e.g. Field: AddAssign + MulAssign + SubAssign + Neg
, and CurveAffine: Mul + Neg
.
Currently a bit is set to distinguish between elements of compressed and uncompressed form, a bit is set to identify the "parity" of a compressed-form element. I should set another bit to indicate it's not the point at infinity, for the reason @daira suggests here: 3faf8c5#commitcomment-23094853
Looping in zcash/zcash#2517 on this.
Once the serialization is changed (and fully tested against rejection test vectors #10) this ticket should be closed when it is specified in src/bls12_381
.
A comment in the impl says:
Lines 55 to 57 in 183a64b
However, the impl seems to actually check this:
// The points (X, Y, Z) and (X', Y', Z')
// are equal when (X * Z'^2) = (X' * Z^2)
// and (Y * Z'^3) = (Y' * Z^3).
I think the comment is wrong and the implementation is correct. Either way, this should be fixed.
It would be convenient to have serde's Serialize
and Deserialize
traits implemented for the groups and fields, so they could be seamlessly used e.g. for storing keys on disk, or sending signatures over the network.
To keep the dependencies minimal, this could be behind a feature flag.
We have to_affine
/to_projective
but also into_repr
.
I am thinking about using this implementation to implement an encryption scheme. Is the Bilinear Diffie-Hellman assumption believed to hold for this pairing? What about stronger assumptions (e.g., Strong Diffie-Hellman, Bilinear Diffie-Hellman Exponent, etc.)?
If you additionally know any papers/other sources discussing this, that would be very useful.
Looks like they will start gating rustc compilation on clippy someday, which means I can always try to compile it on the latest nightly without worrying about clippy breakage.
Rand is at 0.6, while this crate and others around it are still at 0.4. This results in conflicts when I try to use it in combination with other crates which already depend on 0.5 or 0.6.
Would a PR updating to 0.6
be accepted?
Fq12 elements aren't always guaranteed to be in the cyclotomic subgroup, so the name is misleading.
Is there anything to be gained from applying ideas from https://eprint.iacr.org/2017/437 here? I noticed amcl claims constant time operations based on that. All this is irrelevant for zcash perhaps? Right now, I donno anyone who would care about pairings being constant time, for example.
The rand
crate is currently a dependency of this library, as some facilities for randomly generating scalars or curve points are provided generally over random number generators. The rand
crate is not 1.0, and is undergoing refactoring. As such, this library should not be considered 1.0 unless functionality that requires rand
is hidden behind an unstable crate feature.
byteorder
is another dependency of this crate, but is not "public" in the sense that none of its types are exposed in public APIs by this library. This crate thus does not block 1.0, but should be explicitly marked private when public/private dependencies is stable in Rust/cargo.
The primitive numeric types are zero by default, and I wonder whether Field
s should be, too.
One use case is if field elements are used as private keys: The clear_on_drop
crate helps prevent leaking secrets by overwriting the memory before freeing it, but it requires the type to implement Default
. (It's not as trivial as it sounds: it needs to prevent some compiler optimizations.)
It would also enable a few convenience methods from the standard library, like unwrap_or_default
, to work on Field
s the same way as on u64
s and others.
I'd like to use u128
only in this library and remove the backup arithmetic, but only if its arithmetic is lowered properly on target platforms that don't support it.
My ideal strategy:
feature(i128_type)
invocation, since it is no longer needed on nightly or (eventually) the 1.26 beta
compiler. Do patch release.1.26
stable is released, change documentation to remove mentions of nightly. CI can be changed to always target stable, except for clippy. (See #45)u128
lowering definitely works, remove backup arithmetic and use u128-support arithmetic only. The u128-support
feature will do nothing. In the next minor release, remove the u128-support
feature.It seems the benchmark has been failing for the last 4 months: https://ci.z.cash/#/builders/15
Hook up Bellman's CI to this library.
Since this only works for some platforms I'd rather people opt-in for those particular platforms.
This makes it easier to work with the elements generically.
hello, I want to ensure whether the security level of BLS12-381 is 128 bit.
And the signature length of BLS12-381. Is 48 Bytes?
Hashing to G1/G2/Fr/Fq/Fq2 -- with total domain separation.
These have just been stabilized in Rust.
Currently you have to opt-in to it because it's a low level API. I want a simple API that you cannot misuse.
Line 199 in 183a64b
The rand method starts with point on curve and scales by cofactor..
but it doesn't seem to start from random point - the x coordinate is random,
but then the 'get_point_from_x' method seems to deterministically choose the y coord,
rather than randomly from the two options.
So can we still say the final subgroup point is random?
I think there's a good chance it's just random on half the group.
Cause if the equation is of the form y^2=f(x)
(x,y) and (x,-y) are inverses so
also after scaling you always only one of P or -P
This is a stopgap for if we can't manage to get a solid G1/G2 hashing into this library before an audit begins. This allows us to build safe hashing on top, though it won't be as well-specified.
Like speed.z.cash.
#![deny(missing_debug_implementations)]
The main reason to add BN-254 support would be to allow the use of bellman to design circuits that can be verified on Ethereum.
Right now the benchmarks use unstable compiler APIs, so we need to disable them for the stable compiler.
I don't want to stabilize the wNAF API, since I would prefer to have a wrapper around these functions that prevents downstream users from supplying incorrect or inconsistent window table sizes. I'll have to explore all of the ways in which the API is used by bellman and other projects before I settle on a design for such a wrapper.
In the mean time, let's hide the wnaf
submodule behind a crate feature marked unstable-wnaf
.
... and document that Field
orderings are lexicographic.
Would it be reasonable to do multiscalar multiplication? Or are downstream crates doing that with bititerators directly somehow?
There is relevant discussion around curve25519 for
For BN254, Dfinity's implementation does pairing in less than 0.8ms (see here):
Notably, we see the signature validation time is 0.8 ms which involves a pairing evaluation.
This contrasts very favourably compared to the numbers given in this post where a BN254 pairing is done 2.5ms with USE_ASM
. Why is the discrepancy so high, and does this suggest that this implementation can be sped up significantly?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.