Skip to content
Merged
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
11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions sec1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ rust-version = "1.70"
[dependencies]
base16ct = { version = "0.2", optional = true, default-features = false }
der = { version = "=0.8.0-pre", optional = true, features = ["oid"] }
generic-array = { version = "0.14.7", optional = true, default-features = false }
hybrid-array = { version = "=0.2.0-pre.8", optional = true, default-features = false }
pkcs8 = { version = "=0.11.0-pre", optional = true, default-features = false }
serdect = { version = "=0.3.0-pre", optional = true, default-features = false, features = ["alloc"] }
subtle = { version = "2", optional = true, default-features = false }
Expand All @@ -35,7 +35,7 @@ std = ["alloc", "der?/std"]

der = ["dep:der", "zeroize"]
pem = ["alloc", "der/pem", "pkcs8/pem"]
point = ["dep:base16ct", "dep:generic-array"]
point = ["dep:base16ct", "dep:hybrid-array"]
serde = ["dep:serdect"]
zeroize = ["dep:zeroize", "der?/zeroize"]

Expand Down
2 changes: 1 addition & 1 deletion sec1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub use crate::error::{Error, Result};
pub use crate::point::EncodedPoint;

#[cfg(feature = "point")]
pub use generic_array::typenum::consts;
pub use hybrid_array::typenum::consts;

#[cfg(feature = "der")]
pub use crate::{parameters::EcParameters, private_key::EcPrivateKey, traits::DecodeEcPrivateKey};
Expand Down
107 changes: 58 additions & 49 deletions sec1/src/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use core::{
cmp::Ordering,
fmt::{self, Debug},
hash::{Hash, Hasher},
ops::Add,
ops::{Add, Sub},
str,
};
use generic_array::{
use hybrid_array::{
typenum::{U1, U24, U28, U32, U48, U66},
ArrayLength, GenericArray,
Array, ArraySize,
};

#[cfg(feature = "alloc")]
Expand All @@ -31,23 +31,31 @@ use subtle::{Choice, ConditionallySelectable};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

/// Trait for supported modulus sizes which precomputes the typenums for
/// various point encodings so they don't need to be included as bounds.
/// Trait for supported modulus sizes which precomputes the typenums for various point encodings so
/// they don't need to be included as bounds.
// TODO(tarcieri): replace this all with const generic expressions.
pub trait ModulusSize: 'static + ArrayLength<u8> + Copy + Debug {
/// Size of a compressed point for the given elliptic curve when encoded
/// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm
/// (including leading `0x02` or `0x03` tag byte).
type CompressedPointSize: 'static + ArrayLength<u8> + Copy + Debug;

/// Size of an uncompressed point for the given elliptic curve when encoded
/// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm
/// (including leading `0x04` tag byte).
type UncompressedPointSize: 'static + ArrayLength<u8> + Copy + Debug;

/// Size of an untagged point for given elliptic curve, i.e. size of two
/// serialized base field elements.
type UntaggedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
pub trait ModulusSize: 'static + ArraySize + Copy + Debug {
/// Size of a compressed point for the given elliptic curve when encoded using the SEC1
/// `Elliptic-Curve-Point-to-Octet-String` algorithm (including leading `0x02` or `0x03`
/// tag byte).
type CompressedPointSize: 'static
+ ArraySize
+ Copy
+ Debug
+ Add<Self, Output = Self::UncompressedPointSize>;

/// Size of an uncompressed point for the given elliptic curve when encoded using the SEC1
/// `Elliptic-Curve-Point-to-Octet-String` algorithm (including leading `0x04` tag byte).
type UncompressedPointSize: 'static + ArraySize + Copy + Debug;

/// Size of an untagged point for given elliptic curve, i.e. size of two serialized base field
/// elements when concatenated.
type UntaggedPointSize: 'static
+ ArraySize
+ Copy
+ Debug
+ Add<U1, Output = Self::UncompressedPointSize>
+ Sub<Self, Output = Self>;
}

