Skip to content

Commit 734b7b1

Browse files
authored
sec1: migrate from generic-array to hybrid-array (#1298)
The RustCrypto/traits crates are now using `hybrid-array`, which features a mostly safe implementation that natively leverages const generics as much as possible.
1 parent a40e97b commit 734b7b1

File tree

4 files changed

+71
-53
lines changed

4 files changed

+71
-53
lines changed

Cargo.lock

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sec1/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rust-version = "1.70"
1818
[dependencies]
1919
base16ct = { version = "0.2", optional = true, default-features = false }
2020
der = { version = "=0.8.0-pre", optional = true, features = ["oid"] }
21-
generic-array = { version = "0.14.7", optional = true, default-features = false }
21+
hybrid-array = { version = "=0.2.0-pre.8", optional = true, default-features = false }
2222
pkcs8 = { version = "=0.11.0-pre", optional = true, default-features = false }
2323
serdect = { version = "=0.3.0-pre", optional = true, default-features = false, features = ["alloc"] }
2424
subtle = { version = "2", optional = true, default-features = false }
@@ -35,7 +35,7 @@ std = ["alloc", "der?/std"]
3535

3636
der = ["dep:der", "zeroize"]
3737
pem = ["alloc", "der/pem", "pkcs8/pem"]
38-
point = ["dep:base16ct", "dep:generic-array"]
38+
point = ["dep:base16ct", "dep:hybrid-array"]
3939
serde = ["dep:serdect"]
4040
zeroize = ["dep:zeroize", "der?/zeroize"]
4141

sec1/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub use crate::error::{Error, Result};
4949
pub use crate::point::EncodedPoint;
5050

5151
#[cfg(feature = "point")]
52-
pub use generic_array::typenum::consts;
52+
pub use hybrid_array::typenum::consts;
5353

5454
#[cfg(feature = "der")]
5555
pub use crate::{parameters::EcParameters, private_key::EcPrivateKey, traits::DecodeEcPrivateKey};

sec1/src/point.rs

Lines changed: 58 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ use core::{
1111
cmp::Ordering,
1212
fmt::{self, Debug},
1313
hash::{Hash, Hasher},
14-
ops::Add,
14+
ops::{Add, Sub},
1515
str,
1616
};
17-
use generic_array::{
17+
use hybrid_array::{
1818
typenum::{U1, U24, U28, U32, U48, U66},
19-
ArrayLength, GenericArray,
19+
Array, ArraySize,
2020
};
2121

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

34-
/// Trait for supported modulus sizes which precomputes the typenums for
35-
/// various point encodings so they don't need to be included as bounds.
34+
/// Trait for supported modulus sizes which precomputes the typenums for various point encodings so
35+
/// they don't need to be included as bounds.
3636
// TODO(tarcieri): replace this all with const generic expressions.
37-
pub trait ModulusSize: 'static + ArrayLength<u8> + Copy + Debug {
38-
/// Size of a compressed point for the given elliptic curve when encoded
39-
/// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm
40-
/// (including leading `0x02` or `0x03` tag byte).
41-
type CompressedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
42-
43-
/// Size of an uncompressed point for the given elliptic curve when encoded
44-
/// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm
45-
/// (including leading `0x04` tag byte).
46-
type UncompressedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
47-
48-
/// Size of an untagged point for given elliptic curve, i.e. size of two
49-
/// serialized base field elements.
50-
type UntaggedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
37+
pub trait ModulusSize: 'static + ArraySize + Copy + Debug {
38+
/// Size of a compressed point for the given elliptic curve when encoded using the SEC1
39+
/// `Elliptic-Curve-Point-to-Octet-String` algorithm (including leading `0x02` or `0x03`
40+
/// tag byte).
41+
type CompressedPointSize: 'static
42+
+ ArraySize
43+
+ Copy
44+
+ Debug
45+
+ Add<Self, Output = Self::UncompressedPointSize>;
46+
47+
/// Size of an uncompressed point for the given elliptic curve when encoded using the SEC1
48+
/// `Elliptic-Curve-Point-to-Octet-String` algorithm (including leading `0x04` tag byte).
49+
type UncompressedPointSize: 'static + ArraySize + Copy + Debug;
50+
51+
/// Size of an untagged point for given elliptic curve, i.e. size of two serialized base field
52+
/// elements when concatenated.
53+
type UntaggedPointSize: 'static
54+
+ ArraySize
55+
+ Copy
56+
+ Debug
57+
+ Add<U1, Output = Self::UncompressedPointSize>
58+
+ Sub<Self, Output = Self>;
5159
}
5260

5361
macro_rules! impl_modulus_size {
@@ -60,6 +68,7 @@ macro_rules! impl_modulus_size {
6068
}
6169
}
6270

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

6574
/// SEC1 encoded curve point.
@@ -72,7 +81,7 @@ pub struct EncodedPoint<Size>
7281
where
7382
Size: ModulusSize,
7483
{
75-
bytes: GenericArray<u8, Size::UncompressedPointSize>,
84+
bytes: Array<u8, Size::UncompressedPointSize>,
7685
}
7786

7887
#[allow(clippy::len_without_is_empty)]
@@ -103,24 +112,24 @@ where
103112
return Err(Error::PointEncoding);
104113
}
105114

