Skip to content

Commit

Permalink
Add Secp256r1 Curve (#916)
Browse files Browse the repository at this point in the history
* add field

* add curve

* update readme

* fix readme
  • Loading branch information
ColoCarletti authored Sep 17, 2024
1 parent b2ab353 commit 11f6846
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 6 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# lambdaworks

> From the heights of these towers of fields, forty centuries of mathematics look down on us.
> From the heights of these towers of fields, forty centuries of mathematics look down on us.
This library provides efficient implementation of cryptographic primitives used to build proving systems. Along with it, many backends for proving systems are shipped, and compatibility with different frontends is supported.

Expand Down Expand Up @@ -96,6 +96,7 @@ List of symbols:
| Bandersnatch | 🏗️ | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: |
| secp256k1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| secq256k1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| secq256r1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| **STARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** |
| STARK Prover | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| CAIRO Prover | 🏗️ | :x: | :x: | :x: | :x: |
Expand Down Expand Up @@ -126,11 +127,11 @@ Additionally, provers are compatible with the following frontends and VMs:
| Groth16 | Circom | 🏗️ |
| Plonk | Gnark | 🏗️ |
| Plonk | Noir | :x: |
| Stark | Winterfell | :heavy_check_mark: |
| Stark | Winterfell | :heavy_check_mark: |
| Stark | Miden | :heavy_check_mark: |
| Stark | Cairo | :heavy_check_mark: |

This can be used in a multi prover setting for extra security, or as a standalone to be used with Rust.
This can be used in a multi prover setting for extra security, or as a standalone to be used with Rust.

## Additional tooling usage

Expand Down Expand Up @@ -177,7 +178,7 @@ If you want to learn about proof systems/cryptography, we have a list of resourc

## 📚 References and acknowledgements

The following links, repos, companies and projects have been important in the development of this library and we want to thank and acknowledge them.
The following links, repos, companies and projects have been important in the development of this library and we want to thank and acknowledge them.

- [Starkware](https://starkware.co/)
- [Polygon](https://polygon.technology/)
Expand Down
5 changes: 3 additions & 2 deletions math/src/elliptic_curve/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This folder contains the different elliptic curve models currently supported by
- [Twisted Edwards](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve/edwards)
- [Montgomery](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve/montgomery)

Each of the curve models can have one or more coordinate systems, such as homogeneous projective, Jacobian, XZ coordinates, etc. These are used for reasons of performance. It is possible to define an operation, $\oplus$, taking two points over an elliptic curve, $E$ and obtain a third one, such that $(E, \oplus)$ is a group.
Each of the curve models can have one or more coordinate systems, such as homogeneous projective, Jacobian, XZ coordinates, etc. These are used for reasons of performance. It is possible to define an operation, $\oplus$, taking two points over an elliptic curve, $E$ and obtain a third one, such that $(E, \oplus)$ is a group.

This part makes use of lambdaworks finite fields. If you are unfamiliar with it or underlying concepts, refer to the [section on finite fields](../field/README.md).

Expand All @@ -21,6 +21,7 @@ The following curves are currently supported:
- [Starknet's curve](https://github.com/lambdaclass/lambdaworks/blob/main/math/src/elliptic_curve/short_weierstrass/curves/stark_curve.rs)
- [secp256k1](./short_weierstrass/curves/secp256k1/curve.rs): Bitcoin's curve. The implementation is not constant time, so it cannot be used to sign messages!
- [secq256k1](./short_weierstrass/curves/secq256k1/curve.rs): It has the same curve equation as secp256k1, a different generator and their order r and the modulus p are swapped. It uses ```secp256k1_scalarfield``` as a base field, which has modulus r.
- [secp256r1](./short_weierstrass/curves/secp256r1/curve.rs): Used for digital signatures, also known as: P-256 and prime256v1.

## Twisted Edwards

Expand Down Expand Up @@ -116,7 +117,7 @@ let y = g2_affine.y();
## Multiscalar multiplication

One common operation for different proof systems is the Mutiscalar Multiplication (MSM), which is given by a set of points $P_0 , P_1 , P_2 , ... , P_n$ and scalars $a_0 , a_1 , a_2 ... n_n$ (the scalars belong to the scalar field of the elliptic curve, which is the field whose size matches the size of the elliptic curve's group):
$$R = \sum_k a_k P_k$$
$$R = \sum_k a_k P_k$$
This operation could be implemented by using `operate_with_self` with each point and scalar and then add the results using `operate_with`, but this is not efficient. lambdaworks provides an optimized [MSM using Pippenger's algorithm](https://github.com/lambdaclass/lambdaworks/blob/main/math/src/msm/pippenger.rs). A naïve version is given [here](https://github.com/lambdaclass/lambdaworks/blob/main/math/src/msm/naive.rs). Below we show how to use MSM in the context of a polynomial commitment scheme: the scalars are the coefficients of the polynomials and the points are provided by an SRS.
```rust
fn commit(&self, p: &Polynomial<FieldElement<F>>) -> Self::Commitment {
Expand Down
1 change: 1 addition & 0 deletions math/src/elliptic_curve/short_weierstrass/curves/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod bn_254;
pub mod grumpkin;
pub mod pallas;
pub mod secp256k1;
pub mod secp256r1;
pub mod secq256k1;
pub mod stark_curve;
pub mod test_curve_1;
Expand Down
169 changes: 169 additions & 0 deletions math/src/elliptic_curve/short_weierstrass/curves/secp256r1/curve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint;
use crate::elliptic_curve::traits::IsEllipticCurve;
use crate::field::fields::secp256r1_field::Secp256r1PrimeField;
use crate::{
elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement,
};

/// This implementation is not constant time and cannot be used to sign messages. You can use it to check signatures
#[derive(Clone, Debug)]
pub struct Secp256r1Curve;

impl IsEllipticCurve for Secp256r1Curve {
type BaseField = Secp256r1PrimeField;
type PointRepresentation = ShortWeierstrassProjectivePoint<Self>;

fn generator() -> Self::PointRepresentation {
Self::PointRepresentation::new([
FieldElement::<Self::BaseField>::from_hex_unchecked(
"6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
),
FieldElement::<Self::BaseField>::from_hex_unchecked(
"4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
),
FieldElement::one(),
])
}
}

impl IsShortWeierstrass for Secp256r1Curve {
fn a() -> FieldElement<Self::BaseField> {
FieldElement::<Self::BaseField>::from_hex_unchecked(
"ffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
)
}
fn b() -> FieldElement<Self::BaseField> {
FieldElement::<Self::BaseField>::from_hex_unchecked(
"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{
cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError,
field::element::FieldElement, unsigned_integer::element::U256,
};

use super::Secp256r1Curve;

#[allow(clippy::upper_case_acronyms)]
type FE = FieldElement<Secp256r1PrimeField>;

fn point_1() -> ShortWeierstrassProjectivePoint<Secp256r1Curve> {
let x = FE::from_hex_unchecked(
"6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
);
let y = FE::from_hex_unchecked(
"4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
);
Secp256r1Curve::create_point_from_affine(x, y).unwrap()
}

fn point_1_times_5() -> ShortWeierstrassProjectivePoint<Secp256r1Curve> {
let x = FE::from_hex_unchecked(
"51590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED",
);
let y = FE::from_hex_unchecked(
"E0C17DA8904A727D8AE1BF36BF8A79260D012F00D4D80888D1D0BB44FDA16DA4",
);
Secp256r1Curve::create_point_from_affine(x, y).unwrap()
}

#[test]
fn adding_five_times_point_1_works() {
let point_1 = point_1();
let point_1_times_5 = point_1_times_5();
assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5);
}

#[test]
fn create_valid_point_works() {
let p = point_1();
assert_eq!(
*p.x(),
FE::from_hex_unchecked(
"6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"
)
);
assert_eq!(
*p.y(),
FE::from_hex_unchecked(
"4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
)
);
assert_eq!(*p.z(), FE::from_hex_unchecked("1"));
}

#[test]
fn create_invalid_points_returns_an_error() {
assert_eq!(
Secp256r1Curve::create_point_from_affine(FE::from(0), FE::from(1)),
Err(EllipticCurveError::InvalidPoint)
);
}

#[test]
fn equality_works() {
let g = Secp256r1Curve::generator();
let g2 = g.operate_with_self(2_u16);
let g2_other = g.operate_with(&g);
assert_ne!(&g2, &g);
assert_eq!(&g, &g);
assert_eq!(&g2, &g2_other);
}

#[test]
fn g_operated_with_g_satifies_ec_equation() {
let g = Secp256r1Curve::generator();
let g2 = g.operate_with_self(2_u16);

// get x and y from affine coordinates
let g2_affine = g2.to_affine();
let x = g2_affine.x();
let y = g2_affine.y();

// calculate both sides of Secp256r1 curve equation
let a = Secp256r1Curve::a();
let b = Secp256r1Curve::b();
let y_sq_0 = x.pow(3_u16) + (a * x) + b;
let y_sq_1 = y.pow(2_u16);

assert_eq!(y_sq_0, y_sq_1);
}

#[test]
fn operate_with_self_works_1() {
let g = Secp256r1Curve::generator();
assert_eq!(
g.operate_with(&g).operate_with(&g),
g.operate_with_self(3_u16)
);
}

#[test]
fn generator_has_right_order() {
let g = Secp256r1Curve::generator();
assert_eq!(
g.operate_with_self(U256::from_hex_unchecked(
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
))
.to_affine(),
ShortWeierstrassProjectivePoint::neutral_element()
);
}

#[test]
fn inverse_works() {
let g = Secp256r1Curve::generator();
assert_eq!(
g.operate_with_self(U256::from_hex_unchecked(
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254C"
))
.to_affine(),
g.operate_with_self(5u64).neg().to_affine()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod curve;
2 changes: 2 additions & 0 deletions math/src/field/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub mod pallas_field;
pub mod secp256k1_field;
/// Implementation of secp256k1 scalar field.
pub mod secp256k1_scalarfield;
/// Implementation of secp256r1 base field.
pub mod secp256r1_field;
/// Implementation of the u64 Goldilocks Prime field (p = 2^64 - 2^32 + 1)
pub mod u64_goldilocks_field;
/// Implementation of prime fields over 64 bit unsigned integers.
Expand Down
17 changes: 17 additions & 0 deletions math/src/field/fields/secp256r1_field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use crate::{
field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField},
unsigned_integer::element::U256,
};

type Secp256r1MontgomeryBackendPrimeField<T> = MontgomeryBackendPrimeField<T, 4>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MontgomeryConfigSecp256r1PrimeField;
impl IsModulus<U256> for MontgomeryConfigSecp256r1PrimeField {
const MODULUS: U256 = U256::from_hex_unchecked(
"0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
);
}

pub type Secp256r1PrimeField =
Secp256r1MontgomeryBackendPrimeField<MontgomeryConfigSecp256r1PrimeField>;

0 comments on commit 11f6846

Please sign in to comment.