diff --git a/ed448-goldilocks/README.md b/ed448-goldilocks/README.md index 7b7bc93b6..4dcbfeeb8 100644 --- a/ed448-goldilocks/README.md +++ b/ed448-goldilocks/README.md @@ -17,27 +17,27 @@ It is intended to be portable, fast, and safe. ## Usage ```rust -use ed448_goldilocks::{Ed448, EdwardsPoint, CompressedEdwardsY, EdwardsScalar, sha3::Shake256}; +use ed448_goldilocks::{Ed448, EdwardsPoint, CompressedEdwardsY, Scalar, sha3::Shake256}; use elliptic_curve::consts::U84; use elliptic_curve::Field; use elliptic_curve::group::GroupEncoding; use hash2curve::{ExpandMsgXof, GroupDigest}; use rand_core::OsRng; -let secret_key = EdwardsScalar::TWO; +let secret_key = Scalar::TWO; let public_key = EdwardsPoint::GENERATOR * &secret_key; assert_eq!(public_key, EdwardsPoint::GENERATOR + EdwardsPoint::GENERATOR); -let secret_key = EdwardsScalar::try_from_rng(&mut OsRng).unwrap(); +let secret_key = Scalar::try_from_rng(&mut OsRng).unwrap(); let public_key = EdwardsPoint::GENERATOR * &secret_key; let compressed_public_key = public_key.to_bytes(); assert_eq!(compressed_public_key.len(), 57); let hashed_scalar = hash2curve::hash_to_scalar::::ExpandMsg, U84>(&[b"test"], &[b"test DST"]).unwrap(); -let input = hex_literal::hex!("8108d09ce4ea5707d44a6e52d75f290d0a0801cd5e366b9a0e6f72c75246ea5042963192c01703749adb0f5a4b1ab0586ccc6cf58cfd6d0e00"); -let expected_scalar = EdwardsScalar::from_canonical_bytes(&input.into()).unwrap(); +let input = hex_literal::hex!("8108d09ce4ea5707d44a6e52d75f290d0a0801cd5e366b9a0e6f72c75246ea5042963192c01703749adb0f5a4b1ab0586ccc6cf58cfd6d0e"); +let expected_scalar = Scalar::from_canonical_bytes(&input.into()).unwrap(); assert_eq!(hashed_scalar, expected_scalar); let hashed_point = Ed448::hash_from_bytes(b"test", b"test DST").unwrap(); diff --git a/ed448-goldilocks/benches/bench.rs b/ed448-goldilocks/benches/bench.rs index 872831725..9ac27c35f 100644 --- a/ed448-goldilocks/benches/bench.rs +++ b/ed448-goldilocks/benches/bench.rs @@ -1,7 +1,5 @@ use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; -use ed448_goldilocks::{ - Decaf448, DecafPoint, DecafScalar, Ed448, EdwardsPoint, EdwardsScalar, MontgomeryPoint, -}; +use ed448_goldilocks::{Decaf448, DecafPoint, Ed448, EdwardsPoint, MontgomeryPoint, Scalar}; use elliptic_curve::group::GroupEncoding; use elliptic_curve::{Field, Group}; use hash2curve::GroupDigest; @@ -14,7 +12,7 @@ pub fn ed448(c: &mut Criterion) { b.iter_batched( || { let point = EdwardsPoint::try_from_rng(&mut OsRng).unwrap(); - let scalar = EdwardsScalar::try_from_rng(&mut OsRng).unwrap(); + let scalar = Scalar::try_from_rng(&mut OsRng).unwrap(); (point, scalar) }, |(point, scalar)| point * scalar, @@ -72,7 +70,7 @@ pub fn decaf448(c: &mut Criterion) { b.iter_batched( || { let point = DecafPoint::try_from_rng(&mut OsRng).unwrap(); - let scalar = DecafScalar::try_from_rng(&mut OsRng).unwrap(); + let scalar = Scalar::try_from_rng(&mut OsRng).unwrap(); (point, scalar) }, |(point, scalar)| point * scalar, @@ -131,7 +129,7 @@ pub fn x448(c: &mut Criterion) { || { let mut point = MontgomeryPoint::default(); OsRng.try_fill_bytes(&mut point.0).unwrap(); - let scalar = EdwardsScalar::try_from_rng(&mut OsRng).unwrap(); + let scalar = Scalar::try_from_rng(&mut OsRng).unwrap(); (point, scalar) }, |(point, scalar)| &point * &scalar, diff --git a/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs b/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs index 8379bcbe6..893aed11d 100644 --- a/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs +++ b/ed448-goldilocks/src/curve/scalar_mul/variable_base.rs @@ -1,11 +1,11 @@ #![allow(non_snake_case)] use super::window::wnaf::LookupTable; -use crate::EdwardsScalar; +use crate::Scalar; use crate::curve::twedwards::{extended::ExtendedPoint, extensible::ExtensiblePoint}; use subtle::{Choice, ConditionallyNegatable}; -pub fn variable_base(point: &ExtendedPoint, s: &EdwardsScalar) -> ExtensiblePoint { +pub fn variable_base(point: &ExtendedPoint, s: &Scalar) -> ExtensiblePoint { let mut result = ExtensiblePoint::IDENTITY; // Recode Scalar @@ -40,12 +40,13 @@ mod test { use crate::TWISTED_EDWARDS_BASE_POINT; use crate::curve::scalar_mul::double_and_add; use elliptic_curve::bigint::U448; + use elliptic_curve::scalar::FromUintUnchecked; #[test] fn test_scalar_mul() { // XXX: In the future use known multiples from Sage in bytes form? let twisted_point = TWISTED_EDWARDS_BASE_POINT; - let scalar = EdwardsScalar::new(U448::from_be_hex( + let scalar = Scalar::from_uint_unchecked(U448::from_be_hex( "05ca185aee2e1b73def437f63c003777083f83043fe5bf1aab454c66b64629d1de8026c1307f665ead0b70151533427ce128ae786ee372b7", )); @@ -66,11 +67,11 @@ mod test { let x = TWISTED_EDWARDS_BASE_POINT; // Test that 1 * P = P - let exp = variable_base(&x, &EdwardsScalar::from(1u8)); + let exp = variable_base(&x, &Scalar::from(1u8)); assert!(x == exp); // Test that 2 * (P + P) = 4 * P let expected_two_x = x.add_extended(&x).double(); - let got = variable_base(&x, &EdwardsScalar::from(4u8)); + let got = variable_base(&x, &Scalar::from(4u8)); assert!(expected_two_x == got); } } diff --git a/ed448-goldilocks/src/decaf.rs b/ed448-goldilocks/src/decaf.rs index d907c60e5..302dfd700 100644 --- a/ed448-goldilocks/src/decaf.rs +++ b/ed448-goldilocks/src/decaf.rs @@ -4,11 +4,9 @@ pub mod affine; mod ops; pub mod points; -mod scalar; pub use affine::AffinePoint; pub use points::{CompressedDecaf, DecafPoint}; -pub use scalar::{DecafScalar, DecafScalarBytes, WideDecafScalarBytes}; // https://www.rfc-editor.org/rfc/rfc9496#name-group-elements-from-uniform- #[test] diff --git a/ed448-goldilocks/src/decaf/affine.rs b/ed448-goldilocks/src/decaf/affine.rs index d071cd999..788ca8bd7 100644 --- a/ed448-goldilocks/src/decaf/affine.rs +++ b/ed448-goldilocks/src/decaf/affine.rs @@ -1,7 +1,8 @@ use crate::curve::twedwards::affine::AffinePoint as InnerAffinePoint; use crate::field::FieldElement; -use crate::{Decaf448FieldBytes, DecafPoint, DecafScalar, ORDER}; +use crate::{Decaf448FieldBytes, DecafPoint, ORDER, Scalar}; use core::ops::Mul; +use elliptic_curve::scalar::FromUintUnchecked; use elliptic_curve::{ Error, point::{AffineCoordinates, NonIdentity}, @@ -47,7 +48,8 @@ impl AffineCoordinates for AffinePoint { CtOption::new( point, - point.0.is_on_curve() & (point * DecafScalar::new(*ORDER)).ct_eq(&DecafPoint::IDENTITY), + point.0.is_on_curve() + & (point * Scalar::from_uint_unchecked(*ORDER)).ct_eq(&DecafPoint::IDENTITY), ) } @@ -105,13 +107,13 @@ impl From> for AffinePoint { } } -impl Mul<&DecafScalar> for &AffinePoint { +impl Mul<&Scalar> for &AffinePoint { type Output = DecafPoint; #[inline] - fn mul(self, scalar: &DecafScalar) -> DecafPoint { + fn mul(self, scalar: &Scalar) -> DecafPoint { self.to_decaf() * scalar } } -define_mul_variants!(LHS = AffinePoint, RHS = DecafScalar, Output = DecafPoint); +define_mul_variants!(LHS = AffinePoint, RHS = Scalar, Output = DecafPoint); diff --git a/ed448-goldilocks/src/decaf/ops.rs b/ed448-goldilocks/src/decaf/ops.rs index 1ba168537..bb01f03d5 100644 --- a/ed448-goldilocks/src/decaf/ops.rs +++ b/ed448-goldilocks/src/decaf/ops.rs @@ -1,32 +1,34 @@ -use crate::{DecafAffinePoint, DecafScalar, curve::scalar_mul::double_and_add}; +use crate::{Decaf448, DecafAffinePoint, Scalar, curve::scalar_mul::double_and_add}; use core::{ borrow::Borrow, iter::Sum, ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; -use elliptic_curve::CurveGroup; +use elliptic_curve::{CurveGroup, scalar::FromUintUnchecked}; use super::DecafPoint; +elliptic_curve::scalar_impls!(Decaf448, Scalar); + /// Scalar Mul Operations -impl Mul<&DecafScalar> for &DecafPoint { +impl Mul<&Scalar> for &DecafPoint { type Output = DecafPoint; - fn mul(self, scalar: &DecafScalar) -> DecafPoint { + fn mul(self, scalar: &Scalar) -> DecafPoint { // XXX: We can do better than double and add DecafPoint(double_and_add(&self.0, scalar.bits()).to_extended()) } } -define_mul_variants!(LHS = DecafPoint, RHS = DecafScalar, Output = DecafPoint); +define_mul_variants!(LHS = DecafPoint, RHS = Scalar, Output = DecafPoint); -impl<'s> MulAssign<&'s DecafScalar> for DecafPoint { - fn mul_assign(&mut self, scalar: &'s DecafScalar) { +impl<'s> MulAssign<&'s Scalar> for DecafPoint { + fn mul_assign(&mut self, scalar: &'s Scalar) { *self = *self * scalar; } } -impl MulAssign for DecafPoint { - fn mul_assign(&mut self, scalar: DecafScalar) { +impl MulAssign for DecafPoint { + fn mul_assign(&mut self, scalar: Scalar) { *self = *self * scalar; } } diff --git a/ed448-goldilocks/src/decaf/points.rs b/ed448-goldilocks/src/decaf/points.rs index a090cb30a..06c4ac07c 100644 --- a/ed448-goldilocks/src/decaf/points.rs +++ b/ed448-goldilocks/src/decaf/points.rs @@ -167,7 +167,7 @@ impl TryFrom<&DecafPointBytes> for DecafPoint { } impl Group for DecafPoint { - type Scalar = DecafScalar; + type Scalar = Scalar; fn try_from_rng(rng: &mut R) -> Result where @@ -239,9 +239,9 @@ impl CofactorGroup for DecafPoint { impl PrimeGroup for DecafPoint {} -impl LinearCombination<[(DecafPoint, DecafScalar); N]> for DecafPoint {} +impl LinearCombination<[(DecafPoint, Scalar); N]> for DecafPoint {} -impl LinearCombination<[(DecafPoint, DecafScalar)]> for DecafPoint {} +impl LinearCombination<[(DecafPoint, Scalar)]> for DecafPoint {} impl CurveGroup for DecafPoint { type AffineRepr = DecafAffinePoint; @@ -599,7 +599,10 @@ impl From> for DecafPoint { mod test { use super::*; use crate::TWISTED_EDWARDS_BASE_POINT; + use elliptic_curve::PrimeField; + use elliptic_curve::consts::U64; use hash2curve::ExpandMsgXof; + use hex_literal::hex; use sha3::Shake256; #[test] @@ -761,6 +764,72 @@ mod test { assert_ne!(point, DecafPoint::GENERATOR); } + #[test] + fn scalar_hash() { + let msg = b"hello world"; + let dst = b"decaf448_XOF:SHAKE256_D448MAP_RO_"; + let res = + hash2curve::hash_to_scalar::, U64>(&[msg], &[dst]) + .unwrap(); + let expected: [u8; 56] = hex_literal::hex!( + "55e7b59aa035db959409c6b69b817a18c8133d9ad06687665f5720672924da0a84eab7fee415ef13e7aaebdd227291ee8e156f32c507ad2e" + ); + assert_eq!(res.to_repr(), Array::from(expected)); + } + + /// Taken from . + #[test] + fn hash_to_scalar_voprf() { + struct TestVector { + dst: &'static [u8], + sk_sm: &'static [u8], + } + + const KEY_INFO: &[u8] = b"test key"; + const SEED: &[u8] = + &hex!("a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3"); + + const TEST_VECTORS: &[TestVector] = &[ + TestVector { + dst: b"DeriveKeyPairOPRFV1-\x00-decaf448-SHAKE256", + sk_sm: &hex!( + "e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29ecc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e" + ), + }, + TestVector { + dst: b"DeriveKeyPairOPRFV1-\x01-decaf448-SHAKE256", + sk_sm: &hex!( + "e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104fcc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e" + ), + }, + TestVector { + dst: b"DeriveKeyPairOPRFV1-\x02-decaf448-SHAKE256", + sk_sm: &hex!( + "792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22de402990eb143fd7c67ac66be75e0609705ecea800992aac8e19" + ), + }, + ]; + + let key_info_len = u16::try_from(KEY_INFO.len()).unwrap().to_be_bytes(); + + 'outer: for test_vector in TEST_VECTORS { + for counter in 0_u8..=u8::MAX { + let scalar = hash2curve::hash_to_scalar::, U64>( + &[SEED, &key_info_len, KEY_INFO, &counter.to_be_bytes()], + &[test_vector.dst], + ) + .unwrap(); + + if !bool::from(scalar.is_zero()) { + assert_eq!(scalar.to_bytes().as_slice(), test_vector.sk_sm); + continue 'outer; + } + } + + panic!("deriving key failed"); + } + } + // TODO: uncomment once elliptic-curve-tools is updated to match elliptic-curve 0.14 // #[test] // fn test_sum_of_products() { use elliptic_curve_tools::SumOfProducts; let values = [ (Scalar::from(8u8), DecafPoint::GENERATOR), (Scalar::from(9u8), DecafPoint::GENERATOR), (Scalar::from(10u8), DecafPoint::GENERATOR), (Scalar::from(11u8), DecafPoint::GENERATOR), (Scalar::from(12u8), DecafPoint::GENERATOR), ]; let expected = DecafPoint::GENERATOR * Scalar::from(50u8); let result = DecafPoint::sum_of_products(&values); assert_eq!(result, expected); } diff --git a/ed448-goldilocks/src/decaf/scalar.rs b/ed448-goldilocks/src/decaf/scalar.rs deleted file mode 100644 index 95eed5bfb..000000000 --- a/ed448-goldilocks/src/decaf/scalar.rs +++ /dev/null @@ -1,348 +0,0 @@ -use crate::Decaf448; -use crate::field::{CurveWithScalar, ORDER, Scalar, ScalarBytes, WideScalarBytes}; - -use elliptic_curve::array::Array; -use elliptic_curve::bigint::{Limb, NonZero, U448, U512}; -use elliptic_curve::consts::{U56, U64}; -use elliptic_curve::ops::Reduce; -use elliptic_curve::scalar::FromUintUnchecked; -use subtle::{Choice, ConstantTimeEq, CtOption}; - -impl CurveWithScalar for Decaf448 { - type ReprSize = U56; - - fn from_bytes_mod_order_wide(input: &WideScalarBytes) -> Scalar { - let value = ( - U448::from_le_slice(&input[..56]), - U448::from_le_slice(&input[56..112]), - ); - Scalar::new(U448::rem_wide_vartime(value, ORDER.as_nz_ref())) - } - - fn from_canonical_bytes(bytes: &ScalarBytes) -> subtle::CtOption> { - // Check that the 10 high bits are not set - let is_valid = (bytes[55] >> 6).ct_eq(&0); - let bytes: [u8; 56] = core::array::from_fn(|i| bytes[i]); - let candidate = Scalar::new(U448::from_le_slice(&bytes)); - - // underflow means candidate < ORDER, thus canonical - let (_, underflow) = candidate.scalar.borrowing_sub(&ORDER, Limb::ZERO); - let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); - CtOption::new(candidate, underflow & is_valid) - } - - fn to_repr(scalar: &Scalar) -> ScalarBytes { - scalar.to_bytes().into() - } -} - -/// [`Decaf448`] scalar field. -pub type DecafScalar = Scalar; - -impl DecafScalar { - /// Construct a `Scalar` by reducing a 896-bit little-endian integer - /// modulo the group order ℓ. - pub fn from_bytes_mod_order_wide(input: &WideDecafScalarBytes) -> DecafScalar { - Decaf448::from_bytes_mod_order_wide(input) - } -} - -elliptic_curve::scalar_impls!(Decaf448, DecafScalar); - -/// The number of bytes needed to represent the scalar field -pub type DecafScalarBytes = ScalarBytes; -/// The number of bytes needed to represent the safely create a scalar from a random bytes -pub type WideDecafScalarBytes = WideScalarBytes; - -#[cfg(feature = "bits")] -impl From<&DecafScalar> for elliptic_curve::scalar::ScalarBits { - fn from(scalar: &DecafScalar) -> Self { - scalar.scalar.to_words().into() - } -} - -impl Reduce> for DecafScalar { - fn reduce(value: &Array) -> Self { - const SEMI_WIDE_MODULUS: NonZero = NonZero::::new_unwrap(U512::from_be_hex( - "00000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", - )); - - let mut num = U512::from_le_slice(value); - num %= SEMI_WIDE_MODULUS; - let mut words = [0; U448::LIMBS]; - words.copy_from_slice(&num.to_words()[..U448::LIMBS]); - Scalar::new(U448::from_words(words)) - } -} - -#[cfg(test)] -mod test { - use super::*; - use elliptic_curve::PrimeField; - use elliptic_curve::array::Array; - use hash2curve::ExpandMsgXof; - use hex_literal::hex; - use proptest::property_test; - use sha3::Shake256; - - #[test] - fn test_basic_add() { - let five = DecafScalar::from(5u8); - let six = DecafScalar::from(6u8); - - assert_eq!(five + six, DecafScalar::from(11u8)) - } - - #[test] - fn test_basic_sub() { - let ten = DecafScalar::from(10u8); - let five = DecafScalar::from(5u8); - assert_eq!(ten - five, DecafScalar::from(5u8)) - } - - #[test] - fn test_basic_mul() { - let ten = DecafScalar::from(10u8); - let five = DecafScalar::from(5u8); - - assert_eq!(ten * five, DecafScalar::from(50u8)) - } - - #[test] - fn test_mul() { - let a = DecafScalar::new(U448::from_be_hex( - "1e63e8073b089f0747cf8cac2c3dc2732aae8688a8fa552ba8cb0ae8c0be082e74d657641d9ac30a087b8fb97f8ed27dc96a3c35ffb823a3", - )); - - let b = DecafScalar::new(U448::from_be_hex( - "16c5450acae1cb680a92de2d8e59b30824e8d4991adaa0e7bc343bcbd099595b188c6b1a1e30b38b17aa6d9be416b899686eb329d8bedc42", - )); - - let exp = DecafScalar::new(U448::from_be_hex( - "31e055c14ca389edfccd61b3203d424bb9036ff6f2d89c1e07bcd93174e9335f36a1492008a3a0e46abd26f5994c9c2b1f5b3197a18d010a", - )); - - assert_eq!(a * b, exp) - } - #[test] - fn test_basic_square() { - let a = DecafScalar::new(U448::from_be_hex( - "3162081604b3273b930392e5d2391f9d21cc3078f22c69514bb395e08dccc4866f08f3311370f8b83fa50692f640922b7e56a34bcf5fac3d", - )); - let expected_a_squared = DecafScalar::new(U448::from_be_hex( - "1c1e32fc66b21c9c42d6e8e20487193cf6d49916421b290098f30de3713006cfe8ee9d21eeef7427f82a1fe036630c74b9acc2c2ede40f04", - )); - - assert_eq!(a.square(), expected_a_squared) - } - - #[test] - fn test_sanity_check_index_mut() { - let mut x = DecafScalar::ONE; - x[0] = 2; - assert_eq!(x, DecafScalar::from(2u8)) - } - #[test] - fn test_basic_halving() { - let eight = DecafScalar::from(8u8); - let four = DecafScalar::from(4u8); - let two = DecafScalar::from(2u8); - assert_eq!(eight.div_by_2(), four); - assert_eq!(four.div_by_2(), two); - assert_eq!(two.div_by_2(), DecafScalar::ONE); - } - - #[test] - fn test_equals() { - let a = DecafScalar::from(5u8); - let b = DecafScalar::from(5u8); - let c = DecafScalar::from(10u8); - assert_eq!(a, b); - assert_ne!(a, c); - } - - #[test] - fn test_basic_inversion() { - // Test inversion from 2 to 100 - for i in 1..=100u8 { - let x = DecafScalar::from(i); - let x_inv = x.invert(); - assert_eq!(x_inv * x, DecafScalar::ONE) - } - - // Inversion of zero is zero - let zero = DecafScalar::ZERO; - let expected_zero = zero.invert(); - assert_eq!(expected_zero, zero) - } - #[test] - fn test_serialise() { - let scalar = DecafScalar::new(U448::from_be_hex( - "0d79f6e375d3395ed9a6c4c3c49a1433fd7c58aa38363f74e9ab2c22a22347d79988f8e01e8a309f862a9f1052fcd042b9b1ed7115598f62", - )); - let got = DecafScalar::from_canonical_bytes(&scalar.into()).unwrap(); - assert_eq!(scalar, got) - } - #[test] - fn test_from_canonical_bytes() { - // ff..ff should fail - let mut bytes = DecafScalarBytes::from(hex!( - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - )); - bytes.reverse(); - let s = DecafScalar::from_canonical_bytes(&bytes); - assert!(>::into(s.is_none())); - - // n should fail - let mut bytes = DecafScalarBytes::from(hex!( - "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3" - )); - bytes.reverse(); - let s = DecafScalar::from_canonical_bytes(&bytes); - assert!(>::into(s.is_none())); - - // n-1 should work - let mut bytes = DecafScalarBytes::from(hex!( - "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" - )); - bytes.reverse(); - let s = DecafScalar::from_canonical_bytes(&bytes); - match Option::::from(s) { - Some(s) => assert_eq!(s, DecafScalar::ZERO - DecafScalar::ONE), - None => panic!("should not return None"), - }; - } - - #[test] - fn test_from_bytes_mod_order_wide() { - // n should become 0 - let mut bytes = WideDecafScalarBytes::from(hex!( - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3" - )); - bytes.reverse(); - let s = DecafScalar::from_bytes_mod_order_wide(&bytes); - assert_eq!(s, DecafScalar::ZERO); - - // n-1 should stay the same - let mut bytes = WideDecafScalarBytes::from(hex!( - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" - )); - bytes.reverse(); - let s = DecafScalar::from_bytes_mod_order_wide(&bytes); - assert_eq!(s, DecafScalar::ZERO - DecafScalar::ONE); - - // n+1 should become 1 - let mut bytes = WideDecafScalarBytes::from(hex!( - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f4" - )); - bytes.reverse(); - let s = DecafScalar::from_bytes_mod_order_wide(&bytes); - assert_eq!(s, DecafScalar::ONE); - - // 2^896-1 should become 0x3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b5f - let bytes = WideDecafScalarBytes::from(hex!( - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - )); - let s = DecafScalar::from_bytes_mod_order_wide(&bytes); - let mut bytes = DecafScalarBytes::from(hex!( - "3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b5f" - )); - bytes.reverse(); - let reduced = DecafScalar::from_canonical_bytes(&bytes).unwrap(); - assert_eq!(s, reduced); - } - - #[cfg(all(feature = "alloc", feature = "serde"))] - #[test] - fn serde() { - use elliptic_curve::PrimeField; - - let res = serde_json::to_string(&DecafScalar::TWO_INV); - assert!(res.is_ok()); - let sj = res.unwrap(); - - let res = serde_json::from_str::(&sj); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), DecafScalar::TWO_INV); - - let res = serde_bare::to_vec(&DecafScalar::TWO_INV); - assert!(res.is_ok()); - let sb = res.unwrap(); - assert_eq!(sb.len(), 57); - - let res = serde_bare::from_slice::(&sb); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), DecafScalar::TWO_INV); - } - - #[test] - fn scalar_hash() { - let msg = b"hello world"; - let dst = b"decaf448_XOF:SHAKE256_D448MAP_RO_"; - let res = - hash2curve::hash_to_scalar::, U64>(&[msg], &[dst]) - .unwrap(); - let expected: [u8; 56] = hex_literal::hex!( - "55e7b59aa035db959409c6b69b817a18c8133d9ad06687665f5720672924da0a84eab7fee415ef13e7aaebdd227291ee8e156f32c507ad2e" - ); - assert_eq!(res.to_repr(), Array::from(expected)); - } - - /// Taken from . - #[test] - fn hash_to_scalar_voprf() { - struct TestVector { - dst: &'static [u8], - sk_sm: &'static [u8], - } - - const KEY_INFO: &[u8] = b"test key"; - const SEED: &[u8] = - &hex!("a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3"); - - const TEST_VECTORS: &[TestVector] = &[ - TestVector { - dst: b"DeriveKeyPairOPRFV1-\x00-decaf448-SHAKE256", - sk_sm: &hex!( - "e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29ecc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e" - ), - }, - TestVector { - dst: b"DeriveKeyPairOPRFV1-\x01-decaf448-SHAKE256", - sk_sm: &hex!( - "e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104fcc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e" - ), - }, - TestVector { - dst: b"DeriveKeyPairOPRFV1-\x02-decaf448-SHAKE256", - sk_sm: &hex!( - "792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22de402990eb143fd7c67ac66be75e0609705ecea800992aac8e19" - ), - }, - ]; - - let key_info_len = u16::try_from(KEY_INFO.len()).unwrap().to_be_bytes(); - - 'outer: for test_vector in TEST_VECTORS { - for counter in 0_u8..=u8::MAX { - let scalar = hash2curve::hash_to_scalar::, U64>( - &[SEED, &key_info_len, KEY_INFO, &counter.to_be_bytes()], - &[test_vector.dst], - ) - .unwrap(); - - if !bool::from(scalar.is_zero()) { - assert_eq!(scalar.to_bytes().as_slice(), test_vector.sk_sm); - continue 'outer; - } - } - - panic!("deriving key failed"); - } - } - - #[property_test] - fn from_canonical_bytes(bytes: [u8; 56]) { - DecafScalar::from_canonical_bytes(&bytes.into()); - } -} diff --git a/ed448-goldilocks/src/edwards.rs b/ed448-goldilocks/src/edwards.rs index 5d9e0fe11..d04093c78 100644 --- a/ed448-goldilocks/src/edwards.rs +++ b/ed448-goldilocks/src/edwards.rs @@ -11,7 +11,5 @@ /// If this is a problem, one can use a different isogeny strategy (Decaf) pub(crate) mod affine; pub(crate) mod extended; -mod scalar; pub use affine::{AffinePoint, CompressedEdwardsY}; pub use extended::EdwardsPoint; -pub use scalar::{EdwardsScalar, EdwardsScalarBytes, WideEdwardsScalarBytes}; diff --git a/ed448-goldilocks/src/edwards/affine.rs b/ed448-goldilocks/src/edwards/affine.rs index 9aadffdc4..952776c0a 100644 --- a/ed448-goldilocks/src/edwards/affine.rs +++ b/ed448-goldilocks/src/edwards/affine.rs @@ -47,19 +47,19 @@ impl elliptic_curve::point::AffineCoordinates for AffinePoint { fn from_coordinates(x: &Self::FieldRepr, y: &Self::FieldRepr) -> CtOption { let point = Self { - x: FieldElement::from_bytes_extended(&x.0), - y: FieldElement::from_bytes_extended(&y.0), + x: FieldElement::from_bytes(&x.0), + y: FieldElement::from_bytes(&y.0), }; CtOption::new(point, point.is_on_curve()) } fn x(&self) -> Self::FieldRepr { - Ed448FieldBytes::from(self.x.to_bytes_extended()) + Ed448FieldBytes::from(self.x.to_bytes()) } fn y(&self) -> Self::FieldRepr { - Ed448FieldBytes::from(self.y.to_bytes_extended()) + Ed448FieldBytes::from(self.y.to_bytes()) } fn x_is_odd(&self) -> Choice { @@ -188,20 +188,16 @@ impl TryFrom for NonIdentity { } } -impl Mul<&EdwardsScalar> for &AffinePoint { +impl Mul<&Scalar> for &AffinePoint { type Output = EdwardsPoint; #[inline] - fn mul(self, scalar: &EdwardsScalar) -> Self::Output { + fn mul(self, scalar: &Scalar) -> Self::Output { self.to_edwards() * scalar } } -define_mul_variants!( - LHS = AffinePoint, - RHS = EdwardsScalar, - Output = EdwardsPoint -); +define_mul_variants!(LHS = AffinePoint, RHS = Scalar, Output = EdwardsPoint); /// The compressed internal representation of a point on the Twisted Edwards Curve pub type PointBytes = [u8; 57]; diff --git a/ed448-goldilocks/src/edwards/extended.rs b/ed448-goldilocks/src/edwards/extended.rs index c376cbe47..c9f3fc1ab 100644 --- a/ed448-goldilocks/src/edwards/extended.rs +++ b/ed448-goldilocks/src/edwards/extended.rs @@ -15,6 +15,7 @@ use elliptic_curve::{ group::{Group, GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup}, ops::{BatchInvert, LinearCombination}, point::NonIdentity, + scalar::FromUintUnchecked, }; use rand_core::TryRngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -100,7 +101,7 @@ impl PartialEq for EdwardsPoint { impl Eq for EdwardsPoint {} impl Group for EdwardsPoint { - type Scalar = EdwardsScalar; + type Scalar = Scalar; fn try_from_rng(rng: &mut R) -> Result where @@ -280,9 +281,9 @@ impl From<&EdwardsPoint> for AffinePoint { } } -impl LinearCombination<[(EdwardsPoint, EdwardsScalar); N]> for EdwardsPoint {} +impl LinearCombination<[(EdwardsPoint, Scalar); N]> for EdwardsPoint {} -impl LinearCombination<[(EdwardsPoint, EdwardsScalar)]> for EdwardsPoint {} +impl LinearCombination<[(EdwardsPoint, Scalar)]> for EdwardsPoint {} impl CurveGroup for EdwardsPoint { type AffineRepr = AffinePoint; @@ -326,7 +327,7 @@ impl EdwardsPoint { } /// Generic scalar multiplication to compute s*P - pub fn scalar_mul(&self, scalar: &EdwardsScalar) -> Self { + pub fn scalar_mul(&self, scalar: &Scalar) -> Self { // Compute floor(s/4) let scalar_div_four = scalar.div_by_2().div_by_2(); @@ -515,6 +516,8 @@ impl TryFrom for NonIdentity { // Addition and Subtraction // ------------------------------------------------------------------------ +elliptic_curve::scalar_impls!(Ed448, Scalar); + impl Add<&EdwardsPoint> for &EdwardsPoint { type Output = EdwardsPoint; @@ -667,26 +670,22 @@ impl Neg for EdwardsPoint { // Scalar multiplication // ------------------------------------------------------------------------ -impl<'b> MulAssign<&'b EdwardsScalar> for EdwardsPoint { - fn mul_assign(&mut self, scalar: &'b EdwardsScalar) { +impl<'b> MulAssign<&'b Scalar> for EdwardsPoint { + fn mul_assign(&mut self, scalar: &'b Scalar) { let result = *self * scalar; *self = result; } } -define_mul_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsScalar); +define_mul_assign_variants!(LHS = EdwardsPoint, RHS = Scalar); -define_mul_variants!( - LHS = EdwardsPoint, - RHS = EdwardsScalar, - Output = EdwardsPoint -); +define_mul_variants!(LHS = EdwardsPoint, RHS = Scalar, Output = EdwardsPoint); -impl Mul<&EdwardsScalar> for &EdwardsPoint { +impl Mul<&Scalar> for &EdwardsPoint { type Output = EdwardsPoint; /// Scalar multiplication: compute `scalar * self`. - fn mul(self, scalar: &EdwardsScalar) -> EdwardsPoint { + fn mul(self, scalar: &Scalar) -> EdwardsPoint { self.scalar_mul(scalar) } } @@ -782,7 +781,7 @@ where #[cfg(test)] mod tests { use super::*; - use elliptic_curve::Field; + use elliptic_curve::{Field, PrimeField, consts::U84}; use hex_literal::hex; use proptest::{prop_assert_eq, property_test}; use rand_core::{OsRng, TryRngCore}; @@ -1003,6 +1002,18 @@ mod tests { } } + #[test] + fn scalar_hash() { + let msg = b"hello world"; + let dst = b"edwards448_XOF:SHAKE256_ELL2_RO_"; + let res = hash2curve::hash_to_scalar::, U84>(&[msg], &[dst]) + .unwrap(); + let expected: [u8; 56] = hex_literal::hex!( + "2d32a08f09b88275cc5f437e625696b18de718ed94559e17e4d64aafd143a8527705132178b5ce7395ea6214735387398a35913656b49513" + ); + assert_eq!(res.to_repr(), Array::from(expected)); + } + // TODO: uncomment once elliptic-curve-tools is updated to match elliptic-curve 0.14 // #[test] // fn test_sum_of_products() { @@ -1058,27 +1069,27 @@ mod tests { use rand_core::SeedableRng; let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0); - let x = EdwardsScalar::random(&mut rng); - let b = EdwardsScalar::random(&mut rng); + let x = Scalar::random(&mut rng); + let b = Scalar::random(&mut rng); let g1 = EdwardsPoint::GENERATOR; let g2 = Ed448::hash_from_bytes(b"test_pow_add_mul", b"test DST").unwrap(); let expected_commitment = g1 * x + g2 * b; - let shift = EdwardsScalar::from(256u16); - let x_bytes = x.to_bytes_rfc_8032(); - let mut sum = EdwardsScalar::ZERO; - let mut components = [EdwardsPoint::IDENTITY; 57]; - for i in 1..57 { - let r = EdwardsScalar::random(&mut rng); + let shift = Scalar::from(256u16); + let x_bytes = x.to_repr(); + let mut sum = Scalar::ZERO; + let mut components = [EdwardsPoint::IDENTITY; 56]; + for i in 1..56 { + let r = Scalar::random(&mut rng); sum += r * shift.pow([i as u64]); - components[i] = g1 * EdwardsScalar::from(x_bytes[i]) + g2 * r; + components[i] = g1 * Scalar::from(x_bytes[i]) + g2 * r; } - components[0] = g1 * EdwardsScalar::from(x_bytes[0]) + g2 * (b - sum); + components[0] = g1 * Scalar::from(x_bytes[0]) + g2 * (b - sum); let mut computed_commitment = EdwardsPoint::IDENTITY; - for i in (0..57).rev() { + for i in (0..56).rev() { computed_commitment *= shift; computed_commitment += components[i]; } @@ -1116,8 +1127,8 @@ mod tests { } #[property_test] - fn fuzz_is_torsion_free(bytes: [u8; 57]) { - let scalar = EdwardsScalar::from_bytes_mod_order(&bytes.into()); + fn fuzz_is_torsion_free(bytes: [u8; 56]) { + let scalar = Scalar::from_bytes_mod_order(&bytes.into()); let mut point = EdwardsPoint::mul_by_generator(&scalar); prop_assert_eq!(point.is_torsion_free().unwrap_u8(), 1); diff --git a/ed448-goldilocks/src/edwards/scalar.rs b/ed448-goldilocks/src/edwards/scalar.rs deleted file mode 100644 index 2a1fd5a8c..000000000 --- a/ed448-goldilocks/src/edwards/scalar.rs +++ /dev/null @@ -1,323 +0,0 @@ -use crate::Ed448; -use crate::field::{CurveWithScalar, ORDER, Scalar, ScalarBytes, WideScalarBytes}; - -use elliptic_curve::array::Array; -use elliptic_curve::bigint::{Limb, NonZero, U448, U704}; -use elliptic_curve::consts::{U57, U84, U88}; -use elliptic_curve::ops::Reduce; -use elliptic_curve::scalar::FromUintUnchecked; -use subtle::{Choice, ConstantTimeEq, CtOption}; - -impl CurveWithScalar for Ed448 { - type ReprSize = U57; - - fn from_bytes_mod_order_wide(input: &WideScalarBytes) -> Scalar { - // top multiplier = 2^896 mod ℓ - const TOP_MULTIPLIER: U448 = U448::from_be_hex( - "3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b60", - ); - let value = ( - U448::from_le_slice(&input[..56]), - U448::from_le_slice(&input[56..112]), - ); - let mut top = [0u8; 56]; - top[..2].copy_from_slice(&input[112..]); - let top = U448::from_le_slice(&top).mul_mod(&TOP_MULTIPLIER, ORDER.as_nz_ref()); - let bottom = U448::rem_wide_vartime(value, ORDER.as_nz_ref()); - Scalar::new(bottom.add_mod(&top, ORDER.as_nz_ref())) - } - - fn from_canonical_bytes(bytes: &ScalarBytes) -> subtle::CtOption> { - // Check that the 10 high bits are not set - let is_valid = bytes[56].ct_eq(&0) | (bytes[55] >> 6).ct_eq(&0); - let bytes: [u8; 56] = core::array::from_fn(|i| bytes[i]); - let candidate = Scalar::new(U448::from_le_slice(&bytes)); - - // underflow means candidate < ORDER, thus canonical - let (_, underflow) = candidate.scalar.borrowing_sub(&ORDER, Limb::ZERO); - let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); - CtOption::new(candidate, underflow & is_valid) - } - - fn to_repr(scalar: &Scalar) -> ScalarBytes { - scalar.to_bytes_rfc_8032() - } -} - -/// [`Ed448`] scalar field. -pub type EdwardsScalar = Scalar; - -impl EdwardsScalar { - /// Serialize the scalar into 57 bytes, per RFC 8032. - /// Byte 56 will always be zero. - pub fn to_bytes_rfc_8032(&self) -> EdwardsScalarBytes { - let mut bytes = EdwardsScalarBytes::default(); - bytes[..56].copy_from_slice(&self.to_bytes()); - bytes - } - - /// Construct a `Scalar` by reducing a 912-bit little-endian integer - /// modulo the group order ℓ. - pub fn from_bytes_mod_order_wide(input: &WideEdwardsScalarBytes) -> EdwardsScalar { - Ed448::from_bytes_mod_order_wide(input) - } -} - -elliptic_curve::scalar_impls!(Ed448, EdwardsScalar); - -/// The number of bytes needed to represent the scalar field -pub type EdwardsScalarBytes = ScalarBytes; -/// The number of bytes needed to represent the safely create a scalar from a random bytes -pub type WideEdwardsScalarBytes = WideScalarBytes; - -#[cfg(feature = "bits")] -impl From<&EdwardsScalar> for elliptic_curve::scalar::ScalarBits { - fn from(scalar: &EdwardsScalar) -> Self { - scalar.scalar.to_words().into() - } -} - -impl Reduce> for EdwardsScalar { - fn reduce(value: &Array) -> Self { - const SEMI_WIDE_MODULUS: NonZero = NonZero::::new_unwrap(U704::from_be_hex( - "00000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", - )); - let mut tmp = Array::::default(); - tmp[4..].copy_from_slice(&value[..]); - - let mut num = U704::from_be_slice(&tmp[..]); - num %= SEMI_WIDE_MODULUS; - let mut words = [0; U448::LIMBS]; - words.copy_from_slice(&num.to_words()[..U448::LIMBS]); - Scalar::new(U448::from_words(words)) - } -} - -#[cfg(test)] -mod test { - use super::*; - use elliptic_curve::array::Array; - use hash2curve::ExpandMsgXof; - use hex_literal::hex; - use proptest::property_test; - use sha3::Shake256; - - #[test] - fn test_basic_add() { - let five = EdwardsScalar::from(5u8); - let six = EdwardsScalar::from(6u8); - - assert_eq!(five + six, EdwardsScalar::from(11u8)) - } - - #[test] - fn test_basic_sub() { - let ten = EdwardsScalar::from(10u8); - let five = EdwardsScalar::from(5u8); - assert_eq!(ten - five, EdwardsScalar::from(5u8)) - } - - #[test] - fn test_basic_mul() { - let ten = EdwardsScalar::from(10u8); - let five = EdwardsScalar::from(5u8); - - assert_eq!(ten * five, EdwardsScalar::from(50u8)) - } - - #[test] - fn test_mul() { - let a = EdwardsScalar::new(U448::from_be_hex( - "1e63e8073b089f0747cf8cac2c3dc2732aae8688a8fa552ba8cb0ae8c0be082e74d657641d9ac30a087b8fb97f8ed27dc96a3c35ffb823a3", - )); - - let b = EdwardsScalar::new(U448::from_be_hex( - "16c5450acae1cb680a92de2d8e59b30824e8d4991adaa0e7bc343bcbd099595b188c6b1a1e30b38b17aa6d9be416b899686eb329d8bedc42", - )); - - let exp = EdwardsScalar::new(U448::from_be_hex( - "31e055c14ca389edfccd61b3203d424bb9036ff6f2d89c1e07bcd93174e9335f36a1492008a3a0e46abd26f5994c9c2b1f5b3197a18d010a", - )); - - assert_eq!(a * b, exp) - } - #[test] - fn test_basic_square() { - let a = EdwardsScalar::new(U448::from_be_hex( - "3162081604b3273b930392e5d2391f9d21cc3078f22c69514bb395e08dccc4866f08f3311370f8b83fa50692f640922b7e56a34bcf5fac3d", - )); - let expected_a_squared = EdwardsScalar::new(U448::from_be_hex( - "1c1e32fc66b21c9c42d6e8e20487193cf6d49916421b290098f30de3713006cfe8ee9d21eeef7427f82a1fe036630c74b9acc2c2ede40f04", - )); - - assert_eq!(a.square(), expected_a_squared) - } - - #[test] - fn test_sanity_check_index_mut() { - let mut x = EdwardsScalar::ONE; - x[0] = 2; - assert_eq!(x, EdwardsScalar::from(2u8)) - } - #[test] - fn test_basic_halving() { - let eight = EdwardsScalar::from(8u8); - let four = EdwardsScalar::from(4u8); - let two = EdwardsScalar::from(2u8); - assert_eq!(eight.div_by_2(), four); - assert_eq!(four.div_by_2(), two); - assert_eq!(two.div_by_2(), EdwardsScalar::ONE); - } - - #[test] - fn test_equals() { - let a = EdwardsScalar::from(5u8); - let b = EdwardsScalar::from(5u8); - let c = EdwardsScalar::from(10u8); - assert_eq!(a, b); - assert_ne!(a, c); - } - - #[test] - fn test_basic_inversion() { - // Test inversion from 2 to 100 - for i in 1..=100u8 { - let x = EdwardsScalar::from(i); - let x_inv = x.invert(); - assert_eq!(x_inv * x, EdwardsScalar::ONE) - } - - // Inversion of zero is zero - let zero = EdwardsScalar::ZERO; - let expected_zero = zero.invert(); - assert_eq!(expected_zero, zero) - } - #[test] - fn test_serialise() { - let scalar = EdwardsScalar::new(U448::from_be_hex( - "0d79f6e375d3395ed9a6c4c3c49a1433fd7c58aa38363f74e9ab2c22a22347d79988f8e01e8a309f862a9f1052fcd042b9b1ed7115598f62", - )); - let got = EdwardsScalar::from_canonical_bytes(&scalar.into()).unwrap(); - assert_eq!(scalar, got) - } - #[test] - fn test_from_canonical_bytes() { - // ff..ff should fail - let mut bytes = EdwardsScalarBytes::from(hex!( - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - )); - bytes.reverse(); - let s = EdwardsScalar::from_canonical_bytes(&bytes); - assert!(>::into(s.is_none())); - - // n should fail - let mut bytes = EdwardsScalarBytes::from(hex!( - "003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3" - )); - bytes.reverse(); - let s = EdwardsScalar::from_canonical_bytes(&bytes); - assert!(>::into(s.is_none())); - - // n-1 should work - let mut bytes = EdwardsScalarBytes::from(hex!( - "003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" - )); - bytes.reverse(); - let s = EdwardsScalar::from_canonical_bytes(&bytes); - match Option::::from(s) { - Some(s) => assert_eq!(s, EdwardsScalar::ZERO - EdwardsScalar::ONE), - None => panic!("should not return None"), - }; - } - - #[test] - fn test_from_bytes_mod_order_wide() { - // n should become 0 - let mut bytes = WideEdwardsScalarBytes::from(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3" - )); - bytes.reverse(); - let s = EdwardsScalar::from_bytes_mod_order_wide(&bytes); - assert_eq!(s, EdwardsScalar::ZERO); - - // n-1 should stay the same - let mut bytes = WideEdwardsScalarBytes::from(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" - )); - bytes.reverse(); - let s = EdwardsScalar::from_bytes_mod_order_wide(&bytes); - assert_eq!(s, EdwardsScalar::ZERO - EdwardsScalar::ONE); - - // n+1 should become 1 - let mut bytes = WideEdwardsScalarBytes::from(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f4" - )); - bytes.reverse(); - let s = EdwardsScalar::from_bytes_mod_order_wide(&bytes); - assert_eq!(s, EdwardsScalar::ONE); - - // 2^912-1 should become 0x2939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf72c985bb24b6c520e319fb37a63e29800f160787ad1d2e11883fa931e7de81 - let bytes = WideEdwardsScalarBytes::from(hex!( - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - )); - let s = EdwardsScalar::from_bytes_mod_order_wide(&bytes); - let mut bytes = EdwardsScalarBytes::from(hex!( - "002939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf72c985bb24b6c520e319fb37a63e29800f160787ad1d2e11883fa931e7de81" - )); - bytes.reverse(); - let reduced = EdwardsScalar::from_canonical_bytes(&bytes).unwrap(); - assert_eq!(s, reduced); - } - - #[test] - fn test_to_bytes_rfc8032() { - // n-1 - let mut bytes: [u8; 57] = hex!( - "003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" - ); - bytes.reverse(); - let x = EdwardsScalar::ZERO - EdwardsScalar::ONE; - let candidate = x.to_bytes_rfc_8032(); - assert_eq!(&bytes[..], &candidate[..]); - } - - #[cfg(all(feature = "alloc", feature = "serde"))] - #[test] - fn serde() { - use elliptic_curve::PrimeField; - - let res = serde_json::to_string(&EdwardsScalar::TWO_INV); - assert!(res.is_ok()); - let sj = res.unwrap(); - - let res = serde_json::from_str::(&sj); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), EdwardsScalar::TWO_INV); - - let res = serde_bare::to_vec(&EdwardsScalar::TWO_INV); - assert!(res.is_ok()); - let sb = res.unwrap(); - assert_eq!(sb.len(), 57); - - let res = serde_bare::from_slice::(&sb); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), EdwardsScalar::TWO_INV); - } - - #[test] - fn scalar_hash() { - let msg = b"hello world"; - let dst = b"edwards448_XOF:SHAKE256_ELL2_RO_"; - let res = hash2curve::hash_to_scalar::, U84>(&[msg], &[dst]) - .unwrap(); - let expected: [u8; 57] = hex_literal::hex!( - "2d32a08f09b88275cc5f437e625696b18de718ed94559e17e4d64aafd143a8527705132178b5ce7395ea6214735387398a35913656b4951300" - ); - assert_eq!(res.to_bytes_rfc_8032(), Array::from(expected)); - } - - #[property_test] - fn from_canonical_bytes(bytes: [u8; 57]) { - EdwardsScalar::from_canonical_bytes(&bytes.into()); - } -} diff --git a/ed448-goldilocks/src/field.rs b/ed448-goldilocks/src/field.rs index 0f0d5e339..af0b1af70 100644 --- a/ed448-goldilocks/src/field.rs +++ b/ed448-goldilocks/src/field.rs @@ -2,7 +2,6 @@ mod element; mod scalar; pub(crate) use element::*; -pub(crate) use scalar::CurveWithScalar; pub use scalar::{MODULUS_LIMBS, ORDER, Scalar, ScalarBytes, WIDE_ORDER, WideScalarBytes}; use crate::curve::twedwards::extended::ExtendedPoint as TwExtendedPoint; diff --git a/ed448-goldilocks/src/field/scalar.rs b/ed448-goldilocks/src/field/scalar.rs index 607c0cb68..6720e0069 100644 --- a/ed448-goldilocks/src/field/scalar.rs +++ b/ed448-goldilocks/src/field/scalar.rs @@ -3,18 +3,15 @@ use crate::*; use core::cmp::Ordering; use core::fmt::{Debug, Display, Formatter, Result as FmtResult}; use core::iter::{Product, Sum}; -use core::marker::PhantomData; use core::ops::{ Add, AddAssign, Index, IndexMut, Mul, MulAssign, Neg, Shr, ShrAssign, Sub, SubAssign, }; +use elliptic_curve::consts::U114; use elliptic_curve::{ - CurveArithmetic, PrimeField, - array::{ - Array, ArraySize, - typenum::{Prod, Unsigned}, - }, - bigint::{Integer, Limb, U448, U896, Word, Zero}, - consts::U2, + PrimeField, + array::Array, + bigint::{Integer, Limb, NonZero, U448, U512, U704, U896, Word, Zero}, + consts::{U56, U64, U84, U88, U112}, ff::{Field, helpers}, ops::{Invert, Reduce, ReduceNonZero}, scalar::{FromUintUnchecked, IsHigh}, @@ -26,30 +23,17 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreate use elliptic_curve::ff::{FieldBits, PrimeFieldBits}; /// Shared scalar for [`Ed448`] and [`Decaf448`]. -/// Use [`EdwardsScalar`] and [`DecafScalar`] directly. /// /// This is the scalar field /// size = 4q = 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d /// We can therefore use 14 saturated 32-bit limbs -pub struct Scalar { - pub(crate) scalar: U448, - curve: PhantomData, -} +#[derive(Default)] +pub struct Scalar(U448); /// The number of bytes needed to represent the scalar field -pub type ScalarBytes = Array::ReprSize>; +pub type ScalarBytes = Array; /// The number of bytes needed to represent the safely create a scalar from a random bytes -pub type WideScalarBytes = Array::ReprSize, U2>>; - -pub trait CurveWithScalar: 'static + CurveArithmetic + Send + Sync { - type ReprSize: ArraySize: Copy> + Mul: Copy>>; - - fn from_bytes_mod_order_wide(input: &WideScalarBytes) -> Scalar; - - fn from_canonical_bytes(bytes: &ScalarBytes) -> CtOption>; - - fn to_repr(scalar: &Scalar) -> ScalarBytes; -} +pub type WideScalarBytes = Array; /// The order of the scalar field pub const ORDER: Odd = Odd::::from_be_hex( @@ -70,27 +54,21 @@ pub const MODULUS_LIMBS: [u32; 14] = [ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x3fffffff, ]; -impl Clone for Scalar { +impl Clone for Scalar { fn clone(&self) -> Self { *self } } -impl Copy for Scalar {} +impl Copy for Scalar {} -impl Debug for Scalar { +impl Debug for Scalar { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - f.debug_tuple("Scalar").field(&self.scalar).finish() + f.debug_tuple("Scalar").field(&self.0).finish() } } -impl Default for Scalar { - fn default() -> Self { - Self::new(U448::default()) - } -} - -impl Display for Scalar { +impl Display for Scalar { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { let bytes = self.to_repr(); for b in &bytes { @@ -100,166 +78,173 @@ impl Display for Scalar { } } -impl ConstantTimeEq for Scalar { +impl ConstantTimeEq for Scalar { fn ct_eq(&self, other: &Self) -> Choice { self.to_bytes().ct_eq(&other.to_bytes()) } } -impl ConditionallySelectable for Scalar { +impl ConditionallySelectable for Scalar { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Self::new(U448::conditional_select(&a.scalar, &b.scalar, choice)) + Self(U448::conditional_select(&a.0, &b.0, choice)) } } -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { +impl PartialEq for Scalar { + fn eq(&self, other: &Scalar) -> bool { self.ct_eq(other).into() } } -impl Eq for Scalar {} +impl Eq for Scalar {} -impl PartialOrd for Scalar { +impl PartialOrd for Scalar { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Scalar { +impl Ord for Scalar { fn cmp(&self, other: &Self) -> Ordering { - self.scalar.cmp(&other.scalar) + self.0.cmp(&other.0) } } -impl From for Scalar { +impl From for Scalar { fn from(a: u8) -> Self { - Scalar::new(U448::from_u8(a)) + Scalar(U448::from_u8(a)) } } -impl From for Scalar { +impl From for Scalar { fn from(a: u16) -> Self { - Scalar::new(U448::from_u16(a)) + Scalar(U448::from_u16(a)) } } -impl From for Scalar { - fn from(a: u32) -> Scalar { - Scalar::new(U448::from_u32(a)) +impl From for Scalar { + fn from(a: u32) -> Scalar { + Scalar(U448::from_u32(a)) } } -impl From for Scalar { +impl From for Scalar { fn from(a: u64) -> Self { - Scalar::new(U448::from_u64(a)) + Scalar(U448::from_u64(a)) } } -impl From for Scalar { +impl From for Scalar { fn from(a: u128) -> Self { - Scalar::new(U448::from_u128(a)) + Scalar(U448::from_u128(a)) } } -impl Index for Scalar { +#[cfg(feature = "bits")] +impl From<&Scalar> for elliptic_curve::scalar::ScalarBits { + fn from(scalar: &Scalar) -> Self { + scalar.0.to_words().into() + } +} + +impl Index for Scalar { type Output = Word; fn index(&self, index: usize) -> &Self::Output { - &self.scalar.as_words()[index] + &self.0.as_words()[index] } } -impl IndexMut for Scalar { +impl IndexMut for Scalar { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.scalar.as_mut_words()[index] + &mut self.0.as_mut_words()[index] } } // Trait implementations -impl Add<&Scalar> for &Scalar { - type Output = Scalar; +impl Add<&Scalar> for &Scalar { + type Output = Scalar; - fn add(self, rhs: &Scalar) -> Self::Output { + fn add(self, rhs: &Scalar) -> Self::Output { self.addition(rhs) } } -define_add_variants!(GENERIC = C: CurveWithScalar, LHS = Scalar, RHS = Scalar, Output = Scalar); +define_add_variants!(LHS = Scalar, RHS = Scalar, Output = Scalar); -impl AddAssign for Scalar { +impl AddAssign for Scalar { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs } } -impl AddAssign<&Scalar> for Scalar { - fn add_assign(&mut self, rhs: &Scalar) { +impl AddAssign<&Scalar> for Scalar { + fn add_assign(&mut self, rhs: &Scalar) { *self = *self + rhs } } -impl Mul<&Scalar> for &Scalar { - type Output = Scalar; +impl Mul<&Scalar> for &Scalar { + type Output = Scalar; - fn mul(self, rhs: &Scalar) -> Self::Output { + fn mul(self, rhs: &Scalar) -> Self::Output { self.multiply(rhs) } } -define_mul_variants!(GENERIC = C: CurveWithScalar, LHS = Scalar, RHS = Scalar, Output = Scalar); +define_mul_variants!(LHS = Scalar, RHS = Scalar, Output = Scalar); -impl MulAssign for Scalar { +impl MulAssign for Scalar { fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs } } -impl MulAssign<&Scalar> for Scalar { - fn mul_assign(&mut self, rhs: &Scalar) { +impl MulAssign<&Scalar> for Scalar { + fn mul_assign(&mut self, rhs: &Scalar) { *self = *self * rhs } } -impl Sub<&Scalar> for &Scalar { - type Output = Scalar; +impl Sub<&Scalar> for &Scalar { + type Output = Scalar; - fn sub(self, rhs: &Scalar) -> Self::Output { + fn sub(self, rhs: &Scalar) -> Self::Output { self.subtract(rhs) } } -define_sub_variants!(GENERIC = C: CurveWithScalar, LHS = Scalar, RHS = Scalar, Output = Scalar); +define_sub_variants!(LHS = Scalar, RHS = Scalar, Output = Scalar); -impl SubAssign for Scalar { +impl SubAssign for Scalar { fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs } } -impl SubAssign<&Scalar> for Scalar { - fn sub_assign(&mut self, rhs: &Scalar) { +impl SubAssign<&Scalar> for Scalar { + fn sub_assign(&mut self, rhs: &Scalar) { *self = *self - rhs } } -impl Neg for Scalar { - type Output = Scalar; +impl Neg for Scalar { + type Output = Scalar; fn neg(self) -> Self::Output { -&self } } -impl Neg for &Scalar { - type Output = Scalar; +impl Neg for &Scalar { + type Output = Scalar; fn neg(self) -> Self::Output { Scalar::ZERO - self } } -impl Sum for Scalar { +impl Sum for Scalar { fn sum>(iter: I) -> Self { let mut acc = Scalar::ZERO; for s in iter { @@ -269,7 +254,7 @@ impl Sum for Scalar { } } -impl<'a, C: CurveWithScalar> Sum<&'a Scalar> for Scalar { +impl<'a> Sum<&'a Scalar> for Scalar { fn sum>(iter: I) -> Self { let mut acc = Scalar::ZERO; for s in iter { @@ -279,7 +264,7 @@ impl<'a, C: CurveWithScalar> Sum<&'a Scalar> for Scalar { } } -impl Product for Scalar { +impl Product for Scalar { fn product>(iter: I) -> Self { let mut acc = Scalar::ONE; for s in iter { @@ -289,7 +274,7 @@ impl Product for Scalar { } } -impl<'a, C: CurveWithScalar> Product<&'a Scalar> for Scalar { +impl<'a> Product<&'a Scalar> for Scalar { fn product>(iter: I) -> Self { let mut acc = Scalar::ONE; for s in iter { @@ -299,14 +284,14 @@ impl<'a, C: CurveWithScalar> Product<&'a Scalar> for Scalar { } } -impl Field for Scalar { +impl Field for Scalar { const ZERO: Self = Self::ZERO; const ONE: Self = Self::ONE; fn try_from_rng(rng: &mut R) -> Result { - let mut seed = WideScalarBytes::::default(); + let mut seed = WideScalarBytes::default(); rng.try_fill_bytes(&mut seed)?; - Ok(C::from_bytes_mod_order_wide(&seed)) + Ok(Self::from_bytes_mod_order_wide(&seed)) } fn square(&self) -> Self { @@ -326,66 +311,66 @@ impl Field for Scalar { } } -impl PrimeField for Scalar { - type Repr = ScalarBytes; +impl PrimeField for Scalar { + type Repr = ScalarBytes; fn from_repr(repr: Self::Repr) -> CtOption { Self::from_canonical_bytes(&repr) } fn to_repr(&self) -> Self::Repr { - C::to_repr(self) + self.to_bytes().into() } fn is_odd(&self) -> Choice { - Choice::from((self.scalar.to_words()[0] & 1) as u8) + Choice::from((self.0.to_words()[0] & 1) as u8) } const MODULUS: &'static str = "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3"; const NUM_BITS: u32 = 448; const CAPACITY: u32 = Self::NUM_BITS - 1; - const TWO_INV: Self = Self::new(U448::from_be_hex( + const TWO_INV: Self = Self(U448::from_be_hex( "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffbe6511f4e2276da4d76b1b4810b6613946e2c7aa91bc614955ac227a", )); - const MULTIPLICATIVE_GENERATOR: Self = Self::new(U448::from_u8(7)); + const MULTIPLICATIVE_GENERATOR: Self = Self(U448::from_u8(7)); const S: u32 = 1; - const ROOT_OF_UNITY: Self = Self::new(U448::from_be_hex( + const ROOT_OF_UNITY: Self = Self(U448::from_be_hex( "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2", )); - const ROOT_OF_UNITY_INV: Self = Self::new(U448::from_be_hex( + const ROOT_OF_UNITY_INV: Self = Self(U448::from_be_hex( "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2", )); - const DELTA: Self = Self::new(U448::from_u8(49)); + const DELTA: Self = Self(U448::from_u8(49)); } #[cfg(feature = "alloc")] -impl From> for Vec { - fn from(scalar: Scalar) -> Vec { +impl From for Vec { + fn from(scalar: Scalar) -> Vec { Self::from(&scalar) } } #[cfg(feature = "alloc")] -impl From<&Scalar> for Vec { - fn from(scalar: &Scalar) -> Vec { - C::to_repr(scalar).to_vec() +impl From<&Scalar> for Vec { + fn from(scalar: &Scalar) -> Vec { + scalar.to_repr().to_vec() } } -impl From> for ScalarBytes { - fn from(scalar: Scalar) -> ScalarBytes { +impl From for ScalarBytes { + fn from(scalar: Scalar) -> ScalarBytes { Self::from(&scalar) } } -impl From<&Scalar> for ScalarBytes { - fn from(scalar: &Scalar) -> ScalarBytes { - C::to_repr(scalar) +impl From<&Scalar> for ScalarBytes { + fn from(scalar: &Scalar) -> ScalarBytes { + scalar.to_repr() } } #[cfg(feature = "alloc")] -impl TryFrom> for Scalar { +impl TryFrom> for Scalar { type Error = &'static str; fn try_from(bytes: Vec) -> Result { @@ -394,7 +379,7 @@ impl TryFrom> for Scalar { } #[cfg(feature = "alloc")] -impl TryFrom<&Vec> for Scalar { +impl TryFrom<&Vec> for Scalar { type Error = &'static str; fn try_from(bytes: &Vec) -> Result { @@ -402,21 +387,21 @@ impl TryFrom<&Vec> for Scalar { } } -impl TryFrom<&[u8]> for Scalar { +impl TryFrom<&[u8]> for Scalar { type Error = &'static str; fn try_from(bytes: &[u8]) -> Result { - if bytes.len() != C::ReprSize::USIZE { + if bytes.len() != 56 { return Err("invalid byte length"); } - let scalar_bytes = ScalarBytes::::try_from(bytes).expect("invalid scalar bytes"); - Option::>::from(Scalar::from_canonical_bytes(&scalar_bytes)) + let scalar_bytes = ScalarBytes::try_from(bytes).expect("invalid scalar bytes"); + Option::::from(Scalar::from_canonical_bytes(&scalar_bytes)) .ok_or("scalar was not canonically encoded") } } #[cfg(feature = "alloc")] -impl TryFrom> for Scalar { +impl TryFrom> for Scalar { type Error = &'static str; fn try_from(bytes: Box<[u8]>) -> Result { @@ -425,7 +410,7 @@ impl TryFrom> for Scalar { } #[cfg(feature = "serde")] -impl serdect::serde::Serialize for Scalar { +impl serdect::serde::Serialize for Scalar { fn serialize(&self, s: S) -> Result where S: serdect::serde::Serializer, @@ -435,12 +420,12 @@ impl serdect::serde::Serialize for Scalar { } #[cfg(feature = "serde")] -impl<'de, C: CurveWithScalar> serdect::serde::Deserialize<'de> for Scalar { +impl<'de> serdect::serde::Deserialize<'de> for Scalar { fn deserialize(d: D) -> Result where D: serdect::serde::Deserializer<'de>, { - let mut buffer = ScalarBytes::::default(); + let mut buffer = ScalarBytes::default(); serdect::array::deserialize_hex_or_bin(&mut buffer[..56], d)?; Option::from(Self::from_canonical_bytes(&buffer)).ok_or(serdect::serde::de::Error::custom( "scalar was not canonically encoded", @@ -448,11 +433,11 @@ impl<'de, C: CurveWithScalar> serdect::serde::Deserialize<'de> for Scalar { } } -impl elliptic_curve::zeroize::DefaultIsZeroes for Scalar {} +impl elliptic_curve::zeroize::DefaultIsZeroes for Scalar {} -impl core::fmt::LowerHex for Scalar { +impl core::fmt::LowerHex for Scalar { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let tmp = C::to_repr(self); + let tmp = self.to_repr(); for &b in tmp.iter() { write!(f, "{b:02x}")?; } @@ -460,9 +445,9 @@ impl core::fmt::LowerHex for Scalar { } } -impl core::fmt::UpperHex for Scalar { +impl core::fmt::UpperHex for Scalar { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let tmp = C::to_repr(self); + let tmp = self.to_repr(); for &b in tmp.iter() { write!(f, "{b:02X}")?; } @@ -470,48 +455,78 @@ impl core::fmt::UpperHex for Scalar { } } -impl Reduce for Scalar { +impl Reduce> for Scalar { + fn reduce(value: &Array) -> Self { + const SEMI_WIDE_MODULUS: NonZero = NonZero::::new_unwrap(U512::from_be_hex( + "00000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", + )); + + let mut num = U512::from_le_slice(value); + num %= SEMI_WIDE_MODULUS; + let mut words = [0; U448::LIMBS]; + words.copy_from_slice(&num.to_words()[..U448::LIMBS]); + Scalar(U448::from_words(words)) + } +} + +impl Reduce> for Scalar { + fn reduce(value: &Array) -> Self { + const SEMI_WIDE_MODULUS: NonZero = NonZero::::new_unwrap(U704::from_be_hex( + "00000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3", + )); + let mut tmp = Array::::default(); + tmp[4..].copy_from_slice(&value[..]); + + let mut num = U704::from_be_slice(&tmp[..]); + num %= SEMI_WIDE_MODULUS; + let mut words = [0; U448::LIMBS]; + words.copy_from_slice(&num.to_words()[..U448::LIMBS]); + Scalar(U448::from_words(words)) + } +} + +impl Reduce for Scalar { fn reduce(bytes: &U448) -> Self { let (r, underflow) = bytes.borrowing_sub(&ORDER, Limb::ZERO); let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); - Self::new(U448::conditional_select(bytes, &r, !underflow)) + Self(U448::conditional_select(bytes, &r, !underflow)) } } -impl Reduce> for Scalar { - fn reduce(bytes: &ScalarBytes) -> Self { +impl Reduce for Scalar { + fn reduce(bytes: &ScalarBytes) -> Self { Self::reduce(&U448::from_le_slice(bytes)) } } -impl Reduce for Scalar { +impl Reduce for Scalar { fn reduce(bytes: &U896) -> Self { let (r, underflow) = bytes.borrowing_sub(&WIDE_ORDER, Limb::ZERO); let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); - Self::new(U896::conditional_select(bytes, &r, !underflow).split().1) + Self(U896::conditional_select(bytes, &r, !underflow).split().1) } } -impl ReduceNonZero for Scalar { +impl ReduceNonZero for Scalar { fn reduce_nonzero(bytes: &U448) -> Self { let (r, underflow) = bytes.borrowing_sub(&ORDER_MINUS_ONE, Limb::ZERO); let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); - Self::new(U448::conditional_select(bytes, &r, !underflow).wrapping_add(&U448::ONE)) + Self(U448::conditional_select(bytes, &r, !underflow).wrapping_add(&U448::ONE)) } } -impl ReduceNonZero> for Scalar { - fn reduce_nonzero(bytes: &ScalarBytes) -> Self { +impl ReduceNonZero for Scalar { + fn reduce_nonzero(bytes: &ScalarBytes) -> Self { Self::reduce_nonzero(&U448::from_le_slice(bytes)) } } -impl ReduceNonZero for Scalar { +impl ReduceNonZero for Scalar { fn reduce_nonzero(bytes: &U896) -> Self { let (r, underflow) = bytes.borrowing_sub(&WIDE_ORDER_MINUS_ONE, Limb::ZERO); let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); - Self::new( + Self( U896::conditional_select(bytes, &r, !underflow) .split() .1 @@ -520,12 +535,30 @@ impl ReduceNonZero for Scalar { } } +impl Reduce> for Scalar { + fn reduce(bytes: &Array) -> Self { + // top multiplier = 2^896 mod ℓ + const TOP_MULTIPLIER: U448 = U448::from_be_hex( + "3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b60", + ); + let value = ( + U448::from_le_slice(&bytes[..56]), + U448::from_le_slice(&bytes[56..112]), + ); + let mut top = [0u8; 56]; + top[..2].copy_from_slice(&bytes[112..]); + let top = U448::from_le_slice(&top).mul_mod(&TOP_MULTIPLIER, ORDER.as_nz_ref()); + let bottom = U448::rem_wide_vartime(value, ORDER.as_nz_ref()); + Scalar(bottom.add_mod(&top, ORDER.as_nz_ref())) + } +} + #[cfg(feature = "bits")] -impl PrimeFieldBits for Scalar { +impl PrimeFieldBits for Scalar { type ReprBits = [Word; U448::LIMBS]; fn to_le_bits(&self) -> FieldBits { - self.scalar.to_words().into() + self.0.to_words().into() } fn char_le_bits() -> FieldBits { @@ -533,39 +566,39 @@ impl PrimeFieldBits for Scalar { } } -impl From for Scalar { +impl From for Scalar { fn from(uint: U448) -> Self { >::reduce(&uint) } } -impl From<&U448> for Scalar { +impl From<&U448> for Scalar { fn from(uint: &U448) -> Self { Self::from(*uint) } } -impl From> for U448 { - fn from(scalar: Scalar) -> Self { - scalar.scalar +impl From for U448 { + fn from(scalar: Scalar) -> Self { + scalar.0 } } -impl From<&Scalar> for U448 { - fn from(scalar: &Scalar) -> Self { +impl From<&Scalar> for U448 { + fn from(scalar: &Scalar) -> Self { Self::from(*scalar) } } -impl FromUintUnchecked for Scalar { +impl FromUintUnchecked for Scalar { type Uint = U448; fn from_uint_unchecked(uint: U448) -> Self { - Self::new(uint) + Self(uint) } } -impl Invert for Scalar { +impl Invert for Scalar { type Output = CtOption; fn invert(&self) -> CtOption { @@ -573,19 +606,19 @@ impl Invert for Scalar { } } -impl IsHigh for Scalar { +impl IsHigh for Scalar { fn is_high(&self) -> Choice { - self.scalar.ct_gt(&HALF_ORDER) + self.0.ct_gt(&HALF_ORDER) } } -impl AsRef> for Scalar { - fn as_ref(&self) -> &Scalar { +impl AsRef for Scalar { + fn as_ref(&self) -> &Scalar { self } } -impl Shr for Scalar { +impl Shr for Scalar { type Output = Self; fn shr(self, rhs: usize) -> Self::Output { @@ -595,8 +628,8 @@ impl Shr for Scalar { } } -impl Shr for &Scalar { - type Output = Scalar; +impl Shr for &Scalar { + type Output = Scalar; fn shr(self, rhs: usize) -> Self::Output { let mut cp = *self; @@ -605,57 +638,50 @@ impl Shr for &Scalar { } } -impl ShrAssign for Scalar { +impl ShrAssign for Scalar { fn shr_assign(&mut self, shift: usize) { - self.scalar >>= shift; + self.0 >>= shift; } } -impl Scalar { +impl Scalar { /// The multiplicative identity element - pub const ONE: Scalar = Scalar::new(U448::ONE); + pub const ONE: Scalar = Scalar(U448::ONE); /// Twice the multiplicative identity element - pub const TWO: Scalar = Scalar::new(U448::from_u8(2)); + pub const TWO: Scalar = Scalar(U448::from_u8(2)); /// The additive identity element - pub const ZERO: Scalar = Scalar::new(U448::ZERO); - - pub(crate) const fn new(scalar: U448) -> Self { - Self { - scalar, - curve: PhantomData, - } - } + pub const ZERO: Scalar = Scalar(U448::ZERO); /// Compute `self` + `rhs` mod ℓ pub const fn addition(&self, rhs: &Self) -> Self { - Self::new(self.scalar.add_mod(&rhs.scalar, ORDER.as_nz_ref())) + Self(self.0.add_mod(&rhs.0, ORDER.as_nz_ref())) } /// Compute `self` + `self` mod ℓ pub const fn double(&self) -> Self { - Self::new(self.scalar.double_mod(ORDER.as_nz_ref())) + Self(self.0.double_mod(ORDER.as_nz_ref())) } /// Compute `self` - `rhs` mod ℓ pub const fn subtract(&self, rhs: &Self) -> Self { - Self::new(self.scalar.sub_mod(&rhs.scalar, ORDER.as_nz_ref())) + Self(self.0.sub_mod(&rhs.0, ORDER.as_nz_ref())) } /// Compute `self` * `rhs` mod ℓ pub const fn multiply(&self, rhs: &Self) -> Self { - let wide_value = self.scalar.widening_mul(&rhs.scalar); - Self::new(U448::rem_wide_vartime(wide_value, ORDER.as_nz_ref())) + let wide_value = self.0.widening_mul(&rhs.0); + Self(U448::rem_wide_vartime(wide_value, ORDER.as_nz_ref())) } /// Square this scalar pub const fn square(&self) -> Self { - let value = self.scalar.square_wide(); - Self::new(U448::rem_wide_vartime(value, ORDER.as_nz_ref())) + let value = self.0.square_wide(); + Self(U448::rem_wide_vartime(value, ORDER.as_nz_ref())) } /// Is this scalar equal to zero? pub fn is_zero(&self) -> Choice { - self.scalar.is_zero() + self.0.is_zero() } // This method was modified from Curve25519-Dalek codebase. [scalar.rs] @@ -713,7 +739,7 @@ impl Scalar { /// Convert this `Scalar` to a little-endian byte array. pub fn to_bytes(&self) -> [u8; 56] { - self.scalar.to_le_byte_array().0 + self.0.to_le_byte_array().0 } /// Invert this scalar @@ -772,11 +798,11 @@ impl Scalar { /// Halves a Scalar modulo the prime pub fn div_by_2(&self) -> Self { - let is_odd = self.scalar.is_odd(); - let if_odd = self.scalar + *ORDER; - let scalar = U448::conditional_select(&self.scalar, &if_odd, is_odd); + let is_odd = self.0.is_odd(); + let if_odd = self.0 + *ORDER; + let scalar = U448::conditional_select(&self.0, &if_odd, is_odd); - Self::new(scalar >> 1) + Self(scalar >> 1) } /// Attempt to construct a `Scalar` from a canonical byte representation. @@ -786,14 +812,37 @@ impl Scalar { /// - `Some(s)`, where `s` is the `Scalar` corresponding to `bytes`, /// if `bytes` is a canonical byte representation; /// - `None` if `bytes` is not a canonical byte representation. - pub fn from_canonical_bytes(bytes: &ScalarBytes) -> CtOption { - C::from_canonical_bytes(bytes) + pub fn from_canonical_bytes(bytes: &ScalarBytes) -> CtOption { + // Check that the 10 high bits are not set + let is_valid = (bytes[55] >> 6).ct_eq(&0); + let candidate = Scalar(U448::from_le_slice(bytes)); + + // underflow means candidate < ORDER, thus canonical + let (_, underflow) = candidate.0.borrowing_sub(&ORDER, Limb::ZERO); + let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8); + CtOption::new(candidate, underflow & is_valid) + } + + #[cfg(feature = "signing")] + pub(crate) fn from_rfc_8032_bytes(bytes: &Array) -> subtle::CtOption { + Self::from_canonical_bytes(bytes.split_ref().0) + .and_then(|scalar| CtOption::new(scalar, bytes[56].ct_eq(&0))) } /// Construct a Scalar by reducing a 448-bit little-endian integer modulo the group order ℓ - pub fn from_bytes_mod_order(input: &ScalarBytes) -> Scalar { + pub fn from_bytes_mod_order(input: &ScalarBytes) -> Scalar { let value = U448::from_le_slice(&input[..56]); - Self::new(value.rem_vartime(ORDER.as_nz_ref())) + Self(value.rem_vartime(ORDER.as_nz_ref())) + } + + /// Construct a `Scalar` by reducing a 896-bit little-endian integer + /// modulo the group order ℓ. + pub fn from_bytes_mod_order_wide(input: &WideScalarBytes) -> Scalar { + let value = ( + U448::from_le_slice(&input[..56]), + U448::from_le_slice(&input[56..112]), + ); + Scalar(U448::rem_wide_vartime(value, ORDER.as_nz_ref())) } /// Return a `Scalar` chosen uniformly at random using a user-provided RNG. @@ -806,8 +855,210 @@ impl Scalar { /// /// A random scalar within ℤ/lℤ. pub fn random(rng: &mut R) -> Self { - let mut scalar_bytes = WideScalarBytes::::default(); + let mut scalar_bytes = WideScalarBytes::default(); rng.fill_bytes(&mut scalar_bytes); - C::from_bytes_mod_order_wide(&scalar_bytes) + Self::from_bytes_mod_order_wide(&scalar_bytes) + } +} + +#[cfg(test)] +mod test { + use super::*; + use hex_literal::hex; + use proptest::property_test; + + #[test] + fn test_basic_add() { + let five = Scalar::from(5u8); + let six = Scalar::from(6u8); + + assert_eq!(five + six, Scalar::from(11u8)) + } + + #[test] + fn test_basic_sub() { + let ten = Scalar::from(10u8); + let five = Scalar::from(5u8); + assert_eq!(ten - five, Scalar::from(5u8)) + } + + #[test] + fn test_basic_mul() { + let ten = Scalar::from(10u8); + let five = Scalar::from(5u8); + + assert_eq!(ten * five, Scalar::from(50u8)) + } + + #[test] + fn test_mul() { + let a = Scalar(U448::from_be_hex( + "1e63e8073b089f0747cf8cac2c3dc2732aae8688a8fa552ba8cb0ae8c0be082e74d657641d9ac30a087b8fb97f8ed27dc96a3c35ffb823a3", + )); + + let b = Scalar(U448::from_be_hex( + "16c5450acae1cb680a92de2d8e59b30824e8d4991adaa0e7bc343bcbd099595b188c6b1a1e30b38b17aa6d9be416b899686eb329d8bedc42", + )); + + let exp = Scalar(U448::from_be_hex( + "31e055c14ca389edfccd61b3203d424bb9036ff6f2d89c1e07bcd93174e9335f36a1492008a3a0e46abd26f5994c9c2b1f5b3197a18d010a", + )); + + assert_eq!(a * b, exp) + } + #[test] + fn test_basic_square() { + let a = Scalar(U448::from_be_hex( + "3162081604b3273b930392e5d2391f9d21cc3078f22c69514bb395e08dccc4866f08f3311370f8b83fa50692f640922b7e56a34bcf5fac3d", + )); + let expected_a_squared = Scalar(U448::from_be_hex( + "1c1e32fc66b21c9c42d6e8e20487193cf6d49916421b290098f30de3713006cfe8ee9d21eeef7427f82a1fe036630c74b9acc2c2ede40f04", + )); + + assert_eq!(a.square(), expected_a_squared) + } + + #[test] + fn test_sanity_check_index_mut() { + let mut x = Scalar::ONE; + x[0] = 2; + assert_eq!(x, Scalar::from(2u8)) + } + #[test] + fn test_basic_halving() { + let eight = Scalar::from(8u8); + let four = Scalar::from(4u8); + let two = Scalar::from(2u8); + assert_eq!(eight.div_by_2(), four); + assert_eq!(four.div_by_2(), two); + assert_eq!(two.div_by_2(), Scalar::ONE); + } + + #[test] + fn test_equals() { + let a = Scalar::from(5u8); + let b = Scalar::from(5u8); + let c = Scalar::from(10u8); + assert_eq!(a, b); + assert_ne!(a, c); + } + + #[test] + fn test_basic_inversion() { + // Test inversion from 2 to 100 + for i in 1..=100u8 { + let x = Scalar::from(i); + let x_inv = x.invert(); + assert_eq!(x_inv * x, Scalar::ONE) + } + + // Inversion of zero is zero + let zero = Scalar::ZERO; + let expected_zero = zero.invert(); + assert_eq!(expected_zero, zero) + } + #[test] + fn test_serialise() { + let scalar = Scalar(U448::from_be_hex( + "0d79f6e375d3395ed9a6c4c3c49a1433fd7c58aa38363f74e9ab2c22a22347d79988f8e01e8a309f862a9f1052fcd042b9b1ed7115598f62", + )); + let got = Scalar::from_canonical_bytes(&scalar.into()).unwrap(); + assert_eq!(scalar, got) + } + #[test] + fn test_from_canonical_bytes() { + // ff..ff should fail + let mut bytes = ScalarBytes::from(hex!( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )); + bytes.reverse(); + let s = Scalar::from_canonical_bytes(&bytes); + assert!(>::into(s.is_none())); + + // n should fail + let mut bytes = ScalarBytes::from(hex!( + "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3" + )); + bytes.reverse(); + let s = Scalar::from_canonical_bytes(&bytes); + assert!(>::into(s.is_none())); + + // n-1 should work + let mut bytes = ScalarBytes::from(hex!( + "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" + )); + bytes.reverse(); + let s = Scalar::from_canonical_bytes(&bytes); + match Option::::from(s) { + Some(s) => assert_eq!(s, Scalar::ZERO - Scalar::ONE), + None => panic!("should not return None"), + }; + } + + #[test] + fn test_from_bytes_mod_order_wide() { + // n should become 0 + let mut bytes = WideScalarBytes::from(hex!( + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3" + )); + bytes.reverse(); + let s = Scalar::from_bytes_mod_order_wide(&bytes); + assert_eq!(s, Scalar::ZERO); + + // n-1 should stay the same + let mut bytes = WideScalarBytes::from(hex!( + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2" + )); + bytes.reverse(); + let s = Scalar::from_bytes_mod_order_wide(&bytes); + assert_eq!(s, Scalar::ZERO - Scalar::ONE); + + // n+1 should become 1 + let mut bytes = WideScalarBytes::from(hex!( + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f4" + )); + bytes.reverse(); + let s = Scalar::from_bytes_mod_order_wide(&bytes); + assert_eq!(s, Scalar::ONE); + + // 2^896-1 should become 0x3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b5f + let bytes = WideScalarBytes::from(hex!( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )); + let s = Scalar::from_bytes_mod_order_wide(&bytes); + let mut bytes = ScalarBytes::from(hex!( + "3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b5f" + )); + bytes.reverse(); + let reduced = Scalar::from_canonical_bytes(&bytes).unwrap(); + assert_eq!(s, reduced); + } + + #[cfg(all(feature = "alloc", feature = "serde"))] + #[test] + fn serde() { + use elliptic_curve::PrimeField; + + let res = serde_json::to_string(&Scalar::TWO_INV); + assert!(res.is_ok()); + let sj = res.unwrap(); + + let res = serde_json::from_str::(&sj); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Scalar::TWO_INV); + + let res = serde_bare::to_vec(&Scalar::TWO_INV); + assert!(res.is_ok()); + let sb = res.unwrap(); + assert_eq!(sb.len(), 57); + + let res = serde_bare::from_slice::(&sb); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Scalar::TWO_INV); + } + + #[property_test] + fn from_canonical_bytes(bytes: [u8; 56]) { + Scalar::from_canonical_bytes(&bytes.into()); } } diff --git a/ed448-goldilocks/src/lib.rs b/ed448-goldilocks/src/lib.rs index 6013b30b4..73fb38762 100644 --- a/ed448-goldilocks/src/lib.rs +++ b/ed448-goldilocks/src/lib.rs @@ -49,15 +49,9 @@ pub(crate) mod sign; pub(crate) use field::{GOLDILOCKS_BASE_POINT, TWISTED_EDWARDS_BASE_POINT}; -pub use decaf::{ - AffinePoint as DecafAffinePoint, CompressedDecaf, DecafPoint, DecafScalar, DecafScalarBytes, - WideDecafScalarBytes, -}; -pub use edwards::{ - AffinePoint, CompressedEdwardsY, EdwardsPoint, EdwardsScalar, EdwardsScalarBytes, - WideEdwardsScalarBytes, -}; -pub use field::{MODULUS_LIMBS, ORDER, Scalar, WIDE_ORDER}; +pub use decaf::{AffinePoint as DecafAffinePoint, CompressedDecaf, DecafPoint}; +pub use edwards::{AffinePoint, CompressedEdwardsY, EdwardsPoint}; +pub use field::{MODULUS_LIMBS, ORDER, Scalar, ScalarBytes, WIDE_ORDER, WideScalarBytes}; pub use montgomery::{MontgomeryPoint, ProjectiveMontgomeryPoint}; #[cfg(feature = "signing")] pub use sign::*; @@ -86,7 +80,7 @@ pub type Ed448ScalarBits = elliptic_curve::scalar::ScalarBits; pub type Ed448NonZeroScalar = elliptic_curve::NonZeroScalar; impl Curve for Ed448 { - type FieldBytesSize = U57; + type FieldBytesSize = U56; type Uint = U448; const ORDER: Odd = ORDER; @@ -113,7 +107,7 @@ impl FieldBytesEncoding for U448 { impl elliptic_curve::CurveArithmetic for Ed448 { type AffinePoint = AffinePoint; type ProjectivePoint = EdwardsPoint; - type Scalar = EdwardsScalar; + type Scalar = Scalar; } impl GroupDigest for Ed448 { @@ -165,7 +159,7 @@ impl FieldBytesEncoding for U448 { impl elliptic_curve::CurveArithmetic for Decaf448 { type AffinePoint = DecafAffinePoint; type ProjectivePoint = DecafPoint; - type Scalar = DecafScalar; + type Scalar = Scalar; } impl GroupDigest for Decaf448 { diff --git a/ed448-goldilocks/src/macros.rs b/ed448-goldilocks/src/macros.rs index 3ed23149f..b80ea7a73 100644 --- a/ed448-goldilocks/src/macros.rs +++ b/ed448-goldilocks/src/macros.rs @@ -13,8 +13,8 @@ /// Define borrow and non-borrow variants of `Add`. macro_rules! define_add_variants { - ($(GENERIC = $generic:ident: $bound:ident,)? LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b $(, $generic: $bound)?> Add<&'b $rhs> for $lhs { + (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { + impl<'b> Add<&'b $rhs> for $lhs { type Output = $out; fn add(self, rhs: &'b $rhs) -> $out { @@ -22,7 +22,7 @@ macro_rules! define_add_variants { } } - impl<'a $(, $generic: $bound)?> Add<$rhs> for &'a $lhs { + impl<'a> Add<$rhs> for &'a $lhs { type Output = $out; fn add(self, rhs: $rhs) -> $out { @@ -30,7 +30,7 @@ macro_rules! define_add_variants { } } - impl $(<$generic: $bound>)? Add<$rhs> for $lhs { + impl Add<$rhs> for $lhs { type Output = $out; fn add(self, rhs: $rhs) -> $out { @@ -54,7 +54,7 @@ macro_rules! define_add_assign_variants { /// Define borrow and non-borrow variants of `Sub`. macro_rules! define_sub_variants { ($(GENERIC = $generic:ident: $bound:ident,)? LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b $(, $generic: $bound)?> Sub<&'b $rhs> for $lhs { + impl<'b> Sub<&'b $rhs> for $lhs { type Output = $out; fn sub(self, rhs: &'b $rhs) -> $out { @@ -62,7 +62,7 @@ macro_rules! define_sub_variants { } } - impl<'a $(, $generic: $bound)?> Sub<$rhs> for &'a $lhs { + impl<'a> Sub<$rhs> for &'a $lhs { type Output = $out; fn sub(self, rhs: $rhs) -> $out { @@ -94,7 +94,7 @@ macro_rules! define_sub_assign_variants { /// Define borrow and non-borrow variants of `Mul`. macro_rules! define_mul_variants { ($(GENERIC = $generic:ident: $bound:ident,)? LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b $(, $generic: $bound)?> Mul<&'b $rhs> for $lhs { + impl<'b> Mul<&'b $rhs> for $lhs { type Output = $out; fn mul(self, rhs: &'b $rhs) -> $out { @@ -102,7 +102,7 @@ macro_rules! define_mul_variants { } } - impl<'a $(, $generic: $bound)?> Mul<$rhs> for &'a $lhs { + impl<'a> Mul<$rhs> for &'a $lhs { type Output = $out; fn mul(self, rhs: $rhs) -> $out { diff --git a/ed448-goldilocks/src/montgomery.rs b/ed448-goldilocks/src/montgomery.rs index 3fef4d895..984382d7b 100644 --- a/ed448-goldilocks/src/montgomery.rs +++ b/ed448-goldilocks/src/montgomery.rs @@ -11,7 +11,7 @@ #![allow(non_snake_case)] // use crate::constants::A_PLUS_TWO_OVER_FOUR; -use crate::EdwardsScalar; +use crate::Scalar; use crate::edwards::extended::EdwardsPoint; use crate::field::FieldElement; use core::fmt; @@ -80,11 +80,11 @@ pub struct ProjectiveMontgomeryPoint { W: FieldElement, } -impl Mul<&EdwardsScalar> for &MontgomeryPoint { +impl Mul<&Scalar> for &MontgomeryPoint { type Output = MontgomeryPoint; #[allow(clippy::suspicious_arithmetic_impl)] - fn mul(self, scalar: &EdwardsScalar) -> MontgomeryPoint { + fn mul(self, scalar: &Scalar) -> MontgomeryPoint { // Algorithm 8 of Costello-Smith 2017 let affine_u = FieldElement::from_bytes(&self.0); let mut x0 = ProjectiveMontgomeryPoint::identity(); @@ -109,7 +109,7 @@ impl Mul<&EdwardsScalar> for &MontgomeryPoint { } } -impl Mul<&MontgomeryPoint> for &EdwardsScalar { +impl Mul<&MontgomeryPoint> for &Scalar { type Output = MontgomeryPoint; fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint { @@ -226,7 +226,7 @@ mod tests { #[test] fn test_montgomery_edwards() { - let scalar = EdwardsScalar::from(200u32); + let scalar = Scalar::from(200u32); use crate::GOLDILOCKS_BASE_POINT as bp; // Montgomery scalar mul diff --git a/ed448-goldilocks/src/sign.rs b/ed448-goldilocks/src/sign.rs index 530f046af..1c7ba6304 100644 --- a/ed448-goldilocks/src/sign.rs +++ b/ed448-goldilocks/src/sign.rs @@ -87,7 +87,7 @@ pub use signature; pub use signing_key::*; pub use verifying_key::*; -use crate::{CompressedEdwardsY, EdwardsPoint, EdwardsScalar}; +use crate::{CompressedEdwardsY, EdwardsPoint, Scalar}; use elliptic_curve::array::Array; use elliptic_curve::group::GroupEncoding; @@ -118,7 +118,7 @@ pub const ALGORITHM_ID: pkcs8::AlgorithmIdentifierRef<'static> = pkcs8::Algorith impl From for Signature { fn from(inner: InnerSignature) -> Self { let mut s = [0u8; SECRET_KEY_LENGTH]; - s.copy_from_slice(&inner.s.to_bytes_rfc_8032()); + s[..56].copy_from_slice(&inner.s.to_bytes()); Self::from_components(inner.r.to_bytes(), s) } } @@ -128,7 +128,7 @@ impl TryFrom<&Signature> for InnerSignature { fn try_from(signature: &Signature) -> Result { let s_bytes: &Array = (signature.s_bytes()).into(); - let s = Option::from(EdwardsScalar::from_canonical_bytes(s_bytes)) + let s = Option::from(Scalar::from_rfc_8032_bytes(s_bytes)) .ok_or(SigningError::InvalidSignatureSComponent)?; let r = CompressedEdwardsY::from(*signature.r_bytes()) .decompress() @@ -141,7 +141,7 @@ impl TryFrom<&Signature> for InnerSignature { pub(crate) struct InnerSignature { pub(crate) r: EdwardsPoint, - pub(crate) s: EdwardsScalar, + pub(crate) s: Scalar, } impl TryFrom for InnerSignature { diff --git a/ed448-goldilocks/src/sign/expanded.rs b/ed448-goldilocks/src/sign/expanded.rs index 931649991..d67b3b4a8 100644 --- a/ed448-goldilocks/src/sign/expanded.rs +++ b/ed448-goldilocks/src/sign/expanded.rs @@ -1,10 +1,12 @@ use crate::{ - EdwardsPoint, EdwardsScalar, EdwardsScalarBytes, SECRET_KEY_LENGTH, SecretKey, SigningError, - VerifyingKey, WideEdwardsScalarBytes, + EdwardsPoint, Scalar, SecretKey, SigningError, VerifyingKey, sign::{HASH_HEAD, InnerSignature}, }; use elliptic_curve::{ + array::Array, + consts::{U57, U114}, group::GroupEncoding, + ops::Reduce, zeroize::{Zeroize, ZeroizeOnDrop}, }; use sha3::{ @@ -15,9 +17,9 @@ use sha3::{ #[derive(Clone)] pub struct ExpandedSecretKey { pub(crate) seed: SecretKey, - pub(crate) scalar: EdwardsScalar, + pub(crate) scalar: Scalar, pub(crate) public_key: VerifyingKey, - pub(crate) hash_prefix: EdwardsScalarBytes, + pub(crate) hash_prefix: Array, } impl Zeroize for ExpandedSecretKey { @@ -44,23 +46,19 @@ impl ZeroizeOnDrop for ExpandedSecretKey {} impl ExpandedSecretKey { pub fn from_seed(seed: &SecretKey) -> Self { - let mut reader = Shake256::default().chain(seed).finalize_xof(); - let mut bytes = WideEdwardsScalarBytes::default(); - reader.read(&mut bytes); - let mut scalar_bytes = EdwardsScalarBytes::default(); - scalar_bytes.copy_from_slice(&bytes[..SECRET_KEY_LENGTH]); + let mut bytes = Array::::default(); + Shake256::default() + .chain(seed) + .finalize_xof_into(&mut bytes); + let (mut scalar_bytes, hash_prefix) = bytes.split::(); // The two least significant bits of the first byte are cleared // All eight most significant bits of the last byte are cleared // with the highest bit of the second byte set. scalar_bytes[0] &= 0xFC; - scalar_bytes[56] = 0; scalar_bytes[55] |= 0x80; - let scalar = EdwardsScalar::from_bytes_mod_order(&scalar_bytes); - - let mut hash_prefix = EdwardsScalarBytes::default(); - hash_prefix.copy_from_slice(&bytes[SECRET_KEY_LENGTH..]); + let scalar = Scalar::from_bytes_mod_order(scalar_bytes.split_ref().0); let point = EdwardsPoint::GENERATOR * scalar; let public_key = VerifyingKey { @@ -121,9 +119,9 @@ impl ExpandedSecretKey { .chain(self.hash_prefix) .chain(m) .finalize_xof_reset(); - let mut bytes = WideEdwardsScalarBytes::default(); + let mut bytes = Array::::default(); reader.read(&mut bytes); - let r = EdwardsScalar::from_bytes_mod_order_wide(&bytes); + let r = Scalar::reduce(&bytes); // R = r*B let big_r = EdwardsPoint::GENERATOR * r; @@ -140,7 +138,7 @@ impl ExpandedSecretKey { .chain(m) .finalize_xof(); reader.read(&mut bytes); - let k = EdwardsScalar::from_bytes_mod_order_wide(&bytes); + let k = Scalar::reduce(&bytes); Ok(InnerSignature { r: big_r, s: r + k * self.scalar, diff --git a/ed448-goldilocks/src/sign/signing_key.rs b/ed448-goldilocks/src/sign/signing_key.rs index 423e43fcb..2271fb2a1 100644 --- a/ed448-goldilocks/src/sign/signing_key.rs +++ b/ed448-goldilocks/src/sign/signing_key.rs @@ -4,7 +4,10 @@ use crate::sign::expanded::ExpandedSecretKey; use crate::*; use core::fmt::{self, Debug, Formatter}; -use elliptic_curve::zeroize::{Zeroize, ZeroizeOnDrop}; +use elliptic_curve::{ + array::Array, + zeroize::{Zeroize, ZeroizeOnDrop}, +}; use rand_core::CryptoRng; use sha3::digest::{ Digest, ExtendableOutput, FixedOutput, FixedOutputReset, HashMarker, Update, XofReader, @@ -18,8 +21,8 @@ use crate::{PUBLIC_KEY_LENGTH, edwards::affine::PointBytes}; /// Ed448 secret key as defined in [RFC8032 § 5.2.5] /// -/// The private key is 57 octets (448 bits, 56 bytes) long. -pub type SecretKey = EdwardsScalarBytes; +/// The private key is 57 octets (456 bits) long. +pub type SecretKey = Array; /// Signing hash trait for Ed448ph pub trait PreHash { @@ -198,7 +201,7 @@ impl TryFrom<&[u8]> for SigningKey { return Err("Invalid length for a signing key"); } Ok(Self::from( - EdwardsScalarBytes::try_from(value).expect("Invalid length"), + SecretKey::try_from(value).expect("Invalid length"), )) } } @@ -470,11 +473,11 @@ impl SigningKey { &self.secret.seed } - /// Return the clamped [`EdwardsScalar`] for this [`SigningKey`]. + /// Return the clamped [`Scalar`] for this [`SigningKey`]. /// /// This is the scalar that is actually used for signing. /// Be warned, this is secret material that should be handled with care. - pub fn to_scalar(&self) -> EdwardsScalar { + pub fn to_scalar(&self) -> Scalar { self.secret.scalar } diff --git a/ed448-goldilocks/src/sign/verifying_key.rs b/ed448-goldilocks/src/sign/verifying_key.rs index 0fecb16b8..d3cbf1ccd 100644 --- a/ed448-goldilocks/src/sign/verifying_key.rs +++ b/ed448-goldilocks/src/sign/verifying_key.rs @@ -3,10 +3,7 @@ use crate::edwards::affine::PointBytes; use crate::sign::{HASH_HEAD, InnerSignature}; -use crate::{ - CompressedEdwardsY, Context, EdwardsPoint, EdwardsScalar, PreHash, Signature, SigningError, - WideEdwardsScalarBytes, -}; +use crate::{CompressedEdwardsY, Context, EdwardsPoint, PreHash, Scalar, Signature, SigningError}; #[cfg(feature = "serde")] use crate::PUBLIC_KEY_LENGTH; @@ -18,7 +15,7 @@ use core::{ fmt::{self, Debug, Formatter}, hash::{Hash, Hasher}, }; -use elliptic_curve::Group; +use elliptic_curve::{Group, array::Array, consts::U114, ops::Reduce}; use sha3::{ Shake256, digest::{Digest, ExtendableOutput, FixedOutput, HashMarker, Update, XofReader}, @@ -288,7 +285,7 @@ impl VerifyingKey { } // SHAKE256(dom4(F, C) || R || A || PH(M), 114) -> scalar k - let mut bytes = WideEdwardsScalarBytes::default(); + let mut bytes = Array::::default(); let ctx_len = ctx.len() as u8; let mut reader = Shake256::default() .chain(HASH_HEAD) @@ -300,7 +297,7 @@ impl VerifyingKey { .chain(m) .finalize_xof(); reader.read(&mut bytes); - let k = EdwardsScalar::from_bytes_mod_order_wide(&bytes); + let k = Scalar::reduce(&bytes); // Check the verification equation [S]B = R + [k]A. let lhs = EdwardsPoint::GENERATOR * inner_signature.s; let rhs = inner_signature.r + (self.point * k); diff --git a/x448/src/lib.rs b/x448/src/lib.rs index 40cd456c8..efa9a15f3 100644 --- a/x448/src/lib.rs +++ b/x448/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] use ed448_goldilocks::{ - MontgomeryPoint, + MontgomeryPoint, Scalar, elliptic_curve::{ array::{Array, typenum::U56}, bigint::U448, @@ -11,8 +11,6 @@ use ed448_goldilocks::{ use rand_core::{CryptoRng, RngCore}; use zeroize::Zeroize; -type MontgomeryScalar = ed448_goldilocks::Scalar; - /// Given an [`EphemeralSecret`] Key, compute the corresponding public key /// using the generator specified in RFC7748 impl From<&EphemeralSecret> for PublicKey { @@ -107,9 +105,9 @@ impl EphemeralSecret { } /// Views an Secret as a Scalar - fn as_scalar(&self) -> MontgomeryScalar { + fn as_scalar(&self) -> Scalar { let secret = U448::from_le_slice(&self.0); - MontgomeryScalar::from_uint_unchecked(secret) + Scalar::from_uint_unchecked(secret) } /// Performs a Diffie-hellman key exchange between the secret key and an external public key