diff --git a/README.md b/README.md index 80f4c4b81..89d66b851 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ List of symbols: | Pallas | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | | Vesta | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | | 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: | | **STARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | | STARK Prover | :heavy_check_mark: | :x: | :x: | :x: | :x: | | CAIRO Prover | 🏗️ | :x: | :x: | :x: | :x: | diff --git a/math/src/elliptic_curve/README.md b/math/src/elliptic_curve/README.md index cb672df2c..6dbd51e3f 100644 --- a/math/src/elliptic_curve/README.md +++ b/math/src/elliptic_curve/README.md @@ -19,6 +19,7 @@ The following curves are currently supported: - [Pallas](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve/short_weierstrass/curves/pallas), useful for recursive SNARKs when used with Vesta. - [Vesta](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve/short_weierstrass/curves/vesta), useful for recursive SNARKs when used with Pallas. - [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! ## Twisted Edwards @@ -170,3 +171,5 @@ In many curves, the base field contains some spare bits (as is the case of BLS12 - [What every developer needs to know about elliptic curves](https://blog.lambdaclass.com/what-every-developer-needs-to-know-about-elliptic-curves/) - [How we implemented the BN254 Ate pairing in lambdaworks](https://blog.lambdaclass.com/how-we-implemented-the-bn254-ate-pairing-in-lambdaworks/) - [Exploring elliptic curve pairings by Vitalik](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627) +- [A survey of elliptic curves for proof systems](https://eprint.iacr.org/2022/586.pdf) +- [Taxonomy of pairing-friendly elliptic curves](https://eprint.iacr.org/2006/372.pdf) diff --git a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs index 55807d8dc..549a29809 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs @@ -3,6 +3,7 @@ pub mod bls12_381; pub mod bn_254; pub mod grumpkin; pub mod pallas; +pub mod secp256k1; pub mod stark_curve; pub mod test_curve_1; pub mod test_curve_2; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/curve.rs new file mode 100644 index 000000000..82b58ab5e --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/curve.rs @@ -0,0 +1,165 @@ +use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::field::fields::secp256k1_field::Secp256k1PrimeField; +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 Secp256k1Curve; + +impl IsEllipticCurve for Secp256k1Curve { + type BaseField = Secp256k1PrimeField; + type PointRepresentation = ShortWeierstrassProjectivePoint; + + fn generator() -> Self::PointRepresentation { + Self::PointRepresentation::new([ + FieldElement::::from_hex_unchecked( + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + ), + FieldElement::::from_hex_unchecked( + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", + ), + FieldElement::one(), + ]) + } +} + +impl IsShortWeierstrass for Secp256k1Curve { + fn a() -> FieldElement { + FieldElement::from(0) + } + + fn b() -> FieldElement { + FieldElement::from(7) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + field::element::FieldElement, unsigned_integer::element::U256, + }; + + use super::Secp256k1Curve; + + #[allow(clippy::upper_case_acronyms)] + type FE = FieldElement; + + fn point_1() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + ); + let y = FE::from_hex_unchecked( + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", + ); + Secp256k1Curve::create_point_from_affine(x, y).unwrap() + } + + fn point_1_times_5() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "2F8BDE4D1A07209355B4A7250A5C5128E88B84BDDC619AB7CBA8D569B240EFE4", + ); + let y = FE::from_hex_unchecked( + "D8AC222636E5E3D6D4DBA9DDA6C9C426F788271BAB0D6840DCA87D3AA6AC62D6", + ); + Secp256k1Curve::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( + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + ) + ); + assert_eq!( + *p.y(), + FE::from_hex_unchecked( + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" + ) + ); + assert_eq!(*p.z(), FE::from_hex_unchecked("1")); + } + + #[test] + fn create_invalid_points_returns_an_error() { + assert_eq!( + Secp256k1Curve::create_point_from_affine(FE::from(0), FE::from(1)), + Err(EllipticCurveError::InvalidPoint) + ); + } + + #[test] + fn equality_works() { + let g = Secp256k1Curve::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 = Secp256k1Curve::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 secp256k1 curve equation + let five = Secp256k1Curve::b(); + let y_sq_0 = x.pow(3_u16) + five; + 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 = Secp256k1Curve::generator(); + assert_eq!( + g.operate_with(&g).operate_with(&g), + g.operate_with_self(3_u16) + ); + } + + #[test] + fn generator_has_right_order() { + let g = Secp256k1Curve::generator(); + assert_eq!( + g.operate_with_self(U256::from_hex_unchecked( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" + )) + .to_affine(), + ShortWeierstrassProjectivePoint::neutral_element() + ); + } + + #[test] + fn inverse_works() { + let g = Secp256k1Curve::generator(); + assert_eq!( + g.operate_with_self(U256::from_hex_unchecked( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413C" + )) + .to_affine(), + g.operate_with_self(5u64).neg().to_affine() + ); + } +} diff --git a/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/mod.rs new file mode 100644 index 000000000..201a862ce --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/mod.rs @@ -0,0 +1 @@ +pub mod curve; diff --git a/math/src/field/README.md b/math/src/field/README.md index d4a0050c8..c19b302aa 100644 --- a/math/src/field/README.md +++ b/math/src/field/README.md @@ -14,6 +14,8 @@ This folder contains the different field backends, including field extensions. T - [Base field of BLS12-381](https://github.com/lambdaclass/lambdaworks/blob/main/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs) and its quadratic, sextic and twelth degree extensions. - [Scalar field of BLS12-377](https://github.com/lambdaclass/lambdaworks/blob/main/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs) - [Base field of BLS12-377](https://github.com/lambdaclass/lambdaworks/blob/main/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs) +- [Base field of secp256k1](./fields/secp256k1_field.rs): the base field of Bitcoin's elliptic curve. +- [Scalar field of secp256k1](./fields/secp256k1_scalarfield.rs): the scalar field of Bitcoin's elliptic curve. You also have the tooling to define quadratic and cubic extension fields. @@ -27,11 +29,11 @@ Differences of 3% are common for some measurements, so small differences are not ARM - M1 -| Operation| N | Arkworks | lambdaworks | -| -------- | --- | --------- | ----------- | +| Operation| N | Arkworks | lambdaworks | +| -------- | --- | --------- | ----------- | | `mul` | 10k | 112 μs | 115 μs | -| `add` | 1M | 8.5 ms | 7.0 ms | -| `sub` | 1M | 7.53 ms | 7.12 ms | +| `add` | 1M | 8.5 ms | 7.0 ms | +| `sub` | 1M | 7.53 ms | 7.12 ms | | `pow` | 10k | 11.2 ms | 12.4 ms | | `invert` | 10k | 30.0 ms | 27.2 ms | diff --git a/math/src/field/fields/mod.rs b/math/src/field/fields/mod.rs index 48b94e901..bd7fbbb94 100644 --- a/math/src/field/fields/mod.rs +++ b/math/src/field/fields/mod.rs @@ -7,6 +7,10 @@ pub mod montgomery_backed_prime_fields; pub mod p448_goldilocks_prime_field; /// Implemenation of Pallas field pub mod pallas_field; +/// Implementation of secp256k1 base field. +pub mod secp256k1_field; +/// Implementation of secp256k1 scalar field. +pub mod secp256k1_scalarfield; /// 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. diff --git a/math/src/field/fields/secp256k1_field.rs b/math/src/field/fields/secp256k1_field.rs new file mode 100644 index 000000000..7cff0e2eb --- /dev/null +++ b/math/src/field/fields/secp256k1_field.rs @@ -0,0 +1,17 @@ +use crate::{ + field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + unsigned_integer::element::U256, +}; + +type Secp256k1MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MontgomeryConfigSecp256k1PrimeField; +impl IsModulus for MontgomeryConfigSecp256k1PrimeField { + const MODULUS: U256 = U256::from_hex_unchecked( + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", + ); +} + +pub type Secp256k1PrimeField = + Secp256k1MontgomeryBackendPrimeField; diff --git a/math/src/field/fields/secp256k1_scalarfield.rs b/math/src/field/fields/secp256k1_scalarfield.rs new file mode 100644 index 000000000..d1c9bfb1b --- /dev/null +++ b/math/src/field/fields/secp256k1_scalarfield.rs @@ -0,0 +1,17 @@ +use crate::{ + field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + unsigned_integer::element::U256, +}; + +type Secp256k1MontgomeryBackendScalarField = MontgomeryBackendPrimeField; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MontgomeryConfigSecp256k1ScalarField; +impl IsModulus for MontgomeryConfigSecp256k1ScalarField { + const MODULUS: U256 = U256::from_hex_unchecked( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + ); +} + +pub type Secp256k1ScalarField = + Secp256k1MontgomeryBackendScalarField;