|
| 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 | +} |
0 commit comments