From efe66b11b2350d9689b80dbc0f7589038ebc8c03 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 20 Nov 2017 16:44:49 -0800 Subject: [PATCH 01/10] Add Makefile target for internal docs --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index e4bc142..5baa02c 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ doc: cargo rustdoc --features "nightly yolocrypto" -- --html-in-header rustdoc-include-katex-header.html + +doc-internal: + cargo rustdoc --features "nightly yolocrypto" -- --html-in-header rustdoc-include-katex-header.html --no-defaults --passes "collapse-docs" --passes "unindent-comments" + From e94c6f0a96d039b6ab07418d70a88cd64e0e7a97 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 20 Nov 2017 16:45:30 -0800 Subject: [PATCH 02/10] Make more Scalar methods pub(crate) --- src/scalar.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 8041cc7..35f53db 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -333,7 +333,7 @@ impl Scalar { } /// Get the bits of the scalar. - pub fn bits(&self) -> [i8; 256] { + pub(crate) fn bits(&self) -> [i8; 256] { let mut bits = [0i8; 256]; for i in 0..256 { // As i runs from 0..256, the bottom 3 bits index the bit, @@ -354,7 +354,7 @@ impl Scalar { /// Intuitively, this is like a binary expansion, except that we /// allow some coefficients to grow up to `2^(w-1)` so that the /// nonzero coefficients are as sparse as possible. - pub fn non_adjacent_form(&self) -> [i8; 256] { + pub(crate) fn non_adjacent_form(&self) -> [i8; 256] { // Step 1: write out bits of the scalar let mut naf = self.bits(); @@ -399,7 +399,7 @@ impl Scalar { /// /// Precondition: self[31] <= 127. This is the case whenever /// `self` is reduced. - pub fn to_radix_16(&self) -> [i8; 64] { + pub(crate) fn to_radix_16(&self) -> [i8; 64] { debug_assert!(self[31] <= 127); let mut output = [0i8; 64]; From 033a90890cc8d9fbc074bcb7db82c920837ae772 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 21 Nov 2017 10:43:49 -0800 Subject: [PATCH 03/10] Remove Scalar::{add, sub, mul} methods --- src/scalar.rs | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 35f53db..5b0eac9 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -115,47 +115,47 @@ impl IndexMut for Scalar { impl<'b> MulAssign<&'b Scalar> for Scalar { fn mul_assign(&mut self, _rhs: &'b Scalar) { - *self = Scalar::mul(self, _rhs) + *self = UnpackedScalar::mul(&self.unpack(), &_rhs.unpack()).pack(); } } impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { type Output = Scalar; fn mul(self, _rhs: &'b Scalar) -> Scalar { - Scalar::mul(self, _rhs) + UnpackedScalar::mul(&self.unpack(), &_rhs.unpack()).pack() } } impl<'b> AddAssign<&'b Scalar> for Scalar { fn add_assign(&mut self, _rhs: &'b Scalar) { - *self = Scalar::add(self, _rhs); + *self = UnpackedScalar::add(&self.unpack(), &_rhs.unpack()).pack(); } } impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { type Output = Scalar; fn add(self, _rhs: &'b Scalar) -> Scalar { - Scalar::add(self, _rhs) + UnpackedScalar::add(&self.unpack(), &_rhs.unpack()).pack() } } impl<'b> SubAssign<&'b Scalar> for Scalar { fn sub_assign(&mut self, _rhs: &'b Scalar) { - *self = Scalar::sub(self, _rhs); + *self = UnpackedScalar::sub(&self.unpack(), &_rhs.unpack()).pack(); } } impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { type Output = Scalar; fn sub(self, _rhs: &'b Scalar) -> Scalar { - Scalar::sub(self, _rhs) + UnpackedScalar::sub(&self.unpack(), &_rhs.unpack()).pack() } } impl<'a> Neg for &'a Scalar { type Output = Scalar; fn neg(self) -> Scalar { - Scalar::sub(&Scalar::zero(), self) + &Scalar::zero() - self } } @@ -433,21 +433,6 @@ impl Scalar { UnpackedScalar::from_bytes(&self.0) } - /// Compute `a + b` (mod l) - pub fn add(a: &Scalar, b: &Scalar) -> Scalar { - UnpackedScalar::add(&a.unpack(), &b.unpack()).pack() - } - - /// Compute `a - b` (mod l). - pub fn sub(a: &Scalar, b: &Scalar) -> Scalar { - UnpackedScalar::sub(&a.unpack(), &b.unpack()).pack() - } - - /// Compute `a * b` (mod l). - pub fn mul(a: &Scalar, b: &Scalar) -> Scalar { - UnpackedScalar::mul(&a.unpack(), &b.unpack()).pack() - } - /// Compute `(a * b) + c` (mod l). pub fn multiply_add(a: &Scalar, b: &Scalar, c: &Scalar) -> Scalar { UnpackedScalar::add(&UnpackedScalar::mul(&a.unpack(), &b.unpack()), &c.unpack()).pack() From df182b79d05cbcf64b04ffc74aae41939c591cdb Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 21 Nov 2017 10:50:57 -0800 Subject: [PATCH 04/10] Rename Scalar::reduce to Scalar::reduce_wide This opens the `Scalar::reduce` name for reduction mod l. --- src/scalar.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 5b0eac9..ec5e225 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -254,7 +254,7 @@ impl Scalar { pub fn random(rng: &mut T) -> Self { let mut scalar_bytes = [0u8; 64]; rng.fill_bytes(&mut scalar_bytes); - Scalar::reduce(&scalar_bytes) + Scalar::reduce_wide(&scalar_bytes) } /// Hash a slice of bytes into a scalar. @@ -299,7 +299,7 @@ impl Scalar { // XXX this seems clumsy let mut output = [0u8; 64]; output.copy_from_slice(hash.result().as_slice()); - Scalar::reduce(&output) + Scalar::reduce_wide(&output) } /// View this `Scalar` as a sequence of bytes. @@ -439,7 +439,7 @@ impl Scalar { } /// Reduce a 512-bit little endian number mod l - pub fn reduce(input: &[u8; 64]) -> Scalar { + pub fn reduce_wide(input: &[u8; 64]) -> Scalar { UnpackedScalar::from_bytes_wide(input).pack() } } @@ -585,11 +585,11 @@ mod test { // also_a = (a mod l) tmp[0..32].copy_from_slice(&a_bytes[..]); - let also_a = Scalar::reduce(&tmp); + let also_a = Scalar::reduce_wide(&tmp); // also_b = (b mod l) tmp[0..32].copy_from_slice(&b_bytes[..]); - let also_b = Scalar::reduce(&tmp); + let also_b = Scalar::reduce_wide(&tmp); let expected_c = &a * &b; let also_expected_c = &also_a * &also_b; @@ -670,7 +670,7 @@ mod test { } #[test] - fn scalar_reduce() { + fn reduce_wide() { let mut bignum = [0u8; 64]; // set bignum = x + 2^256x for i in 0..32 { @@ -683,7 +683,7 @@ mod test { 69, 99, 158, 216, 23, 173, 63, 100, 204, 0, 91, 50, 219, 153, 57, 249, 28, 82, 31, 197, 100, 165, 192, 8]); - let test_red = Scalar::reduce(&bignum); + let test_red = Scalar::reduce_wide(&bignum); for i in 0..32 { assert!(test_red[i] == reduced[i]); } @@ -719,7 +719,7 @@ mod test { #[test] - fn montgomery_reduce_matches_reduce() { + fn montgomery_reduce_matches_reduce_wide() { let mut bignum = [0u8; 64]; // set bignum = x + 2^256x @@ -733,7 +733,7 @@ mod test { 69, 99, 158, 216, 23, 173, 63, 100, 204, 0, 91, 50, 219, 153, 57, 249, 28, 82, 31, 197, 100, 165, 192, 8]); - let reduced = Scalar::reduce(&bignum); + let reduced = Scalar::reduce_wide(&bignum); // The reduced scalar should match the expected assert_eq!(reduced.0, expected.0); From d88f92276a3b17afe389b232d1b3effbe4260eac Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 21 Nov 2017 11:28:25 -0800 Subject: [PATCH 05/10] Add Scalar::reduce method --- src/scalar.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index ec5e225..b7bd2b0 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -47,6 +47,7 @@ use subtle::ConditionallyAssignable; use subtle::Equal; use backend; +use constants; /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. #[cfg(feature="radix_51")] @@ -438,6 +439,14 @@ impl Scalar { UnpackedScalar::add(&UnpackedScalar::mul(&a.unpack(), &b.unpack()), &c.unpack()).pack() } + /// Reduce this `Scalar` mod l. + pub fn reduce(&self) -> Scalar { + let x = self.unpack(); + let xR = UnpackedScalar::mul_internal(&x, &constants::R); + let x_mod_l = UnpackedScalar::montgomery_reduce(&xR); + x_mod_l.pack() + } + /// Reduce a 512-bit little endian number mod l pub fn reduce_wide(input: &[u8; 64]) -> Scalar { UnpackedScalar::from_bytes_wide(input).pack() @@ -669,6 +678,17 @@ mod test { } } + #[test] + fn reduce() { + let biggest = Scalar([0xff; 32]); + // sage: l = 2^252 + 27742317777372353535851937790883648493 + // sage: big = 2^256 - 1 + // sage: repr((big % l).digits(256)) + let biggest_mod_l = Scalar([28, 149, 152, 141, 116, 49, 236, 214, 112, 207, 125, 115, 244, 91, 239, 198, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15]); + let reduced = biggest.reduce(); + assert_eq!(reduced, biggest_mod_l); + } + #[test] fn reduce_wide() { let mut bignum = [0u8; 64]; @@ -717,7 +737,6 @@ mod test { assert_eq!(should_be_unpacked.0, unpacked.0); } - #[test] fn montgomery_reduce_matches_reduce_wide() { let mut bignum = [0u8; 64]; @@ -769,6 +788,13 @@ mod bench { use super::*; use super::test::{X}; + #[bench] + fn reduce(b: &mut Bencher) { + let unreduced = Scalar([0xff; 32]); + + b.iter(|| unreduced.reduce()); + } + #[bench] fn scalar_random(b: &mut Bencher) { let mut csprng: OsRng = OsRng::new().unwrap(); From d32fe9772b296511638f662da75b71a41bc61eb6 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 22 Nov 2017 16:04:47 -0800 Subject: [PATCH 06/10] Ensure that all Scalars are bounded by 2^255. This commit defines a Scalar to hold an integer representing an element of Z/lZ. Applications like X/Ed25519 that care about the bit-patterns of the scalars they use can set a specific bit-pattern using the `from_bits` constructor. Applications that want to treat scalars as integers mod l can use the `from_bytes_mod_order` constructor. Either way, the constructor ensures that the integer representing each Scalar is bounded by 2^255 so that the high bit is set. This means that any Scalar object is always safe to use for scalar multiplication, while maintaining compatibility with both the Ristretto use-case and the X/Ed25519 usecase. --- src/constants.rs | 34 +++--- src/edwards.rs | 30 +++--- src/montgomery.rs | 14 --- src/scalar.rs | 260 ++++++++++++++++++++++++++++++---------------- 4 files changed, 206 insertions(+), 132 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 27c06ef..1ec4850 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -64,28 +64,34 @@ pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BAS /// `BASEPOINT_ORDER` is the order of base point, i.e. `l = 2^252 + /// 27742317777372353535851937790883648493`, in little-endian bytes. -pub const BASEPOINT_ORDER: Scalar = Scalar([ - 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, - 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -]); +pub const BASEPOINT_ORDER: Scalar = Scalar{ + bytes: [ + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + ], +}; /// `BASEPOINT_ORDER_MINUS_1` is the order of base point minus one, i.e. `l-1`, in little-endian bytes. -pub const BASEPOINT_ORDER_MINUS_1: Scalar = Scalar([ - 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, - 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -]); +pub const BASEPOINT_ORDER_MINUS_1: Scalar = Scalar{ + bytes: [ + 0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + ], +}; /// `BASEPOINT_ORDER_MINUS_2` is the order of base point minus two, i.e. `l-2`, in little-endian bytes. -pub const BASEPOINT_ORDER_MINUS_2: Scalar = Scalar([ +pub const BASEPOINT_ORDER_MINUS_2: Scalar = Scalar{ + bytes: [ 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, -]); + ], +}; // Precomputed basepoint table is generated into a file by build.rs diff --git a/src/edwards.rs b/src/edwards.rs index d31db32..552f696 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -878,18 +878,24 @@ mod test { 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96, 0x96, 0x70]); /// 4493907448824000747700850167940867464579944529806937181821189941592931634714 - pub static A_SCALAR: Scalar = Scalar([ - 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, - 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d, - 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, - 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09]); + pub static A_SCALAR: Scalar = Scalar{ + bytes: [ + 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, + 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d, + 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, + 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09, + ], + }; /// 2506056684125797857694181776241676200180934651973138769173342316833279714961 - pub static B_SCALAR: Scalar = Scalar([ - 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, - 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0, 0xb3, 0x2e, - 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, - 0x56, 0xa7, 0xd4, 0xaa, 0xb8, 0x60, 0x8a, 0x05]); + pub static B_SCALAR: Scalar = Scalar{ + bytes: [ + 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, + 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0, 0xb3, 0x2e, + 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, + 0x56, 0xa7, 0xd4, 0xaa, 0xb8, 0x60, 0x8a, 0x05, + ], + }; /// A_SCALAR * basepoint, computed with ed25519.py pub static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([ @@ -1050,8 +1056,8 @@ mod test { #[test] #[cfg(feature="precomputed_tables")] fn basepoint_mult_two_vs_basepoint2() { - let mut two_bytes = [0u8; 32]; two_bytes[0] = 2; - let bp2 = &constants::ED25519_BASEPOINT_TABLE * &Scalar(two_bytes); + let two = Scalar::from_u64(2); + let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two; assert_eq!(bp2.compress(), BASE2_CMPRSSD); } diff --git a/src/montgomery.rs b/src/montgomery.rs index 9f42fab..94476e5 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -549,20 +549,6 @@ mod test { assert_eq!(result.compress(), expected.to_montgomery().compress()); } - - #[test] - #[should_panic(expected = "assertion failed: self[31] <= 127")] - #[cfg(feature="precomputed_tables")] - fn ladder_matches_scalarmult_with_scalar_high_bit_set() { - let mut s: Scalar = Scalar::one(); - - s[31] = 255; - - let result: MontgomeryPoint = &BASE_COMPRESSED_MONTGOMERY.decompress() * &s; - let expected: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s; - - assert_eq!(result.compress(), expected.to_montgomery().compress()) - } } #[cfg(all(test, feature = "bench"))] diff --git a/src/scalar.rs b/src/scalar.rs index b7bd2b0..1c9b890 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -12,28 +12,31 @@ //! Arithmetic for scalar multiplication. //! -//! The Ed25519 basepoint P has prime order +//! Both the Ristretto group and the Ed25519 basepoint have prime order +//! \\( \ell = 2\^{252} + 27742317777372353535851937790883648493 \\). //! -//! l = 2^252 + 27742317777372353535851937790883648493. +//! The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which +//! represents an element of \\(\mathbb Z / \ell\\). //! -//! Thus a multiple `aP` of the basepoint (with a ∈ ℤ) depends only -//! on the value of `a (mod l)`, or equivalently, the image of `a` in -//! the quotient ℤ/lℤ. +//! The code is intended to be useful with both the Ristretto group +//! (where everything is done modulo \\( \ell \\), and the X/Ed25519 +//! setting, which mandates specific bit-twiddles that are not +//! well-defined modulo \\( \ell \\). //! -//! The `Scalar` struct represents an element in ℤ/lℤ. +//! To create a `Scalar` by reducing a 256-bit integer mod \\( \ell \\), +//! use `Scalar::from_bytes_mod_order`. //! -//! In contrast to `FieldElement`s, `Scalar`s are stored in -//! memory as bytes, allowing easy access to the bits of the `Scalar` -//! when multiplying a point by a scalar. For efficient arithmetic -//! between two scalars, the `UnpackedScalar` struct (internally -//! either `Scalar32` or `Scalar64`) is stored as limbs. +//! To create a `Scalar` with a specific bit-pattern (e.g., for +//! compatibility with X25519 "clamping"), use `Scalar::from_bits`. +//! +//! All arithmetic on `Scalars` is done modulo \\( \ell \\). use core::fmt::Debug; use core::ops::Neg; use core::ops::{Add, AddAssign}; use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; -use core::ops::{Index, IndexMut}; +use core::ops::{Index}; use core::cmp::{Eq, PartialEq}; #[cfg(feature = "std")] @@ -50,10 +53,16 @@ use backend; use constants; /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. +/// +/// This is a type alias for one of the scalar types in the `backend` +/// module. #[cfg(feature="radix_51")] type UnpackedScalar = backend::u64::scalar::Scalar64; /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. +/// +/// This is a type alias for one of the scalar types in the `backend` +/// module. #[cfg(not(feature="radix_51"))] type UnpackedScalar = backend::u32::scalar::Scalar32; @@ -64,11 +73,47 @@ type UnpackedScalar = backend::u32::scalar::Scalar32; /// /// is the order of the basepoint. The `Scalar` is stored as bytes. #[derive(Copy, Clone)] -pub struct Scalar(pub [u8; 32]); +pub struct Scalar { + /// `bytes` is a little-endian byte encoding of an integer representing a scalar modulo the group order. + /// + /// # Invariant + /// + /// The integer representing this scalar must be bounded above by 2^255, or equivalently the high bit of `bytes[31]` must be zero. + /// + // XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could make the Scalar constructors const fns and use those instead. + pub(crate) bytes: [u8; 32], +} + +impl Scalar { + /// Construct a `Scalar` by reducing a 256-bit integer modulo the group order. + pub fn from_bytes_mod_order(bytes: [u8;32]) -> Scalar { + // Temporarily allow s_unreduced.bytes > 2^255 ... + let s_unreduced = Scalar{bytes: bytes}; + + // Then reduce mod the group order and return the reduced representative. + let s = s_unreduced.reduce(); + debug_assert_eq!(0u8, s[31] >> 7); + + s + } + + /// Construct a `Scalar` from the low 255 bits of a 256-bit integer. + /// + /// This function is intended for applications like X25519 which + /// require specific bit-patterns when performing scalar + /// multiplication. + pub fn from_bits(bytes: [u8; 32]) -> Scalar { + let mut s = Scalar{bytes: bytes}; + // Ensure that s < 2^255 by masking the high bit + s.bytes[31] &= 0b0111_1111; + + s + } +} impl Debug for Scalar { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "Scalar: {:?}", &self.0[..]) + write!(f, "Scalar{{\n\tbytes: {:?},\n}}", &self.bytes) } } @@ -85,7 +130,7 @@ impl PartialEq for Scalar { /// /// True if they are equal, and false otherwise. fn eq(&self, other: &Self) -> bool { - slices_equal(&self.0, &other.0) == 1u8 + slices_equal(&self.bytes, &other.bytes) == 1u8 } } @@ -96,21 +141,16 @@ impl Equal for Scalar { /// /// `1u8` if they are equal, and `0u8` otherwise. fn ct_eq(&self, other: &Self) -> u8 { - slices_equal(&self.0, &other.0) + slices_equal(&self.bytes, &other.bytes) } } impl Index for Scalar { type Output = u8; + /// Index the bytes of the representative for this `Scalar`. Mutation is not permitted. fn index(&self, _index: usize) -> &u8 { - &(self.0[_index]) - } -} - -impl IndexMut for Scalar { - fn index_mut(&mut self, _index: usize) -> &mut u8 { - &mut (self.0[_index]) + &(self.bytes[_index]) } } @@ -169,10 +209,8 @@ impl ConditionallyAssignable for Scalar { /// # use curve25519_dalek::scalar::Scalar; /// # use subtle::ConditionallyAssignable; /// # fn main() { - /// let a = Scalar([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - /// 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); - /// let b = Scalar([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - /// 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); + /// let a = Scalar::from_bits([0u8;32]); + /// let b = Scalar::from_bits([1u8;32]); /// let mut t = a; /// t.conditional_assign(&b, 0u8); /// assert!(t[0] == a[0]); @@ -190,7 +228,7 @@ impl ConditionallyAssignable for Scalar { // if choice = 1u8, mask = (-1i8) as u8 = 11111111 let mask = -(choice as i8) as u8; for i in 0..32 { - self[i] ^= mask & (self[i] ^ other[i]); + self.bytes[i] ^= mask & (self.bytes[i] ^ other.bytes[i]); } } } @@ -305,27 +343,31 @@ impl Scalar { /// View this `Scalar` as a sequence of bytes. pub fn as_bytes(&self) -> &[u8; 32] { - &self.0 + &self.bytes } /// Construct the additive identity pub fn zero() -> Self { - Scalar([0u8; 32]) + Scalar { bytes: [0u8; 32]} } /// Construct the multiplicative identity pub fn one() -> Self { - Scalar([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) + Scalar { + bytes: [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + } } /// Construct a scalar from the given `u64`. pub fn from_u64(x: u64) -> Scalar { - let mut s = Scalar::zero(); + let mut s_bytes = [0u8; 32]; for i in 0..8 { - s[i] = (x >> (i*8)) as u8; + s_bytes[i] = (x >> (i*8)) as u8; } - s + Scalar{ bytes: s_bytes } } /// Compute the multiplicative inverse of this scalar. @@ -339,7 +381,7 @@ impl Scalar { for i in 0..256 { // As i runs from 0..256, the bottom 3 bits index the bit, // while the upper bits index the byte. - bits[i] = ((self.0[i>>3] >> (i&7)) & 1u8) as i8; + bits[i] = ((self.bytes[i>>3] >> (i&7)) & 1u8) as i8; } bits } @@ -431,7 +473,7 @@ impl Scalar { /// Unpack this `Scalar` to an `UnpackedScalar` pub(crate) fn unpack(&self) -> UnpackedScalar { - UnpackedScalar::from_bytes(&self.0) + UnpackedScalar::from_bytes(&self.bytes) } /// Compute `(a * b) + c` (mod l). @@ -456,7 +498,7 @@ impl Scalar { impl UnpackedScalar { /// Pack the limbs of this `UnpackedScalar` into a `Scalar`. fn pack(&self) -> Scalar { - Scalar(self.to_bytes()) + Scalar{ bytes: self.to_bytes() } } /// Compute the multiplicative inverse of this scalar. @@ -524,48 +566,69 @@ mod test { use constants; /// x = 2238329342913194256032495932344128051776374960164957527413114840482143558222 - pub static X: Scalar = Scalar( - [0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, - 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52, - 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, - 0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04]); + pub static X: Scalar = Scalar{ + bytes: [ + 0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, + 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52, + 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, + 0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04, + ], + }; /// 1/x = 6859937278830797291664592131120606308688036382723378951768035303146619657244 - pub static XINV: Scalar = Scalar( - [0x1c, 0xdc, 0x17, 0xfc, 0xe0, 0xe9, 0xa5, 0xbb, - 0xd9, 0x24, 0x7e, 0x56, 0xbb, 0x01, 0x63, 0x47, - 0xbb, 0xba, 0x31, 0xed, 0xd5, 0xa9, 0xbb, 0x96, - 0xd5, 0x0b, 0xcd, 0x7a, 0x3f, 0x96, 0x2a, 0x0f]); + pub static XINV: Scalar = Scalar{ + bytes: [ + 0x1c, 0xdc, 0x17, 0xfc, 0xe0, 0xe9, 0xa5, 0xbb, + 0xd9, 0x24, 0x7e, 0x56, 0xbb, 0x01, 0x63, 0x47, + 0xbb, 0xba, 0x31, 0xed, 0xd5, 0xa9, 0xbb, 0x96, + 0xd5, 0x0b, 0xcd, 0x7a, 0x3f, 0x96, 0x2a, 0x0f, + ], + }; /// y = 2592331292931086675770238855846338635550719849568364935475441891787804997264 - pub static Y: Scalar = Scalar( - [0x90, 0x76, 0x33, 0xfe, 0x1c, 0x4b, 0x66, 0xa4, - 0xa2, 0x8d, 0x2d, 0xd7, 0x67, 0x83, 0x86, 0xc3, - 0x53, 0xd0, 0xde, 0x54, 0x55, 0xd4, 0xfc, 0x9d, - 0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, 0xbb, 0x05]); + pub static Y: Scalar = Scalar{ + bytes: [ + 0x90, 0x76, 0x33, 0xfe, 0x1c, 0x4b, 0x66, 0xa4, + 0xa2, 0x8d, 0x2d, 0xd7, 0x67, 0x83, 0x86, 0xc3, + 0x53, 0xd0, 0xde, 0x54, 0x55, 0xd4, 0xfc, 0x9d, + 0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, 0xbb, 0x05, + ], + }; /// z = 5033871415930814945849241457262266927579821285980625165479289807629491019013 - pub static Z: Scalar = Scalar( - [0x05, 0x9d, 0x3e, 0x0b, 0x09, 0x26, 0x50, 0x3d, - 0xa3, 0x84, 0xa1, 0x3c, 0x92, 0x7a, 0xc2, 0x06, - 0x41, 0x98, 0xcf, 0x34, 0x3a, 0x24, 0xd5, 0xb7, - 0xeb, 0x33, 0x6a, 0x2d, 0xfc, 0x11, 0x21, 0x0b]); + pub static Z: Scalar = Scalar{ + bytes: [ + 0x05, 0x9d, 0x3e, 0x0b, 0x09, 0x26, 0x50, 0x3d, + 0xa3, 0x84, 0xa1, 0x3c, 0x92, 0x7a, 0xc2, 0x06, + 0x41, 0x98, 0xcf, 0x34, 0x3a, 0x24, 0xd5, 0xb7, + 0xeb, 0x33, 0x6a, 0x2d, 0xfc, 0x11, 0x21, 0x0b, + ], + }; /// w = 3486911242272497535104403593250518247409663771668155364040899665266216860804 - static W: Scalar = Scalar( - [0x84, 0xfc, 0xbc, 0x4f, 0x78, 0x12, 0xa0, 0x06, - 0xd7, 0x91, 0xd9, 0x7a, 0x3a, 0x27, 0xdd, 0x1e, - 0x21, 0x43, 0x45, 0xf7, 0xb1, 0xb9, 0x56, 0x7a, - 0x81, 0x30, 0x73, 0x44, 0x96, 0x85, 0xb5, 0x07]); + static W: Scalar = Scalar{ + bytes: [ + 0x84, 0xfc, 0xbc, 0x4f, 0x78, 0x12, 0xa0, 0x06, + 0xd7, 0x91, 0xd9, 0x7a, 0x3a, 0x27, 0xdd, 0x1e, + 0x21, 0x43, 0x45, 0xf7, 0xb1, 0xb9, 0x56, 0x7a, + 0x81, 0x30, 0x73, 0x44, 0x96, 0x85, 0xb5, 0x07, + ], + }; /// x*y = 5690045403673944803228348699031245560686958845067437804563560795922180092780 - static X_TIMES_Y: Scalar = Scalar( - [0x6c, 0x33, 0x74, 0xa1, 0x89, 0x4f, 0x62, 0x21, - 0x0a, 0xaa, 0x2f, 0xe1, 0x86, 0xa6, 0xf9, 0x2c, - 0xe0, 0xaa, 0x75, 0xc2, 0x77, 0x95, 0x81, 0xc2, - 0x95, 0xfc, 0x08, 0x17, 0x9a, 0x73, 0x94, 0x0c]); + static X_TIMES_Y: Scalar = Scalar{ + bytes: [ + 0x6c, 0x33, 0x74, 0xa1, 0x89, 0x4f, 0x62, 0x21, + 0x0a, 0xaa, 0x2f, 0xe1, 0x86, 0xa6, 0xf9, 0x2c, + 0xe0, 0xaa, 0x75, 0xc2, 0x77, 0x95, 0x81, 0xc2, + 0x95, 0xfc, 0x08, 0x17, 0x9a, 0x73, 0x94, 0x0c, + ], + }; - static A_SCALAR: Scalar = Scalar([ - 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, - 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d, - 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, - 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09]); + static A_SCALAR: Scalar = Scalar{ + bytes: [ + 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, + 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d, + 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, + 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09, + ], + }; static A_NAF: [i8; 256] = [0,13,0,0,0,0,0,0,0,7,0,0,0,0,0,0,-9,0,0,0,0,-11,0,0,0,0,3,0,0,0,0,1, @@ -586,9 +649,9 @@ mod test { // LE bytes of 6432735165214683820902750800207468552549813371247423777071615116673864412038 let c_bytes = [134, 171, 119, 216, 180, 128, 178, 62, 171, 132, 32, 62, 34, 119, 104, 193, 47, 215, 181, 250, 14, 207, 172, 93, 75, 207, 211, 103, 144, 204, 56, 14]; - let a = Scalar(a_bytes); - let b = Scalar(b_bytes); - let c = Scalar(c_bytes); + let a = Scalar::from_bytes_mod_order(a_bytes); + let b = Scalar::from_bytes_mod_order(b_bytes); + let c = Scalar::from_bytes_mod_order(c_bytes); let mut tmp = [0u8; 64]; @@ -641,8 +704,7 @@ mod test { #[test] fn impl_add() { - let mut two = Scalar::zero(); two[0] = 2; - let two = two; + let two = Scalar::from_u64(2); let one = Scalar::one(); let should_be_two = &one + &one; assert_eq!(should_be_two, two); @@ -680,13 +742,19 @@ mod test { #[test] fn reduce() { - let biggest = Scalar([0xff; 32]); + let biggest = Scalar::from_bytes_mod_order([0xff; 32]); // sage: l = 2^252 + 27742317777372353535851937790883648493 // sage: big = 2^256 - 1 // sage: repr((big % l).digits(256)) - let biggest_mod_l = Scalar([28, 149, 152, 141, 116, 49, 236, 214, 112, 207, 125, 115, 244, 91, 239, 198, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15]); - let reduced = biggest.reduce(); - assert_eq!(reduced, biggest_mod_l); + let biggest_mod_l = Scalar{ + bytes: [ + 28, 149, 152, 141, 116, 49, 236, 214, + 112, 207, 125, 115, 244, 91, 239, 198, + 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 15, + ], + }; + assert_eq!(biggest, biggest_mod_l); } #[test] @@ -699,10 +767,14 @@ mod test { } // 3958878930004874126169954872055634648693766179881526445624823978500314864344 // = x + 2^256x (mod l) - let reduced = Scalar([216, 154, 179, 139, 210, 121, 2, 71, - 69, 99, 158, 216, 23, 173, 63, 100, - 204, 0, 91, 50, 219, 153, 57, 249, - 28, 82, 31, 197, 100, 165, 192, 8]); + let reduced = Scalar{ + bytes: [ + 216, 154, 179, 139, 210, 121, 2, 71, + 69, 99, 158, 216, 23, 173, 63, 100, + 204, 0, 91, 50, 219, 153, 57, 249, + 28, 82, 31, 197, 100, 165, 192, 8, + ], + }; let test_red = Scalar::reduce_wide(&bignum); for i in 0..32 { assert!(test_red[i] == reduced[i]); @@ -748,14 +820,18 @@ mod test { } // x + 2^256x (mod l) // = 3958878930004874126169954872055634648693766179881526445624823978500314864344 - let expected = Scalar([216, 154, 179, 139, 210, 121, 2, 71, - 69, 99, 158, 216, 23, 173, 63, 100, - 204, 0, 91, 50, 219, 153, 57, 249, - 28, 82, 31, 197, 100, 165, 192, 8]); + let expected = Scalar{ + bytes: [ + 216, 154, 179, 139, 210, 121, 2, 71, + 69, 99, 158, 216, 23, 173, 63, 100, + 204, 0, 91, 50, 219, 153, 57, 249, + 28, 82, 31, 197, 100, 165, 192, 8 + ], + }; let reduced = Scalar::reduce_wide(&bignum); // The reduced scalar should match the expected - assert_eq!(reduced.0, expected.0); + assert_eq!(reduced.bytes, expected.bytes); // (x + 2^256x) * R let interim = UnpackedScalar::mul_internal(&UnpackedScalar::from_bytes_wide(&bignum), @@ -790,7 +866,7 @@ mod bench { #[bench] fn reduce(b: &mut Bencher) { - let unreduced = Scalar([0xff; 32]); + let unreduced = Scalar::from_bits([0xff; 32]); b.iter(|| unreduced.reduce()); } From 34639725d4a49cfcfc05349e784671beb9a882f8 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 22 Nov 2017 16:22:16 -0800 Subject: [PATCH 07/10] clean up reduction test --- src/scalar.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 1c9b890..b8b8072 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -621,6 +621,18 @@ mod test { ], }; + /// sage: l = 2^252 + 27742317777372353535851937790883648493 + /// sage: big = 2^256 - 1 + /// sage: repr((big % l).digits(256)) + static CANONICAL_2_256_MINUS_1: Scalar = Scalar{ + bytes: [ + 28, 149, 152, 141, 116, 49, 236, 214, + 112, 207, 125, 115, 244, 91, 239, 198, + 254, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 15, + ], + }; + static A_SCALAR: Scalar = Scalar{ bytes: [ 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, @@ -743,18 +755,7 @@ mod test { #[test] fn reduce() { let biggest = Scalar::from_bytes_mod_order([0xff; 32]); - // sage: l = 2^252 + 27742317777372353535851937790883648493 - // sage: big = 2^256 - 1 - // sage: repr((big % l).digits(256)) - let biggest_mod_l = Scalar{ - bytes: [ - 28, 149, 152, 141, 116, 49, 236, 214, - 112, 207, 125, 115, 244, 91, 239, 198, - 254, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 15, - ], - }; - assert_eq!(biggest, biggest_mod_l); + assert_eq!(biggest, CANONICAL_2_256_MINUS_1); } #[test] From 9855260bfd24d3adaa491942b415f1f86546eb8f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 22 Nov 2017 16:23:32 -0800 Subject: [PATCH 08/10] Add a method to test if a Scalar is canonical --- src/scalar.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index b8b8072..0006140 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -489,6 +489,28 @@ impl Scalar { x_mod_l.pack() } + /// Check whether this `Scalar` is the canonical representative mod \\(\ell\\). + /// + /// This is intended for uses like input validation, where variable-time code is acceptable. + /// + /// ``` + /// # extern crate curve25519_dalek; + /// # extern crate subtle; + /// # use curve25519_dalek::scalar::Scalar; + /// # use subtle::ConditionallyAssignable; + /// # fn main() { + /// // 2^255 - 1, since `from_bits` clears the high bit + /// let _2_255_minus_1 = Scalar::from_bits([0xff;32]); + /// assert!(!_2_255_minus_1.is_canonical()); + /// + /// let reduced = _2_255_minus_1.reduce(); + /// assert!(reduced.is_canonical()); + /// # } + /// ``` + pub fn is_canonical(&self) -> bool { + *self == self.reduce() + } + /// Reduce a 512-bit little endian number mod l pub fn reduce_wide(input: &[u8; 64]) -> Scalar { UnpackedScalar::from_bytes_wide(input).pack() From c1f63028793f23ce1a674705d960b5c58e8a91ae Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 22 Nov 2017 16:45:16 -0800 Subject: [PATCH 09/10] Add a Scalar::from_bytes_canonical method --- src/scalar.rs | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 0006140..73110e9 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -23,13 +23,16 @@ //! setting, which mandates specific bit-twiddles that are not //! well-defined modulo \\( \ell \\). //! +//! To create a `Scalar` from a supposedly canonical encoding, use +//! `Scalar::from_canonical_bytes`. +//! //! To create a `Scalar` by reducing a 256-bit integer mod \\( \ell \\), //! use `Scalar::from_bytes_mod_order`. //! //! To create a `Scalar` with a specific bit-pattern (e.g., for //! compatibility with X25519 "clamping"), use `Scalar::from_bits`. //! -//! All arithmetic on `Scalars` is done modulo \\( \ell \\). +//! All arithmetic on `Scalars` is done modulo \\( \ell \\). use core::fmt::Debug; use core::ops::Neg; @@ -97,6 +100,25 @@ impl Scalar { s } + /// Attempt to construct a `Scalar` from a canonical byte representation. + /// + /// # Return + /// + /// - `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: [u8; 32]) -> Option { + // Check that the high bit is not set + if (bytes[31] >> 7) != 0u8 { return None; } + let candidate = Scalar::from_bits(bytes); + + if candidate.is_canonical() { + Some(candidate) + } else { + None + } + } + /// Construct a `Scalar` from the low 255 bits of a 256-bit integer. /// /// This function is intended for applications like X25519 which @@ -867,12 +889,30 @@ mod test { assert_eq!(montgomery_reduced.0, expected.unpack().0) } - #[cfg(feature = "serde")] - use serde_cbor; + #[test] + fn canonical_decoding() { + // canonical encoding of 1667457891 + let canonical_bytes = [99, 99, 99, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,]; + + // encoding of + // 7265385991361016183439748078976496179028704920197054998554201349516117938192 + // = 28380414028753969466561515933501938171588560817147392552250411230663687203 (mod l) + // non_canonical because unreduced mod l + let non_canonical_bytes_because_unreduced = [16; 32]; + + // encoding with high bit set, to check that the parser isn't pre-masking the high bit + let non_canonical_bytes_because_highbit = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128]; + + assert!( Scalar::from_canonical_bytes(canonical_bytes).is_some() ); + assert!( Scalar::from_canonical_bytes(non_canonical_bytes_because_unreduced).is_none() ); + assert!( Scalar::from_canonical_bytes(non_canonical_bytes_because_highbit).is_none() ); + } #[test] #[cfg(feature = "serde")] fn serde_cbor_scalar_roundtrip() { + // XXX remove serde_cbor + use serde_cbor; let output = serde_cbor::to_vec(&X).unwrap(); let parsed: Scalar = serde_cbor::from_slice(&output).unwrap(); assert_eq!(parsed, X); From f165b63ee965d5c0b793b312a6ccdd69b71d37fb Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 22 Nov 2017 17:01:44 -0800 Subject: [PATCH 10/10] Encode scalars canonically using Serde. --- src/scalar.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 73110e9..17e8143 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -265,7 +265,7 @@ impl Serialize for Scalar { fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_bytes(self.as_bytes()) + serializer.serialize_bytes(self.reduce().as_bytes()) } } @@ -280,17 +280,25 @@ impl<'de> Deserialize<'de> for Scalar { type Value = Scalar; fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("a 32-byte scalar value") + formatter.write_str("a canonically-encoded 32-byte scalar value") } fn visit_bytes(self, v: &[u8]) -> Result where E: serde::de::Error { if v.len() == 32 { - // array_ref turns &[u8] into &[u8;32] let mut bytes = [0u8;32]; bytes.copy_from_slice(v); - Ok(Scalar(bytes)) + + static ERRMSG: &'static str = "encoding was not canonical"; + + Scalar::from_canonical_bytes(bytes) + .ok_or( + serde::de::Error::invalid_value( + serde::de::Unexpected::Bytes(v), + &ERRMSG, + ) + ) } else { Err(serde::de::Error::invalid_length(v.len(), &self)) }