diff --git a/ed448-goldilocks/src/curve/twedwards/affine.rs b/ed448-goldilocks/src/curve/twedwards/affine.rs index d328c4f8f..8d713dc46 100644 --- a/ed448-goldilocks/src/curve/twedwards/affine.rs +++ b/ed448-goldilocks/src/curve/twedwards/affine.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +use crate::TWISTED_EDWARDS_BASE_POINT; use crate::curve::twedwards::{extended::ExtendedPoint, extensible::ExtensiblePoint}; use crate::field::FieldElement; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; @@ -30,6 +31,12 @@ impl AffinePoint { y: FieldElement::ONE, }; + /// Identity element + pub(crate) const GENERATOR: AffinePoint = AffinePoint { + x: TWISTED_EDWARDS_BASE_POINT.X, + y: TWISTED_EDWARDS_BASE_POINT.Y, + }; + /// Checks if the AffinePoint is on the TwistedEdwards curve pub(crate) fn is_on_curve(&self) -> Choice { let xx = self.x.square(); @@ -140,7 +147,6 @@ mod tests { #[test] fn test_negation() { - use crate::TWISTED_EDWARDS_BASE_POINT; let a = TWISTED_EDWARDS_BASE_POINT.to_extensible().to_affine(); assert_eq!(a.is_on_curve().unwrap_u8(), 1); diff --git a/ed448-goldilocks/src/decaf/affine.rs b/ed448-goldilocks/src/decaf/affine.rs index d071cd999..778ed074f 100644 --- a/ed448-goldilocks/src/decaf/affine.rs +++ b/ed448-goldilocks/src/decaf/affine.rs @@ -1,13 +1,15 @@ use crate::curve::twedwards::affine::AffinePoint as InnerAffinePoint; +use crate::decaf::points::DecafPointRepr; use crate::field::FieldElement; use crate::{Decaf448FieldBytes, DecafPoint, DecafScalar, ORDER}; -use core::ops::Mul; +use core::ops::{Mul, Neg}; use elliptic_curve::{ Error, + group::{GroupEncoding, prime::PrimeCurveAffine}, point::{AffineCoordinates, NonIdentity}, zeroize::DefaultIsZeroes, }; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; +use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, CtOption}; /// Affine point on the twisted curve #[derive(Copy, Clone, Debug, Default)] @@ -70,9 +72,82 @@ impl AffineCoordinates for AffinePoint { impl DefaultIsZeroes for AffinePoint {} +impl GroupEncoding for AffinePoint { + type Repr = DecafPointRepr; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes_unchecked(bytes) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + let s = FieldElement::from_bytes(&bytes.0); + //XX: Check for canonical encoding and sign, + // Copied this check from Dalek: The From_bytes function does not throw an error, if the bytes exceed the prime. + // However, to_bytes reduces the Field element before serialising + // So we can use to_bytes -> from_bytes and if the representations are the same, then the element was already in reduced form + let s_bytes_check = s.to_bytes(); + let s_encoding_is_canonical = s_bytes_check[..].ct_eq(&bytes.0); + let s_is_negative = s.is_negative(); + // if s_encoding_is_canonical.unwrap_u8() == 0u8 || s.is_negative().unwrap_u8() == 1u8 { + // return None; + // } + + let ss = s.square(); + let u1 = FieldElement::ONE - ss; + let u2 = FieldElement::ONE + ss; + let u1_sqr = u1.square(); + + let v = ss * (FieldElement::NEG_FOUR_TIMES_TWISTED_D) + u1_sqr; // XXX: constantify please + + let (I, ok) = (v * u1_sqr).inverse_square_root(); + + let Dx = I * u1; + let Dxs = s.double() * Dx; + + let mut x = (Dxs * I) * v; + let k = Dxs * FieldElement::DECAF_FACTOR; + x.conditional_negate(k.is_negative()); + + let y = Dx * u2; + let pt = InnerAffinePoint { x, y }; + + CtOption::new( + Self(pt), + ok & pt.is_on_curve() & s_encoding_is_canonical & !s_is_negative, + ) + } + + fn to_bytes(&self) -> Self::Repr { + self.to_decaf().to_bytes() + } +} + +impl PrimeCurveAffine for AffinePoint { + type Scalar = DecafScalar; + type Curve = DecafPoint; + + fn identity() -> Self { + Self::IDENTITY + } + + fn generator() -> Self { + Self::GENERATOR + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Self::IDENTITY) + } + + fn to_curve(&self) -> Self::Curve { + self.to_decaf() + } +} + impl AffinePoint { /// The identity point pub const IDENTITY: Self = Self(InnerAffinePoint::IDENTITY); + /// The generator point + pub const GENERATOR: Self = Self(InnerAffinePoint::GENERATOR); /// Convert to DecafPoint pub fn to_decaf(&self) -> DecafPoint { @@ -115,3 +190,23 @@ impl Mul<&DecafScalar> for &AffinePoint { } define_mul_variants!(LHS = AffinePoint, RHS = DecafScalar, Output = DecafPoint); + +impl Neg for AffinePoint { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(self.0.negate()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use elliptic_curve::CurveGroup; + + #[test] + fn generator() { + assert_eq!(AffinePoint::GENERATOR.to_decaf(), DecafPoint::GENERATOR); + assert_eq!(DecafPoint::GENERATOR.to_affine(), AffinePoint::GENERATOR); + } +} diff --git a/ed448-goldilocks/src/decaf/points.rs b/ed448-goldilocks/src/decaf/points.rs index a090cb30a..f7de3329c 100644 --- a/ed448-goldilocks/src/decaf/points.rs +++ b/ed448-goldilocks/src/decaf/points.rs @@ -6,7 +6,11 @@ use elliptic_curve::{ CurveGroup, Error, Group, array::Array, consts::U56, - group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup}, + group::{ + GroupEncoding, + cofactor::CofactorGroup, + prime::{PrimeCurve, PrimeGroup}, + }, ops::LinearCombination, point::NonIdentity, }; @@ -239,6 +243,10 @@ impl CofactorGroup for DecafPoint { impl PrimeGroup for DecafPoint {} +impl PrimeCurve for DecafPoint { + type Affine = DecafAffinePoint; +} + impl LinearCombination<[(DecafPoint, DecafScalar); N]> for DecafPoint {} impl LinearCombination<[(DecafPoint, DecafScalar)]> for DecafPoint {} @@ -535,43 +543,7 @@ impl CompressedDecaf { /// Decompress a point if it is valid pub fn decompress(&self) -> CtOption { - let s = FieldElement::from_bytes(&self.0); - //XX: Check for canonical encoding and sign, - // Copied this check from Dalek: The From_bytes function does not throw an error, if the bytes exceed the prime. - // However, to_bytes reduces the Field element before serialising - // So we can use to_bytes -> from_bytes and if the representations are the same, then the element was already in reduced form - let s_bytes_check = s.to_bytes(); - let s_encoding_is_canonical = s_bytes_check[..].ct_eq(&self.0); - let s_is_negative = s.is_negative(); - // if s_encoding_is_canonical.unwrap_u8() == 0u8 || s.is_negative().unwrap_u8() == 1u8 { - // return None; - // } - - let ss = s.square(); - let u1 = FieldElement::ONE - ss; - let u2 = FieldElement::ONE + ss; - let u1_sqr = u1.square(); - - let v = ss * (FieldElement::NEG_FOUR_TIMES_TWISTED_D) + u1_sqr; // XXX: constantify please - - let (I, ok) = (v * u1_sqr).inverse_square_root(); - - let Dx = I * u1; - let Dxs = s.double() * Dx; - - let mut X = (Dxs * I) * v; - let k = Dxs * FieldElement::DECAF_FACTOR; - X.conditional_negate(k.is_negative()); - - let Y = Dx * u2; - let Z = FieldElement::ONE; - let T = X * Y; - let pt = ExtendedPoint { X, Y, Z, T }; - - CtOption::new( - DecafPoint(pt), - ok & pt.is_on_curve() & s_encoding_is_canonical & !s_is_negative, - ) + DecafAffinePoint::from_bytes((&self.0).into()).map(|pt| pt.to_decaf()) } /// Get the bytes of this compressed point @@ -633,109 +605,118 @@ mod test { #[test] fn test_vectors_lib_decaf() { // Testing small multiples of basepoint. Taken from reference implementation. - let compressed = [ + const COMPRESSED: [DecafPointBytes; 16] = [ // Taken from libdecaf, where they were computed using SAGE script - CompressedDecaf([ + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]), - CompressedDecaf([ + ], + [ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - ]), - CompressedDecaf([ + ], + [ 200, 152, 235, 79, 135, 249, 124, 86, 76, 111, 214, 31, 199, 228, 150, 137, 49, 74, 31, 129, 142, 200, 94, 235, 59, 213, 81, 74, 200, 22, 211, 135, 120, 246, 158, 243, 71, 168, 159, 202, 129, 126, 102, 222, 253, 237, 206, 23, 140, 124, 199, 9, 178, 17, 110, 117, - ]), - CompressedDecaf([ + ], + [ 160, 192, 155, 242, 186, 114, 8, 253, 160, 244, 191, 227, 208, 245, 178, 154, 84, 48, 18, 48, 109, 67, 131, 27, 90, 220, 111, 231, 248, 89, 111, 163, 8, 118, 61, 177, 84, 104, 50, 59, 17, 207, 110, 74, 235, 140, 24, 254, 68, 103, 143, 68, 84, 90, 105, 188, - ]), - CompressedDecaf([ + ], + [ 180, 111, 24, 54, 170, 40, 124, 10, 90, 86, 83, 240, 236, 94, 249, 233, 3, 244, 54, 226, 28, 21, 112, 194, 154, 217, 229, 245, 150, 218, 151, 238, 175, 23, 21, 10, 227, 11, 203, 49, 116, 208, 75, 194, 215, 18, 200, 199, 120, 157, 124, 180, 253, 161, 56, 244, - ]), - CompressedDecaf([ + ], + [ 28, 91, 190, 207, 71, 65, 223, 170, 231, 157, 183, 45, 250, 206, 0, 234, 170, 197, 2, 194, 6, 9, 52, 182, 234, 174, 202, 106, 32, 189, 61, 169, 224, 190, 135, 119, 247, 208, 32, 51, 209, 177, 88, 132, 35, 34, 129, 164, 31, 199, 248, 14, 237, 4, 175, 94, - ]), - CompressedDecaf([ + ], + [ 134, 255, 1, 130, 212, 15, 127, 158, 219, 120, 98, 81, 88, 33, 189, 103, 191, 214, 22, 90, 60, 68, 222, 149, 215, 223, 121, 184, 119, 156, 207, 100, 96, 227, 198, 139, 112, 193, 106, 170, 40, 15, 45, 123, 63, 34, 215, 69, 185, 122, 137, 144, 108, 252, 71, 108, - ]), - CompressedDecaf([ + ], + [ 80, 43, 203, 104, 66, 235, 6, 240, 228, 144, 50, 186, 232, 124, 85, 76, 3, 29, 109, 77, 45, 118, 148, 239, 191, 156, 70, 141, 72, 34, 12, 80, 248, 202, 40, 132, 51, 100, 215, 12, 238, 146, 214, 254, 36, 110, 97, 68, 143, 157, 185, 128, 139, 59, 36, 8, - ]), - CompressedDecaf([ + ], + [ 12, 152, 16, 241, 226, 235, 211, 137, 202, 167, 137, 55, 77, 120, 0, 121, 116, 239, 77, 23, 34, 115, 22, 244, 14, 87, 139, 51, 104, 39, 218, 63, 107, 72, 42, 71, 148, 235, 106, 57, 117, 185, 113, 181, 225, 56, 143, 82, 233, 30, 162, 241, 188, 176, 249, 18, - ]), - CompressedDecaf([ + ], + [ 32, 212, 29, 133, 161, 141, 86, 87, 162, 150, 64, 50, 21, 99, 187, 208, 76, 47, 251, 208, 163, 122, 123, 164, 58, 79, 125, 38, 60, 226, 111, 175, 78, 31, 116, 249, 244, 181, 144, 198, 146, 41, 174, 87, 31, 227, 127, 166, 57, 181, 184, 235, 72, 189, 154, 85, - ]), - CompressedDecaf([ + ], + [ 230, 180, 184, 244, 8, 199, 1, 13, 6, 1, 231, 237, 160, 195, 9, 161, 164, 39, 32, 214, 208, 107, 87, 89, 253, 196, 225, 239, 226, 45, 7, 109, 108, 68, 212, 47, 80, 141, 103, 190, 70, 41, 20, 210, 139, 142, 220, 227, 46, 112, 148, 48, 81, 100, 175, 23, - ]), - CompressedDecaf([ + ], + [ 190, 136, 187, 184, 108, 89, 193, 61, 142, 157, 9, 171, 152, 16, 95, 105, 194, 209, 221, 19, 77, 188, 211, 176, 134, 54, 88, 245, 49, 89, 219, 100, 192, 225, 57, 209, 128, 243, 200, 155, 130, 150, 208, 174, 50, 68, 25, 192, 111, 168, 127, 199, 218, 175, 52, 193, - ]), - CompressedDecaf([ + ], + [ 164, 86, 249, 54, 151, 105, 232, 240, 137, 2, 18, 74, 3, 20, 199, 160, 101, 55, 160, 110, 50, 65, 31, 79, 147, 65, 89, 80, 161, 123, 173, 250, 116, 66, 182, 33, 116, 52, 163, 160, 94, 244, 91, 229, 241, 11, 215, 178, 239, 142, 160, 12, 67, 30, 222, 197, - ]), - CompressedDecaf([ + ], + [ 24, 110, 69, 44, 68, 102, 170, 67, 131, 180, 192, 2, 16, 213, 46, 121, 34, 219, 249, 119, 30, 139, 71, 226, 41, 169, 183, 183, 60, 141, 16, 253, 126, 240, 182, 228, 21, 48, 249, 31, 36, 163, 237, 154, 183, 31, 163, 139, 152, 178, 254, 71, 70, 213, 29, 104, - ]), - CompressedDecaf([ + ], + [ 74, 231, 253, 202, 233, 69, 63, 25, 90, 142, 173, 92, 190, 26, 123, 150, 153, 103, 59, 82, 196, 10, 178, 121, 39, 70, 72, 135, 190, 83, 35, 127, 127, 58, 33, 185, 56, 212, 13, 14, 201, 225, 91, 29, 81, 48, 177, 63, 254, 216, 19, 115, 165, 62, 43, 67, - ]), - CompressedDecaf([ + ], + [ 132, 25, 129, 195, 191, 238, 195, 246, 12, 254, 202, 117, 217, 216, 220, 23, 244, 108, 240, 16, 111, 36, 34, 181, 154, 236, 88, 10, 88, 243, 66, 39, 46, 58, 94, 87, 90, 5, 93, 219, 5, 19, 144, 197, 76, 36, 198, 236, 177, 224, 172, 235, 7, 95, 96, 86, - ]), + ], ]; let mut point = DecafPoint::IDENTITY; let generator = DecafPoint::GENERATOR; - for compressed_point in compressed.iter() { - assert_eq!(&point.compress(), compressed_point); + for compressed_point in COMPRESSED.iter() { + assert_eq!(&point.to_bytes().0, compressed_point); + assert_eq!(&point.to_affine().to_bytes().0, compressed_point); point += generator; - let decompressed_point = compressed_point.decompress(); - assert_eq!(decompressed_point.is_some().unwrap_u8(), 1u8); + assert!( + DecafPoint::from_bytes(compressed_point.into()) + .into_option() + .is_some() + ); + assert!( + DecafAffinePoint::from_bytes(compressed_point.into()) + .into_option() + .is_some() + ); } } diff --git a/ed448-goldilocks/src/edwards/affine.rs b/ed448-goldilocks/src/edwards/affine.rs index 9aadffdc4..31da15b20 100644 --- a/ed448-goldilocks/src/edwards/affine.rs +++ b/ed448-goldilocks/src/edwards/affine.rs @@ -1,8 +1,14 @@ use crate::field::FieldElement; use crate::*; use core::fmt::{Display, Formatter, LowerHex, Result as FmtResult, UpperHex}; -use core::ops::Mul; -use elliptic_curve::{Error, point::NonIdentity, zeroize::DefaultIsZeroes}; +use core::ops::{Mul, Neg}; +use elliptic_curve::{ + Error, + array::Array, + group::{GroupEncoding, prime::PrimeCurveAffine}, + point::NonIdentity, + zeroize::DefaultIsZeroes, +}; use rand_core::TryRngCore; use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -73,6 +79,60 @@ impl elliptic_curve::point::AffineCoordinates for AffinePoint { impl DefaultIsZeroes for AffinePoint {} +impl GroupEncoding for AffinePoint { + type Repr = Array; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + Self::from_bytes_unchecked(bytes) + .and_then(|pt| CtOption::new(pt, pt.is_on_curve() & pt.to_edwards().is_torsion_free())) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + let (y_bytes, sign) = bytes.split_ref::(); + + // Recover x using y + let y = FieldElement::from_bytes(&y_bytes.0); + let yy = y.square(); + let dyy = FieldElement::EDWARDS_D * yy; + let numerator = FieldElement::ONE - yy; + let denominator = FieldElement::ONE - dyy; + + let (mut x, is_res) = FieldElement::sqrt_ratio(&numerator, &denominator); + + // Compute correct sign of x + let compressed_sign_bit = Choice::from(sign[0] >> 7); + let is_negative = x.is_negative(); + x.conditional_negate(compressed_sign_bit ^ is_negative); + + CtOption::new(AffinePoint { x, y }, is_res) + } + + fn to_bytes(&self) -> Self::Repr { + self.compress().0.into() + } +} + +impl PrimeCurveAffine for AffinePoint { + type Scalar = EdwardsScalar; + type Curve = EdwardsPoint; + + fn identity() -> Self { + Self::IDENTITY + } + + fn generator() -> Self { + Self::GENERATOR + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Self::IDENTITY) + } + + fn to_curve(&self) -> Self::Curve { + self.to_edwards() + } +} + impl AffinePoint { /// The identity point pub const IDENTITY: AffinePoint = AffinePoint { @@ -80,6 +140,12 @@ impl AffinePoint { y: FieldElement::ONE, }; + /// The identity point + pub const GENERATOR: AffinePoint = AffinePoint { + x: GOLDILOCKS_BASE_POINT.X, + y: GOLDILOCKS_BASE_POINT.Y, + }; + /// Generate a random [`AffinePoint`]. pub fn try_from_rng(rng: &mut R) -> Result where @@ -203,6 +269,17 @@ define_mul_variants!( Output = EdwardsPoint ); +impl Neg for AffinePoint { + type Output = Self; + + fn neg(self) -> Self::Output { + Self { + x: -self.x, + y: self.y, + } + } +} + /// The compressed internal representation of a point on the Twisted Edwards Curve pub type PointBytes = [u8; 57]; @@ -389,27 +466,7 @@ impl CompressedEdwardsY { /// Returns `None` if the input is not the \\(y\\)-coordinate of a /// curve point. pub fn decompress_unchecked(&self) -> CtOption { - // Safe to unwrap here as the underlying data structure is a slice - let (sign, b) = self.0.split_last().expect("slice is non-empty"); - - let mut y_bytes: [u8; 56] = [0; 56]; - y_bytes.copy_from_slice(b); - - // Recover x using y - let y = FieldElement::from_bytes(&y_bytes); - let yy = y.square(); - let dyy = FieldElement::EDWARDS_D * yy; - let numerator = FieldElement::ONE - yy; - let denominator = FieldElement::ONE - dyy; - - let (mut x, is_res) = FieldElement::sqrt_ratio(&numerator, &denominator); - - // Compute correct sign of x - let compressed_sign_bit = Choice::from(sign >> 7); - let is_negative = x.is_negative(); - x.conditional_negate(compressed_sign_bit ^ is_negative); - - CtOption::new(AffinePoint { x, y }, is_res) + AffinePoint::from_bytes_unchecked((&self.0).into()) } /// Attempt to decompress to an `AffinePoint`. @@ -419,8 +476,7 @@ impl CompressedEdwardsY { /// - if the input point is not on the curve. /// - if the input point has nonzero torsion component. pub fn decompress(&self) -> CtOption { - self.decompress_unchecked() - .and_then(|pt| CtOption::new(pt, pt.is_on_curve() & pt.to_edwards().is_torsion_free())) + AffinePoint::from_bytes((&self.0).into()) } /// View this `CompressedEdwardsY` as an array of bytes. @@ -433,3 +489,14 @@ impl CompressedEdwardsY { self.0 } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn generator() { + assert_eq!(AffinePoint::GENERATOR.to_edwards(), EdwardsPoint::GENERATOR); + assert_eq!(EdwardsPoint::GENERATOR.to_affine(), AffinePoint::GENERATOR); + } +} diff --git a/ed448-goldilocks/src/edwards/extended.rs b/ed448-goldilocks/src/edwards/extended.rs index c376cbe47..f33873ae9 100644 --- a/ed448-goldilocks/src/edwards/extended.rs +++ b/ed448-goldilocks/src/edwards/extended.rs @@ -12,7 +12,11 @@ use crate::*; use elliptic_curve::{ BatchNormalize, CurveGroup, Error, array::Array, - group::{Group, GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup}, + group::{ + Group, GroupEncoding, + cofactor::CofactorGroup, + prime::{PrimeCurve, PrimeGroup}, + }, ops::{BatchInvert, LinearCombination}, point::NonIdentity, }; @@ -135,19 +139,11 @@ impl GroupEncoding for EdwardsPoint { type Repr = Array; fn from_bytes(bytes: &Self::Repr) -> CtOption { - let mut value = [0u8; 57]; - value.copy_from_slice(bytes); - CompressedEdwardsY(value) - .decompress() - .map(|point| point.to_edwards()) + AffinePoint::from_bytes(bytes).map(|pt| pt.to_edwards()) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - let mut value = [0u8; 57]; - value.copy_from_slice(bytes); - CompressedEdwardsY(value) - .decompress() - .map(|point| point.to_edwards()) + AffinePoint::from_bytes_unchecked(bytes).map(|pt| pt.to_edwards()) } fn to_bytes(&self) -> Self::Repr { @@ -300,6 +296,10 @@ impl CurveGroup for EdwardsPoint { } } +impl PrimeCurve for EdwardsPoint { + type Affine = AffinePoint; +} + impl EdwardsPoint { /// Generator for the prime subgroup pub const GENERATOR: Self = GOLDILOCKS_BASE_POINT;