Skip to content

Commit eb4e07e

Browse files
committed
Add MontgomeryScalar
1 parent d6529d6 commit eb4e07e

File tree

5 files changed

+271
-13
lines changed

5 files changed

+271
-13
lines changed

ed448-goldilocks/src/field/scalar.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreate
2626
use elliptic_curve::ff::{FieldBits, PrimeFieldBits};
2727

2828
/// Shared scalar for [`Ed448`] and [`Decaf448`].
29-
/// Use [`EdwardsScalar`] and [`DecafScalar`] directly.
29+
/// Use [`EdwardsScalar`], [`DecafScalar`] and [`MontgomeryScalar`] directly.
3030
///
3131
/// This is the scalar field
3232
/// size = 4q = 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
@@ -821,4 +821,9 @@ impl<C: CurveWithScalar> Scalar<C> {
821821
rng.fill_bytes(&mut scalar_bytes);
822822
C::from_bytes_mod_order_wide(&scalar_bytes)
823823
}
824+
825+
/// Convert to other [`Scalar`] type
826+
pub fn to_scalar<O: CurveWithScalar>(&self) -> Scalar<O> {
827+
Scalar::new(self.scalar)
828+
}
824829
}

ed448-goldilocks/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ pub use edwards::{
6161
};
6262
pub use field::{MODULUS_LIMBS, ORDER, Scalar, WIDE_ORDER};
6363
pub use montgomery::{
64-
MontgomeryPoint, MontgomeryXpoint, ProjectiveMontgomeryPoint, ProjectiveMontgomeryXpoint,
64+
MontgomeryPoint, MontgomeryScalar, MontgomeryScalarBytes, MontgomeryXpoint,
65+
ProjectiveMontgomeryPoint, ProjectiveMontgomeryXpoint, WideMontgomeryScalarBytes,
6566
};
6667
#[cfg(feature = "signing")]
6768
pub use sign::*;

ed448-goldilocks/src/montgomery.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
#![allow(non_snake_case)]
1212

1313
mod point;
14+
mod scalar;
1415
mod x;
1516

