diff --git a/Cargo.lock b/Cargo.lock index ad84075ec..a3ba8e06b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,9 +141,8 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3" +version = "0.7.4" +source = "git+https://github.com/RustCrypto/crypto-bigint#ec6615fad09d26f8518378f1fdfe96a635ddab2d" dependencies = [ "cpubits", "ctutils", diff --git a/Cargo.toml b/Cargo.toml index 79c85b5b2..90dd21ad4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,3 +57,5 @@ unused_qualifications = "warn" crypto-common = { path = "crypto-common" } digest = { path = "digest" } signature = { path = "signature" } + +crypto-bigint = { git = "https://github.com/RustCrypto/crypto-bigint" } diff --git a/elliptic-curve/Cargo.toml b/elliptic-curve/Cargo.toml index 66379b977..48b65101e 100644 --- a/elliptic-curve/Cargo.toml +++ b/elliptic-curve/Cargo.toml @@ -17,7 +17,7 @@ representing various elliptic curve forms, scalars, points, and public/secret ke [dependencies] array = { package = "hybrid-array", version = "0.4", default-features = false, features = ["zeroize"] } -bigint = { package = "crypto-bigint", version = "0.7", default-features = false, features = ["hybrid-array", "rand_core", "subtle", "zeroize"] } +bigint = { package = "crypto-bigint", version = "0.7.4", default-features = false, features = ["hybrid-array", "rand_core", "subtle", "zeroize"] } base16ct = "1" common = { package = "crypto-common", version = "0.2", features = ["rand_core"] } rand_core = { version = "0.10", default-features = false } diff --git a/elliptic-curve/src/dev/mock_curve.rs b/elliptic-curve/src/dev/mock_curve.rs index 1057517e3..5e0d70826 100644 --- a/elliptic-curve/src/dev/mock_curve.rs +++ b/elliptic-curve/src/dev/mock_curve.rs @@ -5,8 +5,7 @@ //! generically over curves without having to pull in a complete curve implementation. use crate::{ - BatchNormalize, Curve, CurveAffine, CurveArithmetic, CurveGroup, FieldBytesEncoding, Generate, - PrimeCurve, + BatchNormalize, Curve, CurveAffine, CurveArithmetic, CurveGroup, Generate, PrimeCurve, array::typenum::U32, bigint::{Limb, Odd, U256, modular::Retrieve}, ctutils, @@ -350,8 +349,6 @@ impl Retrieve for Scalar { } } -impl FieldBytesEncoding for U256 {} - impl From for Scalar { fn from(n: u64) -> Scalar { Self(n.into()) diff --git a/elliptic-curve/src/field.rs b/elliptic-curve/src/field.rs index 0e068e235..c61c90b17 100644 --- a/elliptic-curve/src/field.rs +++ b/elliptic-curve/src/field.rs @@ -1,10 +1,8 @@ -//! Field elements. +//! Field element encoding support. -use crate::{ - Curve, - bigint::{ArrayEncoding, ByteArray, Integer}, -}; +use crate::Curve; use array::{Array, typenum::Unsigned}; +use bigint::{ArrayEncoding, ByteOrder, Encoding}; /// Size of serialized field elements of this elliptic curve. pub type FieldBytesSize = ::FieldBytesSize; @@ -12,40 +10,38 @@ pub type FieldBytesSize = ::FieldBytesSize; /// Byte representation of a base/scalar field element of a given curve. pub type FieldBytes = Array>; -/// Trait for decoding/encoding `Curve::Uint` from/to [`FieldBytes`] using -/// curve-specific rules. +/// Decode the provided [`FieldBytes`] as an integer. /// -/// Namely a curve's modulus may be smaller than the big integer type used to -/// internally represent field elements (since the latter are multiples of the -/// limb size), such as in the case of curves like NIST P-224 and P-521, and so -/// it may need to be padded/truncated to the right length. +/// Note that the resulting integer is the raw representation of the given `bytes` and is not +/// reduced by any modulus. +pub fn bytes_to_uint(bytes: &FieldBytes) -> C::Uint { + C::Uint::from_slice_truncated(bytes, modulus_bits::(), C::FIELD_ENDIANNESS) +} + +/// Encode the provided integer as [`FieldBytes`]. /// -/// Additionally, different curves have different endianness conventions, also -/// captured here. -pub trait FieldBytesEncoding: ArrayEncoding + Integer -where - C: Curve, -{ - /// Decode unsigned integer from serialized field element. - /// - /// The default implementation assumes a big endian encoding. - fn decode_field_bytes(field_bytes: &FieldBytes) -> Self { - debug_assert!(field_bytes.len() <= Self::ByteSize::USIZE); - let mut byte_array = ByteArray::::default(); - let offset = Self::ByteSize::USIZE.saturating_sub(field_bytes.len()); - byte_array[offset..].copy_from_slice(field_bytes); - Self::from_be_byte_array(byte_array) +/// Note that the output may be truncated if it overflows the width of [`FieldBytes`]. +pub fn uint_to_bytes(uint: &C::Uint) -> FieldBytes { + let field_bytes_len = FieldBytesSize::::USIZE; + let uint_bytes_len = <::Uint as ArrayEncoding>::ByteSize::USIZE; + debug_assert!(field_bytes_len <= uint_bytes_len); + + let mut field_bytes = FieldBytes::::default(); + match C::FIELD_ENDIANNESS { + ByteOrder::BigEndian => { + let offset = uint_bytes_len.saturating_sub(field_bytes_len); + field_bytes.copy_from_slice(&uint.to_be_byte_array()[offset..]); + } + ByteOrder::LittleEndian => { + field_bytes.copy_from_slice(&uint.to_le_byte_array()[..field_bytes_len]); + } } - /// Encode unsigned integer into serialized field element. - /// - /// The default implementation assumes a big endian encoding. - fn encode_field_bytes(&self) -> FieldBytes { - let mut field_bytes = FieldBytes::::default(); - debug_assert!(field_bytes.len() <= Self::ByteSize::USIZE); + field_bytes +} - let offset = Self::ByteSize::USIZE.saturating_sub(field_bytes.len()); - field_bytes.copy_from_slice(&self.to_be_byte_array()[offset..]); - field_bytes - } +// TODO(tarcieri): store full bit precision of the modulus on `Curve` +#[allow(clippy::cast_possible_truncation)] +const fn modulus_bits() -> u32 { + (FieldBytesSize::::USIZE * 8) as u32 } diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index a2443fd1b..a89bb985f 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -64,6 +64,7 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +pub mod field; pub mod point; pub mod scalar; @@ -77,7 +78,6 @@ pub mod ops; pub mod sec1; mod error; -mod field; mod macros; mod secret_key; @@ -88,14 +88,13 @@ mod public_key; pub use crate::{ error::{Error, Result}, - field::{FieldBytes, FieldBytesEncoding, FieldBytesSize}, + field::{FieldBytes, FieldBytesSize}, scalar::ScalarValue, secret_key::SecretKey, }; pub use array; pub use array::typenum::consts; -pub use bigint; -pub use bigint::ctutils; +pub use bigint::{self, ByteOrder, ctutils}; pub use common; pub use common::Generate; pub use rand_core; @@ -163,11 +162,13 @@ pub trait Curve: 'static + Copy + Clone + Debug + Default + Eq + Ord + Send + Sy + bigint::RandomMod + bigint::Unsigned + zeroize::Zeroize - + FieldBytesEncoding + ShrAssign; /// Order of this curve's prime order subgroup, i.e. number of elements in the scalar field. const ORDER: Odd; + + /// Endianness used for serializing field elements of this curve. + const FIELD_ENDIANNESS: ByteOrder = ByteOrder::BigEndian; } /// Marker trait for elliptic curves with prime order. diff --git a/elliptic-curve/src/scalar/value.rs b/elliptic-curve/src/scalar/value.rs index 1a986c658..f649da577 100644 --- a/elliptic-curve/src/scalar/value.rs +++ b/elliptic-curve/src/scalar/value.rs @@ -1,10 +1,11 @@ //! Integer values within the range of a given [`Curve`]'s scalar modulus. use crate::{ - Curve, Error, FieldBytes, FieldBytesEncoding, Result, + Curve, Error, FieldBytes, Result, array::Array, bigint::{AddMod, ConstOne, ConstZero, Integer, Limb, NegMod, Odd, RandomMod, SubMod, Zero}, ctutils::{self, CtEq, CtGt, CtLt, CtSelect}, + field, scalar::{FromUintUnchecked, IsHigh}, }; use base16ct::HexDisplay; @@ -74,7 +75,7 @@ where /// Decode [`ScalarValue`] from a serialized field element pub fn from_bytes(bytes: &FieldBytes) -> CtOption { - Self::new(C::Uint::decode_field_bytes(bytes)) + Self::new(field::bytes_to_uint::(bytes)) } /// Decode [`ScalarValue`] from a big endian byte slice. @@ -114,7 +115,7 @@ where /// Encode [`ScalarValue`] as a serialized field element. pub fn to_bytes(&self) -> FieldBytes { - self.inner.encode_field_bytes() + field::uint_to_bytes::(&self.inner) } /// Convert to a `C::Uint`.