Skip to content

Commit a6e1726

Browse files
committed
Decouple element from Group
1 parent e767543 commit a6e1726

File tree

9 files changed

+165
-152
lines changed

9 files changed

+165
-152
lines changed

src/group/mod.rs

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,38 @@ use crate::{Error, Result};
2828

2929
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
3030
/// subgroup is noted additively — as in the draft RFC — in this trait.
31-
pub trait Group:
32-
Copy
33-
+ Sized
34-
+ ConstantTimeEq
35-
+ for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self>
36-
+ for<'a> Add<&'a Self, Output = Self>
37-
{
31+
pub trait Group {
3832
/// The ciphersuite identifier as dictated by
3933
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
4034
const SUITE_ID: usize;
4135

36+
/// The type of group elements
37+
type Elem: Copy
38+
+ Sized
39+
+ ConstantTimeEq
40+
+ Zeroize
41+
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>
42+
+ for<'a> Add<&'a Self::Elem, Output = Self::Elem>;
43+
44+
/// The byte length necessary to represent group elements
45+
type ElemLen: ArrayLength<u8> + 'static;
46+
47+
/// The type of base field scalars
48+
type Scalar: Zeroize
49+
+ Copy
50+
+ ConstantTimeEq
51+
+ for<'a> Add<&'a Self::Scalar, Output = Self::Scalar>
52+
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>
53+
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>;
54+
55+
/// The byte length necessary to represent scalars
56+
type ScalarLen: ArrayLength<u8> + 'static;
57+
4258
/// transforms a password and domain separation tag (DST) into a curve point
4359
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
4460
msg: &[u8],
4561
dst: GenericArray<u8, D>,
46-
) -> Result<Self>
62+
) -> Result<Self::Elem>
4763
where
4864
<D as Add<U1>>::Output: ArrayLength<u8>;
4965

@@ -60,16 +76,6 @@ pub trait Group:
6076
where
6177
<D as Add<U1>>::Output: ArrayLength<u8>;
6278

63-
/// The type of base field scalars
64-
type Scalar: Zeroize
65-
+ Copy
66-
+ ConstantTimeEq
67-
+ for<'a> Add<&'a Self::Scalar, Output = Self::Scalar>
68-
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>
69-
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>;
70-
/// The byte length necessary to represent scalars
71-
type ScalarLen: ArrayLength<u8> + 'static;
72-
7379
/// Return a scalar from its fixed-length bytes representation, without
7480
/// checking if the scalar is zero.
7581
fn from_scalar_slice_unchecked(
@@ -90,28 +96,28 @@ pub trait Group:
9096

9197
/// picks a scalar at random
9298
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
99+
93100
/// Serializes a scalar to bytes
94101
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
102+
95103
/// The multiplicative inverse of this scalar
96104
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar;
97105

98-
/// The byte length necessary to represent group elements
99-
type ElemLen: ArrayLength<u8> + 'static;
100-
101106
/// Return an element from its fixed-length bytes representation. This is
102107
/// the unchecked version, which does not check for deserializing the
103108
/// identity element
104-
fn from_element_slice_unchecked(element_bits: &GenericArray<u8, Self::ElemLen>)
105-
-> Result<Self>;
109+
fn from_element_slice_unchecked(
110+
element_bits: &GenericArray<u8, Self::ElemLen>,
111+
) -> Result<Self::Elem>;
106112

107113
/// Return an element from its fixed-length bytes representation. If the
108114
/// element is the identity element, return an error.
109115
fn from_element_slice<'a>(
110116
element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>,
111-
) -> Result<Self> {
117+
) -> Result<Self::Elem> {
112118
let elem = Self::from_element_slice_unchecked(element_bits.into())?;
113119

114-
if Self::ct_eq(&elem, &<Self as Group>::identity()).into() {
120+
if Self::Elem::ct_eq(&elem, &Self::identity()).into() {
115121
// found the identity element
116122
return Err(Error::PointError);
117123
}
@@ -120,26 +126,21 @@ pub trait Group:
120126
}
121127

122128
/// Serializes the `self` group element
123-
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen>;
129+
fn to_arr(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen>;
124130

125131
/// Get the base point for the group
126-
fn base_point() -> Self;
132+
fn base_point() -> Self::Elem;
127133

128134
/// Returns if the group element is equal to the identity (1)
129-
fn is_identity(&self) -> bool {
130-
self.ct_eq(&<Self as Group>::identity()).into()
135+
fn is_identity(elem: Self::Elem) -> bool {
136+
elem.ct_eq(&Self::identity()).into()
131137
}
132138

133139
/// Returns the identity group element
134-
fn identity() -> Self;
140+
fn identity() -> Self::Elem;
135141

136142
/// Returns the scalar representing zero
137143
fn scalar_zero() -> Self::Scalar;
138-
139-
/// Set the contents of self to the identity value
140-
fn zeroize(&mut self) {
141-
*self = <Self as Group>::identity();
142-
}
143144
}
144145

145146
#[cfg(test)]

src/group/p256.rs

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use p256_::elliptic_curve::group::GroupEncoding;
2929
use p256_::elliptic_curve::ops::Reduce;
3030
use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
3131
use p256_::elliptic_curve::Field;
32-
use p256_::{AffinePoint, EncodedPoint, ProjectivePoint};
32+
use p256_::{AffinePoint, EncodedPoint, NistP256, ProjectivePoint, Scalar};
3333
use rand_core::{CryptoRng, RngCore};
3434
use subtle::{Choice, ConditionallySelectable};
3535

@@ -41,15 +41,23 @@ use crate::{Error, Result};
4141
pub type L = U48;
4242

4343
#[cfg(feature = "p256")]
44-
impl Group for ProjectivePoint {
44+
impl Group for NistP256 {
4545
const SUITE_ID: usize = 0x0003;
4646

47+
type Elem = ProjectivePoint;
48+
49+
type ElemLen = U33;
50+
51+
type Scalar = Scalar;
52+
53+
type ScalarLen = U32;
54+
4755
// Implements the `hash_to_curve()` function from
4856
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
4957
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
5058
msg: &[u8],
5159
dst: GenericArray<u8, D>,
52-
) -> Result<Self>
60+
) -> Result<Self::Elem>
5361
where
5462
<D as Add<U1>>::Output: ArrayLength<u8>,
5563
{
@@ -133,55 +141,51 @@ impl Group for ProjectivePoint {
133141
let mut result = GenericArray::default();
134142
result[..bytes.len()].copy_from_slice(&bytes);
135143

136-
Ok(p256_::Scalar::from_be_bytes_reduced(result))
144+
Ok(Scalar::from_be_bytes_reduced(result))
137145
}
138146

139-
type ElemLen = U33;
140-
type Scalar = p256_::Scalar;
141-
type ScalarLen = U32;
142-
143147
fn from_scalar_slice_unchecked(
144148
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
145149
) -> Result<Self::Scalar> {
146-
Ok(Self::Scalar::from_be_bytes_reduced(*scalar_bits))
150+
Ok(Scalar::from_be_bytes_reduced(*scalar_bits))
147151
}
148152

149153
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
150-
Self::Scalar::random(rng)
154+
Scalar::random(rng)
151155
}
152156

153157
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
154158
scalar.into()
155159
}
156160

157161
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
158-
scalar.invert().unwrap_or(Self::Scalar::zero())
162+
scalar.invert().unwrap_or(Scalar::zero())
159163
}
160164

