Skip to content

Commit

Permalink
Add Ed448-Goldilocks elliptic curve (#557)
Browse files Browse the repository at this point in the history
* Fix equality testing in P448-Goldilocks prime field

* Implement IsPrimeField for P448GoldilocksPrimeField

* Add Ed448-Goldilocks elliptic curve
  • Loading branch information
techiepriyansh authored Oct 6, 2023
1 parent ea9d5cc commit 2c2e0f7
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 31 deletions.
99 changes: 99 additions & 0 deletions math/src/elliptic_curve/edwards/curves/ed448_goldilocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::{
elliptic_curve::{
edwards::{point::EdwardsProjectivePoint, traits::IsEdwards},
traits::IsEllipticCurve,
},
field::{element::FieldElement, fields::p448_goldilocks_prime_field::P448GoldilocksPrimeField},
};

#[derive(Debug, Clone)]
pub struct Ed448Goldilocks;

impl IsEllipticCurve for Ed448Goldilocks {
type BaseField = P448GoldilocksPrimeField;
type PointRepresentation = EdwardsProjectivePoint<Self>;

/// Taken from https://www.rfc-editor.org/rfc/rfc7748#page-6
fn generator() -> Self::PointRepresentation {
Self::PointRepresentation::new([
FieldElement::<Self::BaseField>::from_hex("4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e").unwrap(),
FieldElement::<Self::BaseField>::from_hex("693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14").unwrap(),
FieldElement::one(),
])
}
}

impl IsEdwards for Ed448Goldilocks {
fn a() -> FieldElement<Self::BaseField> {
FieldElement::one()
}

fn d() -> FieldElement<Self::BaseField> {
-FieldElement::from(39081)
}
}

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

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

fn generator_times_5() -> EdwardsProjectivePoint<Ed448Goldilocks> {
let x = FE::from_hex("7a9f9335a48dcb0e2ba7601eedb50def80cbcf728562ada756d761e8958812808bc0d57a920c3c96f07b2d8cefc6f950d0a99d1092030034").unwrap();
let y = FE::from_hex("adfd751a2517edd3b9109ce4fd580ade260ca1823ab18fced86551f7b698017127d7a4ee59d2b33c58405512881f225443b4731472f435eb").unwrap();
Ed448Goldilocks::create_point_from_affine(x, y).unwrap()
}

fn point_1() -> EdwardsProjectivePoint<Ed448Goldilocks> {
let x = FE::from_hex("c591e0987244569fbb80a8edda5f9c5b30d9e7862acbb19ac0f24b766fe29c5e5782efc0e7a169f0c55c5524f8a9f9333ca985ec56404926").unwrap();
let y = FE::from_hex("f969573bf05f19ac5824718d7483d3b83a3ece5847e25487ae2a176290ad2b1cb9c7f4dd55f6ca6c50209556d7fc16e0683c3177356ac9bc").unwrap();
Ed448Goldilocks::create_point_from_affine(x, y).unwrap()
}

fn point_1_times_7() -> EdwardsProjectivePoint<Ed448Goldilocks> {
let x = FE::from_hex("63c9cd1d79f027458015c2013fc819dd0f46f71c21a11fee0c32998acd17bac5b06d0f2f1e1539cfc33223a6e989b2b119dae9bbb16c3743").unwrap();
let y = FE::from_hex("654de66ab8be9fbeec6e72798a0ba2bb39c1888b99104de6cb0acf4516ea5e018bd292a1855f0fea673a5d8e8724d1b19ca52817db624f06").unwrap();
Ed448Goldilocks::create_point_from_affine(x, y).unwrap()
}

#[test]
fn generator_satisfies_defining_equation() {
let g = Ed448Goldilocks::generator().to_affine();
assert_eq!(Ed448Goldilocks::defining_equation(g.x(), g.y()), FE::zero());
}

#[test]
fn adding_generator_five_times_works() {
let g_times_5 = Ed448Goldilocks::generator().operate_with_self(5_u16);
assert_eq!(g_times_5, generator_times_5());
}

#[test]
fn adding_point_1_seven_times_works() {
let point_1 = point_1();
let point_1_times_7 = point_1_times_7();
assert_eq!(point_1.operate_with_self(7_u16), point_1_times_7);
}

#[test]
fn create_valid_point_works() {
let p = point_1();
assert_eq!(*p.x(), FE::from_hex("c591e0987244569fbb80a8edda5f9c5b30d9e7862acbb19ac0f24b766fe29c5e5782efc0e7a169f0c55c5524f8a9f9333ca985ec56404926").unwrap());
assert_eq!(*p.y(), FE::from_hex("f969573bf05f19ac5824718d7483d3b83a3ece5847e25487ae2a176290ad2b1cb9c7f4dd55f6ca6c50209556d7fc16e0683c3177356ac9bc").unwrap());
assert_eq!(*p.z(), FE::one());
}

#[test]
fn create_invalid_points_panics() {
assert_eq!(
Ed448Goldilocks::create_point_from_affine(FE::from(1), FE::from(1)).unwrap_err(),
EllipticCurveError::InvalidPoint
)
}
}
1 change: 1 addition & 0 deletions math/src/elliptic_curve/edwards/curves/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod bandersnatch;
pub mod ed448_goldilocks;
pub mod tiny_jub_jub;
160 changes: 129 additions & 31 deletions math/src/field/fields/p448_goldilocks_prime_field.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::errors::CreationError;
use crate::field::errors::FieldError;
use crate::field::traits::IsField;
use crate::field::traits::{IsField, IsPrimeField};
use crate::unsigned_integer::element::UnsignedInteger;

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -133,8 +134,15 @@ impl IsField for P448GoldilocksPrimeField {
Self::mul(a, &b_inv)
}

