From 96fcfae84817334428fb296d5e33d708a068bae7 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 31 Jan 2023 19:40:37 -0700 Subject: [PATCH] elliptic-curve: factor out `FieldBytesEncoding` trait Extracts a trait to be impl'd on `Curve::Uint` which provides curve-specific rules for how field elements should be encoded as bytes. These rules include both endianness and how to pad/truncate bytes to match the size of the integer (which may be larger than the size of the field modulus, to be a multiple of the limb size) --- elliptic-curve/src/dev.rs | 4 ++- elliptic-curve/src/field.rs | 44 ++++++++++++++++++++++++-- elliptic-curve/src/lib.rs | 30 +++--------------- elliptic-curve/src/scalar/primitive.rs | 6 ++-- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/elliptic-curve/src/dev.rs b/elliptic-curve/src/dev.rs index 2d45bf32f..f59085742 100644 --- a/elliptic-curve/src/dev.rs +++ b/elliptic-curve/src/dev.rs @@ -15,7 +15,7 @@ use crate::{ sec1::{CompressedPoint, FromEncodedPoint, ToEncodedPoint}, subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}, zeroize::DefaultIsZeroes, - Curve, CurveArithmetic, PrimeCurve, + Curve, CurveArithmetic, FieldBytesEncoding, PrimeCurve, }; use core::{ iter::{Product, Sum}, @@ -339,6 +339,8 @@ impl Reduce 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 d1cae0179..605f6c728 100644 --- a/elliptic-curve/src/field.rs +++ b/elliptic-curve/src/field.rs @@ -1,10 +1,50 @@ //! Field elements. -use crate::Curve; -use generic_array::GenericArray; +use crate::{ + bigint::{ArrayEncoding, ByteArray, Integer}, + Curve, +}; +use generic_array::{typenum::Unsigned, GenericArray}; /// Size of serialized field elements of this elliptic curve. pub type FieldBytesSize = ::FieldBytesSize; /// Byte representation of a base/scalar field element of a given curve. pub type FieldBytes = GenericArray>; + +/// Trait for decoding/encoding `Curve::Uint` from/to [`FieldBytes`] using +/// curve-specific rules. +/// +/// 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. +/// +/// 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(); + byte_array[..field_bytes.len()].copy_from_slice(field_bytes); + Self::from_be_byte_array(byte_array) + } + + /// 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); + + let len = field_bytes.len(); + field_bytes.copy_from_slice(&self.to_be_byte_array()[..len]); + field_bytes + } +} diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index cda6ebad7..7e5b3879a 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -105,7 +105,7 @@ mod voprf; pub use crate::{ error::{Error, Result}, - field::{FieldBytes, FieldBytesSize}, + field::{FieldBytes, FieldBytesEncoding, FieldBytesSize}, scalar::ScalarPrimitive, secret_key::SecretKey, }; @@ -140,8 +140,7 @@ use core::{ fmt::Debug, ops::{Add, ShrAssign}, }; -use crypto_bigint::{ArrayEncoding, ByteArray}; -use generic_array::{typenum::Unsigned, ArrayLength}; +use generic_array::ArrayLength; /// Algorithm [`ObjectIdentifier`][`pkcs8::ObjectIdentifier`] for elliptic /// curve public key cryptography (`id-ecPublicKey`). @@ -167,7 +166,7 @@ pub trait Curve: 'static + Copy + Clone + Debug + Default + Eq + Ord + Send + Sy type FieldBytesSize: ArrayLength + Add + Eq; /// Integer type used to represent field elements of this elliptic curve. - type Uint: ArrayEncoding + type Uint: bigint::ArrayEncoding + bigint::AddMod + bigint::Encoding + bigint::Integer @@ -176,33 +175,12 @@ pub trait Curve: 'static + Copy + Clone + Debug + Default + Eq + Ord + Send + Sy + bigint::RandomMod + bigint::SubMod + zeroize::Zeroize + + FieldBytesEncoding + ShrAssign; /// Order of this elliptic curve, i.e. number of elements in the scalar /// field. const ORDER: Self::Uint; - - /// Decode unsigned integer from serialized field element. - /// - /// The default implementation assumes a big endian encoding. - fn decode_field_bytes(field_bytes: &FieldBytes) -> Self::Uint { - debug_assert!(field_bytes.len() <= ::ByteSize::USIZE); - let mut byte_array = ByteArray::::default(); - byte_array[..field_bytes.len()].copy_from_slice(field_bytes); - Self::Uint::from_be_byte_array(byte_array) - } - - /// Encode unsigned integer into serialized field element. - /// - /// The default implementation assumes a big endian encoding. - fn encode_field_bytes(uint: &Self::Uint) -> FieldBytes { - let mut field_bytes = FieldBytes::::default(); - debug_assert!(field_bytes.len() <= ::ByteSize::USIZE); - - let len = field_bytes.len(); - field_bytes.copy_from_slice(&uint.to_be_byte_array()[..len]); - field_bytes - } } /// Marker trait for elliptic curves with prime order. diff --git a/elliptic-curve/src/scalar/primitive.rs b/elliptic-curve/src/scalar/primitive.rs index 0da18f589..c81f1bd7e 100644 --- a/elliptic-curve/src/scalar/primitive.rs +++ b/elliptic-curve/src/scalar/primitive.rs @@ -4,7 +4,7 @@ use crate::{ bigint::{prelude::*, Limb, NonZero}, scalar::FromUintUnchecked, scalar::IsHigh, - Curve, Error, FieldBytes, Result, + Curve, Error, FieldBytes, FieldBytesEncoding, Result, }; use base16ct::HexDisplay; use core::{ @@ -78,7 +78,7 @@ where /// Decode [`ScalarPrimitive`] from a serialized field element pub fn from_bytes(bytes: &FieldBytes) -> CtOption { - Self::new(C::decode_field_bytes(bytes)) + Self::new(C::Uint::decode_field_bytes(bytes)) } /// Decode [`ScalarPrimitive`] from a big endian byte slice. @@ -117,7 +117,7 @@ where /// Encode [`ScalarPrimitive`] as a serialized field element. pub fn to_bytes(&self) -> FieldBytes { - C::encode_field_bytes(&self.inner) + self.inner.encode_field_bytes() } /// Convert to a `C::Uint`.