macro_rules! impl_modulus_size {
Expand All @@ -60,6 +68,7 @@ macro_rules! impl_modulus_size {
}
}

// Support for 192-bit, 224-bit, 256-bit, 384-bit, and 521-bit modulus sizes
impl_modulus_size!(U24, U28, U32, U48, U66);

/// SEC1 encoded curve point.
Expand All @@ -72,7 +81,7 @@ pub struct EncodedPoint<Size>
where
Size: ModulusSize,
{
bytes: GenericArray<u8, Size::UncompressedPointSize>,
bytes: Array<u8, Size::UncompressedPointSize>,
}

#[allow(clippy::len_without_is_empty)]
Expand Down Expand Up @@ -103,24 +112,24 @@ where
return Err(Error::PointEncoding);
}

let mut bytes = GenericArray::default();
let mut bytes = Array::default();
bytes[..expected_len].copy_from_slice(input);
Ok(Self { bytes })
}

/// Decode elliptic curve point from raw uncompressed coordinates, i.e.
/// encoded as the concatenated `x || y` coordinates with no leading SEC1
/// tag byte (which would otherwise be `0x04` for an uncompressed point).
pub fn from_untagged_bytes(bytes: &GenericArray<u8, Size::UntaggedPointSize>) -> Self {
let (x, y) = bytes.split_at(Size::to_usize());
Self::from_affine_coordinates(x.into(), y.into(), false)
pub fn from_untagged_bytes(bytes: &Array<u8, Size::UntaggedPointSize>) -> Self {
let (x, y) = bytes.split_ref();
Self::from_affine_coordinates(x, y, false)
}

/// Encode an elliptic curve point from big endian serialized coordinates
/// (with optional point compression)
pub fn from_affine_coordinates(
x: &GenericArray<u8, Size>,
y: &GenericArray<u8, Size>,
x: &Array<u8, Size>,
y: &Array<u8, Size>,
compress: bool,
) -> Self {
let tag = if compress {
Expand All @@ -129,7 +138,7 @@ where
Tag::Uncompressed
};

let mut bytes = GenericArray::default();
let mut bytes = Array::default();
bytes[0] = tag.into();
bytes[1..(Size::to_usize() + 1)].copy_from_slice(x);

Expand Down Expand Up @@ -200,27 +209,28 @@ where
return Coordinates::Identity;
}

let (x, y) = self.bytes[1..].split_at(Size::to_usize());
let (x_bytes, y_bytes) = self.bytes[1..].split_at(Size::to_usize());
let x = Array::ref_from_slice(x_bytes);

if self.is_compressed() {
Coordinates::Compressed {
x: x.into(),
x,
y_is_odd: self.tag() as u8 & 1 == 1,
}
} else if self.is_compact() {
Coordinates::Compact { x: x.into() }
Coordinates::Compact { x }
} else {
Coordinates::Uncompressed {
x: x.into(),
y: y.into(),
x,
y: Array::ref_from_slice(y_bytes),
}
}
}

