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, Product},
11 hash::{Hash, Hasher},
12};
13
14use zeroize::Zeroize;
15use subtle::{ConstantTimeEq, ConditionallySelectable};
16
17use rand_core::RngCore;
18use digest::{consts::U64, Digest, HashMarker};
19
20use subtle::{Choice, CtOption};
21
22pub use curve25519_dalek as dalek;
23
24use dalek::{
25 constants::{self, BASEPOINT_ORDER},
26 scalar::Scalar as DScalar,
27 edwards::{EdwardsPoint as DEdwardsPoint, EdwardsBasepointTable, CompressedEdwardsY},
28 ristretto::{RistrettoPoint as DRistrettoPoint, RistrettoBasepointTable, CompressedRistretto},
29};
30pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE};
31
32use group::{
33 ff::{Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes},
34 Group, GroupEncoding,
35 prime::PrimeGroup,
36};
37
38mod field;
39pub use field::FieldElement;
40
41mod ciphersuite;
42pub use crate::ciphersuite::{Ed25519, Ristretto};
43
44#[rustversion::since(1.66)]
46mod black_box {
47 pub(crate) fn black_box<T>(val: T) -> T {
48 #[allow(clippy::incompatible_msrv)]
49 core::hint::black_box(val)
50 }
51}
52#[rustversion::before(1.66)]
53mod black_box {
54 pub(crate) fn black_box<T>(val: T) -> T {
55 val
56 }
57}
58use black_box::black_box;
59
60fn u8_from_bool(bit_ref: &mut bool) -> u8 {
61 let bit_ref = black_box(bit_ref);
62
63 let mut bit = black_box(*bit_ref);
64 #[allow(clippy::cast_lossless)]
65 let res = black_box(bit as u8);
66 bit.zeroize();
67 debug_assert!((res | 1) == 1);
68
69 bit_ref.zeroize();
70 res
71}
72
73fn choice(mut value: bool) -> Choice {
75 Choice::from(u8_from_bool(&mut value))
76}
77
78macro_rules! deref_borrow {
79 ($Source: ident, $Target: ident) => {
80 impl Deref for $Source {
81 type Target = $Target;
82
83 fn deref(&self) -> &Self::Target {
84 &self.0
85 }
86 }
87
88 impl Borrow<$Target> for $Source {
89 fn borrow(&self) -> &$Target {
90 &self.0
91 }
92 }
93
94 impl Borrow<$Target> for &$Source {
95 fn borrow(&self) -> &$Target {
96 &self.0
97 }
98 }
99 };
100}
101
102macro_rules! constant_time {
103 ($Value: ident, $Inner: ident) => {
104 impl ConstantTimeEq for $Value {
105 fn ct_eq(&self, other: &Self) -> Choice {
106 self.0.ct_eq(&other.0)
107 }
108 }
109
110 impl ConditionallySelectable for $Value {
111 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
112 $Value($Inner::conditional_select(&a.0, &b.0, choice))
113 }
114 }
115 };
116}
117pub(crate) use constant_time;
118
119macro_rules! math_op {
120 (
121 $Value: ident,
122 $Other: ident,
123 $Op: ident,
124 $op_fn: ident,
125 $Assign: ident,
126 $assign_fn: ident,
127 $function: expr
128 ) => {
129 impl $Op<$Other> for $Value {
130 type Output = $Value;
131 fn $op_fn(self, other: $Other) -> Self::Output {
132 Self($function(self.0, other.0))
133 }
134 }
135 impl $Assign<$Other> for $Value {
136 fn $assign_fn(&mut self, other: $Other) {
137 self.0 = $function(self.0, other.0);
138 }
139 }
140 impl<'a> $Op<&'a $Other> for $Value {
141 type Output = $Value;
142 fn $op_fn(self, other: &'a $Other) -> Self::Output {
143 Self($function(self.0, other.0))
144 }
145 }
146 impl<'a> $Assign<&'a $Other> for $Value {
147 fn $assign_fn(&mut self, other: &'a $Other) {
148 self.0 = $function(self.0, other.0);
149 }
150 }
151 };
152}
153pub(crate) use math_op;
154
155macro_rules! math {
156 ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
157 math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
158 math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
159 math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
160 };
161}
162pub(crate) use math;
163
164macro_rules! math_neg {
165 ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
166 math!($Value, $Factor, $add, $sub, $mul);
167
168 impl Neg for $Value {
169 type Output = Self;
170 fn neg(self) -> Self::Output {
171 Self(-self.0)
172 }
173 }
174 };
175}
176
177#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
179pub struct Scalar(pub DScalar);
180deref_borrow!(Scalar, DScalar);
181constant_time!(Scalar, DScalar);
182math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul);
183
184macro_rules! from_wrapper {
185 ($uint: ident) => {
186 impl From<$uint> for Scalar {
187 fn from(a: $uint) -> Scalar {
188 Scalar(DScalar::from(a))
189 }
190 }
191 };
192}
193
194from_wrapper!(u8);
195from_wrapper!(u16);
196from_wrapper!(u32);
197from_wrapper!(u64);
198from_wrapper!(u128);
199
200impl Scalar {
201 pub fn pow(&self, other: Scalar) -> Scalar {
202 let mut table = [Scalar::ONE; 16];
203 table[1] = *self;
204 for i in 2 .. 16 {
205 table[i] = table[i - 1] * self;
206 }
207
208 let mut res = Scalar::ONE;
209 let mut bits = 0;
210 for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
211 bits <<= 1;
212 let mut bit = u8_from_bool(&mut bit);
213 bits |= bit;
214 bit.zeroize();
215
216 if ((i + 1) % 4) == 0 {
217 if i != 3 {
218 for _ in 0 .. 4 {
219 res *= res;
220 }
221 }
222
223 let mut scale_by = Scalar::ONE;
224 #[allow(clippy::needless_range_loop)]
225 for i in 0 .. 16 {
226 #[allow(clippy::cast_possible_truncation)] {
228 scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8)));
229 }
230 }
231 res *= scale_by;
232 bits = 0;
233 }
234 }
235 res
236 }
237
238 pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
240 Self(DScalar::from_bytes_mod_order_wide(bytes))
241 }
242
243 pub fn from_hash<D: Digest<OutputSize = U64> + HashMarker>(hash: D) -> Scalar {
245 let mut output = [0u8; 64];
246 output.copy_from_slice(&hash.finalize());
247 let res = Scalar(DScalar::from_bytes_mod_order_wide(&output));
248 output.zeroize();
249 res
250 }
251}
252
253impl Field for Scalar {
254 const ZERO: Scalar = Scalar(DScalar::ZERO);
255 const ONE: Scalar = Scalar(DScalar::ONE);
256
257 fn random(rng: impl RngCore) -> Self {
258 Self(<DScalar as Field>::random(rng))
259 }
260
261 fn square(&self) -> Self {
262 Self(self.0.square())
263 }
264 fn double(&self) -> Self {
265 Self(self.0.double())
266 }
267 fn invert(&self) -> CtOption<Self> {
268 <DScalar as Field>::invert(&self.0).map(Self)
269 }
270
271 fn sqrt(&self) -> CtOption<Self> {
272 self.0.sqrt().map(Self)
273 }
274
275 fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
276 let (choice, res) = DScalar::sqrt_ratio(num, div);
277 (choice, Self(res))
278 }
279}
280
281impl PrimeField for Scalar {
282 type Repr = [u8; 32];
283
284 const MODULUS: &'static str = <DScalar as PrimeField>::MODULUS;
285
286 const NUM_BITS: u32 = <DScalar as PrimeField>::NUM_BITS;
287 const CAPACITY: u32 = <DScalar as PrimeField>::CAPACITY;
288
289 const TWO_INV: Scalar = Scalar(<DScalar as PrimeField>::TWO_INV);
290
291 const MULTIPLICATIVE_GENERATOR: Scalar =
292 Scalar(<DScalar as PrimeField>::MULTIPLICATIVE_GENERATOR);
293 const S: u32 = <DScalar as PrimeField>::S;
294
295 const ROOT_OF_UNITY: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY);
296 const ROOT_OF_UNITY_INV: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY_INV);
297
298 const DELTA: Scalar = Scalar(<DScalar as PrimeField>::DELTA);
299
300 fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
301 <DScalar as PrimeField>::from_repr(bytes).map(Scalar)
302 }
303 fn to_repr(&self) -> [u8; 32] {
304 self.0.to_repr()
305 }
306
307 fn is_odd(&self) -> Choice {
308 self.0.is_odd()
309 }
310
311 fn from_u128(num: u128) -> Self {
312 Scalar(DScalar::from_u128(num))
313 }
314}
315
316impl PrimeFieldBits for Scalar {
317 type ReprBits = [u8; 32];
318
319 fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
320 self.to_repr().into()
321 }
322
323 fn char_le_bits() -> FieldBits<Self::ReprBits> {
324 BASEPOINT_ORDER.to_bytes().into()
325 }
326}
327
328impl FromUniformBytes<64> for Scalar {
329 fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
330 Self::from_bytes_mod_order_wide(bytes)
331 }
332}
333
334impl Sum<Scalar> for Scalar {
335 fn sum<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
336 Self(DScalar::sum(iter))
337 }
338}
339
340impl<'a> Sum<&'a Scalar> for Scalar {
341 fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
342 Self(DScalar::sum(iter))
343 }
344}
345
346impl Product<Scalar> for Scalar {
347 fn product<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
348 Self(DScalar::product(iter))
349 }
350}
351
352impl<'a> Product<&'a Scalar> for Scalar {
353 fn product<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
354 Self(DScalar::product(iter))
355 }
356}
357
358macro_rules! dalek_group {
359 (
360 $Point: ident,
361 $DPoint: ident,
362 $torsion_free: expr,
363
364 $Table: ident,
365
366 $DCompressed: ident,
367
368 $BASEPOINT_POINT: ident,
369 $BASEPOINT_TABLE: ident
370 ) => {
371 #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
378 pub struct $Point(pub $DPoint);
379 deref_borrow!($Point, $DPoint);
380 constant_time!($Point, $DPoint);
381 math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul);
382
383 pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT);
385
386 impl Sum<$Point> for $Point {
387 fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point {
388 Self($DPoint::sum(iter))
389 }
390 }
391 impl<'a> Sum<&'a $Point> for $Point {
392 fn sum<I: Iterator<Item = &'a $Point>>(iter: I) -> $Point {
393 Self($DPoint::sum(iter))
394 }
395 }
396
397 impl Group for $Point {
398 type Scalar = Scalar;
399 fn random(mut rng: impl RngCore) -> Self {
400 loop {
401 let mut bytes = [0; 32];
402 rng.fill_bytes(&mut bytes);
403 let Some(point) = Option::<$Point>::from($Point::from_bytes(&bytes)) else {
404 continue;
405 };
406 if !bool::from(point.is_identity()) {
408 return point;
409 }
410 }
411 }
412 fn identity() -> Self {
413 Self($DPoint::identity())
414 }
415 fn generator() -> Self {
416 $BASEPOINT_POINT
417 }
418 fn is_identity(&self) -> Choice {
419 self.0.ct_eq(&$DPoint::identity())
420 }
421 fn double(&self) -> Self {
422 Self(self.0.double())
423 }
424 }
425
426 impl GroupEncoding for $Point {
427 type Repr = [u8; 32];
428
429 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
430 let decompressed = $DCompressed(*bytes).decompress();
431 let point = decompressed.unwrap_or($DPoint::identity());
433 CtOption::new(
434 $Point(point),
435 choice(black_box(decompressed).is_some()) & choice($torsion_free(point)),
436 )
437 }
438
439 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
440 $Point::from_bytes(bytes)
441 }
442
443 fn to_bytes(&self) -> Self::Repr {
444 self.0.to_bytes()
445 }
446 }
447
448 impl PrimeGroup for $Point {}
449
450 impl Mul<Scalar> for &$Table {
451 type Output = $Point;
452 fn mul(self, b: Scalar) -> $Point {
453 $Point(&b.0 * self)
454 }
455 }
456
457 #[allow(unknown_lints, renamed_and_removed_lints)]
461 #[allow(clippy::derived_hash_with_manual_eq, clippy::derive_hash_xor_eq)]
462 impl Hash for $Point {
463 fn hash<H: Hasher>(&self, state: &mut H) {
464 self.to_bytes().hash(state);
465 }
466 }
467 };
468}
469
470dalek_group!(
471 EdwardsPoint,
472 DEdwardsPoint,
473 |point: DEdwardsPoint| point.is_torsion_free(),
474 EdwardsBasepointTable,
475 CompressedEdwardsY,
476 ED25519_BASEPOINT_POINT,
477 ED25519_BASEPOINT_TABLE
478);
479
480impl EdwardsPoint {
481 pub fn mul_by_cofactor(&self) -> EdwardsPoint {
482 EdwardsPoint(self.0.mul_by_cofactor())
483 }
484}
485
486dalek_group!(
487 RistrettoPoint,
488 DRistrettoPoint,
489 |_| true,
490 RistrettoBasepointTable,
491 CompressedRistretto,
492 RISTRETTO_BASEPOINT_POINT,
493 RISTRETTO_BASEPOINT_TABLE
494);
495
496#[test]
497fn test_ed25519_group() {
498 ff_group_tests::group::test_prime_group_bits::<_, EdwardsPoint>(&mut rand_core::OsRng);
499}
500
501#[test]
502fn test_ristretto_group() {
503 ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng);
504}