diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs index 1011130b6..cb38c2611 100644 --- a/elliptic-curve/src/secret_key.rs +++ b/elliptic-curve/src/secret_key.rs @@ -8,7 +8,7 @@ #[cfg(all(feature = "pkcs8", feature = "sec1"))] mod pkcs8; -use crate::{Curve, Error, FieldBytes, Result, ScalarValue}; +use crate::{Curve, Error, FieldBytes, Result, ScalarValue, bigint::ByteOrder}; use array::typenum::Unsigned; use common::{Generate, InvalidKey, KeySizeUser, TryKeyInit}; use core::fmt::{self, Debug}; @@ -148,10 +148,11 @@ where /// Deserialize secret key from an encoded secret scalar passed as a byte slice. /// - /// The slice is expected to be a minimum of 24-bytes (192-bytes) and at most + /// The slice is expected to be a minimum of 24 bytes (192 bits) and at most /// `C::FieldBytesSize` bytes in length. /// - /// Byte slices shorter than the field size are handled by zero padding the input. + /// Byte slices shorter than the field size are padded with zeros at the most-significant end, + /// according to [`Curve::FIELD_ENDIANNESS`]. /// /// NOTE: this function is variable-time with respect to the input length. To avoid a timing /// sidechannel, always ensure that the input has been pre-padded to `C::FieldBytesSize`. @@ -165,15 +166,22 @@ where Self::from_bytes(field_bytes) } else if (Self::MIN_SIZE..C::FieldBytesSize::USIZE).contains(&slice.len()) { let mut bytes = Zeroizing::new(FieldBytes::::default()); - let offset = C::FieldBytesSize::USIZE.saturating_sub(slice.len()); - bytes[offset..].copy_from_slice(slice); + + match C::FIELD_ENDIANNESS { + ByteOrder::BigEndian => { + let offset = C::FieldBytesSize::USIZE - slice.len(); + bytes[offset..].copy_from_slice(slice); + } + ByteOrder::LittleEndian => bytes[..slice.len()].copy_from_slice(slice), + } + Self::from_bytes(&bytes) } else { Err(Error) } } - /// Serialize raw secret scalar as a big endian integer. + /// Serialize the raw secret scalar using the curve's configured field endianness. pub fn to_bytes(&self) -> FieldBytes { self.inner.to_bytes() } diff --git a/elliptic-curve/tests/secret_key.rs b/elliptic-curve/tests/secret_key.rs index 7be475143..71e61e9af 100644 --- a/elliptic-curve/tests/secret_key.rs +++ b/elliptic-curve/tests/secret_key.rs @@ -2,27 +2,57 @@ #![cfg(feature = "dev")] -use elliptic_curve::dev::SecretKey; +use elliptic_curve::{ + Curve, SecretKey, + array::typenum::U32, + bigint::{ByteOrder, Odd, U256}, + dev::SecretKey as MockSecretKey, +}; + +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +struct LittleEndianMockCurve; + +impl Curve for LittleEndianMockCurve { + type FieldBytesSize = U32; + type Uint = U256; + + const ORDER: Odd = Odd::::from_be_hex( + "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + ); + const FIELD_ENDIANNESS: ByteOrder = ByteOrder::LittleEndian; +} #[test] fn from_empty_slice() { - assert!(SecretKey::from_slice(&[]).is_err()); + assert!(MockSecretKey::from_slice(&[]).is_err()); } #[test] fn from_slice_expected_size() { let bytes = [1u8; 32]; - assert!(SecretKey::from_slice(&bytes).is_ok()); + assert!(MockSecretKey::from_slice(&bytes).is_ok()); } #[test] fn from_slice_allowed_short() { let bytes = [1u8; 24]; - assert!(SecretKey::from_slice(&bytes).is_ok()); + assert!(MockSecretKey::from_slice(&bytes).is_ok()); +} + +#[test] +fn from_slice_allowed_short_little_endian() { + let mut bytes = [0u8; 24]; + bytes[0] = 1; + + let secret_key = SecretKey::::from_slice(&bytes).unwrap(); + let encoded = secret_key.to_bytes(); + + assert_eq!(&encoded[..bytes.len()], &bytes); + assert_eq!(&encoded[bytes.len()..], &[0u8; 8]); } #[test] fn from_slice_too_short() { let bytes = [1u8; 23]; // min 24-bytes - assert!(SecretKey::from_slice(&bytes).is_err()); + assert!(MockSecretKey::from_slice(&bytes).is_err()); }