1#![allow(deprecated)]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![no_std] #![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
50fn 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 #[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 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 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 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 #[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}