/// Taken from https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/per_field/f_generic.tmpl.c
fn eq(a: &U56x8, b: &U56x8) -> bool {
a.limbs == b.limbs
let mut c = Self::sub(a, b);
Self::strong_reduce(&mut c);
let mut ret = 0u64;
for limb in c.limbs.iter() {
ret |= limb;
}
ret == 0
}

fn zero() -> U56x8 {
Expand All @@ -156,12 +164,38 @@ impl IsField for P448GoldilocksPrimeField {

fn from_base_type(x: U56x8) -> U56x8 {
let mut x = x;
Self::weak_reduce(&mut x);
Self::strong_reduce(&mut x);
x
}
}

impl IsPrimeField for P448GoldilocksPrimeField {
type RepresentativeType = U448;

fn representative(a: &U56x8) -> U448 {
let mut a = *a;
Self::strong_reduce(&mut a);

let mut r = U448::from_u64(0);
for i in (0..7).rev() {
r = r << 56;
r = r + U448::from_u64(a.limbs[i]);
}
r
}

fn from_hex(hex_string: &str) -> Result<Self::BaseType, CreationError> {
U56x8::from_hex(hex_string)
}

fn field_bit_size() -> usize {
448
}
}

impl P448GoldilocksPrimeField {
/// Reduces the value in each limb to less than 2^57 (2^56 + 2^8 - 2 is the largest possible value in a limb after this reduction)
/// Taken from https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/p448/arch_ref64/f_impl.h
fn weak_reduce(a: &mut U56x8) {
let a = &mut a.limbs;

Expand All @@ -175,15 +209,55 @@ impl P448GoldilocksPrimeField {

a[0] = (a[0] & mask) + tmp;
}

/// Reduces the number to its canonical form
/// Taken from https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/per_field/f_generic.tmpl.c
fn strong_reduce(a: &mut U56x8) {
P448GoldilocksPrimeField::weak_reduce(a);

const MODULUS: U56x8 = U56x8 {
limbs: [
0xffffffffffffff,
0xffffffffffffff,
0xffffffffffffff,
0xffffffffffffff,
0xfffffffffffffe,
0xffffffffffffff,
0xffffffffffffff,
0xffffffffffffff,
],
};
let mask = (1u128 << 56) - 1;

let mut scarry = 0i128;
for i in 0..8 {
scarry = scarry + (a.limbs[i] as i128) - (MODULUS.limbs[i] as i128);
a.limbs[i] = ((scarry as u128) & mask) as u64;
scarry >>= 56;
}

assert!((scarry as u64) == 0 || (scarry as u64).wrapping_add(1) == 0);

let scarry_0 = scarry as u64;
let mut carry = 0u128;

for i in 0..8 {
carry = carry + (a.limbs[i] as u128) + ((scarry_0 & MODULUS.limbs[i]) as u128);
a.limbs[i] = (carry & mask) as u64;
carry >>= 56;
}

assert!((carry as u64).wrapping_add(scarry_0) == 0);
}
}

impl U56x8 {
pub const fn from(value: &str) -> Self {
pub const fn from_hex(hex_string: &str) -> Result<Self, CreationError> {
let mut result = [0u64; 8];
let mut limb = 0;
let mut limb_index = 0;
let mut shift = 0;
let value = value.as_bytes();
let value = hex_string.as_bytes();
let mut i: usize = value.len();
while i > 0 {
i -= 1;
Expand All @@ -192,7 +266,7 @@ impl U56x8 {
c @ b'a'..=b'f' => (c as u64 - 'a' as u64 + 10) << shift,
c @ b'A'..=b'F' => (c as u64 - 'A' as u64 + 10) << shift,
_ => {
panic!("Malformed hex expression.")
return Err(CreationError::InvalidHexString);
}
};
shift += 4;
Expand All @@ -205,7 +279,7 @@ impl U56x8 {
}
result[limb_index] = limb;

U56x8 { limbs: result }
Ok(U56x8 { limbs: result })
}
}

Expand All @@ -214,16 +288,16 @@ mod tests {
use super::*;

#[test]
fn construc_u56x8_from_hex_string_1() {
fn construct_u56x8_from_hex_string_1() {
let hex_str = "1";
let num = U56x8::from(hex_str);
let num = U56x8::from_hex(hex_str).unwrap();
assert_eq!(num.limbs, [1, 0, 0, 0, 0, 0, 0, 0]);
}

#[test]
fn construct_u56x8_from_hex_string_2() {
let hex_str = "49bbeeaa7102b38a0cfba4634f64a288bcb9b1366599f7afcb5453567ef7c34cce0f7139c6dea4841497172f637c7bbbf3ca1990ad88381e";
let num = U56x8::from(hex_str);
let num = U56x8::from_hex(hex_str).unwrap();
assert_eq!(
num.limbs,
[
Expand All @@ -239,64 +313,85 @@ mod tests {
);
}

#[test]
fn strong_reduce_test1() {
let mut num = U56x8::from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
P448GoldilocksPrimeField::strong_reduce(&mut num);
assert_eq!(num.limbs, [0, 0, 0, 0, 0, 0, 0, 0]);
}

#[test]
fn strong_reduce_test2() {
let mut num = U56x8::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000").unwrap();
P448GoldilocksPrimeField::strong_reduce(&mut num);
assert_eq!(num.limbs, [1, 0, 0, 0, 0, 0, 0, 0]);
}

#[test]
fn representative_test() {
let num = U56x8::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000029").unwrap();
let r = P448GoldilocksPrimeField::representative(&num);
assert_eq!(r, U448::from_u64(42));
}

#[test]
fn p448_add_test_1() {
let num1 = U56x8::from("73c7941e36ee1e12b2105fb96634848d62def10bc1782576cfa7f54486820202847bbfb2e8f89ff7707f9913b8cf9b9efaf2029cfd6d3fa9");
let num2 = U56x8::from("f3ef02193a11b6ea80be4bd2944d32c4674456a888b470b14e0cf223bed114bb28146d967f0d220cf20be2016dc84f51e5d5e29a71751f06");
let num1 = U56x8::from_hex("73c7941e36ee1e12b2105fb96634848d62def10bc1782576cfa7f54486820202847bbfb2e8f89ff7707f9913b8cf9b9efaf2029cfd6d3fa9").unwrap();
let num2 = U56x8::from_hex("f3ef02193a11b6ea80be4bd2944d32c4674456a888b470b14e0cf223bed114bb28146d967f0d220cf20be2016dc84f51e5d5e29a71751f06").unwrap();
let num3 = P448GoldilocksPrimeField::add(&num1, &num2);
assert_eq!(num3, U56x8::from("67b6963770ffd4fd32ceab8bfa81b751ca2347b44a2c96281db4e769455316bdac902d496805c204628b7b152697eaf0e0c7e5376ee25eb0"));
assert_eq!(num3, U56x8::from_hex("67b6963770ffd4fd32ceab8bfa81b751ca2347b44a2c96281db4e769455316bdac902d496805c204628b7b152697eaf0e0c7e5376ee25eb0").unwrap());
}

#[test]
fn p448_sub_test_1() {
let num1 = U56x8::from("22264a9d5272984a996cc5eef6bd165e63bc70f2050bbd5bc24343df9cc25f826cef7bff7466963a82cd59f36671c724c53b8b27330ea076");
let num2 = U56x8::from("7a0063b5cd729df62c0e77071727639e06d0892eacb505569e8b47a99175d1d09a4bd7c22a2168c1fb9f3de31d9633d92341f84d000633b1");
let num1 = U56x8::from_hex("22264a9d5272984a996cc5eef6bd165e63bc70f2050bbd5bc24343df9cc25f826cef7bff7466963a82cd59f36671c724c53b8b27330ea076").unwrap();
let num2 = U56x8::from_hex("7a0063b5cd729df62c0e77071727639e06d0892eacb505569e8b47a99175d1d09a4bd7c22a2168c1fb9f3de31d9633d92341f84d000633b1").unwrap();
let num3 = P448GoldilocksPrimeField::sub(&num1, &num2);
assert_eq!(num3, U56x8::from("a825e6e784fffa546d5e4ee7df95b2c05cebe7c35856b80523b7fc350b4c8db1d2a3a43d4a452d78872e1c1048db934ba1f992da33086cc4"));
assert_eq!(num3, U56x8::from_hex("a825e6e784fffa546d5e4ee7df95b2c05cebe7c35856b80523b7fc350b4c8db1d2a3a43d4a452d78872e1c1048db934ba1f992da33086cc4").unwrap());
}

#[test]
fn p448_neg_test_1() {
let num1 = U56x8::from("21183d1faa857cd3f08d54871837b06d70af4e6b85173c0ff02685147f38e8b9af3141baad0067f3514a527bd3e7405a953c3a8fa9a15bb3");
let num1 = U56x8::from_hex("21183d1faa857cd3f08d54871837b06d70af4e6b85173c0ff02685147f38e8b9af3141baad0067f3514a527bd3e7405a953c3a8fa9a15bb3").unwrap();
let num2 = P448GoldilocksPrimeField::neg(&num1);
assert_eq!(num2, U56x8::from("dee7c2e0557a832c0f72ab78e7c84f928f50b1947ae8c3f00fd97aea80c7174650cebe4552ff980caeb5ad842c18bfa56ac3c570565ea44c"));
assert_eq!(num2, U56x8::from_hex("dee7c2e0557a832c0f72ab78e7c84f928f50b1947ae8c3f00fd97aea80c7174650cebe4552ff980caeb5ad842c18bfa56ac3c570565ea44c").unwrap());
}

#[test]
fn p448_mul_test_1() {
let num1 = U56x8::from("a");
let num2 = U56x8::from("b");
let num1 = U56x8::from_hex("a").unwrap();
let num2 = U56x8::from_hex("b").unwrap();
let num3 = P448GoldilocksPrimeField::mul(&num1, &num2);
assert_eq!(num3, U56x8::from("6e"));
assert_eq!(num3, U56x8::from_hex("6e").unwrap());
}

#[test]
fn p448_mul_test_2() {
let num1 = U56x8::from("b7aa542ac8824fbf654ee0ab4ea5eb3b0ad65b48bfef5e4d8b84ab5737e9283c06ecbadd799688cdf73cd7d077d53b5e6f738b264086d034");
let num2 = U56x8::from("89a36d8b491f5a9af136a35061a59aa2c65353a3c99bb205a53c7ae2f37e6ae492f24248fc549344ba2f203c6d5b2b5dab216fdd1a7dcf87");
let num1 = U56x8::from_hex("b7aa542ac8824fbf654ee0ab4ea5eb3b0ad65b48bfef5e4d8b84ab5737e9283c06ecbadd799688cdf73cd7d077d53b5e6f738b264086d034").unwrap();
let num2 = U56x8::from_hex("89a36d8b491f5a9af136a35061a59aa2c65353a3c99bb205a53c7ae2f37e6ae492f24248fc549344ba2f203c6d5b2b5dab216fdd1a7dcf87").unwrap();
let num3 = P448GoldilocksPrimeField::mul(&num1, &num2);
assert_eq!(num3, U56x8::from("f61c57f70d8a1eaf261907d08eb1086c2289f7bbb6ff6a0dfd016f91ac9eda658879b52a654a10b2ce123717fad3ab15b1e77ce643683886"));
assert_eq!(num3, U56x8::from_hex("f61c57f70d8a1eaf261907d08eb1086c2289f7bbb6ff6a0dfd016f91ac9eda658879b52a654a10b2ce123717fad3ab15b1e77ce643683886").unwrap());
}

#[test]
fn p448_pow_test_1() {
let num1 = U56x8::from("6b1b1d952930ee34fb6ed3521f7653293fd7e01de2027673d3d5a0bf3dc0688530bec50b3dfca4df28cc432bec1198e17fde3e1cc79e5732");
let num1 = U56x8::from_hex("6b1b1d952930ee34fb6ed3521f7653293fd7e01de2027673d3d5a0bf3dc0688530bec50b3dfca4df28cc432bec1198e17fde3e1cc79e5732").unwrap();
let num2 = P448GoldilocksPrimeField::pow(&num1, 65537u64);
assert_eq!(num2, U56x8::from("ec48eda1579a0879c01e8853e4a718ede9cd6bcf88d6696b47dc4dce7d2acdd1a37674aa455d84126800893975c95bb47c40b098a9e30836"));
assert_eq!(num2, U56x8::from_hex("ec48eda1579a0879c01e8853e4a718ede9cd6bcf88d6696b47dc4dce7d2acdd1a37674aa455d84126800893975c95bb47c40b098a9e30836").unwrap());
}

#[test]
fn p448_inv_test_1() {
let num1 = U56x8::from("b86e226f5ac29af28c74e272fc129ab167798f70dedd2ce76aa76204a23beb74c8ddba2a643196c62ee35a18472d6de7d82b6af4b2fc5e58");
let num2 = U56x8::from("bb2bd89a1297c7a6052b41be503aa7de2cd6e6775396e76bf995f27f1dccf69131067824ded693bdd6e58fe7c2276fa92ec1d9a0048b9be6");
let num1 = U56x8::from_hex("b86e226f5ac29af28c74e272fc129ab167798f70dedd2ce76aa76204a23beb74c8ddba2a643196c62ee35a18472d6de7d82b6af4b2fc5e58").unwrap();
let num2 = U56x8::from_hex("bb2bd89a1297c7a6052b41be503aa7de2cd6e6775396e76bf995f27f1dccf69131067824ded693bdd6e58fe7c2276fa92ec1d9a0048b9be6").unwrap();
let num3 = P448GoldilocksPrimeField::div(&num1, &num2);
assert_eq!(num3, U56x8::from("707b5cc75967b58ebd28d14d4ed7ed9eaae1187d0b359c7733cf61b1a5c87fc88228ca532c50f19d1ba57146ca2e38417922033f647c8d9"));
assert_eq!(num3, U56x8::from_hex("707b5cc75967b58ebd28d14d4ed7ed9eaae1187d0b359c7733cf61b1a5c87fc88228ca532c50f19d1ba57146ca2e38417922033f647c8d9").unwrap());
}

#[test]
fn p448_from_u64_test_1() {
let num = P448GoldilocksPrimeField::from_u64(2012613457133209520u64);
assert_eq!(num, U56x8::from("1bee3d46a69887b0"));
assert_eq!(num, U56x8::from_hex("1bee3d46a69887b0").unwrap());
}

#[test]
Expand All @@ -306,6 +401,9 @@ mod tests {
limbs[1] = 6217911673150459564u64;
let num1 = U56x8 { limbs };
let num2 = P448GoldilocksPrimeField::from_base_type(num1);
assert_eq!(num2, U56x8::from("564a75b90ae34f8155d5821d7e9484"));
assert_eq!(
num2,
U56x8::from_hex("564a75b90ae34f8155d5821d7e9484").unwrap()
);
}
}

0 comments on commit 2c2e0f7

Please sign in to comment.