1617
pub use point::{MontgomeryPoint, ProjectiveMontgomeryPoint};
18+
pub use scalar::{MontgomeryScalar, MontgomeryScalarBytes, WideMontgomeryScalarBytes};
1719
pub use x::{MontgomeryXpoint, ProjectiveMontgomeryXpoint};
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
use elliptic_curve::bigint::{Limb, U448};
2+
use elliptic_curve::consts::U56;
3+
use subtle::{Choice, CtOption};
4+
5+
use crate::field::{CurveWithScalar, NZ_ORDER, ScalarBytes, WideScalarBytes};
6+
use crate::{Curve448, ORDER, Scalar};
7+
8+
impl CurveWithScalar for Curve448 {
9+
type ReprSize = U56;
10+
11+
fn from_bytes_mod_order_wide(input: &WideScalarBytes<Self>) -> Scalar<Self> {
12+
let value = (
13+
U448::from_le_slice(&input[..56]),
14+
U448::from_le_slice(&input[56..112]),
15+
);
16+
Scalar::new(U448::rem_wide_vartime(value, &NZ_ORDER))
17+
}
18+
19+
fn from_canonical_bytes(bytes: &ScalarBytes<Self>) -> subtle::CtOption<Scalar<Self>> {
20+
fn is_zero(b: u8) -> Choice {
21+
let res = b as i8;
22+
Choice::from((((res | -res) >> 7) + 1) as u8)
23+
}
24+
25+
// Check that the 10 high bits are not set
26+
let is_valid = is_zero(bytes[55] >> 6);
27+
let bytes: [u8; 56] = core::array::from_fn(|i| bytes[i]);
28+
let candidate = Scalar::new(U448::from_le_slice(&bytes));
29+
30+
// underflow means candidate < ORDER, thus canonical
31+
let (_, underflow) = candidate.scalar.borrowing_sub(&ORDER, Limb::ZERO);
32+
let underflow = Choice::from((underflow.0 >> (Limb::BITS - 1)) as u8);
33+
CtOption::new(candidate, underflow & is_valid)
34+
}
35+
36+
fn to_repr(scalar: &Scalar<Self>) -> ScalarBytes<Self> {
37+
scalar.to_bytes().into()
38+
}
39+
}
40+
41+
/// [`Curve448`] scalar field.
42+
pub type MontgomeryScalar = Scalar<Curve448>;
43+
44+
impl MontgomeryScalar {
45+
/// Construct a `Scalar` by reducing a 896-bit little-endian integer
46+
/// modulo the group order ℓ.
47+
pub fn from_bytes_mod_order_wide(input: &WideMontgomeryScalarBytes) -> MontgomeryScalar {
48+
Curve448::from_bytes_mod_order_wide(input)
49+
}
50+
}
51+
52+
/// The number of bytes needed to represent the scalar field
53+
pub type MontgomeryScalarBytes = ScalarBytes<Curve448>;
54+
/// The number of bytes needed to represent the safely create a scalar from a random bytes
55+
pub type WideMontgomeryScalarBytes = WideScalarBytes<Curve448>;
56+
57+
#[cfg(test)]
58+
mod test {
59+
use super::*;
60+
use hex_literal::hex;
61+
62+
#[test]
63+
fn test_basic_add() {
64+
let five = MontgomeryScalar::from(5u8);
65+
let six = MontgomeryScalar::from(6u8);
66+
67+
assert_eq!(five + six, MontgomeryScalar::from(11u8))
68+
}
69+
70+
#[test]
71+
fn test_basic_sub() {
72+
let ten = MontgomeryScalar::from(10u8);
73+
let five = MontgomeryScalar::from(5u8);
74+
assert_eq!(ten - five, MontgomeryScalar::from(5u8))
75+
}
76+
77+
#[test]
78+
fn test_basic_mul() {
79+
let ten = MontgomeryScalar::from(10u8);
80+
let five = MontgomeryScalar::from(5u8);
81+
82+
assert_eq!(ten * five, MontgomeryScalar::from(50u8))
83+
}
84+
85+
#[test]
86+
fn test_mul() {
87+
let a = MontgomeryScalar::new(U448::from_be_hex(
88+
"1e63e8073b089f0747cf8cac2c3dc2732aae8688a8fa552ba8cb0ae8c0be082e74d657641d9ac30a087b8fb97f8ed27dc96a3c35ffb823a3",
89+
));
90+
91+
let b = MontgomeryScalar::new(U448::from_be_hex(
92+
"16c5450acae1cb680a92de2d8e59b30824e8d4991adaa0e7bc343bcbd099595b188c6b1a1e30b38b17aa6d9be416b899686eb329d8bedc42",
93+
));
94+
95+
let exp = MontgomeryScalar::new(U448::from_be_hex(
96+
"31e055c14ca389edfccd61b3203d424bb9036ff6f2d89c1e07bcd93174e9335f36a1492008a3a0e46abd26f5994c9c2b1f5b3197a18d010a",
97+
));
98+
99+
assert_eq!(a * b, exp)
100+
}
101+
#[test]
102+
fn test_basic_square() {
103+
let a = MontgomeryScalar::new(U448::from_be_hex(
104+
"3162081604b3273b930392e5d2391f9d21cc3078f22c69514bb395e08dccc4866f08f3311370f8b83fa50692f640922b7e56a34bcf5fac3d",
105+
));
106+
let expected_a_squared = MontgomeryScalar::new(U448::from_be_hex(
107+
"1c1e32fc66b21c9c42d6e8e20487193cf6d49916421b290098f30de3713006cfe8ee9d21eeef7427f82a1fe036630c74b9acc2c2ede40f04",
108+
));
109+
110+
assert_eq!(a.square(), expected_a_squared)
111+
}
112+
113+
#[test]
114+
fn test_sanity_check_index_mut() {
115+
let mut x = MontgomeryScalar::ONE;
116+
x[0] = 2;
117+
assert_eq!(x, MontgomeryScalar::from(2u8))
118+
}
119+
#[test]
120+
fn test_basic_halving() {
121+
let eight = MontgomeryScalar::from(8u8);
122+
let four = MontgomeryScalar::from(4u8);
123+
let two = MontgomeryScalar::from(2u8);
124+
assert_eq!(eight.halve(), four);
125+
assert_eq!(four.halve(), two);
126+
assert_eq!(two.halve(), MontgomeryScalar::ONE);
127+
}
128+
129+
#[test]
130+
fn test_equals() {
131+
let a = MontgomeryScalar::from(5u8);
132+
let b = MontgomeryScalar::from(5u8);
133+
let c = MontgomeryScalar::from(10u8);
134+
assert_eq!(a, b);
135+
assert_ne!(a, c);
136+
}
137+
138+
#[test]
139+
fn test_basic_inversion() {
140+
// Test inversion from 2 to 100
141+
for i in 1..=100u8 {
142+
let x = MontgomeryScalar::from(i);
143+
let x_inv = x.invert();
144+
assert_eq!(x_inv * x, MontgomeryScalar::ONE)
145+
}
146+
147+
// Inversion of zero is zero
148+
let zero = MontgomeryScalar::ZERO;
149+
let expected_zero = zero.invert();
150+
assert_eq!(expected_zero, zero)
151+
}
152+
#[test]
153+
fn test_serialise() {
154+
let scalar = MontgomeryScalar::new(U448::from_be_hex(
155+
"0d79f6e375d3395ed9a6c4c3c49a1433fd7c58aa38363f74e9ab2c22a22347d79988f8e01e8a309f862a9f1052fcd042b9b1ed7115598f62",
156+
));
157+
let got = MontgomeryScalar::from_canonical_bytes(&scalar.into()).unwrap();
158+
assert_eq!(scalar, got)
159+
}
160+
#[test]
161+
fn test_from_canonical_bytes() {
162+
// ff..ff should fail
163+
let mut bytes = MontgomeryScalarBytes::from(hex!(
164+
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
165+
));
166+
bytes.reverse();
167+
let s = MontgomeryScalar::from_canonical_bytes(&bytes);
168+
assert!(<Choice as Into<bool>>::into(s.is_none()));
169+
170+
// n should fail
171+
let mut bytes = MontgomeryScalarBytes::from(hex!(
172+
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3"
173+
));
174+
bytes.reverse();
175+
let s = MontgomeryScalar::from_canonical_bytes(&bytes);
176+
assert!(<Choice as Into<bool>>::into(s.is_none()));
177+
178+
// n-1 should work
179+
let mut bytes = MontgomeryScalarBytes::from(hex!(
180+
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2"
181+
));
182+
bytes.reverse();
183+
let s = MontgomeryScalar::from_canonical_bytes(&bytes);
184+
match Option::<MontgomeryScalar>::from(s) {
185+
Some(s) => assert_eq!(s, MontgomeryScalar::ZERO - MontgomeryScalar::ONE),
186+
None => panic!("should not return None"),
187+
};
188+
}
189+
190+
#[test]
191+
fn test_from_bytes_mod_order_wide() {
192+
// n should become 0
193+
let mut bytes = WideMontgomeryScalarBytes::from(hex!(
194+
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3"
195+
));
196+
bytes.reverse();
197+
let s = MontgomeryScalar::from_bytes_mod_order_wide(&bytes);
198+
assert_eq!(s, MontgomeryScalar::ZERO);
199+
200+
// n-1 should stay the same
201+
let mut bytes = WideMontgomeryScalarBytes::from(hex!(
202+
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f2"
203+
));
204+
bytes.reverse();
205+
let s = MontgomeryScalar::from_bytes_mod_order_wide(&bytes);
206+
assert_eq!(s, MontgomeryScalar::ZERO - MontgomeryScalar::ONE);
207+
208+
// n+1 should become 1
209+
let mut bytes = WideMontgomeryScalarBytes::from(hex!(
210+
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f4"
211+
));
212+
bytes.reverse();
213+
let s = MontgomeryScalar::from_bytes_mod_order_wide(&bytes);
214+
assert_eq!(s, MontgomeryScalar::ONE);
215+
216+
// 2^896-1 should become 0x3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b5f
217+
let bytes = WideMontgomeryScalarBytes::from(hex!(
218+
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
219+
));
220+
let s = MontgomeryScalar::from_bytes_mod_order_wide(&bytes);
221+
let mut bytes = MontgomeryScalarBytes::from(hex!(
222+
"3402a939f823b7292052bcb7e4d070af1a9cc14ba3c47c44ae17cf725ee4d8380d66de2388ea18597af32c4bc1b195d9e3539257049b9b5f"
223+
));
224+
bytes.reverse();
225+
let reduced = MontgomeryScalar::from_canonical_bytes(&bytes).unwrap();
226+
assert_eq!(s, reduced);
227+
}
228+
229+
#[cfg(all(feature = "alloc", feature = "serde"))]
230+
#[test]
231+
fn serde() {
232+
use elliptic_curve::PrimeField;
233+
234+
let res = serde_json::to_string(&MontgomeryScalar::TWO_INV);
235+
assert!(res.is_ok());
236+
let sj = res.unwrap();
237+
238+
let res = serde_json::from_str::<MontgomeryScalar>(&sj);
239+
assert!(res.is_ok());
240+
assert_eq!(res.unwrap(), MontgomeryScalar::TWO_INV);
241+
242+
let res = serde_bare::to_vec(&MontgomeryScalar::TWO_INV);
243+
assert!(res.is_ok());
244+
let sb = res.unwrap();
245+
assert_eq!(sb.len(), 57);
246+
247+
let res = serde_bare::from_slice::<MontgomeryScalar>(&sb);
248+
assert!(res.is_ok());
249+
assert_eq!(res.unwrap(), MontgomeryScalar::TWO_INV);
250+
}
251+
}