106-
let mut bytes = GenericArray::default();
115+
let mut bytes = Array::default();
107116
bytes[..expected_len].copy_from_slice(input);
108117
Ok(Self { bytes })
109118
}
110119

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

119128
/// Encode an elliptic curve point from big endian serialized coordinates
120129
/// (with optional point compression)
121130
pub fn from_affine_coordinates(
122-
x: &GenericArray<u8, Size>,
123-
y: &GenericArray<u8, Size>,
131+
x: &Array<u8, Size>,
132+
y: &Array<u8, Size>,
124133
compress: bool,
125134
) -> Self {
126135
let tag = if compress {
@@ -129,7 +138,7 @@ where
129138
Tag::Uncompressed
130139
};
131140

132-
let mut bytes = GenericArray::default();
141+
let mut bytes = Array::default();
133142
bytes[0] = tag.into();
134143
bytes[1..(Size::to_usize() + 1)].copy_from_slice(x);
135144

@@ -200,27 +209,28 @@ where
200209
return Coordinates::Identity;
201210
}
202211

203-
let (x, y) = self.bytes[1..].split_at(Size::to_usize());
212+
let (x_bytes, y_bytes) = self.bytes[1..].split_at(Size::to_usize());
213+
let x = Array::ref_from_slice(x_bytes);
204214

205215
if self.is_compressed() {
206216
Coordinates::Compressed {
207-
x: x.into(),
217+
x,
208218
y_is_odd: self.tag() as u8 & 1 == 1,
209219
}
210220
} else if self.is_compact() {
211-
Coordinates::Compact { x: x.into() }
221+
Coordinates::Compact { x }
212222
} else {
213223
Coordinates::Uncompressed {
214-
x: x.into(),
215-
y: y.into(),
224+
x,
225+
y: Array::ref_from_slice(y_bytes),
216226
}
217227
}
218228
}
219229

