Skip to content

Commit 0f16437

Browse files
committed
Decouple element from Group
1 parent e767543 commit 0f16437

File tree

9 files changed

+237
-214
lines changed

9 files changed

+237
-214
lines changed

src/group/mod.rs

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,47 @@ use digest::{Digest, FixedOutputReset};
2121
use generic_array::typenum::U1;
2222
use generic_array::{ArrayLength, GenericArray};
2323
use rand_core::{CryptoRng, RngCore};
24+
#[cfg(feature = "ristretto255")]
25+
pub use ristretto::Ristretto255;
2426
use subtle::ConstantTimeEq;
2527
use zeroize::Zeroize;
2628

2729
use crate::{Error, Result};
2830

2931
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
3032
/// 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-
{
33+
pub trait Group {
3834
/// The ciphersuite identifier as dictated by
3935
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
4036
const SUITE_ID: usize;
4137

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

@@ -60,16 +78,6 @@ pub trait Group:
6078
where
6179
<D as Add<U1>>::Output: ArrayLength<u8>;
6280

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-
7381
/// Return a scalar from its fixed-length bytes representation, without
7482
/// checking if the scalar is zero.
7583
fn from_scalar_slice_unchecked(
@@ -90,28 +98,28 @@ pub trait Group:
9098

9199
/// picks a scalar at random
92100
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
101+
93102
/// Serializes a scalar to bytes
94103
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
104+
95105
/// The multiplicative inverse of this scalar
96106
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar;
97107

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

107115
/// Return an element from its fixed-length bytes representation. If the
108116
/// element is the identity element, return an error.
109117
fn from_element_slice<'a>(
110118
element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>,
111-
) -> Result<Self> {
119+
) -> Result<Self::Elem> {
112120
let elem = Self::from_element_slice_unchecked(element_bits.into())?;
113121

114-
if Self::ct_eq(&elem, &<Self as Group>::identity()).into() {
122+
if Self::Elem::ct_eq(&elem, &Self::identity()).into() {
115123
// found the identity element
116124
return Err(Error::PointError);
117125
}
@@ -120,26 +128,21 @@ pub trait Group:
120128
}
121129

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

125133
/// Get the base point for the group
126-
fn base_point() -> Self;
134+
fn base_point() -> Self::Elem;
127135

128136
/// 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()
137+
fn is_identity(elem: Self::Elem) -> bool {
138+
elem.ct_eq(&Self::identity()).into()
131139
}
132140

133141
/// Returns the identity group element
134-
fn identity() -> Self;
142+
fn identity() -> Self::Elem;
135143

136144
/// Returns the scalar representing zero
137145
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-
}
143146
}
144147

145148
#[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: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,26 @@ use rand_core::{CryptoRng, RngCore};
2121
use super::Group;
2222
use crate::{Error, Result};
2323

24+
/// [`Group`] implementation for Ristretto255.
25+
pub struct Ristretto255;
26+
2427
// `cfg` here is only needed because of a bug in Rust's crate feature documentation. See: https://github.com/rust-lang/rust/issues/83428
2528
#[cfg(feature = "ristretto255")]
26-
/// The implementation of such a subgroup for Ristretto
27-
impl Group for RistrettoPoint {
29+
impl Group for Ristretto255 {
2830
const SUITE_ID: usize = 0x0001;
2931

32+
type Elem = RistrettoPoint;
33+
34+
type Scalar = Scalar;
35+
36+
type ScalarLen = U32;
37+
3038
// Implements the `hash_to_ristretto255()` function from
3139
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
3240
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
3341
msg: &[u8],
3442
dst: GenericArray<u8, D>,
35-
) -> Result<Self>
43+
) -> Result<Self::Elem>
3644
where
3745
<D as Add<U1>>::Output: ArrayLength<u8>,
3846
{
@@ -70,8 +78,6 @@ impl Group for RistrettoPoint {
7078
))
7179
}
7280

73-
type Scalar = Scalar;
74-
type ScalarLen = U32;
7581
fn from_scalar_slice_unchecked(
7682
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
7783
) -> Result<Self::Scalar> {
@@ -104,25 +110,26 @@ impl Group for RistrettoPoint {
104110
type ElemLen = U32;
105111
fn from_element_slice_unchecked(
106112
element_bits: &GenericArray<u8, Self::ElemLen>,
107-
) -> Result<Self> {
113+
) -> Result<Self::Elem> {
108114
CompressedRistretto::from_slice(element_bits)
109115
.decompress()
110116
.ok_or(Error::PointError)
111117
}
118+
112119
// serialization of a group element
113-
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
114-
self.compress().to_bytes().into()
120+
fn to_arr(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
121+
elem.compress().to_bytes().into()
115122
}
116123

117-
fn base_point() -> Self {
124+
fn base_point() -> Self::Elem {
118125
RISTRETTO_BASEPOINT_POINT
119126
}
120127

121-
fn identity() -> Self {
122-
<Self as Identity>::identity()
128+
fn identity() -> Self::Elem {
129+
RistrettoPoint::identity()
123130
}
124131

125132
fn scalar_zero() -> Self::Scalar {
126-
Self::Scalar::zero()
133+
Scalar::zero()
127134
}
128135
}

src/group/tests.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ use crate::{Error, Group, Result};
1616
fn test_group_properties() -> Result<()> {
1717
#[cfg(feature = "ristretto255")]
1818
{
19-
use curve25519_dalek::ristretto::RistrettoPoint;
19+
use crate::Ristretto255;
2020

21-
test_identity_element_error::<RistrettoPoint>()?;
22-
test_zero_scalar_error::<RistrettoPoint>()?;
21+
test_identity_element_error::<Ristretto255>()?;
22+
test_zero_scalar_error::<Ristretto255>()?;
2323
}
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)