/// Get the x-coordinate for this [`EncodedPoint`].
///
/// Returns `None` if this point is the identity point.
pub fn x(&self) -> Option<&GenericArray<u8, Size>> {
pub fn x(&self) -> Option<&Array<u8, Size>> {
match self.coordinates() {
Coordinates::Identity => None,
Coordinates::Compressed { x, .. } => Some(x),
Expand All @@ -232,7 +242,7 @@ where
/// Get the y-coordinate for this [`EncodedPoint`].
///
/// Returns `None` if this point is compressed or the identity point.
pub fn y(&self) -> Option<&GenericArray<u8, Size>> {
pub fn y(&self) -> Option<&Array<u8, Size>> {
match self.coordinates() {
Coordinates::Compressed { .. } | Coordinates::Identity => None,
Coordinates::Uncompressed { y, .. } => Some(y),
Expand All @@ -255,10 +265,10 @@ where
impl<Size> ConditionallySelectable for EncodedPoint<Size>
where
Size: ModulusSize,
<Size::UncompressedPointSize as ArrayLength<u8>>::ArrayType: Copy,
<Size::UncompressedPointSize as ArraySize>::ArrayType<u8>: Copy,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut bytes = GenericArray::default();
let mut bytes = Array::default();

for (i, byte) in bytes.iter_mut().enumerate() {
*byte = u8::conditional_select(&a.bytes[i], &b.bytes[i], choice);
Expand All @@ -271,7 +281,7 @@ where
impl<Size> Copy for EncodedPoint<Size>
where
Size: ModulusSize,
<Size::UncompressedPointSize as ArrayLength<u8>>::ArrayType: Copy,
<Size::UncompressedPointSize as ArraySize>::ArrayType<u8>: Copy,
{
}

Expand Down Expand Up @@ -382,7 +392,7 @@ where
type Err = Error;

fn from_str(hex: &str) -> Result<Self> {
let mut buf = GenericArray::<u8, Size::UncompressedPointSize>::default();
let mut buf = Array::<u8, Size::UncompressedPointSize>::default();
base16ct::mixed::decode(hex, &mut buf)
.map_err(|_| Error::PointEncoding)
.and_then(Self::from_bytes)
Expand Down Expand Up @@ -426,13 +436,13 @@ pub enum Coordinates<'a, Size: ModulusSize> {
/// Compact curve point
Compact {
/// x-coordinate
x: &'a GenericArray<u8, Size>,
x: &'a Array<u8, Size>,
},

/// Compressed curve point
Compressed {
/// x-coordinate
x: &'a GenericArray<u8, Size>,
x: &'a Array<u8, Size>,

/// Is the y-coordinate odd?
y_is_odd: bool,
Expand All @@ -441,10 +451,10 @@ pub enum Coordinates<'a, Size: ModulusSize> {
/// Uncompressed curve point
Uncompressed {
/// x-coordinate
x: &'a GenericArray<u8, Size>,
x: &'a Array<u8, Size>,

/// y-coordinate
y: &'a GenericArray<u8, Size>,
y: &'a Array<u8, Size>,
},
}

Expand Down Expand Up @@ -556,8 +566,8 @@ impl From<Tag> for u8 {
mod tests {
use super::{Coordinates, Tag};
use core::str::FromStr;
use generic_array::{typenum::U32, GenericArray};
use hex_literal::hex;
use hybrid_array::typenum::U32;

#[cfg(feature = "alloc")]
use alloc::string::ToString;
Expand Down Expand Up @@ -600,7 +610,7 @@ mod tests {

assert_eq!(
compressed_even_y.x().unwrap(),
&hex!("0100000000000000000000000000000000000000000000000000000000000000").into()
&hex!("0100000000000000000000000000000000000000000000000000000000000000")
);
assert_eq!(compressed_even_y.y(), None);

Expand All @@ -625,7 +635,7 @@ mod tests {

assert_eq!(
compressed_odd_y.x().unwrap(),
&hex!("0200000000000000000000000000000000000000000000000000000000000000").into()
&hex!("0200000000000000000000000000000000000000000000000000000000000000")
);
assert_eq!(compressed_odd_y.y(), None);
}
Expand All @@ -649,11 +659,11 @@ mod tests {

assert_eq!(
uncompressed_point.x().unwrap(),
&hex!("1111111111111111111111111111111111111111111111111111111111111111").into()
&hex!("1111111111111111111111111111111111111111111111111111111111111111")
);
assert_eq!(
uncompressed_point.y().unwrap(),
&hex!("2222222222222222222222222222222222222222222222222222222222222222").into()
&hex!("2222222222222222222222222222222222222222222222222222222222222222")
);
}

Expand Down Expand Up @@ -701,8 +711,7 @@ mod tests {
#[test]
fn from_untagged_point() {
let untagged_bytes = hex!("11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222");
let uncompressed_point =
EncodedPoint::from_untagged_bytes(GenericArray::from_slice(&untagged_bytes[..]));
let uncompressed_point = EncodedPoint::from_untagged_bytes(&untagged_bytes.into());
assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]);
}

Expand Down