#elliptic-curve #secret-key

no-std generic-ec

General elliptic curve cryptography

17 unstable releases (4 breaking)

0.4.3 Oct 9, 2024
0.4.1 Aug 27, 2024
0.4.0 Jun 20, 2024
0.2.3 Mar 27, 2024
0.0.0 Apr 12, 2022

#104 in Cryptography

Download history 121/week @ 2024-07-30 83/week @ 2024-08-06 58/week @ 2024-08-13 141/week @ 2024-08-20 327/week @ 2024-08-27 83/week @ 2024-09-03 115/week @ 2024-09-10 141/week @ 2024-09-17 407/week @ 2024-09-24 897/week @ 2024-10-01 2285/week @ 2024-10-08 2111/week @ 2024-10-15 621/week @ 2024-10-22 590/week @ 2024-10-29 617/week @ 2024-11-05 764/week @ 2024-11-12

2,804 downloads per month
Used in 9 crates

MIT/Apache

520KB
3K SLoC

License Docs Crates io Discord

General elliptic curve cryptography

The library provides a set of simple abstractions boosting experience of doing elliptic curve arithmetic in Rust. Aim is to stay simple, generic, and secure. It's handy for developers who implement MPC, zero-knowledge protocols, or any other elliptic crypto algorithms.

generic-ec is no_std and web assembly friendly.

Overview

Crate provides three primitives: a point on elliptic curve Point<E>, an integer modulus group order Scalar<E>, and a secret scalar carrying some sensitive value (e.g. secret key) SecretScalar<E>. E stands for a choice of elliptic curve, it could be any supported curve, e.g. Point<Secp256k1> is an elliptic point on secp256k1 curve.

Exposed API

Limited API is exposed: elliptic point arithmetic (points addition, negation, multiplying at scalar), scalar arithmetic (addition, multiplication, inverse modulo prime group order), and encode/decode to bytes represenstation.

Hash to curve, hash to scalar primitives, accessing affine coordinates of points are available for some curves through FromHash and other traits.

Security & guarantees

Library mitigates a bunch of attacks (such as small-group attack) by design by enforcing following checks:

  • Scalar Scalar<E> must be an integer modulo curve prime order
  • Elliptic point Point<E> must be on the curve
    I.e. elliptic point is guaranteed to satisfy equation of E
  • Point<E> is torsion-free
    Elliptic points should be free of small-group component. This eliminates any kind of small-group attacks.

Point or scalar not meeting above requirements cannot be constructed (in safe Rust), as these checks are always enforced. E.g. if you're deserializing a sequence of bytes that represents an invalid point, deserialization will result into error.

SecretScalar<E>

Sometimes your scalar represents some sensitive value like secret key, and you want to keep it safer. SecretScalar<E> is in-place replacement of Scalar<E> that enforces additional security by storing the scalar value on the heap, and erasing it on drop. Its advantage is that it doesn't leave any trace in memory dump after it's dropped (which is not guaranteed by regular Scalar<E>).

But keep in mind that we can't control the OS which could potentially load RAM page containing sensitive value to the swap disk (i.e. on your HDD/SSD) if you're running low on memory. Or it could do any other fancy stuff. We avoid writing unsafe or OS-specific code that could mitigate this problem.

Points at infinity

It should be noticed that point at infinity (or identity point) is a valid Point<E>. You can construct it by calling Point::<E>::zero(), e.g. Point::<Secp256k1>::zero() is a point at infinity for secp256k1 curve.

If the protocol you're implementing requires points/scalars to be non-zero, you may need to enforce this check by calling .is_zero() method or by using NonZero<T> (NonZero<Point<E>> or NonZero<Scalar<E>>).

Using NonZero<T> gives some compile-time guarantees. For instance, multiplying non-zero point in the prime group at non-zero scalar mod group order is mathematically guaranteed to output non-zero point in that prime group. Thus, multiplying NonZero<Point<E>> at NonZero<Scalar<E>> returns NonZero<Point<E>>.

Supported curves

Crate provides support for following elliptic curves out of box:

Curve Feature Backend
secp256k1 curve-secp256k1 RustCrypto/k256
secp256r1 curve-secp256r1 RustCrypto/p256
stark-curve curve-stark Dfns/stark
Ed25519 curve-ed25519 curve25519-dalek

In order to use one of the supported curves, you need to turn on corresponding feature. E.g. if you want to use secp256k1 curve, add this to Cargo.toml:

[dependency]
generic-ec = { version = "...", features = ["curve-secp256k1"] }

And now you can generate a point on that curve:

use generic_ec::{Point, Scalar, curves::Secp256k1};

let random_point: Point<Secp256k1> = Point::generator() * Scalar::random(&mut rng);

Adding support for other curves

Adding new curve is as easy as implementing Curve trait! If you're missing some curve support, or you're not fine with using existing implementation, you may define your implementation of Curve trait and enjoy using the same handy primitives Point<YOUR_EC>, Scalar<YOUR_EC>, and etc.

Features

  • curve-{name} enables specified curve support. See list of supported curves.
  • all-curves enables all supported curves
  • serde enables points/scalar (de)serialization support. (enabled by default)
  • std enables support of standard library (enabled by default)

Examples

Random scalar / point generation

use generic_ec::{Point, Scalar, curves::Secp256k1};

// Generates random non-zero scalar
let random_scalar = Scalar::<Secp256k1>::random(&mut rng);
// Produces a point that's result of generator multiplied at the random scalar
let point = Point::generator() * &random_scalar;

Diffie-Hellman key exchange

use generic_ec::{Point, SecretScalar, curves::Secp256k1};

let alice_sk = SecretScalar::<Secp256k1>::random(&mut rng);
let alice_pk = Point::generator() * &alice_sk;

let bob_sk = SecretScalar::<Secp256k1>::random(&mut rng);
let bob_pk = Point::generator() * &bob_sk;

let shared_secret_learned_by_alice = bob_pk * &alice_sk;
let shared_secret_learned_by_bob = alice_pk * &bob_sk;
assert_eq!(shared_secret_learned_by_alice, shared_secret_learned_by_bob);

Generic over choice of curve

You can simply make your function generic over choice of curve:

use generic_ec::{Point, Scalar, Curve};
use rand::RngCore;

fn some_generic_computation<E: Curve>(rng: &mut impl RngCore, point: Point<E>) -> Point<E> {
    let blinding = Point::<E>::generator() * Scalar::random(rng);
    let e = &point + &blinding;
    // ... some computation
}

// You can run this function with any supported curve:
use generic_ec::curves::{Secp256k1, Secp256r1};

let point1 = Point::<Secp256k1>::generator().to_point();
let _ = some_generic_computation(&mut rng, point1);

let point2 = Point::<Secp256r1>::generator().to_point();
let _ = some_generic_computation(&mut rng, point2);

// ...

Join us in Discord!

Feel free to reach out to us in Discord!

License

The crate is licensed under MIT or Apache-2.0 at your choice.

Dependencies

~1–3.5MB
~71K SLoC