diff --git a/synedrion/src/cggmp21/params.rs b/synedrion/src/cggmp21/params.rs index 3b4f400c..44be41aa 100644 --- a/synedrion/src/cggmp21/params.rs +++ b/synedrion/src/cggmp21/params.rs @@ -132,7 +132,7 @@ pub trait SchemeParams: Debug + Clone + Send + PartialEq + Eq + Send + Sync + 's /// Converts a curve scalar to the associated integer type. pub(crate) fn uint_from_scalar(value: &Scalar) -> ::Uint { - let scalar_bytes = value.to_bytes(); + let scalar_bytes = value.to_be_bytes(); let mut repr = ::Uint::zero().to_be_bytes(); let uint_len = repr.as_ref().len(); @@ -170,7 +170,7 @@ pub(crate) fn scalar_from_uint(value: &(value: &( // Can unwrap here since the value is within the Scalar range Secret::init_with(|| { - Scalar::try_from_bytes( + Scalar::try_from_be_bytes( repr.expose_secret() .as_ref() .get(uint_len - scalar_len..) - // TODO(dp): @reviewers Do we need a better proof that this is true? If `Uint` is, say, 128 bits long… .expect("Uint is assumed to be bigger than Scalar"), ) .expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar") @@ -235,7 +233,7 @@ pub(crate) fn secret_scalar_from_uint( pub(crate) fn secret_uint_from_scalar( value: &Secret, ) -> Secret<::Uint> { - let scalar_bytes = Secret::init_with(|| value.expose_secret().to_bytes()); + let scalar_bytes = Secret::init_with(|| value.expose_secret().to_be_bytes()); let mut repr = Secret::init_with(|| ::Uint::zero().to_be_bytes()); let uint_len = repr.expose_secret().as_ref().len(); diff --git a/synedrion/src/curve/arithmetic.rs b/synedrion/src/curve/arithmetic.rs index 41017067..edf31d76 100644 --- a/synedrion/src/curve/arithmetic.rs +++ b/synedrion/src/curve/arithmetic.rs @@ -98,7 +98,8 @@ impl Scalar { Self(>::reduce_bytes(&arr)) } - pub fn to_bytes(self) -> k256::FieldBytes { + /// Returns the SEC1 encoding of this scalar (big endian order). + pub fn to_be_bytes(self) -> k256::FieldBytes { self.0.to_bytes() } @@ -114,7 +115,8 @@ impl Scalar { Secret::init_with(|| Self(*sk.as_nonzero_scalar().as_ref())) } - pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result { + /// Attempts to instantiate a `Scalar` from a slice of bytes. Assumes big-endian order. + pub(crate) fn try_from_be_bytes(bytes: &[u8]) -> Result { let arr = GenericArray::>::from_exact_iter(bytes.iter().cloned()) .ok_or("Invalid length of a curve scalar")?; @@ -151,7 +153,7 @@ pub(crate) fn secret_split(rng: &mut impl CryptoRngCore, scalar: Secret, impl<'a> TryFrom<&'a [u8]> for Scalar { type Error = String; fn try_from(val: &'a [u8]) -> Result { - Self::try_from_bytes(val) + Self::try_from_be_bytes(val) } } @@ -169,7 +171,7 @@ impl ConditionallySelectable for Scalar { impl Serialize for Scalar { fn serialize(&self, serializer: S) -> Result { - SliceLike::::serialize(&self.to_bytes(), serializer) + SliceLike::::serialize(&self.to_be_bytes(), serializer) } } @@ -374,3 +376,33 @@ impl<'a> core::iter::Sum<&'a Self> for Point { iter.cloned().sum() } } + +#[cfg(test)] +mod test { + use super::Scalar; + use rand::SeedableRng; + use rand_chacha::ChaChaRng; + #[test] + fn to_and_from_bytes() { + let mut rng = ChaChaRng::from_seed([7u8; 32]); + let s = Scalar::random(&mut rng); + + // Round trip works + let bytes = s.to_be_bytes(); + let s_from_bytes = Scalar::try_from_be_bytes(&bytes).expect("bytes are valid"); + assert_eq!(s, s_from_bytes); + + // …but building a `Scalar` from LE bytes does not. + let mut bytes = bytes; + let le_bytes = bytes + .chunks_exact_mut(8) + .flat_map(|word_bytes| { + word_bytes.reverse(); + word_bytes.to_vec() + }) + .collect::>(); + + let s_from_le_bytes = Scalar::try_from_be_bytes(&le_bytes).expect("bytes are valid-ish"); + assert_ne!(s, s_from_le_bytes, "Using LE bytes should not work") + } +} diff --git a/synedrion/src/curve/ecdsa.rs b/synedrion/src/curve/ecdsa.rs index e03fb0a6..6a51c376 100644 --- a/synedrion/src/curve/ecdsa.rs +++ b/synedrion/src/curve/ecdsa.rs @@ -18,7 +18,7 @@ impl RecoverableSignature { // but consequent usage of it may fail otherwise. let signature = signature.normalize_s().unwrap_or(signature); - let message_bytes = message.to_bytes(); + let message_bytes = message.to_be_bytes(); let recovery_id = RecoveryId::trial_recovery_from_prehash( &VerifyingKey::from_affine(vkey.to_backend().to_affine()).ok()?, &message_bytes,