Skip to content

Commit 93974c9

Browse files
authored
elliptic-curve: extract NonIdentity/NonZeroScalar casting methods (#1903)
These methods encapsulate safely casting references for arrays and slices of `NonIdentity<P>` to array/slice references to the inner `P` type, and the same for `NonZeroScalar` and its inner `Scalar<C>` type. Note the choice of method names follows similar ones in `hybrid-array`: https://docs.rs/hybrid-array/latest/hybrid_array/struct.Array.html#method.cast_slice_to_core
1 parent 5f5f6ab commit 93974c9

File tree

3 files changed

+49
-41
lines changed

3 files changed

+49
-41
lines changed

elliptic-curve/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
66
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
77
)]
8-
// Only allowed for newtype casts.
9-
#![deny(unsafe_code)]
8+
#![deny(unsafe_code)] // Only allowed for newtype casts.
109
#![warn(
1110
clippy::cast_lossless,
1211
clippy::cast_possible_truncation,

elliptic-curve/src/point/non_identity.rs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{BatchNormalize, CurveArithmetic, NonZeroScalar, Scalar};
2222
/// In the context of ECC, it's useful for ensuring that certain arithmetic
2323
/// cannot result in the identity point.
2424
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25-
#[repr(transparent)]
25+
#[repr(transparent)] // SAFETY: needed for `unsafe` safety invariants below
2626
pub struct NonIdentity<P> {
2727
point: P,
2828
}
@@ -51,6 +51,29 @@ where
5151
}
5252
}
5353

54+
impl<P> NonIdentity<P> {
55+
/// Transform array reference containing [`NonIdentity`] points to an array reference to the
56+
/// inner point type.
57+
pub fn cast_array_as_inner<const N: usize>(points: &[Self; N]) -> &[P; N] {
58+
// SAFETY: `NonIdentity` is a `repr(transparent)` newtype for `P` so it's safe to cast to
59+
// the inner `P` type.
60+
#[allow(unsafe_code)]
61+
unsafe {
62+
&*points.as_ptr().cast()
63+
}
64+
}
65+
66+
/// Transform slice containing [`NonIdentity`] points to a slice of the inner point type.
67+
pub fn cast_slice_as_inner(points: &[Self]) -> &[P] {
68+
// SAFETY: `NonIdentity` is a `repr(transparent)` newtype for `P` so it's safe to cast to
69+
// the inner `P` type.
70+
#[allow(unsafe_code)]
71+
unsafe {
72+
&*(points as *const [NonIdentity<P>] as *const [P])
73+
}
74+
}
75+
}
76+
5477
impl<P: Copy> NonIdentity<P> {
5578
/// Return wrapped point.
5679
pub fn to_point(self) -> P {
@@ -114,26 +137,8 @@ where
114137
type Output = [NonIdentity<P::AffineRepr>; N];
115138

116139
fn batch_normalize(points: &[Self; N]) -> [NonIdentity<P::AffineRepr>; N] {
117-
// Ensure casting is safe.
118-
// This always succeeds because `NonIdentity` is `repr(transparent)`.
119-
debug_assert_eq!(size_of::<P>(), size_of::<NonIdentity<P>>());
120-
debug_assert_eq!(align_of::<P>(), align_of::<NonIdentity<P>>());
121-
122-
#[allow(unsafe_code)]
123-
// SAFETY: `NonIdentity` is `repr(transparent)`.
124-
let points: &[P; N] = unsafe { &*points.as_ptr().cast() };
140+
let points = Self::cast_array_as_inner::<N>(points);
125141
let affine_points = <P as BatchNormalize<_>>::batch_normalize(points);
126-
127-
// Ensure `array::map()` can be optimized to a `memcpy`.
128-
debug_assert_eq!(
129-
size_of::<P::AffineRepr>(),
130-
size_of::<NonIdentity<P::AffineRepr>>()
131-
);
132-
debug_assert_eq!(
133-
align_of::<P::AffineRepr>(),
134-
align_of::<NonIdentity<P::AffineRepr>>()
135-
);
136-
137142
affine_points.map(|point| NonIdentity { point })
138143
}
139144
}
@@ -146,26 +151,8 @@ where
146151
type Output = Vec<NonIdentity<P::AffineRepr>>;
147152

148153
fn batch_normalize(points: &[Self]) -> Vec<NonIdentity<P::AffineRepr>> {
149-
// Ensure casting is safe.
150-
// This always succeeds because `NonIdentity` is `repr(transparent)`.
151-
debug_assert_eq!(size_of::<P>(), size_of::<NonIdentity<P>>());
152-
debug_assert_eq!(align_of::<P>(), align_of::<NonIdentity<P>>());
153-
154-
#[allow(unsafe_code)]
155-
// SAFETY: `NonIdentity` is `repr(transparent)`.
156-
let points: &[P] = unsafe { &*(points as *const [NonIdentity<P>] as *const [P]) };
154+
let points = Self::cast_slice_as_inner(points);
157155
let affine_points = <P as BatchNormalize<_>>::batch_normalize(points);
158-
159-
// Ensure `into_iter()` + `collect()` can be optimized away.
160-
debug_assert_eq!(
161-
size_of::<P::AffineRepr>(),
162-
size_of::<NonIdentity<P::AffineRepr>>()
163-
);
164-
debug_assert_eq!(
165-
align_of::<P::AffineRepr>(),
166-
align_of::<NonIdentity<P::AffineRepr>>()
167-
);
168-
169156
affine_points
170157
.into_iter()
171158
.map(|point| NonIdentity { point })

elliptic-curve/src/scalar/nonzero.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use serdect::serde::{Deserialize, Serialize, de, ser};
3333
/// In the context of ECC, it's useful for ensuring that scalar multiplication
3434
/// cannot result in the point at infinity.
3535
#[derive(Clone)]
36+
#[repr(transparent)] // SAFETY: needed for `unsafe` safety invariants below
3637
pub struct NonZeroScalar<C>
3738
where
3839
C: CurveArithmetic,
@@ -88,6 +89,27 @@ where
8889
pub fn from_uint(uint: C::Uint) -> CtOption<Self> {
8990
ScalarPrimitive::new(uint).and_then(|scalar| Self::new(scalar.into()))
9091
}
92+
93+
/// Transform array reference containing [`NonZeroScalar`]s to an array reference to the inner
94+
/// scalar type.
95+
pub fn cast_array_as_inner<const N: usize>(scalars: &[Self; N]) -> &[Scalar<C>; N] {
96+
// SAFETY: `NonZeroScalar` is a `repr(transparent)` newtype for `Scalar<C>` so it's safe to
97+
// cast to the inner scalar type.
98+
#[allow(unsafe_code)]
99+
unsafe {
100+
&*scalars.as_ptr().cast()
101+
}
102+
}
103+
104+
/// Transform slice containing [`NonZeroScalar`]s to a slice of the inner scalar type.
105+
pub fn cast_slice_as_inner(scalars: &[Self]) -> &[Scalar<C>] {
106+
// SAFETY: `NonZeroScalar` is a `repr(transparent)` newtype for `Scalar<C>` so it's safe to
107+
// cast to the inner scalar type.
108+
#[allow(unsafe_code)]
109+
unsafe {
110+
&*(scalars as *const [NonZeroScalar<C>] as *const [Scalar<C>])
111+
}
112+
}
91113
}
92114

93115
impl<C> AsRef<Scalar<C>> for NonZeroScalar<C>

0 commit comments

Comments
 (0)