220230
/// Get the x-coordinate for this [`EncodedPoint`].
221231
///
222232
/// Returns `None` if this point is the identity point.
223-
pub fn x(&self) -> Option<&GenericArray<u8, Size>> {
233+
pub fn x(&self) -> Option<&Array<u8, Size>> {
224234
match self.coordinates() {
225235
Coordinates::Identity => None,
226236
Coordinates::Compressed { x, .. } => Some(x),
@@ -232,7 +242,7 @@ where
232242
/// Get the y-coordinate for this [`EncodedPoint`].
233243
///
234244
/// Returns `None` if this point is compressed or the identity point.
235-
pub fn y(&self) -> Option<&GenericArray<u8, Size>> {
245+
pub fn y(&self) -> Option<&Array<u8, Size>> {
236246
match self.coordinates() {
237247
Coordinates::Compressed { .. } | Coordinates::Identity => None,
238248
Coordinates::Uncompressed { y, .. } => Some(y),
@@ -255,10 +265,10 @@ where
255265
impl<Size> ConditionallySelectable for EncodedPoint<Size>
256266
where
257267
Size: ModulusSize,
258-
<Size::UncompressedPointSize as ArrayLength<u8>>::ArrayType: Copy,
268+
<Size::UncompressedPointSize as ArraySize>::ArrayType<u8>: Copy,
259269
{
260270
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
261-
let mut bytes = GenericArray::default();
271+
let mut bytes = Array::default();
262272

263273
for (i, byte) in bytes.iter_mut().enumerate() {
264274
*byte = u8::conditional_select(&a.bytes[i], &b.bytes[i], choice);
@@ -271,7 +281,7 @@ where
271281
impl<Size> Copy for EncodedPoint<Size>
272282
where
273283
Size: ModulusSize,
274-
<Size::UncompressedPointSize as ArrayLength<u8>>::ArrayType: Copy,
284+
<Size::UncompressedPointSize as ArraySize>::ArrayType<u8>: Copy,
275285
{
276286
}
277287

@@ -382,7 +392,7 @@ where
382392
type Err = Error;
383393

384394
fn from_str(hex: &str) -> Result<Self> {
385-
let mut buf = GenericArray::<u8, Size::UncompressedPointSize>::default();
395+
let mut buf = Array::<u8, Size::UncompressedPointSize>::default();
386396
base16ct::mixed::decode(hex, &mut buf)
387397
.map_err(|_| Error::PointEncoding)
388398
.and_then(Self::from_bytes)
@@ -426,13 +436,13 @@ pub enum Coordinates<'a, Size: ModulusSize> {
426436
/// Compact curve point
427437
Compact {
428438
/// x-coordinate
429-
x: &'a GenericArray<u8, Size>,
439+
x: &'a Array<u8, Size>,
430440
},
431441

432442
/// Compressed curve point
433443
Compressed {
434444
/// x-coordinate
435-
x: &'a GenericArray<u8, Size>,
445+
x: &'a Array<u8, Size>,
436446

437447
/// Is the y-coordinate odd?
438448
y_is_odd: bool,
@@ -441,10 +451,10 @@ pub enum Coordinates<'a, Size: ModulusSize> {
441451
/// Uncompressed curve point
442452
Uncompressed {
443453
/// x-coordinate
444-
x: &'a GenericArray<u8, Size>,
454+
x: &'a Array<u8, Size>,
445455

446456
/// y-coordinate
447-
y: &'a GenericArray<u8, Size>,
457+
y: &'a Array<u8, Size>,
448458
},
449459
}
450460

@@ -556,8 +566,8 @@ impl From<Tag> for u8 {
556566
mod tests {
557567
use super::{Coordinates, Tag};
558568
use core::str::FromStr;
559-
use generic_array::{typenum::U32, GenericArray};
560569
use hex_literal::hex;
570+
use hybrid_array::typenum::U32;
561571

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

601611
assert_eq!(
602612
compressed_even_y.x().unwrap(),
603-
&hex!("0100000000000000000000000000000000000000000000000000000000000000").into()
613+
&hex!("0100000000000000000000000000000000000000000000000000000000000000")
604614
);
605615
assert_eq!(compressed_even_y.y(), None);
606616

@@ -625,7 +635,7 @@ mod tests {
625635

626636
assert_eq!(
627637
compressed_odd_y.x().unwrap(),
628-
&hex!("0200000000000000000000000000000000000000000000000000000000000000").into()
638+
&hex!("0200000000000000000000000000000000000000000000000000000000000000")
629639
);
630640
assert_eq!(compressed_odd_y.y(), None);
631641
}
@@ -649,11 +659,11 @@ mod tests {
649659

650660
assert_eq!(
651661
uncompressed_point.x().unwrap(),
652-
&hex!("1111111111111111111111111111111111111111111111111111111111111111").into()
662+
&hex!("1111111111111111111111111111111111111111111111111111111111111111")
653663
);
654664
assert_eq!(
655665
uncompressed_point.y().unwrap(),
656-
&hex!("2222222222222222222222222222222222222222222222222222222222222222").into()
666+
&hex!("2222222222222222222222222222222222222222222222222222222222222222")
657667
);
658668
}
659669

@@ -701,8 +711,7 @@ mod tests {
701711
#[test]
702712
fn from_untagged_point() {
703713
let untagged_bytes = hex!("11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222");
704-
let uncompressed_point =
705-
EncodedPoint::from_untagged_bytes(GenericArray::from_slice(&untagged_bytes[..]));
714+
let uncompressed_point = EncodedPoint::from_untagged_bytes(&untagged_bytes.into());
706715
assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]);
707716
}
708717

0 commit comments

Comments
 (0)