Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions elliptic-curve/src/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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`.
Expand All @@ -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::<C>::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<C> {
self.inner.to_bytes()
}
Expand Down
40 changes: 35 additions & 5 deletions elliptic-curve/tests/secret_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<U256> = Odd::<U256>::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::<LittleEndianMockCurve>::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());
}
Loading