dalek_ff_group/
lib.rs

1#![allow(deprecated)]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std
4#![doc = include_str!("../README.md")]
5#![allow(clippy::redundant_closure_call)]
6
7use core::{
8  borrow::Borrow,
9  ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
10  iter::{Iterator, Sum},
11  hash::{Hash, Hasher},
12};
13
14use zeroize::Zeroize;
15use subtle::{ConstantTimeEq, ConditionallySelectable};
16
17use rand_core::RngCore;
18
19use subtle::{Choice, CtOption};
20
21use curve25519_dalek::{
22  constants,
23  edwards::{EdwardsPoint as DEdwardsPoint, CompressedEdwardsY},
24};
25pub use curve25519_dalek::{Scalar, RistrettoPoint};
26
27use group::{Group, GroupEncoding, prime::PrimeGroup};
28
29mod field;
30pub use field::FieldElement;
31
32mod ciphersuite;
33pub use crate::ciphersuite::{Ed25519, Ristretto};
34
35use core::hint::black_box;
36
37fn u8_from_bool(bit_ref: &mut bool) -> u8 {
38  let bit_ref = black_box(bit_ref);
39
40  let mut bit = black_box(*bit_ref);
41  #[allow(clippy::cast_lossless)]
42  let res = black_box(bit as u8);
43  bit.zeroize();
44  debug_assert!((res | 1) == 1);
45
46  bit_ref.zeroize();
47  res
48}
49
50// Convert a boolean to a Choice in a *presumably* constant time manner
51fn choice(mut value: bool) -> Choice {
52  Choice::from(u8_from_bool(&mut value))
53}
54
55macro_rules! deref_borrow {
56  ($Source: ident, $Target: ident) => {
57    impl Deref for $Source {
58      type Target = $Target;
59
60      fn deref(&self) -> &Self::Target {
61        &self.0
62      }
63    }
64
65    impl Borrow<$Target> for $Source {
66      fn borrow(&self) -> &$Target {
67        &self.0
68      }
69    }
70
71    impl Borrow<$Target> for &$Source {
72      fn borrow(&self) -> &$Target {
73        &self.0
74      }
75    }
76  };
77}
78
79macro_rules! constant_time {
80  ($Value: ident, $Inner: ident) => {
81    impl ConstantTimeEq for $Value {
82      fn ct_eq(&self, other: &Self) -> Choice {
83        self.0.ct_eq(&other.0)
84      }
85    }
86
87    impl ConditionallySelectable for $Value {
88      fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
89        $Value($Inner::conditional_select(&a.0, &b.0, choice))
90      }
91    }
92  };
93}
94pub(crate) use constant_time;
95
96macro_rules! math_op_without_wrapping {
97  (
98    $Value: ident,
99    $Other: ident,
100    $Op: ident,
101    $op_fn: ident,
102    $Assign: ident,
103    $assign_fn: ident,
104    $function: expr
105  ) => {
106    impl $Op<$Other> for $Value {
107      type Output = $Value;
108      fn $op_fn(self, other: $Other) -> Self::Output {
109        Self($function(self.0, other))
110      }
111    }
112    impl $Assign<$Other> for $Value {
113      fn $assign_fn(&mut self, other: $Other) {
114        self.0 = $function(self.0, other);
115      }
116    }
117    impl<'a> $Op<&'a $Other> for $Value {
118      type Output = $Value;
119      fn $op_fn(self, other: &'a $Other) -> Self::Output {
120        Self($function(self.0, other))
121      }
122    }
123    impl<'a> $Assign<&'a $Other> for $Value {
124      fn $assign_fn(&mut self, other: &'a $Other) {
125        self.0 = $function(self.0, other);
126      }
127    }
128  };
129}
130
131macro_rules! math_op {
132  (
133    $Value: ident,
134    $Other: ident,
135    $Op: ident,
136    $op_fn: ident,
137    $Assign: ident,
138    $assign_fn: ident,
139    $function: expr
140  ) => {
141    impl $Op<$Other> for $Value {
142      type Output = $Value;
143      fn $op_fn(self, other: $Other) -> Self::Output {
144        Self($function(self.0, other.0))
145      }
146    }
147    impl $Assign<$Other> for $Value {
148      fn $assign_fn(&mut self, other: $Other) {
149        self.0 = $function(self.0, other.0);
150      }
151    }
152    impl<'a> $Op<&'a $Other> for $Value {
153      type Output = $Value;
154      fn $op_fn(self, other: &'a $Other) -> Self::Output {
155        Self($function(self.0, other.0))
156      }
157    }
158    impl<'a> $Assign<&'a $Other> for $Value {
159      fn $assign_fn(&mut self, other: &'a $Other) {
160        self.0 = $function(self.0, other.0);
161      }
162    }
163  };
164}
165pub(crate) use math_op;
166
167macro_rules! math {
168  ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
169    math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
170    math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
171    math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
172  };
173}
174pub(crate) use math;
175
176macro_rules! math_neg {
177  ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
178    math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
179    math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
180    math_op_without_wrapping!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
181
182    impl Neg for $Value {
183      type Output = Self;
184      fn neg(self) -> Self::Output {
185        Self(-self.0)
186      }
187    }
188  };
189}
190
191macro_rules! dalek_group {
192  (
193    $Point: ident,
194    $DPoint: ident,
195    $torsion_free: expr,
196
197    $Table: ident,
198
199    $DCompressed: ident,
200
201    $BASEPOINT_POINT: ident,
202    $BASEPOINT_TABLE: ident
203  ) => {
204    /// Wrapper around the dalek Point type.
205    ///
206    /// All operations will be restricted to a prime-order subgroup (equivalent to the group itself
207    /// in the case of Ristretto). The exposure of the internal element does allow bypassing this
208    /// however, which may lead to undefined/computationally-unsafe behavior, and is entirely at
209    /// the user's risk.
210    #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
211    pub struct $Point(pub $DPoint);
212    deref_borrow!($Point, $DPoint);
213    constant_time!($Point, $DPoint);
214    math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul);
215
216    /// The basepoint for this curve.
217    pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT);
218
219    impl Sum<$Point> for $Point {
220      fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point {
221        Self($DPoint::sum(iter))
222      }
223    }
224    impl<'a> Sum<&'a $Point> for $Point {
225      fn sum<I: Iterator<Item = &'a $Point>>(iter: I) -> $Point {
226        Self($DPoint::sum(iter))
227      }
228    }
229
230    impl Group for $Point {
231      type Scalar = Scalar;
232      fn random(mut rng: impl RngCore) -> Self {
233        loop {
234          let mut bytes = [0; 32];
235          rng.fill_bytes(&mut bytes);
236          let Some(point) = Option::<$Point>::from($Point::from_bytes(&bytes)) else {
237            continue;
238          };
239          // Ban identity, per the trait specification
240          if !bool::from(point.is_identity()) {
241            return point;
242          }
243        }
244      }
245      fn identity() -> Self {
246        Self($DPoint::identity())
247      }
248      fn generator() -> Self {
249        $BASEPOINT_POINT
250      }
251      fn is_identity(&self) -> Choice {
252        self.0.ct_eq(&$DPoint::identity())
253      }
254      fn double(&self) -> Self {
255        Self(self.0.double())
256      }
257    }
258
259    impl GroupEncoding for $Point {
260      type Repr = [u8; 32];
261
262      fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
263        let decompressed = $DCompressed(*bytes).decompress();
264        // TODO: Same note on unwrap_or as above
265        let point = decompressed.unwrap_or($DPoint::identity());
266        CtOption::new(
267          $Point(point),
268          choice(black_box(decompressed).is_some()) & choice($torsion_free(point)),
269        )
270      }
271
272      fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
273        $Point::from_bytes(bytes)
274      }
275
276      fn to_bytes(&self) -> Self::Repr {
277        self.0.to_bytes()
278      }
279    }
280
281    impl PrimeGroup for $Point {}
282
283    // Support being used as a key in a table
284    // While it is expensive as a key, due to the field operations required, there's frequently
285    // use cases for public key -> value lookups
286    #[allow(unknown_lints, renamed_and_removed_lints)]
287    #[allow(clippy::derived_hash_with_manual_eq, clippy::derive_hash_xor_eq)]
288    impl Hash for $Point {
289      fn hash<H: Hasher>(&self, state: &mut H) {
290        self.to_bytes().hash(state);
291      }
292    }
293  };
294}
295
296dalek_group!(
297  EdwardsPoint,
298  DEdwardsPoint,
299  |point: DEdwardsPoint| point.is_torsion_free(),
300  EdwardsBasepointTable,
301  CompressedEdwardsY,
302  ED25519_BASEPOINT_POINT,
303  ED25519_BASEPOINT_TABLE
304);
305
306#[test]
307fn test_ed25519_group() {
308  ff_group_tests::group::test_prime_group_bits::<_, EdwardsPoint>(&mut rand_core::OsRng);
309}
310
311#[test]
312fn test_ristretto_group() {
313  ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng);
314}