Skip to content

Commit

Permalink
p384: return CtOption from Scalar::from_*_bytes (#571)
Browse files Browse the repository at this point in the history
...and adds `Scalar::from_*_slice` which returns `Result`.

`<Scalar as PrimeField>::from_repr` now calls `Scalar::from_be_bytes`.

This approach permits constant-time overflow checking for applications
which would benefit from it, and is consistent with the `ScalarCore` API
in the `elliptic_curve` crate:

https://docs.rs/elliptic-curve/0.12.0/elliptic_curve/struct.ScalarCore.html
  • Loading branch information
tarcieri authored May 27, 2022
1 parent b52b87d commit 38ea146
Showing 1 changed file with 36 additions and 38 deletions.
74 changes: 36 additions & 38 deletions p384/src/arithmetic/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use elliptic_curve::{
generic_array::arr,
ops::Reduce,
rand_core::RngCore,
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess, CtOption},
zeroize::DefaultIsZeroes,
Curve as _, Error, IsHigh, Result, ScalarArithmetic, ScalarCore,
};
Expand All @@ -33,7 +33,7 @@ type Fe = fiat_p384_scalar_montgomery_domain_field_element;
type NonMontFe = fiat_p384_scalar_non_montgomery_domain_field_element;

fn frac_modulus_2() -> Scalar {
Scalar::from_le_bytes(&NistP384::ORDER.shr_vartime(1).to_le_bytes()).unwrap()
Scalar::from_le_bytes(NistP384::ORDER.shr_vartime(1).to_le_bytes().into()).unwrap()
}

impl ScalarArithmetic for NistP384 {
Expand All @@ -58,37 +58,36 @@ impl Scalar {
/// Zero scalar.
pub const ZERO: Self = Self([0, 0, 0, 0, 0, 0]);

/// Create a scalar from a canonical, little-endian representation
pub fn from_le_bytes(bytes: &[u8; 48]) -> Result<Self> {
let order_le = NistP384::ORDER.to_le_bytes();
let mut c: u8 = 0;
let mut n: u8 = 1;
for (&s, &l) in bytes.iter().rev().zip(order_le.iter().rev()) {
c |= (((s as u16).wrapping_sub(l as u16) >> 8) as u8) & n;
n &= (((s ^ l) as u16).wrapping_sub(1) >> 8) as u8;
}
if c == 0 {
return Err(Error);
}

/// Create a scalar from a canonical, big-endian representation
pub fn from_be_bytes(bytes: FieldBytes) -> CtOption<Self> {
let mut non_mont = Default::default();
fiat_p384_scalar_from_bytes(&mut non_mont, bytes);
fiat_p384_scalar_from_bytes(&mut non_mont, &swap48(bytes.as_ref()));
let mut mont = Default::default();
fiat_p384_scalar_to_montgomery(&mut mont, &non_mont);
Ok(Scalar(mont))
let out = Scalar(mont);
let overflow = U384::from_be_bytes(bytes.into()).ct_lt(&NistP384::ORDER);
CtOption::new(out, overflow)
}

/// Create a scalar from a canonical, big-endian representation
pub fn from_be_bytes(bytes: &[u8; 48]) -> Result<Self> {
Scalar::from_le_bytes(&swap48(bytes))
/// Decode scalar from a big endian byte slice.
pub fn from_be_slice(slice: &[u8]) -> Result<Self> {
<[u8; 48]>::try_from(slice)
.ok()
.and_then(|array| Self::from_be_bytes(array.into()).into())
.ok_or(Error)
}

/// Returns the little-endian encoding of this scalar.
pub fn to_le_bytes(&self) -> FieldBytes {
let non_mont = self.to_non_mont();
let mut out = [0u8; 48];
fiat_p384_scalar_to_bytes(&mut out, &non_mont.0);
FieldBytes::from(out)
/// Create a scalar from a canonical, little-endian representation
pub fn from_le_bytes(bytes: FieldBytes) -> CtOption<Self> {
Self::from_be_bytes(swap48(&bytes.into()).into())
}

/// Decode scalar from a little endian byte slice.
pub fn from_le_slice(slice: &[u8]) -> Result<Self> {
<[u8; 48]>::try_from(slice)
.ok()
.and_then(|array| Self::from_le_bytes(array.into()).into())
.ok_or(Error)
}

/// Returns the little-endian encoding of this scalar.
Expand All @@ -98,6 +97,14 @@ impl Scalar {
FieldBytes::from(swap48(&out))
}

/// Returns the little-endian encoding of this scalar.
pub fn to_le_bytes(&self) -> FieldBytes {
let non_mont = self.to_non_mont();
let mut out = [0u8; 48];
fiat_p384_scalar_to_bytes(&mut out, &non_mont.0);
FieldBytes::from(out)
}

#[cfg(test)]
/// Returns the SEC1 encoding of this scalar.
///
Expand Down Expand Up @@ -299,12 +306,7 @@ impl PrimeField for Scalar {
const S: u32 = 1;

fn from_repr(bytes: FieldBytes) -> CtOption<Self> {
let mut non_mont = Default::default();
fiat_p384_scalar_from_bytes(&mut non_mont, &swap48(bytes.as_ref()));
let mut mont = Default::default();
fiat_p384_scalar_to_montgomery(&mut mont, &non_mont);
let out = Scalar(mont);
CtOption::new(out, 1.into())
Self::from_be_bytes(bytes)
}

fn to_repr(&self) -> FieldBytes {
Expand Down Expand Up @@ -342,9 +344,7 @@ fn swap48(x: &[u8; 48]) -> [u8; 48] {

impl From<ScalarCore<NistP384>> for Scalar {
fn from(x: ScalarCore<NistP384>) -> Self {
let mut bytes = [0u8; 48];
bytes.copy_from_slice(x.to_le_bytes().as_slice());
Scalar::from_le_bytes(&bytes).expect("non-canonical encoding")
Scalar::from_be_bytes(x.to_be_bytes()).unwrap()
}
}

Expand Down Expand Up @@ -574,9 +574,7 @@ impl PrimeFieldBits for Scalar {

impl From<&ScalarCore<NistP384>> for Scalar {
fn from(scalar: &ScalarCore<NistP384>) -> Scalar {
let mut bytes = [0u8; 48];
bytes.copy_from_slice(scalar.to_le_bytes().as_slice());
Scalar::from_le_bytes(&bytes).expect("non-canonical encoding")
Scalar::from_be_bytes(scalar.to_be_bytes()).unwrap()
}
}

Expand Down

0 comments on commit 38ea146

Please sign in to comment.