ed448-goldilocks/src/montgomery/x.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// use crate::constants::A_PLUS_TWO_OVER_FOUR;
2-
use super::{MontgomeryPoint, ProjectiveMontgomeryPoint};
3-
use crate::EdwardsScalar;
2+
use super::{MontgomeryPoint, MontgomeryScalar, ProjectiveMontgomeryPoint};
43
use crate::edwards::extended::EdwardsPoint;
54
use crate::field::{ConstMontyType, FieldElement};
65
use core::fmt;
@@ -70,15 +69,15 @@ pub struct ProjectiveMontgomeryXpoint {
7069
pub(super) W: FieldElement,
7170
}
7271

73-
impl Mul<&EdwardsScalar> for &MontgomeryXpoint {
72+
impl Mul<&MontgomeryScalar> for &MontgomeryXpoint {
7473
type Output = ProjectiveMontgomeryXpoint;
7574

76-
fn mul(self, scalar: &EdwardsScalar) -> ProjectiveMontgomeryXpoint {
75+
fn mul(self, scalar: &MontgomeryScalar) -> ProjectiveMontgomeryXpoint {
7776
self.mul_internal(scalar).0
7877
}
7978
}
8079

81-
impl Mul<&MontgomeryXpoint> for &EdwardsScalar {
80+
impl Mul<&MontgomeryXpoint> for &MontgomeryScalar {
8281
type Output = ProjectiveMontgomeryXpoint;
8382

8483
fn mul(self, point: &MontgomeryXpoint) -> ProjectiveMontgomeryXpoint {
@@ -119,7 +118,7 @@ impl MontgomeryXpoint {
119118

120119
pub(super) fn mul_internal(
121120
&self,
122-
scalar: &EdwardsScalar,
121+
scalar: &MontgomeryScalar,
123122
) -> (ProjectiveMontgomeryXpoint, ProjectiveMontgomeryXpoint) {
124123
// Algorithm 8 of Costello-Smith 2017
125124
let mut x0 = ProjectiveMontgomeryXpoint::IDENTITY;
@@ -185,15 +184,15 @@ impl PartialEq for ProjectiveMontgomeryXpoint {
185184
}
186185
}
187186

188-
impl Mul<&EdwardsScalar> for &ProjectiveMontgomeryXpoint {
187+
impl Mul<&MontgomeryScalar> for &ProjectiveMontgomeryXpoint {
189188
type Output = ProjectiveMontgomeryXpoint;
190189

191-
fn mul(self, scalar: &EdwardsScalar) -> ProjectiveMontgomeryXpoint {
190+
fn mul(self, scalar: &MontgomeryScalar) -> ProjectiveMontgomeryXpoint {
192191
&self.to_affine() * scalar
193192
}
194193
}
195194

196-
impl Mul<&ProjectiveMontgomeryXpoint> for &EdwardsScalar {
195+
impl Mul<&ProjectiveMontgomeryXpoint> for &MontgomeryScalar {
197196
type Output = ProjectiveMontgomeryXpoint;
198197

199198
fn mul(self, point: &ProjectiveMontgomeryXpoint) -> ProjectiveMontgomeryXpoint {
@@ -311,13 +310,13 @@ mod tests {
311310

312311
#[test]
313312
fn test_montgomery_edwards() {
314-
let scalar = EdwardsScalar::from(200u32);
313+
let scalar = MontgomeryScalar::from(200u32);
315314

316315
// Montgomery scalar mul
317316
let montgomery_res = &(&ProjectiveMontgomeryXpoint::GENERATOR * &scalar) * &scalar;
318317

319318
// Goldilocks scalar mul
320-
let goldilocks_point = EdwardsPoint::GENERATOR * scalar * scalar;
319+
let goldilocks_point = EdwardsPoint::GENERATOR * scalar.to_scalar() * scalar.to_scalar();
321320
assert_eq!(
322321
goldilocks_point.to_montgomery_x(),
323322
montgomery_res.to_affine()

0 commit comments

Comments
 (0)