From fbdb4e21a490e328b0e3af43373bfd6a28e93075 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 2 Jan 2024 09:07:04 -0500 Subject: [PATCH 1/4] Initial commit from https://github.com/shuklaayush/halo2/tree/feat/ed25519 --- src/ed25519/curve.rs | 1085 ++++++++++++++++++++++++++++++++++++++++++ src/ed25519/fq.rs | 310 ++++++++++++ src/ed25519/fr.rs | 330 +++++++++++++ src/ed25519/mod.rs | 7 + src/lib.rs | 1 + 5 files changed, 1733 insertions(+) create mode 100644 src/ed25519/curve.rs create mode 100644 src/ed25519/fq.rs create mode 100644 src/ed25519/fr.rs create mode 100644 src/ed25519/mod.rs diff --git a/src/ed25519/curve.rs b/src/ed25519/curve.rs new file mode 100644 index 00000000..b6eb0e35 --- /dev/null +++ b/src/ed25519/curve.rs @@ -0,0 +1,1085 @@ +use crate::ed25519::Fq; +use crate::ed25519::Fr; +use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; +use core::cmp; +use core::fmt::Debug; +use core::iter::Sum; +use core::ops::{Add, Mul, Neg, Sub}; +use ff::{BatchInverter, Field, PrimeField}; +use group::{self, Curve, Group}; +use group::{prime::PrimeCurveAffine, GroupEncoding}; +use rand::RngCore; +use serde::{Deserialize, Serialize}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +const ED25519_GENERATOR_X: Fq = Fq::from_raw([ + 0xc956_2d60_8f25_d51a, + 0x692c_c760_9525_a7b2, + 0xc0a4_e231_fdd6_dc5c, + 0x2169_36d3_cd6e_53fe, +]); +const ED25519_GENERATOR_Y: Fq = Fq::from_raw([ + 0x6666_6666_6666_6658, + 0x6666_6666_6666_6666, + 0x6666_6666_6666_6666, + 0x6666_6666_6666_6666, +]); + +// `d = -(121665/121666)` +const ED25519_D: Fq = Fq::from_raw([ + 0x75eb_4dca_1359_78a3, + 0x0070_0a4d_4141_d8ab, + 0x8cc7_4079_7779_e898, + 0x5203_6cee_2b6f_fe73, +]); + +const FR_MODULUS_BYTES: [u8; 32] = [ + 237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 16, +]; + +use crate::{ + impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, + impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; + +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub struct Ed25519 { + pub x: Fq, + pub y: Fq, + pub z: Fq, + pub t: Fq, +} + +#[derive(Copy, Clone, Debug, PartialEq, Hash, Serialize, Deserialize)] +pub struct Ed25519Affine { + pub x: Fq, + pub y: Fq, +} + +#[derive(Copy, Clone, Hash)] +pub struct Ed25519Compressed([u8; 32]); + +impl Ed25519 { + /// Constructs an extended point from the neutral element `(0, 1)`. + pub const fn identity() -> Self { + Ed25519 { + x: Fq::zero(), + y: Fq::one(), + z: Fq::one(), + t: Fq::zero(), + } + } + + /// Determines if this point is the identity. + pub fn is_identity(&self) -> Choice { + // If this point is the identity, then + // u = 0 * z = 0 + // and v = 1 * z = z + self.x.ct_eq(&Fq::zero()) & self.y.ct_eq(&self.z) + } + + /// Determines if this point is torsion free and so is contained + /// in the prime order subgroup. + pub fn is_torsion_free(&self) -> Choice { + self.multiply(&FR_MODULUS_BYTES).is_identity() + } + + #[inline] + fn multiply(&self, by: &[u8; 32]) -> Ed25519 { + let zero = Ed25519::identity(); + let mut acc = Ed25519::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading three bits because they're always + // unset for Fr. + for bit in by + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(3) + { + acc = acc.double(); + acc += Ed25519::conditional_select(&zero, &self, bit); + } + + acc + } + + /// Multiplies this element by the cofactor `8`. + pub fn mul_by_cofactor(&self) -> Ed25519 { + self.double().double().double() + } + + pub fn generator() -> Self { + let generator = Ed25519Affine::generator(); + Self { + x: generator.x, + y: generator.y, + z: Fq::one(), + t: generator.x * generator.y, + } + } + + pub fn double(&self) -> Ed25519 { + // A = X1^2 + // B = Y1^2 + // C = 2*Z1^2 + // H = A+B + // E = H-(X1+Y1)^2 + // G = A-B + // F = C+G + // X3 = E*F + // Y3 = G*H + // T3 = E*H + // Z3 = F*G + + let a = self.x.square(); + let b = self.y.square(); + let c = self.z.square().double(); + + let h = a + b; + let e = h - (self.x + self.y).square(); + let g = a - b; + let f = c + g; + + Ed25519 { + x: e * f, + y: g * h, + z: f * g, + t: e * h, + } + } +} + +impl Ed25519 { + fn endomorphism_base(&self) -> Self { + unimplemented!(); + } +} + +impl Ed25519Affine { + /// Constructs the neutral element `(0, 1)`. + pub const fn identity() -> Self { + Ed25519Affine { + x: Fq::zero(), + y: Fq::one(), + } + } + + /// Determines if this point is the identity. + pub fn is_identity(&self) -> Choice { + Ed25519::from(*self).is_identity() + } + + pub fn generator() -> Self { + Self { + x: ED25519_GENERATOR_X, + y: ED25519_GENERATOR_Y, + } + } + + pub fn to_extended(&self) -> Ed25519 { + Ed25519 { + x: self.x, + y: self.y, + z: Fq::one(), + t: self.x * self.y, + } + } + + pub fn random(mut rng: impl RngCore) -> Self { + loop { + let y = Fq::random(&mut rng); + let flip_sign = rng.next_u32() % 2 != 0; + + let y2 = y.square(); + let p = ((y2 - Fq::one()) + * ((Fq::one() + ED25519_D * y2).invert().unwrap_or(Fq::zero()))) + .sqrt() + .map(|x| Ed25519Affine { + x: if flip_sign { -x } else { x }, + y, + }); + + if p.is_some().into() { + use crate::group::cofactor::CofactorGroup; + let p = p.unwrap().to_curve(); + + if bool::from(!p.is_identity()) { + return p.clear_cofactor().to_affine(); + } + } + } + } + + /// Converts this element into its byte representation. + pub fn to_bytes(&self) -> [u8; 32] { + let mut tmp = self.y.to_bytes(); + let u = self.x.to_bytes(); + + // Encode the sign of the u-coordinate in the most + // significant bit. + tmp[31] |= u[0] << 7; + + tmp + } + + /// Attempts to interpret a byte representation of an + /// affine point, failing if the element is not on + /// the curve or non-canonical. + pub fn from_bytes(b: [u8; 32]) -> CtOption { + Self::from_bytes_inner(b, 1.into()) + } + + fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption { + // Grab the sign bit from the representation + let sign = b[31] >> 7; + + // Mask away the sign bit + b[31] &= 0b0111_1111; + + // Interpret what remains as the v-coordinate + Fq::from_bytes(&b).and_then(|v| { + // -u^2 + v^2 = 1 + d.u^2.v^2 + // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) + // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) + // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) + // u^2 (1 + d.v^2) = v^2 - 1 (factor) + // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) + // We know that (1 + d.v^2) is nonzero for all v: + // (1 + d.v^2) = 0 + // d.v^2 = -1 + // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square + + let v2 = v.square(); + + ((v2 - Fq::one()) * ((Fq::one() + ED25519_D * v2).invert().unwrap_or(Fq::zero()))) + .sqrt() + .and_then(|u| { + // Fix the sign of `u` if necessary + let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); + let u_negated = -u; + let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); + + // If u == 0, flip_sign == sign_bit. We therefore want to reject the + // encoding as non-canonical if all of the following occur: + // - ZIP 216 is enabled + // - u == 0 + // - flip_sign == true + let u_is_zero = u.ct_eq(&Fq::zero()); + CtOption::new( + Ed25519Affine { x: final_u, y: v }, + !(zip_216_enabled & u_is_zero & flip_sign), + ) + }) + }) + } +} + +// Compressed +impl std::fmt::Debug for Ed25519Compressed { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0[..].fmt(f) + } +} + +impl Default for Ed25519Compressed { + fn default() -> Self { + Ed25519Compressed([0; 32]) + } +} + +impl AsRef<[u8]> for Ed25519Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for Ed25519Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +// Jacobian implementations +impl<'a> From<&'a Ed25519Affine> for Ed25519 { + fn from(p: &'a Ed25519Affine) -> Ed25519 { + p.to_curve() + } +} + +impl From for Ed25519 { + fn from(p: Ed25519Affine) -> Ed25519 { + p.to_curve() + } +} + +impl Default for Ed25519 { + fn default() -> Ed25519 { + Ed25519::identity() + } +} + +impl subtle::ConstantTimeEq for Ed25519 { + fn ct_eq(&self, other: &Self) -> Choice { + (self.x * other.z).ct_eq(&(other.x * self.z)) + & (self.y * other.z).ct_eq(&(other.y * self.z)) + } +} + +impl subtle::ConditionallySelectable for Ed25519 { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Ed25519 { + x: Fq::conditional_select(&a.x, &b.x, choice), + y: Fq::conditional_select(&a.y, &b.y, choice), + z: Fq::conditional_select(&a.z, &b.z, choice), + t: Fq::conditional_select(&a.t, &b.t, choice), + } + } +} + +impl PartialEq for Ed25519 { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl cmp::Eq for Ed25519 {} + +impl CurveExt for Ed25519 { + type ScalarExt = Fr; + type Base = Fq; + type AffineExt = Ed25519Affine; + + const CURVE_ID: &'static str = "ed25519"; + + fn endo(&self) -> Self { + self.endomorphism_base() + } + + fn jacobian_coordinates(&self) -> (Fq, Fq, Fq) { + unimplemented!(); + } + + fn hash_to_curve<'a>(_: &'a str) -> Box Self + 'a> { + unimplemented!(); + } + + fn is_on_curve(&self) -> Choice { + let affine = Ed25519Affine::from(*self); + + println!("affine: {:?}", affine); + + !self.z.is_zero() & affine.is_on_curve() & (affine.x * affine.y * self.z).ct_eq(&self.t) + } + + fn a() -> Self::Base { + unimplemented!() + } + + fn b() -> Self::Base { + ED25519_D + } + + fn new_jacobian(_x: Self::Base, _y: Self::Base, _z: Self::Base) -> CtOption { + unimplemented!(); + } +} + +impl group::Curve for Ed25519 { + type AffineRepr = Ed25519Affine; + + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + assert_eq!(p.len(), q.len()); + + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `u` field of `AffinePoint` to store the z-coordinate being + // inverted, and the `v` field for scratch space. + q.x = p.z; + } + + BatchInverter::invert_with_internal_scratch(q, |q| &mut q.x, |q| &mut q.y); + + for (p, q) in p.iter().zip(q.iter_mut()).rev() { + let tmp = q.x; + + // Set the coordinates to the correct value + q.x = p.x * &tmp; // Multiply by 1/z + q.y = p.y * &tmp; // Multiply by 1/z + } + } + + fn to_affine(&self) -> Self::AffineRepr { + // Z coordinate is always nonzero, so this is + // its inverse. + let zinv = self.z.invert().unwrap(); + + Ed25519Affine { + x: self.x * zinv, + y: self.y * zinv, + } + } +} + +impl group::Group for Ed25519 { + type Scalar = Fr; + + fn random(mut rng: impl RngCore) -> Self { + Ed25519Affine::random(&mut rng).to_curve() + } + + fn generator() -> Self { + Ed25519::generator() + } + + fn identity() -> Self { + Self::identity() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + #[must_use] + fn double(&self) -> Self { + self.double() + } +} + +impl GroupEncoding for Ed25519 { + type Repr = Ed25519Compressed; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Ed25519Affine::from_bytes(bytes.0).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Ed25519Affine::from_bytes(bytes.0).map(Self::from) + } + + fn to_bytes(&self) -> Self::Repr { + Ed25519Compressed(Ed25519Affine::from(self).to_bytes()) + } +} + +impl crate::serde::SerdeObject for Ed25519 { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), 4 * Fq::size()); + let [x, y, z, t] = [0, 1, 2, 3] + .map(|i| Fq::from_raw_bytes_unchecked(&bytes[i * Fq::size()..(i + 1) * Fq::size()])); + Self { x, y, z, t } + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 4 * Fq::size() { + return None; + } + let [x, y, z, t] = + [0, 1, 2, 3].map(|i| Fq::from_raw_bytes(&bytes[i * Fq::size()..(i + 1) * Fq::size()])); + x.zip(y).zip(z).zip(t).and_then(|(((x, y), z), t)| { + let res = Self { x, y, z, t }; + // Check that the point is on the curve. + bool::from(res.is_on_curve()).then(|| res) + }) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(4 * Fq::size()); + Self::write_raw(self, &mut res).unwrap(); + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let [x, y, z, t] = [(); 4].map(|_| Fq::read_raw_unchecked(reader)); + Self { x, y, z, t } + } + fn read_raw(reader: &mut R) -> std::io::Result { + let x = Fq::read_raw(reader)?; + let y = Fq::read_raw(reader)?; + let z = Fq::read_raw(reader)?; + let t = Fq::read_raw(reader)?; + Ok(Self { x, y, z, t }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + self.x.write_raw(writer)?; + self.y.write_raw(writer)?; + self.z.write_raw(writer)?; + self.t.write_raw(writer) + } +} + +impl group::prime::PrimeGroup for Ed25519 {} + +impl group::prime::PrimeCurve for Ed25519 { + type Affine = Ed25519Affine; +} + +impl group::cofactor::CofactorCurve for Ed25519 { + type Affine = Ed25519Affine; +} + +impl group::cofactor::CofactorGroup for Ed25519 { + type Subgroup = Ed25519; + + fn clear_cofactor(&self) -> Self { + self.mul_by_cofactor() + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, self.is_torsion_free()) + } + + fn is_torsion_free(&self) -> Choice { + self.is_torsion_free() + } +} + +impl<'a> From<&'a Ed25519> for Ed25519Affine { + fn from(p: &'a Ed25519) -> Ed25519Affine { + p.to_affine() + } +} + +impl From for Ed25519Affine { + fn from(p: Ed25519) -> Ed25519Affine { + p.to_affine() + } +} + +impl Default for Ed25519Affine { + fn default() -> Ed25519Affine { + Ed25519Affine::identity() + } +} + +impl subtle::ConstantTimeEq for Ed25519Affine { + fn ct_eq(&self, other: &Self) -> Choice { + self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y) + } +} + +impl subtle::ConditionallySelectable for Ed25519Affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Ed25519Affine { + x: Fq::conditional_select(&a.x, &b.x, choice), + y: Fq::conditional_select(&a.y, &b.y, choice), + } + } +} + +impl cmp::Eq for Ed25519Affine {} + +impl group::GroupEncoding for Ed25519Affine { + type Repr = [u8; 32]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes(*bytes) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + Self::from_bytes(*bytes) + } + + fn to_bytes(&self) -> Self::Repr { + self.to_bytes() + } +} + +impl crate::serde::SerdeObject for Ed25519Affine { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), 2 * Fq::size()); + let [x, y] = + [0, Fq::size()].map(|i| Fq::from_raw_bytes_unchecked(&bytes[i..i + Fq::size()])); + Self { x, y } + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 2 * Fq::size() { + return None; + } + let [x, y] = [0, Fq::size()].map(|i| Fq::from_raw_bytes(&bytes[i..i + Fq::size()])); + x.zip(y).and_then(|(x, y)| { + let res = Self { x, y }; + // Check that the point is on the curve. + bool::from(res.is_on_curve()).then(|| res) + }) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(2 * Fq::size()); + Self::write_raw(self, &mut res).unwrap(); + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let [x, y] = [(); 2].map(|_| Fq::read_raw_unchecked(reader)); + Self { x, y } + } + fn read_raw(reader: &mut R) -> std::io::Result { + let x = Fq::read_raw(reader)?; + let y = Fq::read_raw(reader)?; + Ok(Self { x, y }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + self.x.write_raw(writer)?; + self.y.write_raw(writer) + } +} + +impl group::prime::PrimeCurveAffine for Ed25519Affine { + type Curve = Ed25519; + type Scalar = Fr; + + fn generator() -> Self { + Ed25519Affine::generator() + } + + fn identity() -> Self { + Ed25519Affine::identity() + } + + fn is_identity(&self) -> Choice { + self.is_identity() + } + + fn to_curve(&self) -> Self::Curve { + Ed25519 { + x: self.x, + y: self.y, + z: Fq::one(), + t: self.x * self.y, + } + } +} + +impl group::cofactor::CofactorCurveAffine for Ed25519Affine { + type Curve = Ed25519; + type Scalar = Fr; + + fn identity() -> Self { + ::identity() + } + + fn generator() -> Self { + ::generator() + } + + fn is_identity(&self) -> Choice { + ::is_identity(self) + } + + fn to_curve(&self) -> Self::Curve { + ::to_curve(self) + } +} + +impl CurveAffine for Ed25519Affine { + type ScalarExt = Fr; + type Base = Fq; + type CurveExt = Ed25519; + + fn is_on_curve(&self) -> Choice { + let x2 = self.x.square(); + let y2 = self.y.square(); + + (y2 - x2).ct_eq(&(Fq::one() + ED25519_D * x2 * y2)) + } + + fn coordinates(&self) -> CtOption> { + Coordinates::from_xy(self.x, self.y) + } + + fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { + let p = Ed25519Affine { x, y }; + CtOption::new(p, p.is_on_curve()) + } + + fn a() -> Self::Base { + unimplemented!() + } + + fn b() -> Self::Base { + ED25519_D + } +} + +impl_binops_additive!(Ed25519, Ed25519); +impl_binops_additive!(Ed25519, Ed25519Affine); +impl_binops_additive_specify_output!(Ed25519Affine, Ed25519Affine, Ed25519); +impl_binops_additive_specify_output!(Ed25519Affine, Ed25519, Ed25519); +impl_binops_multiplicative!(Ed25519, Fr); +impl_binops_multiplicative_mixed!(Ed25519Affine, Fr, Ed25519); + +impl<'a> Neg for &'a Ed25519 { + type Output = Ed25519; + + fn neg(self) -> Ed25519 { + Ed25519 { + x: -self.x, + y: self.y, + z: self.z, + t: -self.t, + } + } +} + +impl Neg for Ed25519 { + type Output = Ed25519; + + fn neg(self) -> Ed25519 { + -&self + } +} + +impl Sum for Ed25519 +where + T: core::borrow::Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + +impl<'a, 'b> Add<&'a Ed25519> for &'b Ed25519 { + type Output = Ed25519; + + fn add(self, rhs: &'a Ed25519) -> Ed25519 { + // We perform addition in the extended coordinates. Here we use + // a formula presented by Hisil, Wong, Carter and Dawson in + // "Twisted Edward Curves Revisited" which only requires 8M. + // + // A = (V1 - U1) * (V2 - U2) + // B = (V1 + U1) * (V2 + U2) + // C = 2d * T1 * T2 + // D = 2 * Z1 * Z2 + // E = B - A + // F = D - C + // G = D + C + // H = B + A + // U3 = E * F + // Y3 = G * H + // Z3 = F * G + // T3 = E * H + + let a = (self.x - self.y) * (rhs.x - rhs.y); + let b = (self.x + self.y) * (rhs.x + rhs.y); + let c = (self.t * rhs.t * ED25519_D).double(); + let d = (self.z * rhs.z).double(); + + let e = b - a; + let f = d - c; + let g = d + c; + let h = b + a; + + Ed25519 { + x: e * f, + y: g * h, + z: f * g, + t: e * h, + } + } +} + +impl<'a, 'b> Add<&'a Ed25519Affine> for &'b Ed25519 { + type Output = Ed25519; + + fn add(self, rhs: &'a Ed25519Affine) -> Ed25519 { + self + rhs.to_extended() + } +} + +impl<'a, 'b> Sub<&'a Ed25519> for &'b Ed25519 { + type Output = Ed25519; + + fn sub(self, other: &'a Ed25519) -> Ed25519 { + self + (-other) + } +} + +impl<'a, 'b> Sub<&'a Ed25519Affine> for &'b Ed25519 { + type Output = Ed25519; + + fn sub(self, other: &'a Ed25519Affine) -> Ed25519 { + self + (-other) + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl<'a, 'b> Mul<&'b Fr> for &'a Ed25519 { + type Output = Ed25519; + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading three bits because they're always + // unset for Fr. + fn mul(self, other: &'b Fr) -> Self::Output { + let mut acc = Ed25519::identity(); + for bit in other + .to_repr() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(3) + { + acc = acc.double(); + acc = Ed25519::conditional_select(&acc, &(acc + self), bit); + } + + acc + } +} + +impl<'a> Neg for &'a Ed25519Affine { + type Output = Ed25519Affine; + + fn neg(self) -> Ed25519Affine { + Ed25519Affine { + x: -self.x, + y: self.y, + } + } +} + +impl Neg for Ed25519Affine { + type Output = Ed25519Affine; + + fn neg(self) -> Ed25519Affine { + -&self + } +} + +impl<'a, 'b> Add<&'a Ed25519> for &'b Ed25519Affine { + type Output = Ed25519; + + fn add(self, rhs: &'a Ed25519) -> Ed25519 { + rhs + self + } +} + +impl<'a, 'b> Add<&'a Ed25519Affine> for &'b Ed25519Affine { + type Output = Ed25519; + + fn add(self, rhs: &'a Ed25519Affine) -> Ed25519 { + self.to_extended() + rhs.to_extended() + } +} + +impl<'a, 'b> Sub<&'a Ed25519Affine> for &'b Ed25519Affine { + type Output = Ed25519; + + fn sub(self, other: &'a Ed25519Affine) -> Ed25519 { + self + (-other) + } +} + +impl<'a, 'b> Sub<&'a Ed25519> for &'b Ed25519Affine { + type Output = Ed25519; + + fn sub(self, other: &'a Ed25519) -> Ed25519 { + self + (-other) + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl<'a, 'b> Mul<&'b Fr> for &'a Ed25519Affine { + type Output = Ed25519; + + fn mul(self, other: &'b Fr) -> Self::Output { + let mut acc = Ed25519::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading three bits because they're always + // unset for Fr. + for bit in other + .to_repr() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + { + acc = acc.double(); + acc = Ed25519::conditional_select(&acc, &(acc + self), bit); + } + + acc + } +} + +impl CurveAffineExt for Ed25519Affine { + fn into_coordinates(self) -> (Self::Base, Self::Base) { + (self.x, self.y) + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(Ed25519Affine::identity().is_on_curve())); +} + +#[test] +fn test_d_is_non_quadratic_residue() { + assert!(bool::from(ED25519_D.sqrt().is_none())); + assert!(bool::from((-ED25519_D).sqrt().is_none())); + assert!(bool::from((-ED25519_D).invert().unwrap().sqrt().is_none())); +} + +#[test] +fn test_double() { + let p = Ed25519::generator(); + + assert_eq!(p.double(), p + p); +} + +#[test] +fn test_assoc() { + let p = Ed25519::from(Ed25519Affine { + x: Fq::from_raw([ + 0x4eb5_31fa_487c_0f3e, + 0x1313_5118_1c90_b35e, + 0xdb9a_afaf_f32a_26f7, + 0x5e0c_b226_a2aa_bab4, + ]), + y: Fq::from_raw([ + 0xbf09_6275_684b_b8c9, + 0xc7ba_2458_90af_256d, + 0x5911_9f3e_8638_0eb0, + 0x3793_de18_2f9f_b1d2, + ]), + }) + .mul_by_cofactor(); + assert!(bool::from(p.is_on_curve())); + + assert_eq!( + (p * Fr::from(1000u64)) * Fr::from(3938u64), + p * (Fr::from(1000u64) * Fr::from(3938u64)), + ); +} + +#[test] +fn test_curve() { + crate::tests::curve::curve_tests::(); +} + +#[test] +fn test_serialization() { + crate::tests::curve::random_serialization_test::(); +} + +// #[test] +// #[allow(non_snake_case)] +// fn eddsa_example() { +// use crate::group::cofactor::CofactorGroup; +// use sha2::{Digest, Sha512}; + +// fn hash_to_fr(hash: Sha512) -> Fr { +// let mut output = [0u8; 64]; +// output.copy_from_slice(hash.finalize().as_slice()); + +// Fr::from_bytes_wide(&output) +// } + +// fn seed_to_key(seed: [u8; 32]) -> (Fr, [u8; 32], [u8; 32]) { +// // Expand the seed to a 64-byte array with SHA512. +// let h = Sha512::digest(&seed[..]); + +// // Convert the low half to a scalar with Ed25519 "clamping" +// let s = { +// let mut scalar_bytes = [0u8; 32]; +// scalar_bytes[..].copy_from_slice(&h.as_slice()[0..32]); +// // Clear the lowest three bits to make the scalar a multiple of 8 +// scalar_bytes[0] &= 248; +// // Clear highest bit +// scalar_bytes[31] &= 127; +// // Set second highest bit to 1 +// scalar_bytes[31] |= 64; + +// let mut scalar_bytes_wide = [0u8; 64]; +// scalar_bytes_wide[0..32].copy_from_slice(&scalar_bytes); + +// Fr::from_bytes_wide(&scalar_bytes_wide) +// }; + +// // Extract and cache the high half. +// let prefix = { +// let mut prefix = [0u8; 32]; +// prefix[..].copy_from_slice(&h.as_slice()[32..64]); +// prefix +// }; + +// // Compute the public key as A = [s]B. +// let A = Ed25519::generator() * &s; + +// let A_bytes = A.to_bytes().0; + +// (s, prefix, A_bytes) +// } + +// fn sign(s: Fr, prefix: [u8; 32], A_bytes: [u8; 32], msg: &[u8]) -> ([u8; 32], [u8; 32]) { +// let r = hash_to_fr(Sha512::default().chain(&prefix[..]).chain(msg)); + +// let R_bytes = (Ed25519::generator() * &r).to_bytes().0; + +// let k = hash_to_fr( +// Sha512::default() +// .chain(&R_bytes[..]) +// .chain(&A_bytes[..]) +// .chain(msg), +// ); + +// let s_bytes = (r + s * k).to_bytes(); + +// (R_bytes, s_bytes) +// } + +// fn verify(R_bytes: [u8; 32], s_bytes: [u8; 32], A_bytes: [u8; 32], msg: &[u8]) -> Choice { +// let k = hash_to_fr( +// Sha512::default() +// .chain(&R_bytes[..]) +// .chain(&A_bytes[..]) +// .chain(msg), +// ); +// verify_prehashed(R_bytes, s_bytes, A_bytes, k) +// } + +// fn verify_prehashed(R_bytes: [u8; 32], s_bytes: [u8; 32], A_bytes: [u8; 32], k: Fr) -> Choice { +// // `R_bytes` MUST be an encoding of a point on the twisted Edwards form of Curve25519. +// let R = Ed25519::from_bytes(&Ed25519Compressed(R_bytes)).unwrap(); +// // `s_bytes` MUST represent an integer less than the prime `l`. +// let s = Fr::from_bytes(&s_bytes).unwrap(); +// // `A_bytes` MUST be an encoding of a point on the twisted Edwards form of Curve25519. +// let A = Ed25519::from_bytes(&Ed25519Compressed(A_bytes)).unwrap(); + +// // [8][s]B = [8]R + [8][k]A +// // <=> [8]R = [8][s]B - [8][k]A +// // <=> 0 = [8](R - ([s]B - [k]A)) +// // <=> 0 = [8](R - R') where R' = [s]B - [k]A +// let R_prime = Ed25519::from(Ed25519::generator()) * s - A * k; + +// (R - R_prime).clear_cofactor().is_identity() +// } + +// use rand_core::OsRng; +// let mut rng = OsRng; + +// for _ in 0..1000 { +// // Generate a key pair +// let mut seed = [0u8; 32]; +// rng.fill_bytes(&mut seed[..]); + +// let (s, prefix, A_bytes) = seed_to_key(seed); + +// // Generate a valid signature +// // Suppose `m` is the message +// let msg = b"test message"; + +// let (R_bytes, s_bytes) = sign(s, prefix, A_bytes, msg); + +// // Verify the signature +// assert!(bool::from(verify(R_bytes, s_bytes, A_bytes, msg))); +// } +// } diff --git a/src/ed25519/fq.rs b/src/ed25519/fq.rs new file mode 100644 index 00000000..e16ad2ed --- /dev/null +++ b/src/ed25519/fq.rs @@ -0,0 +1,310 @@ +use core::convert::TryInto; +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; + +use ff::PrimeField; +use rand::RngCore; +use serde::{Deserialize, Serialize}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::arithmetic::{adc, mac, macx, sbb}; + +/// This represents an element of $\mathbb{F}_q$ where +/// +/// `q = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed` +/// +/// is the base field of the ed25519 curve. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. `Fq` values are always in +// Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct Fq(pub(crate) [u64; 4]); + +/// Constant representing the modulus +/// q = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed +const MODULUS: Fq = Fq([ + 0xffffffffffffffed, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x7fffffffffffffff, +]); + +/// The modulus as u32 limbs. +#[cfg(not(target_pointer_width = "64"))] +const MODULUS_LIMBS_32: [u32; 8] = [ + 0xffff_ffed, + 0xffff_fffe, + 0xffff_ffff, + 0xffff_ffff, + 0xffff_ffff, + 0xffff_ffff, + 0xffff_ffff, + 0x7fff_ffff, +]; + +/// Constant representing the modulus as static str +const MODULUS_STR: &str = "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"; + +/// INV = -(p^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x86bca1af286bca1b; + +/// R = 2^256 mod q +/// 0x26 +const R: Fq = Fq([0x26, 0, 0, 0]); + +/// R^2 = 2^512 mod q +/// 0x5a4 +const R2: Fq = Fq([0x5a4, 0, 0, 0]); + +/// R^3 = 2^768 mod q +/// 0xd658 +const R3: Fq = Fq([0xd658, 0, 0, 0]); + +/// 1 / 2 mod q +const TWO_INV: Fq = Fq::from_raw([ + 0xfffffffffffffff7, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x3fffffffffffffff, +]); + +/// sqrt(-1) mod q = 2^((p - 1) / 4) mod q +const SQRT_MINUS_ONE: Fq = Fq::from_raw([ + 0xc4ee1b274a0ea0b0, + 0x2f431806ad2fe478, + 0x2b4d00993dfbd7a7, + 0x2b8324804fc1df0b, +]); + +const ZETA: Fq = Fq::zero(); +const DELTA: Fq = Fq::zero(); +const ROOT_OF_UNITY_INV: Fq = Fq::zero(); + +use crate::{ + field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; +impl_binops_additive!(Fq, Fq); +impl_binops_multiplicative!(Fq, Fq); +field_common!( + Fq, + MODULUS, + INV, + MODULUS_STR, + TWO_INV, + ROOT_OF_UNITY_INV, + DELTA, + ZETA, + R, + R2, + R3 +); +field_arithmetic!(Fq, MODULUS, INV, dense); + +impl Fq { + pub const fn size() -> usize { + 32 + } +} + +impl ff::Field for Fq { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + Self::from_u512([ + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + ]) + } + + fn double(&self) -> Self { + self.double() + } + + #[inline(always)] + fn square(&self) -> Self { + self.square() + } + + /// Computes the square root of this element, if it exists. + fn sqrt(&self) -> CtOption { + // Sqrt = a^((q + 3) / 8) + // OR + // = a^((q + 3) / 8) * sqrt(-1) + // = a^((q + 3) / 8) * (2^((q - 1) / 4)) + // OR + // Doesn't exist + let x1 = self.pow(&[ + 0xfffffffffffffffe, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x0fffffffffffffff, + ]); + + let choice1 = x1.square().ct_eq(&self); + let choice2 = x1.square().ct_eq(&-self); + + let sqrt = Self::conditional_select(&x1, &(x1 * SQRT_MINUS_ONE), choice2); + + CtOption::new(sqrt, choice1 | choice2) + } + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + ff::helpers::sqrt_ratio_generic(num, div) + } + + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + fn invert(&self) -> CtOption { + // a^(-1) = a^(q - 2) + let tmp = self.pow_vartime([ + 0xffffffffffffffeb, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x7fffffffffffffff, + ]); + + CtOption::new(tmp, !self.ct_eq(&Self::zero())) + } + + fn pow_vartime>(&self, exp: S) -> Self { + let mut res = Self::one(); + let mut found_one = false; + for e in exp.as_ref().iter().rev() { + for i in (0..64).rev() { + if found_one { + res = res.square(); + } + + if ((*e >> i) & 1) == 1 { + found_one = true; + res *= self; + } + } + } + res + } +} + +impl ff::PrimeField for Fq { + type Repr = [u8; 32]; + + const NUM_BITS: u32 = 256; + const CAPACITY: u32 = 255; + const MODULUS: &'static str = MODULUS_STR; + const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const TWO_INV: Self = TWO_INV; + const DELTA: Self = DELTA; + const S: u32 = 1; + + fn from_repr(repr: Self::Repr) -> CtOption { + let mut tmp = Fq([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); + tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); + tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); + tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); + + // Try to subtract the modulus + let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.0[0]); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + fn to_repr(&self) -> Self::Repr { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fq::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); + + let mut res = [0; 32]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_repr()[0] & 1) + } + + fn multiplicative_generator() -> Self { + unimplemented!(); + } + + fn root_of_unity() -> Self { + unimplemented!(); + } +} + +#[cfg(test)] +mod test { + use super::*; + use ff::Field; + use rand_core::OsRng; + + #[test] + fn test_sqrt() { + // NB: TWO_INV is standing in as a "random" field element + let v = (Fq::TWO_INV).square().sqrt().unwrap(); + assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); + + for _ in 0..10000 { + let a = Fq::random(OsRng); + let mut b = a; + b = b.square(); + + let b = b.sqrt().unwrap(); + let mut negb = b; + negb = negb.neg(); + + assert!(a == b || a == negb); + } + } + + #[test] + fn test_invert() { + let v = Fq::one().double().invert().unwrap(); + assert!(v == Fq::TWO_INV); + + for _ in 0..10000 { + let a = Fq::random(OsRng); + let b = a.invert().unwrap().invert().unwrap(); + + assert!(a == b); + } + } + + #[test] + fn test_field() { + crate::tests::field::random_field_tests::("ed25519 base".to_string()); + } + + #[test] + fn test_serialization() { + crate::tests::field::random_serialization_test::("ed25519 base".to_string()); + } +} diff --git a/src/ed25519/fr.rs b/src/ed25519/fr.rs new file mode 100644 index 00000000..7ac8932c --- /dev/null +++ b/src/ed25519/fr.rs @@ -0,0 +1,330 @@ +use core::convert::TryInto; +use core::fmt; +use core::ops::{Add, Mul, Neg, Sub}; + +use ff::PrimeField; +use rand::RngCore; +use serde::{Deserialize, Serialize}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::arithmetic::{adc, mac, macx, sbb}; + +use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; + +/// This represents an element of $\mathbb{F}_q$ where +/// +/// `r = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed` +/// +/// is the scalar field of the ed25519 curve. +// The internal representation of this type is four 64-bit unsigned +// integers in little-endian order. `Fr` values are always in +// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct Fr(pub(crate) [u64; 4]); + +/// Constant representing the modulus +/// r = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed +const MODULUS: Fr = Fr([ + 0x5812631a5cf5d3ed, + 0x14def9dea2f79cd6, + 0x0000000000000000, + 0x1000000000000000, +]); + +/// The modulus as u32 limbs. +#[cfg(not(target_pointer_width = "64"))] +const MODULUS_LIMBS_32: [u32; 8] = [ + 0x5cf5_d3ed, + 0x5812_631a, + 0xa2f7_9cd6, + 0x14de_f9de, + 0x0000_0000, + 0x0000_0000, + 0x0000_0000, + 0x1000_0000, +]; + +///Constant representing the modulus as static str +const MODULUS_STR: &str = "0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"; + +/// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xd2b51da312547e1b; + +/// R = 2^256 mod r +/// 0xffffffffffffffffffffffffffffffec6ef5bf4737dcf70d6ec31748d98951d +const R: Fr = Fr([ + 0xd6ec31748d98951d, + 0xc6ef5bf4737dcf70, + 0xfffffffffffffffe, + 0x0fffffffffffffff, +]); + +/// R^2 = 2^512 mod r +/// 0x399411b7c309a3dceec73d217f5be65d00e1ba768859347a40611e3449c0f01 +const R2: Fr = Fr([ + 0xa40611e3449c0f01, + 0xd00e1ba768859347, + 0xceec73d217f5be65, + 0x0399411b7c309a3d, +]); + +/// R^3 = 2^768 mod r +/// 0xe530b773599cec78065dc6c04ec5b65278324e6aef7f3ec2a9e49687b83a2db +const R3: Fr = Fr([ + 0x2a9e49687b83a2db, + 0x278324e6aef7f3ec, + 0x8065dc6c04ec5b65, + 0x0e530b773599cec7, +]); + +/// 1 / 2 mod r +const TWO_INV: Fr = Fr::from_raw([ + 0x2c09318d2e7ae9f7, + 0x0a6f7cef517bce6b, + 0x0000000000000000, + 0x0800000000000000, +]); + +/// sqrt(-1) mod p = 2^((p - 1) / 4) mod p +const SQRT_MINUS_ONE: Fr = Fr::from_raw([ + 0xbe8775dfebbe07d4, + 0x0ef0565342ce83fe, + 0x7d3d6d60abc1c27a, + 0x094a7310e07981e7, +]); + +const ZETA: Fr = Fr::zero(); +const DELTA: Fr = Fr::zero(); +const ROOT_OF_UNITY_INV: Fr = Fr::zero(); + +use crate::{ + field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, +}; +impl_binops_additive!(Fr, Fr); +impl_binops_multiplicative!(Fr, Fr); +field_common!( + Fr, + MODULUS, + INV, + MODULUS_STR, + TWO_INV, + ROOT_OF_UNITY_INV, + DELTA, + ZETA, + R, + R2, + R3 +); +field_arithmetic!(Fr, MODULUS, INV, dense); + +impl Fr { + pub const fn size() -> usize { + 32 + } +} + +impl ff::Field for Fr { + fn random(mut rng: impl RngCore) -> Self { + Self::from_u512([ + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + rng.next_u64(), + ]) + } + + fn zero() -> Self { + Self::zero() + } + + fn one() -> Self { + Self::one() + } + + fn double(&self) -> Self { + self.double() + } + + #[inline(always)] + fn square(&self) -> Self { + self.square() + } + + /// Computes the square root of this element, if it exists. + fn sqrt(&self) -> CtOption { + // Sqrt = a^((p + 3) / 8) + // OR + // = a^((p + 3) / 8) * sqrt(-1) + // = a^((p + 3) / 8) * (2^((p - 1) / 4)) + // OR + // Doesn't exist + let x1 = self.pow(&[ + 0xcb024c634b9eba7e, + 0x029bdf3bd45ef39a, + 0x0000000000000000, + 0x0200000000000000, + ]); + + let choice1 = x1.square().ct_eq(&self); + let choice2 = x1.square().ct_eq(&-self); + + let sqrt = Self::conditional_select(&x1, &(x1 * SQRT_MINUS_ONE), choice2); + + CtOption::new(sqrt, choice1 | choice2) + } + + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + fn invert(&self) -> CtOption { + let tmp = self.pow_vartime([ + 0x5812631a5cf5d3eb, + 0x14def9dea2f79cd6, + 0x0000000000000000, + 0x1000000000000000, + ]); + + CtOption::new(tmp, !self.ct_eq(&Self::zero())) + } + + fn pow_vartime>(&self, exp: S) -> Self { + let mut res = Self::one(); + let mut found_one = false; + for e in exp.as_ref().iter().rev() { + for i in (0..64).rev() { + if found_one { + res = res.square(); + } + + if ((*e >> i) & 1) == 1 { + found_one = true; + res *= self; + } + } + } + res + } +} + +impl ff::PrimeField for Fr { + type Repr = [u8; 32]; + + const NUM_BITS: u32 = 256; + const CAPACITY: u32 = 255; + const S: u32 = 6; + + fn from_repr(repr: Self::Repr) -> CtOption { + let mut tmp = Fr([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); + tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); + tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); + tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); + + // Try to subtract the modulus + let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.0[0]); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + CtOption::new(tmp, Choice::from(is_some)) + } + + fn to_repr(&self) -> Self::Repr { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fr::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); + + let mut res = [0; 32]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_repr()[0] & 1) + } + + fn multiplicative_generator() -> Self { + unimplemented!(); + } + + fn root_of_unity() -> Self { + unimplemented!(); + } +} + +impl SqrtRatio for Fr { + const T_MINUS1_OVER2: [u64; 4] = [0, 0, 0, 0]; + + fn get_lower_32(&self) -> u32 { + let tmp = Fr::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); + tmp.0[0] as u32 + } +} + +#[cfg(test)] +mod test { + use super::*; + use ff::Field; + use rand_core::OsRng; + + #[test] + fn test_sqrt() { + // NB: TWO_INV is standing in as a "random" field element + let v = (Fr::TWO_INV).square().sqrt().unwrap(); + assert!(v == Fr::TWO_INV || (-v) == Fr::TWO_INV); + + for _ in 0..10000 { + let a = Fr::random(OsRng); + let mut b = a; + b = b.square(); + + let b = b.sqrt().unwrap(); + let mut negb = b; + negb = negb.neg(); + + assert!(a == b || a == negb); + } + } + + #[test] + fn test_invert() { + let v = Fr::one().double().invert().unwrap(); + assert!(v == Fr::TWO_INV); + + for _ in 0..10000 { + let a = Fr::random(OsRng); + let b = a.invert().unwrap().invert().unwrap(); + + assert!(a == b); + } + } + + #[test] + fn test_field() { + crate::tests::field::random_field_tests::("ed25519 scalar".to_string()); + } + + #[test] + fn test_serialization() { + crate::tests::field::random_serialization_test::("ed25519 scalar".to_string()); + } +} diff --git a/src/ed25519/mod.rs b/src/ed25519/mod.rs new file mode 100644 index 00000000..aa068603 --- /dev/null +++ b/src/ed25519/mod.rs @@ -0,0 +1,7 @@ +mod curve; +mod fq; +mod fr; + +pub use curve::*; +pub use fq::*; +pub use fr::*; diff --git a/src/lib.rs b/src/lib.rs index 030c11b2..f7e27a4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub mod multicore; pub mod serde; pub mod bn256; +pub mod ed25519; pub mod grumpkin; pub mod pasta; pub mod pluto_eris; From a9b016fd67c699cd1a45915bf1466eb788d91e3a Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 2 Jan 2024 11:23:42 -0500 Subject: [PATCH 2/4] chore: update to `ff v0.13` --- src/ed25519/fq.rs | 41 +++++++++++++++++++++--------- src/ed25519/fr.rs | 63 +++++++++++++++++++++++++++-------------------- 2 files changed, 66 insertions(+), 38 deletions(-) diff --git a/src/ed25519/fq.rs b/src/ed25519/fq.rs index e16ad2ed..b478d60a 100644 --- a/src/ed25519/fq.rs +++ b/src/ed25519/fq.rs @@ -2,7 +2,7 @@ use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; -use ff::PrimeField; +use ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use rand::RngCore; use serde::{Deserialize, Serialize}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -45,6 +45,10 @@ const MODULUS_LIMBS_32: [u32; 8] = [ /// Constant representing the modulus as static str const MODULUS_STR: &str = "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"; +/// Obtained with: +/// `sage: GF(57896044618658097711785492504343953926634992332820282019728792003956564819949).primitive_element()` +const MULTIPLICATIVE_GENERATOR: Fq = Fq::from_raw([0x02, 0x0, 0x0, 0x0]); + /// INV = -(p^{-1} mod 2^64) mod 2^64 const INV: u64 = 0x86bca1af286bca1b; @@ -83,7 +87,7 @@ const ROOT_OF_UNITY_INV: Fq = Fq::zero(); use crate::{ field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, + impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, }; impl_binops_additive!(Fq, Fq); impl_binops_multiplicative!(Fq, Fq); @@ -101,6 +105,8 @@ field_common!( R3 ); field_arithmetic!(Fq, MODULUS, INV, dense); +impl_sum_prod!(Fq); +impl_from_u64!(Fq, R2); impl Fq { pub const fn size() -> usize { @@ -201,8 +207,8 @@ impl ff::PrimeField for Fq { const CAPACITY: u32 = 255; const MODULUS: &'static str = MODULUS_STR; const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; - const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; - const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const ROOT_OF_UNITY: Self = todo!(); + const ROOT_OF_UNITY_INV: Self = todo!(); const TWO_INV: Self = TWO_INV; const DELTA: Self = DELTA; const S: u32 = 1; @@ -216,7 +222,7 @@ impl ff::PrimeField for Fq { tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); // Try to subtract the modulus - let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.0[0]); + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); @@ -236,7 +242,7 @@ impl ff::PrimeField for Fq { fn to_repr(&self) -> Self::Repr { // Turn into canonical form by computing // (a.R) / R = a - let tmp = Fq::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); + let tmp = Fq::montgomery_reduce_short(&self.0); let mut res = [0; 32]; res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); @@ -250,14 +256,27 @@ impl ff::PrimeField for Fq { fn is_odd(&self) -> Choice { Choice::from(self.to_repr()[0] & 1) } +} - fn multiplicative_generator() -> Self { - unimplemented!(); +impl FromUniformBytes<64> for Fq { + /// Converts a 512-bit little endian integer into + /// an `Fq` by reducing by the modulus. + fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { + Self::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) } +} - fn root_of_unity() -> Self { - unimplemented!(); - } +impl WithSmallOrderMulGroup<3> for Fq { + const ZETA: Self = todo!(); } #[cfg(test)] diff --git a/src/ed25519/fr.rs b/src/ed25519/fr.rs index 7ac8932c..2e9c86df 100644 --- a/src/ed25519/fr.rs +++ b/src/ed25519/fr.rs @@ -2,15 +2,13 @@ use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; -use ff::PrimeField; +use ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use rand::RngCore; use serde::{Deserialize, Serialize}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use crate::arithmetic::{adc, mac, macx, sbb}; -use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio}; - /// This represents an element of $\mathbb{F}_q$ where /// /// `r = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed` @@ -100,7 +98,7 @@ const ROOT_OF_UNITY_INV: Fr = Fr::zero(); use crate::{ field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, - impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, + impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, }; impl_binops_additive!(Fr, Fr); impl_binops_multiplicative!(Fr, Fr); @@ -118,6 +116,8 @@ field_common!( R3 ); field_arithmetic!(Fr, MODULUS, INV, dense); +impl_sum_prod!(Fr); +impl_from_u64!(Fr, R2); impl Fr { pub const fn size() -> usize { @@ -126,6 +126,9 @@ impl Fr { } impl ff::Field for Fr { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + fn random(mut rng: impl RngCore) -> Self { Self::from_u512([ rng.next_u64(), @@ -139,14 +142,6 @@ impl ff::Field for Fr { ]) } - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - fn double(&self) -> Self { self.double() } @@ -179,6 +174,10 @@ impl ff::Field for Fr { CtOption::new(sqrt, choice1 | choice2) } + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + ff::helpers::sqrt_ratio_generic(num, div) + } + /// Computes the multiplicative inverse of this element, /// failing if the element is zero. fn invert(&self) -> CtOption { @@ -216,6 +215,12 @@ impl ff::PrimeField for Fr { const NUM_BITS: u32 = 256; const CAPACITY: u32 = 255; + const MODULUS: &'static str = MODULUS_STR; + const MULTIPLICATIVE_GENERATOR: Self = todo!(); + const ROOT_OF_UNITY: Self = todo!(); + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; + const TWO_INV: Self = TWO_INV; + const DELTA: Self = DELTA; const S: u32 = 6; fn from_repr(repr: Self::Repr) -> CtOption { @@ -227,7 +232,7 @@ impl ff::PrimeField for Fr { tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); // Try to subtract the modulus - let (_, borrow) = tmp.0[0].overflowing_sub(MODULUS.0[0]); + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); @@ -247,7 +252,7 @@ impl ff::PrimeField for Fr { fn to_repr(&self) -> Self::Repr { // Turn into canonical form by computing // (a.R) / R = a - let tmp = Fr::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); + let tmp = Fr::montgomery_reduce_short(&self.0); let mut res = [0; 32]; res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); @@ -261,23 +266,27 @@ impl ff::PrimeField for Fr { fn is_odd(&self) -> Choice { Choice::from(self.to_repr()[0] & 1) } +} - fn multiplicative_generator() -> Self { - unimplemented!(); - } - - fn root_of_unity() -> Self { - unimplemented!(); +impl FromUniformBytes<64> for Fr { + /// Converts a 512-bit little endian integer into + /// an `Fq` by reducing by the modulus. + fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { + Self::from_u512([ + u64::from_le_bytes(bytes[0..8].try_into().unwrap()), + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + u64::from_le_bytes(bytes[16..24].try_into().unwrap()), + u64::from_le_bytes(bytes[24..32].try_into().unwrap()), + u64::from_le_bytes(bytes[32..40].try_into().unwrap()), + u64::from_le_bytes(bytes[40..48].try_into().unwrap()), + u64::from_le_bytes(bytes[48..56].try_into().unwrap()), + u64::from_le_bytes(bytes[56..64].try_into().unwrap()), + ]) } } -impl SqrtRatio for Fr { - const T_MINUS1_OVER2: [u64; 4] = [0, 0, 0, 0]; - - fn get_lower_32(&self) -> u32 { - let tmp = Fr::montgomery_reduce_short(self.0[0], self.0[1], self.0[2], self.0[3]); - tmp.0[0] as u32 - } +impl WithSmallOrderMulGroup<3> for Fr { + const ZETA: Self = todo!(); } #[cfg(test)] From 016eab859651d7bc1d3cdebaf8bb82117575bd20 Mon Sep 17 00:00:00 2001 From: Jonathan Wang <31040440+jonathanpwang@users.noreply.github.com> Date: Tue, 2 Jan 2024 11:55:55 -0500 Subject: [PATCH 3/4] chore: constant values cannot be todo!() --- src/ed25519/fq.rs | 12 +++++++++--- src/ed25519/fr.rs | 10 +++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/ed25519/fq.rs b/src/ed25519/fq.rs index b478d60a..ca132397 100644 --- a/src/ed25519/fq.rs +++ b/src/ed25519/fq.rs @@ -80,8 +80,11 @@ const SQRT_MINUS_ONE: Fq = Fq::from_raw([ 0x2b8324804fc1df0b, ]); +/// TODO const ZETA: Fq = Fq::zero(); +/// TODO const DELTA: Fq = Fq::zero(); +/// TODO const ROOT_OF_UNITY_INV: Fq = Fq::zero(); use crate::{ @@ -207,8 +210,10 @@ impl ff::PrimeField for Fq { const CAPACITY: u32 = 255; const MODULUS: &'static str = MODULUS_STR; const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; - const ROOT_OF_UNITY: Self = todo!(); - const ROOT_OF_UNITY_INV: Self = todo!(); + /// TODO + const ROOT_OF_UNITY: Self = Self::one(); + /// TODO + const ROOT_OF_UNITY_INV: Self = Self::zero(); const TWO_INV: Self = TWO_INV; const DELTA: Self = DELTA; const S: u32 = 1; @@ -276,7 +281,8 @@ impl FromUniformBytes<64> for Fq { } impl WithSmallOrderMulGroup<3> for Fq { - const ZETA: Self = todo!(); + /// TODO + const ZETA: Self = ZETA; } #[cfg(test)] diff --git a/src/ed25519/fr.rs b/src/ed25519/fr.rs index 2e9c86df..e500d461 100644 --- a/src/ed25519/fr.rs +++ b/src/ed25519/fr.rs @@ -216,8 +216,11 @@ impl ff::PrimeField for Fr { const NUM_BITS: u32 = 256; const CAPACITY: u32 = 255; const MODULUS: &'static str = MODULUS_STR; - const MULTIPLICATIVE_GENERATOR: Self = todo!(); - const ROOT_OF_UNITY: Self = todo!(); + /// TODO + const MULTIPLICATIVE_GENERATOR: Self = Self::one(); + /// TODO + const ROOT_OF_UNITY: Self = Self::one(); + /// TODO const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; const TWO_INV: Self = TWO_INV; const DELTA: Self = DELTA; @@ -286,7 +289,8 @@ impl FromUniformBytes<64> for Fr { } impl WithSmallOrderMulGroup<3> for Fr { - const ZETA: Self = todo!(); + /// TODO + const ZETA: Self = ZETA; } #[cfg(test)] From b3b058a21f4fc6f7f358a5578b67466204fd8e87 Mon Sep 17 00:00:00 2001 From: Ayush Shukla Date: Tue, 23 Jan 2024 05:15:02 +0530 Subject: [PATCH 4/4] fix: add twisted edwards trait + proper constants (#22) * feat: add twisted edwards trait * chore: clippy fixes * feat: add new trait for te curves * fix: calculate proper constants * chore: fix typos * chore: add sage commands for constants * fix: add backticks for code * fix: put serde behind feature --- src/ed25519/curve.rs | 85 +++++++++++++++++++++++++++----------------- src/ed25519/fq.rs | 71 ++++++++++++++++++++++++------------ src/ed25519/fr.rs | 69 +++++++++++++++++++++++++---------- 3 files changed, 150 insertions(+), 75 deletions(-) diff --git a/src/ed25519/curve.rs b/src/ed25519/curve.rs index b6eb0e35..1c0a17fa 100644 --- a/src/ed25519/curve.rs +++ b/src/ed25519/curve.rs @@ -6,12 +6,14 @@ use core::fmt::Debug; use core::iter::Sum; use core::ops::{Add, Mul, Neg, Sub}; use ff::{BatchInverter, Field, PrimeField}; -use group::{self, Curve, Group}; +use group::{self, Curve}; use group::{prime::PrimeCurveAffine, GroupEncoding}; use rand::RngCore; -use serde::{Deserialize, Serialize}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + const ED25519_GENERATOR_X: Fq = Fq::from_raw([ 0xc956_2d60_8f25_d51a, 0x692c_c760_9525_a7b2, @@ -43,7 +45,8 @@ use crate::{ impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, }; -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] pub struct Ed25519 { pub x: Fq, pub y: Fq, @@ -51,13 +54,14 @@ pub struct Ed25519 { pub t: Fq, } -#[derive(Copy, Clone, Debug, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Hash)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] pub struct Ed25519Affine { pub x: Fq, pub y: Fq, } -#[derive(Copy, Clone, Hash)] +#[derive(Copy, Clone, Hash, Default)] pub struct Ed25519Compressed([u8; 32]); impl Ed25519 { @@ -103,7 +107,7 @@ impl Ed25519 { .skip(3) { acc = acc.double(); - acc += Ed25519::conditional_select(&zero, &self, bit); + acc += Ed25519::conditional_select(&zero, self, bit); } acc @@ -155,12 +159,6 @@ impl Ed25519 { } } -impl Ed25519 { - fn endomorphism_base(&self) -> Self { - unimplemented!(); - } -} - impl Ed25519Affine { /// Constructs the neutral element `(0, 1)`. pub const fn identity() -> Self { @@ -287,12 +285,6 @@ impl std::fmt::Debug for Ed25519Compressed { } } -impl Default for Ed25519Compressed { - fn default() -> Self { - Ed25519Compressed([0; 32]) - } -} - impl AsRef<[u8]> for Ed25519Compressed { fn as_ref(&self) -> &[u8] { &self.0 @@ -357,8 +349,13 @@ impl CurveExt for Ed25519 { const CURVE_ID: &'static str = "ed25519"; + fn is_on_curve(&self) -> Choice { + let affine = Ed25519Affine::from(*self); + !self.z.is_zero() & affine.is_on_curve() & (affine.x * affine.y * self.z).ct_eq(&self.t) + } + fn endo(&self) -> Self { - self.endomorphism_base() + unimplemented!(); } fn jacobian_coordinates(&self) -> (Fq, Fq, Fq) { @@ -369,20 +366,12 @@ impl CurveExt for Ed25519 { unimplemented!(); } - fn is_on_curve(&self) -> Choice { - let affine = Ed25519Affine::from(*self); - - println!("affine: {:?}", affine); - - !self.z.is_zero() & affine.is_on_curve() & (affine.x * affine.y * self.z).ct_eq(&self.t) - } - fn a() -> Self::Base { unimplemented!() } fn b() -> Self::Base { - ED25519_D + unimplemented!() } fn new_jacobian(_x: Self::Base, _y: Self::Base, _z: Self::Base) -> CtOption { @@ -408,8 +397,8 @@ impl group::Curve for Ed25519 { let tmp = q.x; // Set the coordinates to the correct value - q.x = p.x * &tmp; // Multiply by 1/z - q.y = p.y * &tmp; // Multiply by 1/z + q.x = p.x * tmp; // Multiply by 1/z + q.y = p.y * tmp; // Multiply by 1/z } } @@ -482,7 +471,7 @@ impl crate::serde::SerdeObject for Ed25519 { x.zip(y).zip(z).zip(t).and_then(|(((x, y), z), t)| { let res = Self { x, y, z, t }; // Check that the point is on the curve. - bool::from(res.is_on_curve()).then(|| res) + bool::from(res.is_on_curve()).then_some(res) }) } fn to_raw_bytes(&self) -> Vec { @@ -601,7 +590,7 @@ impl crate::serde::SerdeObject for Ed25519Affine { x.zip(y).and_then(|(x, y)| { let res = Self { x, y }; // Check that the point is on the curve. - bool::from(res.is_on_curve()).then(|| res) + bool::from(res.is_on_curve()).then_some(res) }) } fn to_raw_bytes(&self) -> Vec { @@ -697,7 +686,7 @@ impl CurveAffine for Ed25519Affine { } fn b() -> Self::Base { - ED25519_D + unimplemented!() } } @@ -916,6 +905,36 @@ impl CurveAffineExt for Ed25519Affine { } } +pub trait TwistedEdwardsCurveExt: CurveExt { + fn a() -> ::Base; + fn d() -> ::Base; +} + +impl TwistedEdwardsCurveExt for Ed25519 { + fn a() -> Fq { + -Fq::ONE + } + + fn d() -> Fq { + ED25519_D + } +} + +pub trait TwistedEdwardsCurveAffineExt: CurveAffineExt { + fn a() -> ::Base; + fn d() -> ::Base; +} + +impl TwistedEdwardsCurveAffineExt for Ed25519Affine { + fn a() -> Fq { + -Fq::ONE + } + + fn d() -> Fq { + ED25519_D + } +} + #[test] fn test_is_on_curve() { assert!(bool::from(Ed25519Affine::identity().is_on_curve())); diff --git a/src/ed25519/fq.rs b/src/ed25519/fq.rs index ca132397..fed7e413 100644 --- a/src/ed25519/fq.rs +++ b/src/ed25519/fq.rs @@ -1,12 +1,13 @@ use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; - use ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use rand::RngCore; -use serde::{Deserialize, Serialize}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + use crate::arithmetic::{adc, mac, macx, sbb}; /// This represents an element of $\mathbb{F}_q$ where @@ -17,7 +18,8 @@ use crate::arithmetic::{adc, mac, macx, sbb}; // The internal representation of this type is four 64-bit unsigned // integers in little-endian order. `Fq` values are always in // Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] pub struct Fq(pub(crate) [u64; 4]); /// Constant representing the modulus @@ -45,11 +47,11 @@ const MODULUS_LIMBS_32: [u32; 8] = [ /// Constant representing the modulus as static str const MODULUS_STR: &str = "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"; -/// Obtained with: -/// `sage: GF(57896044618658097711785492504343953926634992332820282019728792003956564819949).primitive_element()` +/// Obtained with sage: +/// `GF(q).primitive_element()` const MULTIPLICATIVE_GENERATOR: Fq = Fq::from_raw([0x02, 0x0, 0x0, 0x0]); -/// INV = -(p^{-1} mod 2^64) mod 2^64 +/// INV = -(q^{-1} mod 2^64) mod 2^64 const INV: u64 = 0x86bca1af286bca1b; /// R = 2^256 mod q @@ -72,7 +74,7 @@ const TWO_INV: Fq = Fq::from_raw([ 0x3fffffffffffffff, ]); -/// sqrt(-1) mod q = 2^((p - 1) / 4) mod q +/// sqrt(-1) mod q = 2^((q - 1) / 4) mod q const SQRT_MINUS_ONE: Fq = Fq::from_raw([ 0xc4ee1b274a0ea0b0, 0x2f431806ad2fe478, @@ -80,12 +82,37 @@ const SQRT_MINUS_ONE: Fq = Fq::from_raw([ 0x2b8324804fc1df0b, ]); -/// TODO -const ZETA: Fq = Fq::zero(); -/// TODO -const DELTA: Fq = Fq::zero(); -/// TODO -const ROOT_OF_UNITY_INV: Fq = Fq::zero(); +// Element in small order subgroup (3-order) +// Sage: +// `GF(q).primitive_element() ** ((q - 1) // N)` where N = 3 +const ZETA: Fq = Fq::from_raw([ + 0xaa86d89d8618e538, + 0x1a1aada8413a4550, + 0xd9872fccc55bd529, + 0x381cba36aa6565b5, +]); +// The `2^s` root of unity. +// It can be calculated by exponentiating `MULTIPLICATIVE_GENERATOR` by `t`, +// where `2^s * t = q - 1` with `t` odd. +// Sage: +// `GF(q).primitive_element() ** t` +const ROOT_OF_UNITY: Fq = Fq::from_raw([ + 0xc4ee1b274a0ea0b0, + 0x2f431806ad2fe478, + 0x2b4d00993dfbd7a7, + 0x2b8324804fc1df0b, +]); +// Inverse of `ROOT_OF_UNITY` +const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([ + 0x3b11e4d8b5f15f3d, + 0xd0bce7f952d01b87, + 0xd4b2ff66c2042858, + 0x547cdb7fb03e20f4, +]); +// Generator of the `t-order` multiplicative subgroup +// Sage: +// `GF(q).primitive_element() ** (2**s)` +const DELTA: Fq = Fq::from_raw([0x10, 0, 0, 0]); use crate::{ field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, @@ -151,14 +178,14 @@ impl ff::Field for Fq { // = a^((q + 3) / 8) * (2^((q - 1) / 4)) // OR // Doesn't exist - let x1 = self.pow(&[ + let x1 = self.pow([ 0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 0x0fffffffffffffff, ]); - let choice1 = x1.square().ct_eq(&self); + let choice1 = x1.square().ct_eq(self); let choice2 = x1.square().ct_eq(&-self); let sqrt = Self::conditional_select(&x1, &(x1 * SQRT_MINUS_ONE), choice2); @@ -206,17 +233,16 @@ impl ff::Field for Fq { impl ff::PrimeField for Fq { type Repr = [u8; 32]; + const MODULUS: &'static str = MODULUS_STR; const NUM_BITS: u32 = 256; const CAPACITY: u32 = 255; - const MODULUS: &'static str = MODULUS_STR; - const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; - /// TODO - const ROOT_OF_UNITY: Self = Self::one(); - /// TODO - const ROOT_OF_UNITY_INV: Self = Self::zero(); const TWO_INV: Self = TWO_INV; + const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; + // An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd. + const S: u32 = 2; + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; const DELTA: Self = DELTA; - const S: u32 = 1; fn from_repr(repr: Self::Repr) -> CtOption { let mut tmp = Fq([0, 0, 0, 0]); @@ -281,7 +307,6 @@ impl FromUniformBytes<64> for Fq { } impl WithSmallOrderMulGroup<3> for Fq { - /// TODO const ZETA: Self = ZETA; } diff --git a/src/ed25519/fr.rs b/src/ed25519/fr.rs index e500d461..4ef3ab4b 100644 --- a/src/ed25519/fr.rs +++ b/src/ed25519/fr.rs @@ -1,12 +1,13 @@ use core::convert::TryInto; use core::fmt; use core::ops::{Add, Mul, Neg, Sub}; - use ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; use rand::RngCore; -use serde::{Deserialize, Serialize}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + use crate::arithmetic::{adc, mac, macx, sbb}; /// This represents an element of $\mathbb{F}_q$ where @@ -17,7 +18,8 @@ use crate::arithmetic::{adc, mac, macx, sbb}; // The internal representation of this type is four 64-bit unsigned // integers in little-endian order. `Fr` values are always in // Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] pub struct Fr(pub(crate) [u64; 4]); /// Constant representing the modulus @@ -45,7 +47,11 @@ const MODULUS_LIMBS_32: [u32; 8] = [ ///Constant representing the modulus as static str const MODULUS_STR: &str = "0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"; -/// INV = -(q^{-1} mod 2^64) mod 2^64 +/// Obtained with sage: +/// `GF(r).primitive_element()` +const MULTIPLICATIVE_GENERATOR: Fr = Fr::from_raw([0x02, 0x0, 0x0, 0x0]); + +/// INV = -(r^{-1} mod 2^64) mod 2^64 const INV: u64 = 0xd2b51da312547e1b; /// R = 2^256 mod r @@ -83,7 +89,7 @@ const TWO_INV: Fr = Fr::from_raw([ 0x0800000000000000, ]); -/// sqrt(-1) mod p = 2^((p - 1) / 4) mod p +/// sqrt(-1) mod r = 2^((r - 1) / 4) mod r const SQRT_MINUS_ONE: Fr = Fr::from_raw([ 0xbe8775dfebbe07d4, 0x0ef0565342ce83fe, @@ -91,9 +97,37 @@ const SQRT_MINUS_ONE: Fr = Fr::from_raw([ 0x094a7310e07981e7, ]); -const ZETA: Fr = Fr::zero(); -const DELTA: Fr = Fr::zero(); -const ROOT_OF_UNITY_INV: Fr = Fr::zero(); +// Element in small order subgroup (3-order) +// Sage: +// `GF(r).primitive_element() ** ((r - 1) // N)` where N = 3 +const ZETA: Fr = Fr::from_raw([ + 0x158687e51e07e223, + 0x471dd911c6cce91e, + 0xeb08f579fb8841ae, + 0x0378d9ddc674005f, +]); +// The `2^s` root of unity. +// It can be calculated by exponentiating `MULTIPLICATIVE_GENERATOR` by `t`, +// where `2^s * t = r - 1` with `t` odd. +// Sage: +// `GF(r).primitive_element() ** t` +const ROOT_OF_UNITY: Fr = Fr::from_raw([ + 0xbe8775dfebbe07d4, + 0x0ef0565342ce83fe, + 0x7d3d6d60abc1c27a, + 0x094a7310e07981e7, +]); +// Inverse of `ROOT_OF_UNITY` +const ROOT_OF_UNITY_INV: Fr = Fr::from_raw([ + 0x998aed3a7137cc19, + 0x05eea38b602918d7, + 0x82c2929f543e3d86, + 0x06b58cef1f867e18, +]); +// Generator of the `t-order` multiplicative subgroup +// Sage: +// `GF(r).primitive_element() ** (2**s)` +const DELTA: Fr = Fr::from_raw([0x10, 0, 0, 0]); use crate::{ field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, @@ -159,14 +193,14 @@ impl ff::Field for Fr { // = a^((p + 3) / 8) * (2^((p - 1) / 4)) // OR // Doesn't exist - let x1 = self.pow(&[ + let x1 = self.pow([ 0xcb024c634b9eba7e, 0x029bdf3bd45ef39a, 0x0000000000000000, 0x0200000000000000, ]); - let choice1 = x1.square().ct_eq(&self); + let choice1 = x1.square().ct_eq(self); let choice2 = x1.square().ct_eq(&-self); let sqrt = Self::conditional_select(&x1, &(x1 * SQRT_MINUS_ONE), choice2); @@ -213,18 +247,16 @@ impl ff::Field for Fr { impl ff::PrimeField for Fr { type Repr = [u8; 32]; + const MODULUS: &'static str = MODULUS_STR; const NUM_BITS: u32 = 256; const CAPACITY: u32 = 255; - const MODULUS: &'static str = MODULUS_STR; - /// TODO - const MULTIPLICATIVE_GENERATOR: Self = Self::one(); - /// TODO - const ROOT_OF_UNITY: Self = Self::one(); - /// TODO - const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; const TWO_INV: Self = TWO_INV; + const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; + // An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd. + const S: u32 = 2; + const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; + const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; const DELTA: Self = DELTA; - const S: u32 = 6; fn from_repr(repr: Self::Repr) -> CtOption { let mut tmp = Fr([0, 0, 0, 0]); @@ -289,7 +321,6 @@ impl FromUniformBytes<64> for Fr { } impl WithSmallOrderMulGroup<3> for Fr { - /// TODO const ZETA: Self = ZETA; }