#elliptic-curve #curve #elliptic #schnorr #k256 #schnorr-signature #curve-point

secp

A flexible and secure secp256k1 elliptic curve math library with constant-time support and superb ergonomics

13 releases

0.2.3 Mar 16, 2024
0.2.2 Feb 13, 2024
0.2.0 Nov 25, 2023
0.1.0 Oct 30, 2023
0.0.8 Oct 30, 2023

#349 in Cryptography

Download history 8/week @ 2024-02-03 40/week @ 2024-02-10 21/week @ 2024-02-17 60/week @ 2024-02-24 16/week @ 2024-03-02 143/week @ 2024-03-09 171/week @ 2024-03-16 19/week @ 2024-03-23 28/week @ 2024-03-30 9/week @ 2024-04-06 11/week @ 2024-04-13

71 downloads per month
Used in 3 crates

Unlicense

165KB
3K SLoC

secp

A flexible and secure secp256k1 elliptic curve math library, with constant-time support, and superb ergonomics.

secp takes full advantage of Rust's std::ops traits to make elliptic curve cryptography code easy to read, easy to write, succinct, readable, and secure.

Example

Here's an implementation of simple Schnorr signatures using the secp crate.

use secp::{MaybeScalar, Point, Scalar};
use sha2::{Digest, Sha256};

fn compute_challenge(nonce_point: &Point, pubkey: &Point, msg: &[u8]) -> MaybeScalar {
    let hash: [u8; 32] = Sha256::new()
        .chain_update(&nonce_point.serialize())
        .chain_update(&pubkey.serialize())
        .chain_update(msg)
        .finalize()
        .into();
    MaybeScalar::reduce_from(&hash)
}

fn random_scalar() -> Scalar {
    // In an actual implementation this would produce a scalar value
    // sampled from a CSPRNG.
    Scalar::two()
}

fn schnorr_sign(secret_key: Scalar, message: &[u8]) -> (Point, MaybeScalar) {
    let nonce = random_scalar();
    let nonce_point = nonce.base_point_mul();
    let pubkey = secret_key.base_point_mul();

    let e = compute_challenge(&nonce_point, &pubkey, message);
    let s = nonce + secret_key * e;
    (nonce_point, s)
}

fn schnorr_verify(public_key: Point, signature: (Point, MaybeScalar), message: &[u8]) -> bool {
    let (r, s) = signature;
    let e = compute_challenge(&r, &public_key, message);
    s.base_point_mul() == r + e * public_key
}

let secret_key: Scalar = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    .parse()
    .unwrap();
let public_key = secret_key.base_point_mul();

let message = b"I am the dragon!";

let signature = schnorr_sign(secret_key, message);
assert!(schnorr_verify(public_key, signature, message));

Choice of Backbone

This crate does not implement elliptic curve point math directly. Instead we depend on one of two reputable elliptic curve cryptography libraries:

One or the other can be used. By default, this crate prefers to rely on libsecp256k1, as this is the most vetted and publicly trusted implementation of secp256k1 curve math available anywhere. However, if you need a pure-rust implementation, you can install this crate without it, and use the pure-rust k256 crate instead.

cargo add secp --no-default-features --features k256

If both k256 and secp256k1 features are enabled, then we default to using libsecp256k1 bindings for the actual math, but still provide trait implementations to make this crate interoperable with k256.

Documentation

To see the API documentation, head on over to docs.rs.

Dependencies

~6MB
~66K SLoC