diff --git a/crates/nargo/tests/test_data/ec_baby_jubjub/Nargo.toml b/crates/nargo/tests/test_data/ec_baby_jubjub/Nargo.toml new file mode 100644 index 00000000000..4a5c2a916f0 --- /dev/null +++ b/crates/nargo/tests/test_data/ec_baby_jubjub/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "Baby Jubjub sanity checks" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/crates/nargo/tests/test_data/ec_baby_jubjub/src/main.nr b/crates/nargo/tests/test_data/ec_baby_jubjub/src/main.nr new file mode 100644 index 00000000000..ee9e2e2eeee --- /dev/null +++ b/crates/nargo/tests/test_data/ec_baby_jubjub/src/main.nr @@ -0,0 +1,211 @@ +// Tests may be checked against https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/tree/main/poc + +use dep::std::ec::tecurve::affine::Curve as AffineCurve; +use dep::std::ec::tecurve::affine::Point as Gaffine; +use dep::std::ec::tecurve::curvegroup::Curve; +use dep::std::ec::tecurve::curvegroup::Point as G; + +use dep::std::ec::swcurve::affine::Point as SWGaffine; +use dep::std::ec::swcurve::curvegroup::Point as SWG; + +use dep::std::ec::montcurve::affine::Point as MGaffine; +use dep::std::ec::montcurve::curvegroup::Point as MG; + +fn main() { + // This test only makes sense if Field is the right prime field. + if 21888242871839275222246405745257275088548364400416034343698204186575808495617 == 0 + { + // Define Baby Jubjub (ERC-2494) parameters in affine representation + let bjj_affine = AffineCurve::new(168700, 168696, Gaffine::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + // Test addition + let p1_affine = Gaffine::new(17777552123799933955779906779655732241715742912184938656739573121738514868268, 2626589144620713026669568689430873010625803728049924121243784502389097019475); + let p2_affine = Gaffine::new(16540640123574156134436876038791482806971768689494387082833631921987005038935, 20819045374670962167435360035096875258406992893633759881276124905556507972311); + + let p3_affine = bjj_affine.add(p1_affine, p2_affine); + constrain p3_affine.eq(Gaffine::new(7916061937171219682591368294088513039687205273691143098332585753343424131937, + 14035240266687799601661095864649209771790948434046947201833777492504781204499)); + + // Test scalar multiplication + let p4_affine = bjj_affine.mul(2, p1_affine); + constrain p4_affine.eq(Gaffine::new(6890855772600357754907169075114257697580319025794532037257385534741338397365, + 4338620300185947561074059802482547481416142213883829469920100239455078257889)); + constrain p4_affine.eq(bjj_affine.bit_mul([0,1], p1_affine)); + + // Test subtraction + let p5_affine = bjj_affine.subtract(p3_affine, p3_affine); + constrain p5_affine.eq(Gaffine::zero()); + + // Check that these points are on the curve + constrain bjj_affine.contains(bjj_affine.gen) + & bjj_affine.contains(p1_affine) + & bjj_affine.contains(p2_affine) + & bjj_affine.contains(p3_affine) + & bjj_affine.contains(p4_affine) + & bjj_affine.contains(p5_affine); + + // Test CurveGroup equivalents + let bjj = bjj_affine.into_group(); // Baby Jubjub + + let p1 = p1_affine.into_group(); + let p2 = p2_affine.into_group(); + let p3 = p3_affine.into_group(); + let p4 = p4_affine.into_group(); + let p5 = p5_affine.into_group(); + + // Test addition + constrain p3.eq(bjj.add(p1, p2)); + + // Test scalar multiplication + constrain p4.eq(bjj.mul(2, p1)); + constrain p4.eq(bjj.bit_mul([0,1], p1)); + + // Test subtraction + constrain G::zero().eq(bjj.subtract(p3, p3)); + constrain p5.eq(G::zero()); + + // Check that these points are on the curve + constrain bjj.contains(bjj.gen) + & bjj.contains(p1) + & bjj.contains(p2) + & bjj.contains(p3) + & bjj.contains(p4) + & bjj.contains(p5); + + // Test SWCurve equivalents of the above + // First the affine representation + let bjj_swcurve_affine = bjj_affine.into_swcurve(); + + let p1_swcurve_affine = bjj_affine.map_into_swcurve(p1_affine); + let p2_swcurve_affine = bjj_affine.map_into_swcurve(p2_affine); + let p3_swcurve_affine = bjj_affine.map_into_swcurve(p3_affine); + let p4_swcurve_affine = bjj_affine.map_into_swcurve(p4_affine); + let p5_swcurve_affine = bjj_affine.map_into_swcurve(p5_affine); + + // Addition + constrain p3_swcurve_affine.eq( + bjj_swcurve_affine.add( + p1_swcurve_affine, + p2_swcurve_affine)); + + // Doubling + constrain p4_swcurve_affine.eq(bjj_swcurve_affine.mul(2, p1_swcurve_affine)); + constrain p4_swcurve_affine.eq(bjj_swcurve_affine.bit_mul([0,1], p1_swcurve_affine)); + + // Subtraction + constrain SWGaffine::zero().eq(bjj_swcurve_affine.subtract(p3_swcurve_affine, p3_swcurve_affine)); + constrain p5_swcurve_affine.eq(SWGaffine::zero()); + + // Check that these points are on the curve + constrain bjj_swcurve_affine.contains(bjj_swcurve_affine.gen) + & bjj_swcurve_affine.contains(p1_swcurve_affine) + & bjj_swcurve_affine.contains(p2_swcurve_affine) + & bjj_swcurve_affine.contains(p3_swcurve_affine) + & bjj_swcurve_affine.contains(p4_swcurve_affine) + & bjj_swcurve_affine.contains(p5_swcurve_affine); + + // Then the CurveGroup representation + let bjj_swcurve = bjj.into_swcurve(); + + let p1_swcurve = bjj.map_into_swcurve(p1); + let p2_swcurve = bjj.map_into_swcurve(p2); + let p3_swcurve = bjj.map_into_swcurve(p3); + let p4_swcurve = bjj.map_into_swcurve(p4); + let p5_swcurve = bjj.map_into_swcurve(p5); + + // Addition + constrain p3_swcurve.eq( + bjj_swcurve.add( + p1_swcurve, + p2_swcurve)); + + // Doubling + constrain p4_swcurve.eq(bjj_swcurve.mul(2, p1_swcurve)); + constrain p4_swcurve.eq(bjj_swcurve.bit_mul([0,1], p1_swcurve)); + + // Subtraction + constrain SWG::zero().eq(bjj_swcurve.subtract(p3_swcurve, p3_swcurve)); + constrain p5_swcurve.eq(SWG::zero()); + + // Check that these points are on the curve + constrain bjj_swcurve.contains(bjj_swcurve.gen) + & bjj_swcurve.contains(p1_swcurve) + & bjj_swcurve.contains(p2_swcurve) + & bjj_swcurve.contains(p3_swcurve) + & bjj_swcurve.contains(p4_swcurve) + & bjj_swcurve.contains(p5_swcurve); + + // Test MontCurve conversions + // First the affine representation + let bjj_montcurve_affine = bjj_affine.into_montcurve(); + + let p1_montcurve_affine = p1_affine.into_montcurve(); + let p2_montcurve_affine = p2_affine.into_montcurve(); + let p3_montcurve_affine = p3_affine.into_montcurve(); + let p4_montcurve_affine = p4_affine.into_montcurve(); + let p5_montcurve_affine = p5_affine.into_montcurve(); + + // Addition + constrain p3_montcurve_affine.eq( + bjj_montcurve_affine.add( + p1_montcurve_affine, + p2_montcurve_affine)); + + // Doubling + constrain p4_montcurve_affine.eq(bjj_montcurve_affine.mul(2, p1_montcurve_affine)); + constrain p4_montcurve_affine.eq(bjj_montcurve_affine.bit_mul([0,1], p1_montcurve_affine)); + + // Subtraction + constrain MGaffine::zero().eq(bjj_montcurve_affine.subtract(p3_montcurve_affine, p3_montcurve_affine)); + constrain p5_montcurve_affine.eq(MGaffine::zero()); + + // Check that these points are on the curve + constrain bjj_montcurve_affine.contains(bjj_montcurve_affine.gen) + & bjj_montcurve_affine.contains(p1_montcurve_affine) + & bjj_montcurve_affine.contains(p2_montcurve_affine) + & bjj_montcurve_affine.contains(p3_montcurve_affine) + & bjj_montcurve_affine.contains(p4_montcurve_affine) + & bjj_montcurve_affine.contains(p5_montcurve_affine); + + // Then the CurveGroup representation + let bjj_montcurve = bjj.into_montcurve(); + + let p1_montcurve = p1_montcurve_affine.into_group(); + let p2_montcurve = p2_montcurve_affine.into_group(); + let p3_montcurve = p3_montcurve_affine.into_group(); + let p4_montcurve = p4_montcurve_affine.into_group(); + let p5_montcurve = p5_montcurve_affine.into_group(); + + // Addition + constrain p3_montcurve.eq( + bjj_montcurve.add( + p1_montcurve, + p2_montcurve)); + + // Doubling + constrain p4_montcurve.eq(bjj_montcurve.mul(2, p1_montcurve)); + constrain p4_montcurve.eq(bjj_montcurve.bit_mul([0,1], p1_montcurve)); + + // Subtraction + constrain MG::zero().eq(bjj_montcurve.subtract(p3_montcurve, p3_montcurve)); + constrain p5_montcurve.eq(MG::zero()); + + // Check that these points are on the curve + constrain bjj_montcurve.contains(bjj_montcurve.gen) + & bjj_montcurve.contains(p1_montcurve) + & bjj_montcurve.contains(p2_montcurve) + & bjj_montcurve.contains(p3_montcurve) + & bjj_montcurve.contains(p4_montcurve) + & bjj_montcurve.contains(p5_montcurve); + + // Elligator 2 map-to-curve + let ell2_pt_map = bjj_affine.elligator2_map(27); + + constrain ell2_pt_map.eq(MGaffine::new(7972459279704486422145701269802978968072470631857513331988813812334797879121, 8142420778878030219043334189293412482212146646099536952861607542822144507872).into_tecurve()); + + // SWU map-to-curve + let swu_pt_map = bjj_affine.swu_map(5,27); + + constrain swu_pt_map.eq(bjj_affine.map_from_swcurve(SWGaffine::new(2162719247815120009132293839392097468339661471129795280520343931405114293888, 5341392251743377373758788728206293080122949448990104760111875914082289313973))); + } +} diff --git a/noir_stdlib/src/ec.nr b/noir_stdlib/src/ec.nr new file mode 100644 index 00000000000..cc58b714de7 --- /dev/null +++ b/noir_stdlib/src/ec.nr @@ -0,0 +1,226 @@ +// Elliptic curve implementation +// Overview +// ======== +// The following three elliptic curve representations are admissible: +mod tecurve; // Twisted Edwards curves +mod swcurve; // Elliptic curves in Short Weierstraß form +mod montcurve; // Montgomery curves +// +// Note that Twisted Edwards and Montgomery curves are (birationally) equivalent, so that +// they may be freely converted between one another, whereas Short Weierstraß curves are +// more general. Diagramatically: +// +// tecurve == montcurve ⊂ swcurve +// +// Each module is further divided into two submodules, 'affine' and 'curvegroup', depending +// on the preferred coordinate representation. Affine coordinates are none other than the usual +// two-dimensional Cartesian coordinates used in the definitions of these curves, whereas +// 'CurveGroup' coordinates (terminology borrowed from Arkworks, whose conventions we try +// to follow) are special coordinate systems with respect to which the group operations may be +// implemented more efficiently, usually by means of an appropriate choice of projective coordinates. +// +// In each of these submodules, there is a Point struct and a Curve struct, the former +// representing a point in the coordinate system and the latter a curve configuration. +// +// Points +// ====== +// Points may be instantiated using the associated function `new`, which takes coordinates +// as its arguments. For instance, +// +// `let p = swcurve::Point::new(1,1);` +// +// The additive identity may be constructed by a call to the associated function `zero` of no +// arguments: +// +// `let zero = swcurve::Point::zero();` +// +// Points may be tested for equality by calling the method `eq`: +// +// `let pred = p.eq(zero);` +// +// There is also the method `is_zero` to explicitly check whether a point is the additive identity: +// +// `constrain pred == p.is_zero();` +// +// Points may be negated by calling the `negate` method and converted to CurveGroup (or affine) +// coordinates by calling the `into_group` (resp. `into_affine`) method on them. Finally, +// Points may be freely mapped between their respective Twisted Edwards and Montgomery +// representations by calling the `into_montcurve` or `into_tecurve` methods. For mappings +// between Twisted Edwards/Montgomery curves and Short Weierstraß curves, see the Curve section +// below, as the underlying mappings are those of curves rather than ambient spaces. +// As a rule, Points in affine (or CurveGroup) coordinates are mapped to Points in affine +// (resp. CurveGroup) coordinates. +// +// Curves +// ====== +// A curve configuration (Curve) is completely determined by the Field coefficients of its defining +// equation (a and b in the case of swcurve, a and d in the case of tecurve, and j and k in +// the case of montcurve) together with a generator (`gen`) in the corresponding coordinate system. +// For example, the Baby Jubjub curve configuration as defined in ERC-2494 may be instantiated as a Twisted +// Edwards curve in affine coordinates as follows: +// +// `let bjj_affine = tecurve::Curve::new(168700, 168696, tecurve::Point::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905));` +// +// The `contains` method may be used to check whether a Point lies on a given curve: +// +// `constrain bjj_affine.contains(tecurve::Point::zero());` +// +// The elliptic curve group's addition operation is exposed as the `add` method, e.g. +// +// `let p = bjj_affine.add(bjj_affine.gen, bjj_affine.gen);` +// +// subtraction as the `subtract` method, e.g. +// +// `constrain tecurve::Point::zero().eq(bjj_affine.subtract(bjj_affine.gen, bjj_affine.gen));` +// +// scalar multiplication as the `mul` method, where the scalar is assumed to be a Field* element, e.g. +// +// `constrain tecurve::Point::zero().eq(bjj_affine.mul(2, tecurve::Point::zero());` +// +// There is a scalar multiplication method (`bit_mul`) provided where the scalar input is expected to be +// an array of bits (little-endian convention), as well as a multi-scalar multiplication method** (`msm`) +// which takes an array of Field elements and an array of elliptic curve points as arguments, both assumed +// to be of the same length. +// +// Curve configurations may be converted between different coordinate representations by calling the `into_group` +// and `into_affine` methods on them, e.g. +// +// `let bjj_curvegroup = bjj_affine.into_group();` +// +// Curve configurations may also be converted between different curve representations by calling the `into_swcurve`, +// `into_montcurve` and `into_tecurve` methods subject to the relation between the curve representations mentioned +// above. Note that it is possible to map Points from a Twisted Edwards/Montgomery curve to the corresponding +// Short Weierstraß representation and back, and the methods to do so are exposed as `map_into_swcurve` and +// `map_from_swcurve`, which each take one argument, the point to be mapped. +// +// Curve maps +// ========== +// There are a few different ways of mapping Field elements to elliptic curves. Here we provide the simplified +// Shallue-van de Woestijne-Ulas and Elligator 2 methods, the former being applicable to all curve types +// provided above subject to the constraint that the coefficients of the corresponding Short Weierstraß curve satisfies +// a*b != 0 and the latter being applicable to Montgomery and Twisted Edwards curves subject to the constraint that +// the coefficients of the corresponding Montgomery curve satisfy j*k != 0 and (j^2 - 4)/k^2 is non-square. +// +// The simplified Shallue-van de Woestijne-Ulas method is exposed as the method `swu_map` on the Curve configuration and +// depends on two parameters, a Field element z != -1 for which g(x) - z is irreducible over Field and g(b/(z*a)) is +// square, where g(x) = x^3 + a*x + b is the right-hand side of the defining equation of the corresponding Short +// Weierstraß curve, and a Field element u to be mapped onto the curve. For example, in the case of bjj_affine above, +// it may be determined using the scripts provided at that z = 5. +// +// The Elligator 2 method is exposed as the method `elligator2_map` on the Curve configurations of Montgomery and +// Twisted Edwards curves. Like the simplified SWU method above, it depends on a certain non-square element of Field, +// but this element need not satisfy any further conditions, so it is included as the (Field-dependent) constant +//`ZETA` below. Thus, the `elligator2_map` method depends only on one parameter, the Field element to be mapped onto +// the curve. +// +// For details on all of the above in the context of hashing to elliptic curves, see . +// +// +// *TODO: Replace Field with Bigint. +// **TODO: Support arrays of structs to make this work. + + +// TODO: Replace with built-in backend-dependent constant. +global N_BITS = 254; // Maximum number of bits in field element + +// Field-dependent constant ZETA = a non-square element of Field +// Required for Elligator 2 map +// TODO: Replace with built-in constant. +global ZETA = 5; + +// Field-dependent constants for Tonelli-Shanks algorithm (see sqrt function below) +// TODO: Possibly make this built-in. +global C1 = 28; +global C3 = 40770029410420498293352137776570907027550720424234931066070132305055; +global C5 = 19103219067921713944291392827692070036145651957329286315305642004821462161904; + +// Higher-order version of scalar multiplication +// TODO: Make this work so that the submodules' bit_mul may be defined in terms of it. +//fn bit_mul(add: fn(T,T) -> T, e: T, bits: [u1; N], p: T) -> T { +// let mut out = e; +// let n = bits.len(); +// +// for i in 0..n { +// out = add( +// add(out, out), +// if(bits[n - i - 1] == 0) {e} else {p}); +// } +// +// out +//} + +// Converts Field element to little-endian bit array of length N_BITS +// TODO: Fix built-in to_le_bits(., N_BITS), which yields a 128-periodic bit array +fn to_bits(x: Field) -> [u1; N_BITS] { + let mut x = x; + let mut out = [0; N_BITS]; + for i in 0..N_BITS { + if x != 0 { + out[i] = x as u1; + x = (x - out[i] as Field)/2; + } + } + out +} + +// TODO: Make this built-in. +fn safe_inverse(x: Field) -> Field { + if x == 0 { + 0 + } else { + 1/x + } +} + +// Boolean indicating whether Field element is a square, i.e. whether there exists a y in Field s.t. x = y*y. +fn is_square(x: Field) -> bool { + let v = pow(x, 0 - 1/2); + + v*(v-1) == 0 +} + +// Power function of two Field arguments of arbitrary size. +// Adapted from std::field::pow_32. +fn pow(x: Field, y: Field) -> Field { // As in tests with minor modifications + let mut r = 1 as Field; + let b = to_bits(y); + + for i in 0..N_BITS { + r *= r; + r *= (b[N_BITS - 1 - i] as Field)*x + (1-b[N_BITS - 1 - i] as Field); + } + + r +} + +// Tonelli-Shanks algorithm for computing the square root of a Field element. +// Requires C1 = max{c: 2^c divides (p-1)}, where p is the order of Field +// as well as C3 = (C2 - 1)/2, where C2 = (p-1)/(2^c1), +// and C5 = ZETA^C2, where ZETA is a non-square element of Field. +// These are pre-computed above as globals. +fn sqrt(x: Field) -> Field { + let mut z = pow(x, C3); + let mut t = z*z*x; + z *= x; + let mut b = t; + let mut c = C5; + + for i in 0..(C1-1) { + + for _j in 1..(C1-i-1) { + + b *= b; + + } + + z *= if b == 1 { 1 } else { c }; + + c *= c; + + t *= if b == 1 { 1 } else { c }; + + b = t; + } + + z +} diff --git a/noir_stdlib/src/ec/montcurve.nr b/noir_stdlib/src/ec/montcurve.nr new file mode 100644 index 00000000000..fad5e5e0a97 --- /dev/null +++ b/noir_stdlib/src/ec/montcurve.nr @@ -0,0 +1,380 @@ +mod affine { + // Affine representation of Montgomery curves + // Points are represented by two-dimensional Cartesian coordinates. + // All group operations are induced by those of the corresponding Twisted Edwards curve. + // See e.g. for details on the correspondences. + use crate::ec::montcurve::curvegroup; + use crate::ec::swcurve::affine::Curve as SWCurve; + use crate::ec::swcurve::affine::Point as SWPoint; + use crate::ec::tecurve::affine::Curve as TECurve; + use crate::ec::tecurve::affine::Point as TEPoint; + use crate::ec::is_square; + use crate::ec::safe_inverse; + use crate::ec::sqrt; + use crate::ec::ZETA; + + // Curve specification + struct Curve { // Montgomery Curve configuration (ky^2 = x^3 + j*x^2 + x) + j: Field, + k: Field, + // Generator as point in Cartesian coordinates + gen: Point + } + + // Point in Cartesian coordinates + struct Point { + x: Field, + y: Field, + infty: bool // Indicator for point at infinity + } + + impl Point { + // Point constructor + fn new(x: Field, y: Field) -> Self { + Self {x, y, infty: false} + } + + // Check for equality + fn eq(self, p: Self) -> bool { + (self.infty & p.infty) | (!self.infty & !p.infty & (self.x == p.x) & (self.y == p.y)) + } + + // Check if zero + fn is_zero(self) -> bool { + self.infty == true + } + + // Conversion to CurveGroup coordinates + fn into_group(self) -> curvegroup::Point { + if self.is_zero() == true { + curvegroup::Point::zero() + } else { + let (x,y) = (self.x, self.y); + curvegroup::Point::new(x,y,1) + } + } + + // Additive identity + fn zero() -> Self { + Self {x: 0, y: 0, infty: true} + } + + // Negation + fn negate(self) -> Self { + let Self {x, y, infty} = self; + + Self {x, y: 0-y, infty} + } + + // Map into equivalent Twisted Edwards curve + fn into_tecurve(self) -> TEPoint { + let Self {x, y, infty} = self; + + if (infty == true) | (y*(x+1) == 0) { + TEPoint::zero() + } else { + TEPoint::new(x/y, (x-1)/(x+1)) + } + } + } + + impl Curve { + // Curve constructor + fn new(j: Field, k: Field, gen: Point) -> Self { + // Check curve coefficients + constrain k != 0; + constrain j*j != 4; + + let curve = Self {j, k, gen}; + + // gen should be on the curve + constrain curve.contains(curve.gen); + + curve + } + + // Conversion to CurveGroup coordinates + fn into_group(self) -> curvegroup::Curve { + curvegroup::Curve::new(self.j, self.k, self.gen.into_group()) + } + + // Membership check + fn contains(self, p: Point) -> bool { + let Self {j, k, gen: _gen} = self; + let Point {x, y, infty: infty} = p; + + infty | (k*y*y == x*(x*x + j*x + 1)) + } + + // Point addition + fn add(self, p1: Point, p2: Point) -> Point { + self.into_tecurve().add(p1.into_tecurve(), p2.into_tecurve()).into_montcurve() + } + + // Scalar multiplication with scalar represented by a bit array (little-endian convention). + // If k is the natural number represented by `bits`, then this computes p + ... + p k times. + fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() + } + + // Scalar multiplication (p + ... + p n times) + fn mul(self, n: Field, p: Point) -> Point { + self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() + } + + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) + fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + let mut out = Point::zero(); + + for i in 0..n.len() { + out = self.add(out, self.mul(n[i], p[i])); + } + + out + } + + // Point subtraction + fn subtract(self, p1: Point, p2: Point) -> Point { + self.add(p1, p2.negate()) + } + + // Conversion to equivalent Twisted Edwards curve + fn into_tecurve(self) -> TECurve { + let Self {j, k, gen} = self; + TECurve::new((j+2)/k, (j-2)/k, gen.into_tecurve()) + } + + // Conversion to equivalent Short Weierstraß curve + fn into_swcurve(self) -> SWCurve { + let j = self.j; + let k = self.k; + let a0 = (3-j*j)/(3*k*k); + let b0 = (2*j*j*j - 9*j)/(27*k*k*k); + + SWCurve::new(a0, b0, self.map_into_swcurve(self.gen)) + } + + // Point mapping into equivalent Short Weierstraß curve + fn map_into_swcurve(self, p: Point) -> SWPoint { + if p.is_zero() == true { + SWPoint::zero() + } else { + SWPoint::new((3*p.x + self.j)/(3*self.k), + p.y/self.k) + } + } + + // Point mapping from equivalent Short Weierstraß curve + fn map_from_swcurve(self, p: SWPoint) -> Point { + let SWPoint {x, y, infty} = p; + let j = self.j; + let k = self.k; + + Point {x: (3*k*x - j)/3, y: y*k, infty} + } + + // Elligator 2 map-to-curve method; see . + fn elligator2_map(self, u: Field) -> Point { + let j = self.j; + let k = self.k; + let z = ZETA; // Non-square Field element required for map + + // Check whether curve is admissible + constrain j != 0; + let l = (j*j - 4)/(k*k); + constrain l != 0; + constrain is_square(l) == false; + + let x1 = safe_inverse(1+z*u*u)*(0 - (j/k)); + + let gx1 = x1*x1*x1 + (j/k)*x1*x1 + x1/(k*k); + let x2 = 0 - x1 - (j/k); + let gx2 = x2*x2*x2 + (j/k)*x2*x2 + x2/(k*k); + + let x = if is_square(gx1) == true { x1 } else { x2 }; + + let y = if is_square(gx1) == true { + let y0 = sqrt(gx1); + if y0.sgn0() == 1 { y0 } else { 0 - y0 } + } else { + let y0 = sqrt(gx2); + if y0.sgn0() == 0 { y0 } else { 0 - y0 } + }; + + Point::new(x*k, y*k) + + } + + // SWU map-to-curve method (via rational map) + fn swu_map(self, z: Field, u: Field) -> Point { + self.map_from_swcurve(self.into_swcurve().swu_map(z,u)) + } + } +} +mod curvegroup { + // Affine representation of Montgomery curves + // Points are represented by three-dimensional projective (homogeneous) coordinates. + // All group operations are induced by those of the corresponding Twisted Edwards curve. + // See e.g. for details on the correspondences. + use crate::ec::montcurve::affine; + use crate::ec::swcurve::curvegroup::Curve as SWCurve; + use crate::ec::swcurve::curvegroup::Point as SWPoint; + use crate::ec::tecurve::curvegroup::Curve as TECurve; + use crate::ec::tecurve::curvegroup::Point as TEPoint; + + struct Curve { // Montgomery Curve configuration (ky^2 z = x*(x^2 + j*x*z + z*z)) + j: Field, + k: Field, + // Generator as point in projective coordinates + gen: Point + } + + // Point in projective coordinates + struct Point { + x: Field, + y: Field, + z: Field + } + + impl Point { + // Point constructor + fn new(x: Field, y: Field, z: Field) -> Self { + Self {x, y, z} + } + + // Check for equality + fn eq(self, p: Self) -> bool { + (self.z == p.z) | (((self.x * self.z) == (p.x * p.z)) & ((self.y * self.z) == (p.y * p.z))) + } + + // Check if zero + fn is_zero(self) -> bool { + self.z == 0 + } + + // Conversion to affine coordinates + fn into_affine(self) -> affine::Point { + if self.is_zero() == true{ + affine::Point::zero() + } else { + let (x,y,z) = (self.x, self.y, self.z); + affine::Point::new(x/z, y/z) + } + } + + // Additive identity + fn zero() -> Self { + Self {x: 0, y: 1,z: 0} + } + + // Negation + fn negate(self) -> Self { + let Self {x, y, z} = self; + + Point::new(x, 0-y, z) + } + + // Map into equivalent Twisted Edwards curve + fn into_tecurve(self) -> TEPoint { + self.into_affine().into_tecurve().into_group() + } + } + + impl Curve { + // Curve constructor + fn new(j: Field, k: Field, gen: Point) -> Self { + // Check curve coefficients + constrain k != 0; + constrain j*j != 4; + + let curve = Self {j, k, gen}; + + // gen should be on the curve + constrain curve.contains(curve.gen); + + curve + } + + // Conversion to affine coordinates + fn into_affine(self) -> affine::Curve { + affine::Curve::new(self.j, self.k, self.gen.into_affine()) + } + + // Membership check + fn contains(self, p: Point) -> bool { + let Self {j, k, gen: _gen} = self; + let Point {x, y, z} = p; + + k*y*y*z == x*(x*x + j*x*z + z*z) + } + + // Point addition + fn add(self, p1: Point, p2: Point) -> Point { + self.into_affine().add(p1.into_affine(), p2.into_affine()).into_group() + } + + // Scalar multiplication with scalar represented by a bit array (little-endian convention). + // If k is the natural number represented by `bits`, then this computes p + ... + p k times. + fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() + } + + // Scalar multiplication (p + ... + p n times) + fn mul(self, n: Field, p: Point) -> Point { + self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() + } + + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) + fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + let mut out = Point::zero(); + + for i in 0..n.len() { + out = self.add(out, self.mul(n[i], p[i])); + } + + out + } + + // Point subtraction + fn subtract(self, p1: Point, p2: Point) -> Point { + self.add(p1, p2.negate()) + } + + // Conversion to equivalent Twisted Edwards curve + fn into_tecurve(self) -> TECurve { + let Self {j, k, gen} = self; + TECurve::new((j+2)/k, (j-2)/k, gen.into_tecurve()) + } + + // Conversion to equivalent Short Weierstraß curve + fn into_swcurve(self) -> SWCurve { + let j = self.j; + let k = self.k; + let a0 = (3-j*j)/(3*k*k); + let b0 = (2*j*j*j - 9*j)/(27*k*k*k); + + SWCurve::new(a0, b0, + self.map_into_swcurve(self.gen)) + } + + // Point mapping into equivalent Short Weierstraß curve + fn map_into_swcurve(self, p: Point) -> SWPoint { + self.into_affine().map_into_swcurve(p.into_affine()).into_group() + } + + // Point mapping from equivalent Short Weierstraß curve + fn map_from_swcurve(self, p: SWPoint) -> Point { + self.into_affine().map_from_swcurve(p.into_affine()).into_group() + } + + // Elligator 2 map-to-curve method + fn elligator2_map(self, u: Field) -> Point { + self.into_affine().elligator2_map(u).into_group() + } + + // SWU map-to-curve method (via rational map) + fn swu_map(self, z: Field, u: Field) -> Point { + self.into_affine().swu_map(z,u).into_group() + } + } +} diff --git a/noir_stdlib/src/ec/swcurve.nr b/noir_stdlib/src/ec/swcurve.nr new file mode 100644 index 00000000000..8e2a996e927 --- /dev/null +++ b/noir_stdlib/src/ec/swcurve.nr @@ -0,0 +1,373 @@ +mod affine { + // Affine representation of Short Weierstraß curves + // Points are represented by two-dimensional Cartesian coordinates. + // Group operations are implemented in terms of those in CurveGroup (in this case, extended Twisted Edwards) coordinates + // for reasons of efficiency, cf. . + use crate::ec::swcurve::curvegroup; + use crate::ec::safe_inverse; + use crate::ec::is_square; + use crate::ec::sqrt; + + // Curve specification + struct Curve { // Short Weierstraß curve + // Coefficients in defining equation y^2 = x^3 + ax + b + a: Field, + b: Field, + // Generator as point in Cartesian coordinates + gen: Point + } + + // Point in Cartesian coordinates + struct Point { + x: Field, + y: Field, + infty: bool // Indicator for point at infinity + } + + impl Point { + // Point constructor + fn new(x: Field, y: Field) -> Self { + Self {x, y, infty: false} + } + + // Check for equality + fn eq(self, p: Point) -> bool { + let Self {x: x1, y: y1, infty: inf1} = self; + let Self {x: x2, y: y2, infty: inf2} = p; + + (inf1 & inf2) + | (!inf1 & !inf2 & (x1 == x2) & (y1 == y2)) + } + + // Check if zero + fn is_zero(self) -> bool { + self.eq(Point::zero()) + } + + // Conversion to CurveGroup coordinates + fn into_group(self) -> curvegroup::Point { + let Self {x, y, infty} = self; + + if infty == true { + curvegroup::Point::zero() + } else { + curvegroup::Point::new(x, y, 1) + } + } + + // Additive identity + fn zero() -> Self { + Self {x: 0, y: 0, infty: true} + } + + // Negation + fn negate(self) -> Self { + let Self {x, y, infty} = self; + Self {x, y: 0-y, infty} + } + } + + impl Curve { + // Curve constructor + fn new(a: Field, b: Field, gen: Point) -> Curve { + // Check curve coefficients + constrain 4*a*a*a + 27*b*b != 0; + + let curve = Curve { a, b, gen }; + + // gen should be on the curve + constrain curve.contains(curve.gen); + + curve + } + + // Conversion to CurveGroup coordinates + fn into_group(self) -> curvegroup::Curve { + let Curve{a, b, gen} = self; + + curvegroup::Curve {a, b, gen: gen.into_group()} + } + + // Membership check + fn contains(self, p: Point) -> bool { + let Point {x, y, infty} = p; + infty | (y*y == x*x*x + self.a*x + self.b) + } + + // Point addition, implemented in terms of mixed addition for reasons of efficiency + fn add(self, p1: Point, p2: Point) -> Point { + self.mixed_add(p1, p2.into_group()).into_affine() + } + + // Mixed point addition, i.e. first argument in affine, second in CurveGroup coordinates. + fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { + if p1.is_zero() { + p2 + } else if p2.is_zero() { + p1.into_group() + } else { + let Point {x: x1, y: y1, infty: _inf} = p1; + let curvegroup::Point {x: x2, y: y2, z: z2} = p2; + let you1 = x1*z2*z2; + let you2 = x2; + let s1 = y1*z2*z2*z2; + let s2 = y2; + + if you1 == you2 { + if s1 != s2 { + curvegroup::Point::zero() + } else { + self.into_group().double(p2) + } + } else + { + let h = you2 - you1; + let r = s2 - s1; + let x3 = r*r - h*h*h - 2*you1*h*h; + let y3 = r*(you1*h*h - x3) - s1*h*h*h; + let z3 = h*z2; + + curvegroup::Point::new(x3,y3,z3) + } + } + } + + // Scalar multiplication with scalar represented by a bit array (little-endian convention). + // If k is the natural number represented by `bits`, then this computes p + ... + p k times. + fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + self.into_group().bit_mul(bits, p.into_group()).into_affine() + } + + // Scalar multiplication (p + ... + p n times) + fn mul(self, n: Field, p: Point) -> Point { + self.into_group().mul(n, p.into_group()).into_affine() + } + + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) + fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + let mut out = Point::zero(); + + for i in 0..n.len() { + out = self.add(out, self.mul(n[i], p[i])); + } + + out + } + + // Point subtraction + fn subtract(self, p1: Point, p2: Point) -> Point { + self.add(p1, p2.negate()) + } + + // Simplified Shallue-van de Woestijne-Ulas map-to-curve method; see . + // First determine non-square z != -1 in Field s.t. g(x) - z irreducible over Field and g(b/(z*a)) is square, + // where g(x) = x^3 + a*x + b. swu_map(c,z,.) then maps a Field element to a point on curve c. + fn swu_map(self, z: Field, u: Field) -> Point { + // Check whether curve is admissible + constrain self.a*self.b != 0; + + let Curve {a, b, gen: _gen} = self; + + let tv1 = safe_inverse(z*z*u*u*u*u + u*u*z); + let x1 = if tv1 == 0 {b/(z*a)} else {(0-b/a)*(1 + tv1)}; + let gx1 = x1*x1*x1 + a*x1 + b; + let x2 = z*u*u*x1; + let gx2 = x2*x2*x2 + a*x2 + b; + let (x,y) = if is_square(gx1) == true {(x1, sqrt(gx1))} else {(x2, sqrt(gx2))}; + Point::new(x, if u.sgn0() != y.sgn0() {0-y} else {y}) + } + } +} + +mod curvegroup { + // CurveGroup representation of Weierstraß curves + // Points are represented by three-dimensional Jacobian coordinates. + // See for details. + use crate::ec::swcurve::affine; + + // Curve specification + struct Curve { // Short Weierstraß curve + // Coefficients in defining equation y^2 = x^3 + axz^4 + bz^6 + a: Field, + b: Field, + // Generator as point in Cartesian coordinates + gen: Point + } + + // Point in three-dimensional Jacobian coordinates + struct Point { + x: Field, + y: Field, + z: Field // z = 0 corresponds to point at infinity. + } + + impl Point { + // Point constructor + fn new(x: Field, y: Field, z: Field) -> Self { + Self {x, y, z} + } + + // Check for equality + fn eq(self, p: Point) -> bool { + let Self {x: x1, y: y1, z: z1} = self; + let Self {x: x2, y: y2, z: z2} = p; + + ((z1 == 0) & (z2 == 0)) | ((z1 != 0) & (z2 != 0) & (x1*z2*z2 == x2*z1*z1) & (y1*z2*z2*z2 == y2*z1*z1*z1)) + } + + // Check if zero + fn is_zero(self) -> bool { + self.eq(Point::zero()) + } + + // Conversion to affine coordinates + fn into_affine(self) -> affine::Point { + let Self {x, y, z} = self; + + if z == 0 { + affine::Point::zero() + } else { + affine::Point::new(x/(z*z), y/(z*z*z)) + } + } + + // Additive identity + fn zero() -> Self { + Self {x: 0, y: 0, z: 0} + } + + + // Negation + fn negate(self) -> Self { + let Self {x, y, z} = self; + Self {x, y: 0-y, z} + } + } + + impl Curve { + // Curve constructor + fn new(a: Field, b: Field, gen: Point) -> Curve { + // Check curve coefficients + constrain 4*a*a*a + 27*b*b != 0; + + let curve = Curve { a, b, gen }; + + // gen should be on the curve + constrain curve.contains(curve.gen); + + curve + } + + // Conversion to affine coordinates + fn into_affine(self) -> affine::Curve { + let Curve{a, b, gen} = self; + + affine::Curve {a, b, gen: gen.into_affine()} + } + + // Membership check + fn contains(self, p: Point) -> bool { + let Point {x, y, z} = p; + if z == 0 { + true + } else { + y*y == x*x*x + self.a*x*z*z*z*z + self.b*z*z*z*z*z*z + } + } + + // Addition + fn add(self, p1: Point, p2: Point) -> Point { + + if p1.is_zero() { + p2 + } else if p2.is_zero() { + p1 + } else { + let Point {x: x1, y: y1, z: z1} = p1; + let Point {x: x2, y: y2, z: z2} = p2; + let you1 = x1*z2*z2; + let you2 = x2*z1*z1; + let s1 = y1*z2*z2*z2; + let s2 = y2*z1*z1*z1; + + if you1 == you2 { + if s1 != s2 { + Point::zero() + } else { + self.double(p1) + } + } else { + let h = you2 - you1; + let r = s2 - s1; + let x3 = r*r - h*h*h - 2*you1*h*h; + let y3 = r*(you1*h*h - x3) - s1*h*h*h; + let z3 = h*z1*z2; + + Point::new(x3,y3,z3) + } + } + } + + // Point doubling + fn double(self, p: Point) -> Point { + let Point {x, y, z} = p; + + if p.is_zero() { + p + } else if y == 0 { + Point::zero() + } else { + let s = 4*x*y*y; + let m = 3*x*x + self.a*z*z*z*z; + let x0 = m*m - 2*s; + let y0 = m*(s-x0) - 8*y*y*y*y; + let z0 = 2*y*z; + + Point::new(x0,y0,z0) + } + } + + // Scalar multiplication with scalar represented by a bit array (little-endian convention). + // If k is the natural number represented by `bits`, then this computes p + ... + p k times. + fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + let mut out = Point::zero(); + let n = bits.len(); + + for i in 0..n { + out = self.add( + self.add(out, out), + if(bits[n - i - 1] == 0) {Point::zero()} else {p}); + } + + out + } + + // Scalar multiplication (p + ... + p n times) + fn mul(self, n: Field, p: Point) -> Point { + let n_as_bits = crate::ec::to_bits(n); // N_BITS-bit representation + + self.bit_mul(n_as_bits, p) + } + + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) + fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + let mut out = Point::zero(); + + for i in 0..n.len() { + out = self.add(out, self.mul(n[i], p[i])); + } + + out + } + + // Point subtraction + fn subtract(self, p1: Point, p2: Point) -> Point { + self.add(p1, p2.negate()) + } + + // Simplified SWU map-to-curve method + fn swu_map(self, z: Field, u: Field) -> Point { + self.into_affine().swu_map(z,u).into_group() + } + } +} diff --git a/noir_stdlib/src/ec/tecurve.nr b/noir_stdlib/src/ec/tecurve.nr new file mode 100644 index 00000000000..43c9f5d2017 --- /dev/null +++ b/noir_stdlib/src/ec/tecurve.nr @@ -0,0 +1,424 @@ +mod affine { + // Affine coordinate representation of Twisted Edwards curves + // Points are represented by two-dimensional Cartesian coordinates. + // Group operations are implemented in terms of those in CurveGroup (in this case, extended Twisted Edwards) coordinates + // for reasons of efficiency. + // See for details. + use crate::ec::tecurve::curvegroup; + use crate::ec::montcurve::affine::Curve as MCurve; + use crate::ec::montcurve::affine::Point as MPoint; + use crate::ec::swcurve::affine::Curve as SWCurve; + use crate::ec::swcurve::affine::Point as SWPoint; + + // Curve specification + struct Curve { // Twisted Edwards curve + // Coefficients in defining equation ax^2 + y^2 = 1 + dx^2y^2 + a: Field, + d: Field, + // Generator as point in Cartesian coordinates + gen: Point + } + + // Point in Cartesian coordinates + struct Point { + x: Field, + y: Field + } + + impl Point { + // Point constructor + fn new(x: Field, y: Field) -> Self { + Self { x, y } + } + + // Check for equality + fn eq(self, p: Point) -> bool { + let Self {x: x1, y: y1} = self; + let Self {x: x2, y: y2} = p; + + (x1 == x2) & (y1 == y2) + } + + // Check if zero + fn is_zero(self) -> bool { + self.eq(Point::zero()) + } + + // Conversion to CurveGroup coordinates + fn into_group(self) -> curvegroup::Point { + let Self {x, y} = self; + + curvegroup::Point::new(x, y, x*y, 1) + } + + // Additive identity + fn zero() -> Self { + Point::new(0,1) + } + + // Negation + fn negate(self) -> Self { + let Self {x, y} = self; + Point::new(0-x, y) + } + + // Map into prime-order subgroup of equivalent Montgomery curve + fn into_montcurve(self) -> MPoint { + if self.is_zero() == true { + MPoint::zero() + } else { + let Self {x, y} = self; + let x0 = (1+y)/(1-y); + let y0 = (1+y)/(x*(1-y)); + + MPoint::new(x0,y0) + } + } + } + + + impl Curve { + // Curve constructor + fn new(a: Field, d: Field, gen: Point) -> Curve { + // Check curve coefficients + constrain a*d*(a-d) != 0; + + let curve = Curve {a, d, gen}; + + // gen should be on the curve + constrain curve.contains(curve.gen); + + curve + } + + // Conversion to CurveGroup coordinates + fn into_group(self) -> curvegroup::Curve { + let Curve{a, d, gen} = self; + + curvegroup::Curve {a, d, gen: gen.into_group()} + } + + // Membership check + fn contains(self, p: Point) -> bool { + let Point {x, y} = p; + self.a*x*x + y*y == 1 + self.d*x*x*y*y + } + + // Point addition, implemented in terms of mixed addition for reasons of efficiency + fn add(self, p1: Point, p2: Point) -> Point { + self.mixed_add(p1, p2.into_group()).into_affine() + } + + // Mixed point addition, i.e. first argument in affine, second in CurveGroup coordinates. + fn mixed_add(self, p1: Point, p2: curvegroup::Point) -> curvegroup::Point { + let Point{x: x1, y: y1} = p1; + let curvegroup::Point{x: x2, y: y2, t: t2, z: z2} = p2; + + let a = x1*x2; + let b = y1*y2; + let c = self.d*x1*y1*t2; + let e = (x1 + y1)*(x2 + y2) - a - b; + let f = z2 - c; + let g = z2 + c; + let h = b - self.a*a; + + let x = e*f; + let y = g*h; + let t = e*h; + let z = f*g; + + curvegroup::Point::new(x,y,t,z) + } + + // Scalar multiplication with scalar represented by a bit array (little-endian convention). + // If k is the natural number represented by `bits`, then this computes p + ... + p k times. + fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + self.into_group().bit_mul(bits, p.into_group()).into_affine() + } + + // Scalar multiplication (p + ... + p n times) + fn mul(self, n: Field, p: Point) -> Point { + self.into_group().mul(n, p.into_group()).into_affine() + } + + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) + fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + let mut out = Point::zero(); + + for i in 0..n.len() { + out = self.add(out, self.mul(n[i], p[i])); + } + + out + } + + // Point subtraction + fn subtract(self, p1: Point, p2: Point) -> Point { + self.add(p1, p2.negate()) + } + + // Conversion to equivalent Montgomery curve + fn into_montcurve(self) -> MCurve { + let j = 2*(self.a + self.d)/(self.a - self.d); + let k = 4/(self.a - self.d); + let gen_montcurve = self.gen.into_montcurve(); + + MCurve::new(j, k, gen_montcurve) + } + + // Conversion to equivalent Short Weierstraß curve + fn into_swcurve(self) -> SWCurve { + self.into_montcurve().into_swcurve() + } + + // Point mapping into equivalent Short Weierstraß curve + fn map_into_swcurve(self, p: Point) -> SWPoint { + self.into_montcurve().map_into_swcurve(p.into_montcurve()) + } + + // Point mapping from equivalent Short Weierstraß curve + fn map_from_swcurve(self, p: SWPoint) -> Point { + self.into_montcurve().map_from_swcurve(p).into_tecurve() + } + + // Elligator 2 map-to-curve method (via rational map) + fn elligator2_map(self, u: Field) -> Point { + self.into_montcurve().elligator2_map(u).into_tecurve() + } + + // Simplified SWU map-to-curve method (via rational map) + fn swu_map(self, z: Field, u: Field) -> Point { + self.into_montcurve().swu_map(z,u).into_tecurve() + } + } +} +mod curvegroup { + // CurveGroup coordinate representation of Twisted Edwards curves + // Points are represented by four-dimensional projective coordinates, viz. extended Twisted Edwards coordinates. + // See §3 of for details. + use crate::ec::tecurve::affine; + use crate::ec::montcurve::curvegroup::Curve as MCurve; + use crate::ec::montcurve::curvegroup::Point as MPoint; + use crate::ec::swcurve::curvegroup::Curve as SWCurve; + use crate::ec::swcurve::curvegroup::Point as SWPoint; + + // Curve specification + struct Curve { // Twisted Edwards curve + // Coefficients in defining equation a(x^2 + y^2)z^2 = z^4 + dx^2y^2 + a: Field, + d: Field, + // Generator as point in projective coordinates + gen: Point + } + + // Point in extended twisted Edwards coordinates + struct Point { + x: Field, + y: Field, + t: Field, + z: Field + } + + impl Point { + // Point constructor + fn new(x: Field, y: Field, t: Field, z: Field) -> Self { + Self {x, y, t, z} + } + + // Check for equality + fn eq(self, p: Point) -> bool { + if self.is_zero() == true { + p.is_zero() + } else if p.is_zero() == true { + false + } else { + let Self {x: x1, y: y1, t: _t1, z: z1} = self; + let Self {x: x2, y: y2, t: _t2, z:z2} = p; + + if x1*z2 == x2*z1 { + y1*z2 == y2*z1 + } else { + false + } + } + } + + // Check if zero + fn is_zero(self) -> bool { + let Self {x, y, t, z} = self; + if y == z { + if x == t { + x == 0 + } else { + false + } + } else { + false + } + } + + // Conversion to affine coordinates + fn into_affine(self) -> affine::Point { + let Self {x, y, t: _t, z} = self; + + affine::Point::new(x/z, y/z) + } + + // Additive identity + fn zero() -> Self { + Point::new(0,1,0,1) + } + + // Negation + fn negate(self) -> Self { + let Self {x, y, t, z} = self; + + Point::new(0-x, y, 0-t, z) + } + + // Map into prime-order subgroup of equivalent Montgomery curve + fn into_montcurve(self) -> MPoint { + self.into_affine().into_montcurve().into_group() + } + } + + impl Curve { + // Curve constructor + fn new(a: Field, d: Field, gen: Point) -> Curve { + // Check curve coefficients + constrain a*d*(a-d) != 0; + + let curve = Curve { a, d, gen }; + + // gen should be on the curve + constrain curve.contains(curve.gen); + + curve + } + + // Conversion to affine coordinates + fn into_affine(self) -> affine::Curve { + let Curve{a, d, gen} = self; + + affine::Curve {a, d, gen: gen.into_affine()} + } + + // Membership check + fn contains(self, p: Point) -> bool { + let Point {x, y, t, z} = p; + + (z != 0) & (z*t == x*y) & (z*z*(self.a*x*x + y*y) == z*z + self.d*x*x*y*y) + } + + // Point addition + fn add(self, p1: Point, p2: Point) -> Point { + let Point{x: x1, y: y1, t: t1, z: z1} = p1; + let Point{x: x2, y: y2, t: t2, z: z2} = p2; + + let a = x1*x2; + let b = y1*y2; + let c = self.d*t1*t2; + let d = z1*z2; + let e = (x1 + y1)*(x2 + y2) - a - b; + let f = d - c; + let g = d + c; + let h = b - self.a*a; + + let x = e*f; + let y = g*h; + let t = e*h; + let z = f*g; + + Point::new(x,y,t,z) + } + + // Point doubling, cf. §3.3 + fn double(self, p: Point) -> Point { + let Point{x, y, t: _t, z} = p; + + let a = x*x; + let b = y*y; + let c = 2*z*z; + let d = self.a*a; + let e = (x + y)*(x + y) - a - b; + let g = d + b; + let f = g - c; + let h = d - b; + + let x0 = e*f; + let y0 = g*h; + let t0 = e*h; + let z0 = f*g; + + Point::new(x0, y0, t0, z0) + } + + // Scalar multiplication with scalar represented by a bit array (little-endian convention). + // If k is the natural number represented by `bits`, then this computes p + ... + p k times. + fn bit_mul(self, bits: [u1; N], p: Point) -> Point { + let mut out = Point::zero(); + let n = bits.len(); + + for i in 0..n { + out = self.add( + self.add(out, out), + if(bits[n - i - 1] == 0) {Point::zero()} else {p}); + } + + out + } + + // Scalar multiplication (p + ... + p n times) + fn mul(self, n: Field, p: Point) -> Point { + let n_as_bits = crate::ec::to_bits(n); // N_BITS-bit representation + + self.bit_mul(n_as_bits, p) + } + + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) + fn msm(self, n: [Field; N], p: [Point; N]) -> Point { + let mut out = Point::zero(); + + for i in 0..n.len() { + out = self.add(out, self.mul(n[i], p[i])); + } + + out + } + + // Point subtraction + fn subtract(self, p1: Point, p2: Point) -> Point { + self.add(p1, p2.negate()) + } + + // Conversion to equivalent Montgomery curve + fn into_montcurve(self) -> MCurve { + self.into_affine().into_montcurve().into_group() + } + + // Conversion to equivalent Short Weierstraß curve + fn into_swcurve(self) -> SWCurve { + self.into_montcurve().into_swcurve() + } + + // Point mapping into equivalent short Weierstraß curve + fn map_into_swcurve(self, p: Point) -> SWPoint { + self.into_montcurve().map_into_swcurve(p.into_montcurve()) + } + + // Point mapping from equivalent short Weierstraß curve + fn map_from_swcurve(self, p: SWPoint) -> Point { + self.into_montcurve().map_from_swcurve(p).into_tecurve() + } + + // Elligator 2 map-to-curve method (via rational maps) + fn elligator2_map(self, u: Field) -> Point { + self.into_montcurve().elligator2_map(u).into_tecurve() + } + + // Simplified SWU map-to-curve method (via rational map) + fn swu_map(self, z: Field, u: Field) -> Point { + self.into_montcurve().swu_map(z,u).into_tecurve() + } + } +} diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index bf78fc99999..b7773182d66 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -32,6 +32,11 @@ impl Field { } r } + + // Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ {0, ..., p-1} is even, otherwise sgn0(x mod p) = 1. + fn sgn0(self) -> u1 { + self as u1 + } } #[builtin(modulus_num_bits)] diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index b3d903b7262..abdd56c4975 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -7,6 +7,7 @@ mod scalar_mul; mod sha256; mod sha512; mod field; +mod ec; #[builtin(println)] fn println(_input : T) {}