161165
fn from_element_slice_unchecked(
162166
element_bits: &GenericArray<u8, Self::ElemLen>,
163-
) -> Result<Self> {
164-
Option::from(Self::from_bytes(element_bits)).ok_or(Error::PointError)
167+
) -> Result<Self::Elem> {
168+
Option::from(ProjectivePoint::from_bytes(element_bits)).ok_or(Error::PointError)
165169
}
166170

167-
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
168-
let bytes = self.to_affine().to_encoded_point(true);
171+
fn to_arr(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
172+
let bytes = elem.to_affine().to_encoded_point(true);
169173
let bytes = bytes.as_bytes();
170174
let mut result = GenericArray::default();
171175
result[..bytes.len()].copy_from_slice(bytes);
172176
result
173177
}
174178

175-
fn base_point() -> Self {
176-
Self::generator()
179+
fn base_point() -> Self::Elem {
180+
ProjectivePoint::generator()
177181
}
178182

179-
fn identity() -> Self {
180-
Self::identity()
183+
fn identity() -> Self::Elem {
184+
ProjectivePoint::identity()
181185
}
182186

183187
fn scalar_zero() -> Self::Scalar {
184-
Self::Scalar::zero()
188+
Scalar::zero()
185189
}
186190
}
187191

src/group/ristretto.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ use crate::{Error, Result};
2727
impl Group for RistrettoPoint {
2828
const SUITE_ID: usize = 0x0001;
2929

30+
type Elem = RistrettoPoint;
31+
32+
type Scalar = Scalar;
33+
34+
type ScalarLen = U32;
35+
3036
// Implements the `hash_to_ristretto255()` function from
3137
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
3238
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
@@ -70,8 +76,6 @@ impl Group for RistrettoPoint {
7076
))
7177
}
7278

73-
type Scalar = Scalar;
74-
type ScalarLen = U32;
7579
fn from_scalar_slice_unchecked(
7680
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
7781
) -> Result<Self::Scalar> {
@@ -109,9 +113,10 @@ impl Group for RistrettoPoint {
109113
.decompress()
110114
.ok_or(Error::PointError)
111115
}
116+
112117
// serialization of a group element
113-
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
114-
self.compress().to_bytes().into()
118+
fn to_arr(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
119+
elem.compress().to_bytes().into()
115120
}
116121

117122
fn base_point() -> Self {

src/group/tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ fn test_group_properties() -> Result<()> {
2424

2525
#[cfg(feature = "p256")]
2626
{
27-
use p256_::ProjectivePoint;
27+
use p256_::NistP256;
2828

29-
test_identity_element_error::<ProjectivePoint>()?;
30-
test_zero_scalar_error::<ProjectivePoint>()?;
29+
test_identity_element_error::<NistP256>()?;
30+
test_zero_scalar_error::<NistP256>()?;
3131
}
3232

3333
Ok(())
@@ -36,7 +36,7 @@ fn test_group_properties() -> Result<()> {
3636
// Checks that the identity element cannot be deserialized
3737
fn test_identity_element_error<G: Group>() -> Result<()> {
3838
let identity = G::identity();
39-
let result = G::from_element_slice(&identity.to_arr());
39+
let result = G::from_element_slice(&G::to_arr(identity));
4040
assert!(matches!(result, Err(Error::PointError)));
4141

4242
Ok(())

0 commit comments

Comments
 (0)