Skip to content

Commit

Permalink
Be explicit about byte order expectations (#167)
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdplm authored Dec 13, 2024
1 parent dea2c07 commit 4c04dcf
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 10 deletions.
10 changes: 5 additions & 5 deletions synedrion/src/cggmp21/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,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<P: SchemeParams>(value: &Scalar) -> <P::Paillier as PaillierParams>::Uint {
let scalar_bytes = value.to_bytes();
let scalar_bytes = value.to_be_bytes();
let mut repr = <P::Paillier as PaillierParams>::Uint::zero().to_be_bytes();

let uint_len = repr.as_ref().len();
Expand Down Expand Up @@ -166,7 +166,7 @@ pub(crate) fn scalar_from_uint<P: SchemeParams>(value: &<P::Paillier as Paillier
let scalar_len = Scalar::repr_len();

// Can unwrap here since the value is within the Scalar range
Scalar::try_from_bytes(&repr.as_ref()[uint_len - scalar_len..])
Scalar::try_from_be_bytes(&repr.as_ref()[uint_len - scalar_len..])
.expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar")
}

Expand All @@ -185,7 +185,7 @@ pub(crate) fn scalar_from_wide_uint<P: SchemeParams>(value: &<P::Paillier as Pai
let scalar_len = Scalar::repr_len();

// Can unwrap here since the value is within the Scalar range
Scalar::try_from_bytes(&repr.as_ref()[uint_len - scalar_len..])
Scalar::try_from_be_bytes(&repr.as_ref()[uint_len - scalar_len..])
.expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar")
}

Expand All @@ -208,15 +208,15 @@ pub(crate) fn secret_scalar_from_uint<P: SchemeParams>(

// Can unwrap here since the value is within the Scalar range
Secret::init_with(|| {
Scalar::try_from_bytes(&repr.expose_secret().as_ref()[uint_len - scalar_len..])
Scalar::try_from_be_bytes(&repr.expose_secret().as_ref()[uint_len - scalar_len..])
.expect("the value was reduced modulo `CURVE_ORDER`, so it's a valid curve scalar")
})
}

pub(crate) fn secret_uint_from_scalar<P: SchemeParams>(
value: &Secret<Scalar>,
) -> Secret<<P::Paillier as PaillierParams>::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(|| <P::Paillier as PaillierParams>::Uint::zero().to_be_bytes());

let uint_len = repr.expose_secret().as_ref().len();
Expand Down
40 changes: 36 additions & 4 deletions synedrion/src/curve/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ impl Scalar {
Self(<BackendScalar as Reduce<U256>>::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()
}

Expand All @@ -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<Self, String> {
/// Attempts to instantiate a `Scalar` from a slice of bytes. Assumes big-endian order.
pub(crate) fn try_from_be_bytes(bytes: &[u8]) -> Result<Self, String> {
let arr = GenericArray::<u8, FieldBytesSize<Secp256k1>>::from_exact_iter(bytes.iter().cloned())
.ok_or("Invalid length of a curve scalar")?;

Expand Down Expand Up @@ -151,7 +153,7 @@ pub(crate) fn secret_split(rng: &mut impl CryptoRngCore, scalar: Secret<Scalar>,
impl<'a> TryFrom<&'a [u8]> for Scalar {
type Error = String;
fn try_from(val: &'a [u8]) -> Result<Self, Self::Error> {
Self::try_from_bytes(val)
Self::try_from_be_bytes(val)
}
}

Expand All @@ -169,7 +171,7 @@ impl ConditionallySelectable for Scalar {

impl Serialize for Scalar {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
SliceLike::<Hex>::serialize(&self.to_bytes(), serializer)
SliceLike::<Hex>::serialize(&self.to_be_bytes(), serializer)
}
}

Expand Down Expand Up @@ -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::<Vec<u8>>();

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")
}
}
2 changes: 1 addition & 1 deletion synedrion/src/curve/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 4c04dcf

Please sign in to comment.