Skip to main content

ic_ec/
ed25519.rs

1//! RFC 8032 Ed25519 signatures.
2//!
3//! Points use extended twisted Edwards coordinates `(X : Y : Z : T)` with
4//! `a = -1`. Because `d` is a non-square in GF(2^255-19), the
5//! `add-2008-hwcd-3` formula is *complete*: it is correct for every input pair,
6//! including doubling and the identity. That is what lets scalar multiplication
7//! be a single branch-free loop with no exceptional cases to special-case, and
8//! no timing signal from the shape of the scalar.
9
10use crate::field::Fe;
11use crate::field_ops::{dispatch, Field};
12use crate::scalar;
13use ic_core::ct::Choice;
14
15// The precomputed basepoint table. See the module for why it is `std` only.
16#[cfg(feature = "std")]
17mod basepoint_table;
18// The four-limb field's basepoint and double-scalar multiplications, one
19// assembly block per group operation; see the module.
20#[cfg(all(
21    feature = "std",
22    target_arch = "x86_64",
23    not(any(ic_limb32, ic_ec_portable))
24))]
25mod fast;
26// The same, four lanes at a time on AVX-512 IFMA; see the module.
27#[cfg(all(
28    feature = "std",
29    target_arch = "x86_64",
30    not(any(ic_limb32, ic_ec_portable))
31))]
32mod vector;
33use ic_core::traits::internal::Digest;
34use ic_core::traits::{Algorithm, SelfTest, SignatureScheme};
35use ic_core::{ensure, Result, Zeroize};
36use ic_hash::Sha512;
37
38#[cfg(not(feature = "std"))]
39pub(crate) use self::EdField as Curve;
40/// What a field must provide for the operations on whole scalars here:
41/// [`EdField`], and under `std` the precomputed basepoint tables too.
42#[cfg(feature = "std")]
43pub(crate) use basepoint_table::TableField as Curve;
44
45/// The compressed encoding of the Ed25519 base point.
46#[cfg(test)]
47const BASEPOINT_COMPRESSED: [u8; 32] = [
48    0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
49    0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
50];
51
52/// The curve constant `d = -121665/121666`, as 51-bit limbs.
53const D_LIMBS: [u64; 5] = [
54    929_955_233_495_203,
55    466_365_720_129_213,
56    1_662_059_464_998_953,
57    2_033_849_074_728_123,
58    1_442_794_654_840_575,
59];
60
61/// `2*d`, used directly by the addition formula.
62const D2_LIMBS: [u64; 5] = [
63    1_859_910_466_990_425,
64    932_731_440_258_426,
65    1_072_319_116_312_658,
66    1_815_898_335_770_999,
67    633_789_495_995_903,
68];
69
70/// A square root of -1 in GF(2^255-19), needed for point decompression.
71const SQRT_M1_LIMBS: [u64; 5] = [
72    1_718_705_420_411_056,
73    234_908_883_556_509,
74    2_233_514_472_574_048,
75    2_117_202_627_021_982,
76    765_476_049_583_133,
77];
78
79/// A field the curve arithmetic runs on: [`Field`] and the curve's constants
80/// in its representation.
81///
82/// Two implement it, the portable field and, on x86-64 with BMI2 and ADX, the
83/// four-limb one; every operation below is written once against this and
84/// instantiated for each, and [`dispatch!`] picks one per scalar
85/// multiplication.
86pub(crate) trait EdField: Field + core::fmt::Debug + 'static {
87    /// `d`.
88    const D: Self;
89    /// `2d`.
90    const D2: Self;
91    /// `sqrt(-1)`.
92    const SQRT_M1: Self;
93
94    /// The same element in the portable field.
95    #[cfg(all(feature = "std", any(test, feature = "bench-internals")))]
96    fn to_portable(self) -> Fe {
97        Fe::from_bytes(&self.to_bytes())
98    }
99
100    /// The same element in this field.
101    #[cfg(all(feature = "std", any(test, feature = "bench-internals")))]
102    fn from_portable(x: &Fe) -> Self {
103        Self::from_bytes(&x.to_bytes())
104    }
105}
106
107impl EdField for Fe {
108    const D: Fe = Fe::from_limbs51(D_LIMBS);
109    const D2: Fe = Fe::from_limbs51(D2_LIMBS);
110    const SQRT_M1: Fe = Fe::from_limbs51(SQRT_M1_LIMBS);
111
112    #[cfg(all(feature = "std", any(test, feature = "bench-internals")))]
113    fn to_portable(self) -> Fe {
114        self
115    }
116
117    #[cfg(all(feature = "std", any(test, feature = "bench-internals")))]
118    fn from_portable(x: &Fe) -> Self {
119        *x
120    }
121}
122
123#[cfg(all(target_arch = "x86_64", not(any(ic_limb32, ic_ec_portable))))]
124impl EdField for crate::fe64::Fe64 {
125    const D: Self = crate::fe64::Fe64::from_limbs51(D_LIMBS);
126    const D2: Self = crate::fe64::Fe64::from_limbs51(D2_LIMBS);
127    const SQRT_M1: Self = crate::fe64::Fe64::from_limbs51(SQRT_M1_LIMBS);
128}
129
130/// A point on the curve, in extended twisted Edwards coordinates.
131///
132/// The public face of `Ext` on the portable field. The signing and
133/// verification paths do not go through it: they run on whichever field the
134/// CPU selects, and this is for callers who want the group itself.
135#[derive(Clone, Copy, Debug)]
136pub struct Point(Ext<Fe>);
137
138impl Point {
139    /// The neutral element `(0, 1)`.
140    pub const IDENTITY: Point = Point(Ext::IDENTITY);
141
142    /// The complete `add-2008-hwcd-3` group law for `a = -1`.
143    pub fn add(&self, other: &Point) -> Point {
144        Point(self.0.add(&other.0))
145    }
146
147    /// Point doubling, `dbl-2008-hwcd` for `a = -1`.
148    pub fn double(&self) -> Point {
149        Point(self.0.double())
150    }
151
152    /// Scalar multiplication, constant-time in the scalar.
153    pub fn mul_scalar(&self, s: &[u8; 32]) -> Point {
154        Point(self.0.mul_scalar(s))
155    }
156
157    /// Compress to the 32-byte RFC 8032 encoding.
158    pub fn compress(&self) -> [u8; 32] {
159        self.0.compress()
160    }
161
162    /// Decompress a 32-byte encoding, rejecting non-curve points and
163    /// non-canonical encodings.
164    pub fn decompress(bytes: &[u8; 32]) -> Option<Point> {
165        Ext::decompress(bytes).map(Point)
166    }
167}
168
169/// A point in extended twisted Edwards coordinates, on the field `F`.
170#[derive(Clone, Copy, Debug)]
171pub(crate) struct Ext<F> {
172    x: F,
173    y: F,
174    z: F,
175    t: F,
176}
177
178/// A point part-way through a group operation: `(X : Y : Z : T)` standing for
179/// the affine point `(X/Z, Y/T)`.
180///
181/// Both the doubling and the addition formulas naturally produce this form --
182/// it is what they compute before the four multiplications that put the result
183/// back into extended coordinates. Keeping it is what makes a chain of
184/// doublings cheaper: a doubling reads only `X`, `Y` and `Z`, so on the way to
185/// another doubling the `T` those four multiplications would produce is never
186/// read, and three of them suffice instead of four.
187#[derive(Clone, Copy)]
188pub(crate) struct Completed<F> {
189    x: F,
190    y: F,
191    z: F,
192    t: F,
193}
194
195/// `(X : Y : Z)`, standing for `(X/Z, Y/Z)`. No `T`.
196///
197/// What a doubling needs and all it needs.
198#[derive(Clone, Copy)]
199pub(crate) struct Projective<F> {
200    x: F,
201    y: F,
202    z: F,
203}
204
205/// A point rearranged for addition: `(Y+X, Y-X, Z, 2d·T)`.
206///
207/// The addition formula wants those four quantities and nothing else, so a
208/// point that will be added many times -- every entry of every table here --
209/// stores them instead of `(X, Y, Z, T)`. That turns an addition from nine
210/// multiplications into four: the two sums and differences are already formed,
211/// and `2d·T` has already been scaled.
212#[derive(Clone, Copy)]
213pub(crate) struct Niels<F> {
214    ypx: F,
215    ymx: F,
216    z: F,
217    t2d: F,
218}
219
220/// A point with `Z = 1`, rearranged for addition: `(y+x, y-x, 2d·x·y)`.
221///
222/// [`Niels`] without the `Z`, which removes the one multiplication that used
223/// it. Worth the field inversion it costs to build, for a table entry that
224/// will be added sixty-four times per signature and never changes.
225#[cfg(feature = "std")]
226#[derive(Clone, Copy)]
227pub(crate) struct AffineNiels<F> {
228    ypx: F,
229    ymx: F,
230    t2d: F,
231}
232
233/// The constant-time table operations the windowed multiplication needs. They
234/// mirror [`AffineNiels`]'s, for a table built per call and so never worth the
235/// inversions that would make it affine.
236#[cfg(any(not(feature = "std"), test))]
237impl<F: EdField> Niels<F> {
238    /// The neutral element: `Y + X = Y - X = Z = 1`, `2d·T = 0`.
239    const IDENTITY: Self = Niels {
240        ypx: F::ONE,
241        ymx: F::ONE,
242        z: F::ONE,
243        t2d: F::ZERO,
244    };
245
246    /// Negation swaps the sums and differences and negates `2d·T`; `Z` is
247    /// unchanged.
248    fn conditional_negate(&mut self, choice: Choice) {
249        let swapped_p = self.ymx;
250        let swapped_m = self.ypx;
251        let nt = self.t2d.neg();
252        F::cmov(&mut self.ypx, &swapped_p, choice);
253        F::cmov(&mut self.ymx, &swapped_m, choice);
254        F::cmov(&mut self.t2d, &nt, choice);
255    }
256
257    fn cmov(&mut self, other: &Niels<F>, choice: Choice) {
258        F::cmov(&mut self.ypx, &other.ypx, choice);
259        F::cmov(&mut self.ymx, &other.ymx, choice);
260        F::cmov(&mut self.z, &other.z, choice);
261        F::cmov(&mut self.t2d, &other.t2d, choice);
262    }
263}
264
265#[cfg(feature = "std")]
266impl<F: EdField> AffineNiels<F> {
267    /// The neutral element: `x = 0`, `y = 1`.
268    pub(crate) const IDENTITY: Self = AffineNiels {
269        ypx: F::ONE,
270        ymx: F::ONE,
271        t2d: F::ZERO,
272    };
273
274    /// Negation swaps the sums and differences and negates `2d·x·y`, which is
275    /// what `-(x, y) = (-x, y)` comes to in this form.
276    ///
277    /// `bit` is secret, 0 or 1; it becomes a mask only inside the field's
278    /// `cmov_bit`, behind that field's barrier.
279    pub(crate) fn conditional_negate(&mut self, bit: u64) {
280        let swapped_p = self.ymx;
281        let swapped_m = self.ypx;
282        let nt = self.t2d.neg();
283        F::cmov_bit(&mut self.ypx, &swapped_p, bit);
284        F::cmov_bit(&mut self.ymx, &swapped_m, bit);
285        F::cmov_bit(&mut self.t2d, &nt, bit);
286    }
287
288    /// `self = other` when the secret `bit`, 0 or 1, is 1.
289    pub(crate) fn cmov(&mut self, other: &AffineNiels<F>, bit: u64) {
290        F::cmov_bit(&mut self.ypx, &other.ypx, bit);
291        F::cmov_bit(&mut self.ymx, &other.ymx, bit);
292        F::cmov_bit(&mut self.t2d, &other.t2d, bit);
293    }
294}
295
296impl<F: EdField> Completed<F> {
297    /// Drop to `(X : Y : Z)`, which is three multiplications.
298    fn to_projective(self) -> Projective<F> {
299        Projective {
300            x: self.x.mul(&self.t),
301            y: self.y.mul(&self.z),
302            z: self.z.mul(&self.t),
303        }
304    }
305
306    /// Back to extended coordinates, which is four.
307    ///
308    /// Only needed before an addition, since that is the only operation that
309    /// reads `T`.
310    fn to_extended(self) -> Ext<F> {
311        Ext {
312            x: self.x.mul(&self.t),
313            y: self.y.mul(&self.z),
314            z: self.z.mul(&self.t),
315            t: self.x.mul(&self.y),
316        }
317    }
318}
319
320impl<F: EdField> Projective<F> {
321    /// Recover extended coordinates from projective ones.
322    ///
323    /// `(X : Y : Z)` stands for `(X/Z, Y/Z)`, and extended coordinates want
324    /// `T` with `X*Y = T*Z`, so `T = X*Y/Z`. Only needed once, at the end.
325    fn to_extended_from_projective(self) -> Ext<F> {
326        // Four multiplications, not an inversion. `(X : Y : Z)` stands for
327        // `(X/Z, Y/Z)`, and extended coordinates want `T` with `X*Y = T*Z`;
328        // scaling every coordinate by `Z` gives `(XZ : YZ : Z^2 : XY)`, which
329        // satisfies that directly. Dividing through by `Z` instead would be an
330        // exponentiation -- about two hundred and fifty squarings -- to reach
331        // the same point in a representation nothing here needs.
332        Ext {
333            x: self.x.mul(&self.z),
334            y: self.y.mul(&self.z),
335            z: self.z.square(),
336            t: self.x.mul(&self.y),
337        }
338    }
339
340    /// Double and stay projective, without writing the completed form out.
341    ///
342    /// The same arithmetic as `double()` followed by
343    /// [`Completed::to_projective`], with the intermediate kept in locals. A
344    /// `Completed` is four field elements, 160 bytes, and in a chain of
345    /// doublings it exists only to be consumed by the very next statement;
346    /// spilling and reloading it is pure traffic. This is the path taken at
347    /// every position where the recoding has nothing to add, which is most of
348    /// them.
349    fn double_projective(self) -> Projective<F> {
350        let xx = self.x.square();
351        let yy = self.y.square();
352        let zz2 = {
353            let t = self.z.square();
354            t.add(&t)
355        };
356        let xy_sq = self.x.add(&self.y).square();
357        let yy_plus_xx = yy.add(&xx);
358        let yy_minus_xx = yy.sub(&xx);
359
360        let cx = xy_sq.sub(&yy_plus_xx);
361        let cy = yy_plus_xx;
362        let cz = yy_minus_xx;
363        let ct = zz2.sub(&yy_minus_xx);
364
365        Projective {
366            x: cx.mul(&ct),
367            y: cy.mul(&cz),
368            z: cz.mul(&ct),
369        }
370    }
371
372    /// `dbl-2008-hwcd` for `a = -1`, stopping at the completed form.
373    ///
374    /// Four squarings and no multiplications at all: every multiplication in a
375    /// doubling belongs to the conversion out of the completed form, which is
376    /// why it is worth not doing that conversion in full.
377    fn double(&self) -> Completed<F> {
378        let xx = self.x.square();
379        let yy = self.y.square();
380        let zz2 = {
381            let t = self.z.square();
382            t.add(&t)
383        };
384        let xy_sq = self.x.add(&self.y).square();
385        let yy_plus_xx = yy.add(&xx);
386        let yy_minus_xx = yy.sub(&xx);
387        Completed {
388            x: xy_sq.sub(&yy_plus_xx),
389            y: yy_plus_xx,
390            z: yy_minus_xx,
391            t: zz2.sub(&yy_minus_xx),
392        }
393    }
394}
395
396impl<F: EdField> Ext<F> {
397    /// The neutral element `(0, 1)`.
398    /// `(x, y)` with `Z = 1`.
399    pub(crate) fn from_affine(x: F, y: F) -> Ext<F> {
400        Ext {
401            x,
402            y,
403            z: F::ONE,
404            t: x.mul(&y),
405        }
406    }
407
408    /// The same point on another field.
409    #[cfg(all(feature = "std", any(test, feature = "bench-internals")))]
410    pub(crate) fn convert<G: EdField>(&self) -> Ext<G> {
411        let c = |v: &F| G::from_portable(&v.to_portable());
412        Ext {
413            x: c(&self.x),
414            y: c(&self.y),
415            z: c(&self.z),
416            t: c(&self.t),
417        }
418    }
419
420    pub const IDENTITY: Self = Ext {
421        x: F::ZERO,
422        y: F::ONE,
423        z: F::ONE,
424        t: F::ZERO,
425    };
426
427    /// The complete `add-2008-hwcd-3` group law for `a = -1`.
428    pub fn add(&self, other: &Ext<F>) -> Ext<F> {
429        let a = self.y.sub(&self.x).mul(&other.y.sub(&other.x));
430        let b = self.y.add(&self.x).mul(&other.y.add(&other.x));
431        let c = self.t.mul(&F::D2).mul(&other.t);
432        let d = self.z.mul(&other.z);
433        let d = d.add(&d);
434
435        let e = b.sub(&a);
436        let f = d.sub(&c);
437        let g = d.add(&c);
438        let h = b.add(&a);
439
440        Ext {
441            x: e.mul(&f),
442            y: g.mul(&h),
443            t: e.mul(&h),
444            z: f.mul(&g),
445        }
446    }
447
448    /// Point doubling, `dbl-2008-hwcd` for `a = -1`.
449    ///
450    /// Adding a point to itself works and was what this did, but the general
451    /// addition costs nine multiplications and needs both operands' `T`. The
452    /// dedicated formula is four multiplications and four squarings, and does
453    /// not read `T` at all -- doubling is a function of `X`, `Y` and `Z` alone.
454    ///
455    /// Worth the separate formula because scalar multiplication is doublings
456    /// almost entirely: the non-adjacent form leaves about forty additions
457    /// against two hundred and fifty-six doublings.
458    pub fn double(&self) -> Ext<F> {
459        let aa = self.x.square();
460        let bb = self.y.square();
461        let c = self.z.square();
462        let c = c.add(&c);
463        // a = -1, so D = a*A = -A.
464        let d = aa.neg();
465        // E = (X+Y)^2 - A - B, which is 2*X*Y without a multiplication.
466        let xy = self.x.add(&self.y);
467        let e = xy.square().sub(&aa).sub(&bb);
468        let g = d.add(&bb);
469        let f = g.sub(&c);
470        let h = d.sub(&bb);
471
472        Ext {
473            x: e.mul(&f),
474            y: g.mul(&h),
475            t: e.mul(&h),
476            z: f.mul(&g),
477        }
478    }
479
480    /// Drop `T`, which a doubling does not read.
481    fn to_projective(self) -> Projective<F> {
482        Projective {
483            x: self.x,
484            y: self.y,
485            z: self.z,
486        }
487    }
488
489    /// Rearrange for repeated addition. See [`Niels`].
490    fn to_niels(self) -> Niels<F> {
491        Niels {
492            ypx: self.y.add(&self.x),
493            ymx: self.y.sub(&self.x),
494            z: self.z,
495            t2d: self.t.mul(&F::D2),
496        }
497    }
498
499    /// `self + other`, in four multiplications, stopping at the completed form.
500    ///
501    /// The same `add-2008-hwcd-3` group law [`Point::add`] uses. It costs four
502    /// rather than nine because `other` arrives with its sums, differences and
503    /// `2d·T` already formed, and because the result is left completed rather
504    /// than converted back.
505    fn add_niels(&self, other: &Niels<F>) -> Completed<F> {
506        let pp = self.y.add(&self.x).mul(&other.ypx);
507        let mm = self.y.sub(&self.x).mul(&other.ymx);
508        let tt2d = self.t.mul(&other.t2d);
509        let zz = self.z.mul(&other.z);
510        let zz2 = zz.add(&zz);
511        Completed {
512            x: pp.sub(&mm),
513            y: pp.add(&mm),
514            z: zz2.add(&tt2d),
515            t: zz2.sub(&tt2d),
516        }
517    }
518
519    /// `self - other`.
520    ///
521    /// Negating a Niels point swaps its sums and differences and negates
522    /// `2d·T`, which is cheaper than negating the point it came from and
523    /// rebuilding it.
524    fn sub_niels(&self, other: &Niels<F>) -> Completed<F> {
525        let pp = self.y.add(&self.x).mul(&other.ymx);
526        let mm = self.y.sub(&self.x).mul(&other.ypx);
527        let tt2d = self.t.mul(&other.t2d);
528        let zz = self.z.mul(&other.z);
529        let zz2 = zz.add(&zz);
530        Completed {
531            x: pp.sub(&mm),
532            y: pp.add(&mm),
533            z: zz2.sub(&tt2d),
534            t: zz2.add(&tt2d),
535        }
536    }
537
538    /// Rearrange for repeated addition, with `Z` divided out. See
539    /// [`AffineNiels`].
540    ///
541    /// Costs a field inversion, which is why it is done when a table is built
542    /// and never on a hot path.
543    #[cfg(feature = "std")]
544    pub(crate) fn to_affine_niels(self) -> AffineNiels<F> {
545        let z_inv = self.z.invert();
546        let x = self.x.mul(&z_inv);
547        let y = self.y.mul(&z_inv);
548        AffineNiels {
549            ypx: y.add(&x),
550            ymx: y.sub(&x),
551            t2d: x.mul(&y).mul(&F::D2),
552        }
553    }
554
555    /// `self + other`, in three multiplications.
556    ///
557    /// One fewer than [`Point::add_niels`]: `other` has `Z = 1`, so the
558    /// product of the two `Z`s is just this one's, doubled.
559    #[cfg(feature = "std")]
560    pub(crate) fn add_affine_niels(&self, other: &AffineNiels<F>) -> Completed<F> {
561        let pp = self.y.add(&self.x).mul(&other.ypx);
562        let mm = self.y.sub(&self.x).mul(&other.ymx);
563        let tt2d = self.t.mul(&other.t2d);
564        let zz2 = self.z.add(&self.z);
565        Completed {
566            x: pp.sub(&mm),
567            y: pp.add(&mm),
568            z: zz2.add(&tt2d),
569            t: zz2.sub(&tt2d),
570        }
571    }
572
573    /// `self - other`, for an affine Niels point.
574    ///
575    /// Negating one of these swaps its sums and differences and negates
576    /// `2d·x·y`, so the subtraction is the addition with two operands
577    /// exchanged and one sign flipped. Doing it here rather than by negating a
578    /// copy of the table entry avoids copying it at all -- an entry is three
579    /// field elements, and the variable-time path has no reason to touch it
580    /// with conditional moves.
581    #[cfg(feature = "std")]
582    pub(crate) fn sub_affine_niels(&self, other: &AffineNiels<F>) -> Completed<F> {
583        let pp = self.y.add(&self.x).mul(&other.ymx);
584        let mm = self.y.sub(&self.x).mul(&other.ypx);
585        let tt2d = self.t.mul(&other.t2d);
586        let zz2 = self.z.add(&self.z);
587        Completed {
588            x: pp.sub(&mm),
589            y: pp.add(&mm),
590            z: zz2.sub(&tt2d),
591            t: zz2.add(&tt2d),
592        }
593    }
594
595    /// Constant-time conditional move.
596    fn cmov(&mut self, other: &Ext<F>, choice: Choice) {
597        F::cmov(&mut self.x, &other.x, choice);
598        F::cmov(&mut self.y, &other.y, choice);
599        F::cmov(&mut self.z, &other.z, choice);
600        F::cmov(&mut self.t, &other.t, choice);
601    }
602
603    /// Scalar multiplication, constant-time in the scalar.
604    ///
605    /// Every iteration performs a doubling *and* an addition, selecting between
606    /// the two results with a conditional move, so the instruction trace is
607    /// identical for every scalar.
608    pub fn mul_scalar(&self, s: &[u8; 32]) -> Ext<F> {
609        let mut acc = Ext::IDENTITY;
610        for i in (0..256).rev() {
611            acc = acc.double();
612            let sum = acc.add(self);
613            let bit = Choice::from_u8((s[i / 8] >> (i % 8)) & 1);
614            acc.cmov(&sum, bit);
615        }
616        acc
617    }
618
619    /// Negate: `-(x, y, z, t)` is `(-x, y, z, -t)`.
620    fn negate(&self) -> Ext<F> {
621        Ext {
622            x: self.x.neg(),
623            y: self.y,
624            z: self.z,
625            t: self.t.neg(),
626        }
627    }
628
629    /// Whether two points are the same, without leaving projective space.
630    ///
631    /// `(X : Y : Z)` stands for the affine point `(X/Z, Y/Z)`, so two are equal
632    /// exactly when `X1*Z2 == X2*Z1` and `Y1*Z2 == Y2*Z1`. That is four
633    /// multiplications.
634    ///
635    /// The obvious alternative is to compress both and compare the bytes, and
636    /// that is what verification used to do -- but compression divides by `Z`,
637    /// and a division here is an exponentiation: roughly two hundred and fifty
638    /// squarings each, five hundred to answer a question four multiplications
639    /// settle.
640    ///
641    /// `to_bytes` is used only to canonicalise the two sides before comparing,
642    /// which costs a carry chain and no inversion.
643    /// Whether eight times this point is the identity: whether it lies in the
644    /// small subgroup the cofactor admits. Variable time; for public points.
645    fn has_small_order(&self) -> bool {
646        self.double()
647            .double()
648            .double()
649            .eq_projective(&Ext::<F>::IDENTITY)
650    }
651
652    fn eq_projective(&self, other: &Ext<F>) -> bool {
653        self.x.mul(&other.z).to_bytes() == other.x.mul(&self.z).to_bytes()
654            && self.y.mul(&other.z).to_bytes() == other.y.mul(&self.z).to_bytes()
655    }
656
657    /// Compress to the 32-byte RFC 8032 encoding.
658    pub fn compress(&self) -> [u8; 32] {
659        let z_inv = self.z.invert();
660        let x = self.x.mul(&z_inv);
661        let y = self.y.mul(&z_inv);
662        let mut out = y.to_bytes();
663        // The sign of x rides in the top bit.
664        out[31] |= x.is_negative().unwrap_u8() << 7;
665        out
666    }
667
668    /// Decompress a 32-byte encoding, rejecting non-curve points and
669    /// non-canonical encodings.
670    ///
671    /// RFC 8032 section 5.1.3: a y field of p or more fails to decode. Read
672    /// modulo p it would name a curve point, under an encoding no correct
673    /// encoder produces; nineteen such aliases exist, and accepting them makes
674    /// one point answer to two encodings.
675    pub fn decompress(bytes: &[u8; 32]) -> Option<Ext<F>> {
676        let start = Decompression::<F>::start(bytes)?;
677        let pow = start.uv7.pow22523();
678        start.finish(&pow)
679    }
680
681    /// Two decompressions, their exponentiations run side by side.
682    ///
683    /// The exponentiation is 252 squarings in a single dependency chain, and
684    /// two of them interleaved cost little more than one: each squaring has
685    /// the other chain's to overlap with. One-shot verification decompresses
686    /// both the public key and R, and this is how.
687    fn decompress_pair(a: &[u8; 32], b: &[u8; 32]) -> (Option<Ext<F>>, Option<Ext<F>>) {
688        match (Decompression::<F>::start(a), Decompression::<F>::start(b)) {
689            (Some(sa), Some(sb)) => {
690                let (pa, pb) = F::pow22523_pair(&sa.uv7, &sb.uv7);
691                (sa.finish(&pa), sb.finish(&pb))
692            }
693            (sa, sb) => (
694                sa.and_then(|s| s.finish(&s.uv7.pow22523())),
695                sb.and_then(|s| s.finish(&s.uv7.pow22523())),
696            ),
697        }
698    }
699}
700
701/// A decompression up to its exponentiation: what comes before and after
702/// `(u v^7)^((p-5)/8)`, separated so two can share it.
703struct Decompression<F> {
704    y: F,
705    u: F,
706    v: F,
707    uv3: F,
708    uv7: F,
709    sign: Choice,
710}
711
712impl<F: EdField> Decompression<F> {
713    fn start(bytes: &[u8; 32]) -> Option<Self> {
714        let sign = Choice::from_u8(bytes[31] >> 7);
715        let mut y_bytes = *bytes;
716        y_bytes[31] &= 0x7f;
717        let y = F::from_bytes(&y_bytes);
718        // The encoding is public, so comparing it in variable time is fine.
719        if y.to_bytes() != y_bytes {
720            return None;
721        }
722
723        // Solve x^2 = (y^2 - 1) / (d*y^2 + 1).
724        let y2 = y.square();
725        let u = y2.sub(&F::ONE);
726        let v = y2.mul(&F::D).add(&F::ONE);
727
728        // x = u*v^3 * (u*v^7)^((p-5)/8)
729        let v3 = v.square().mul(&v);
730        let v7 = v3.square().mul(&v);
731        Some(Self {
732            y,
733            u,
734            v,
735            uv3: u.mul(&v3),
736            uv7: u.mul(&v7),
737            sign,
738        })
739    }
740
741    /// The rest, given `pow = (u v^7)^((p-5)/8)`.
742    fn finish(&self, pow: &F) -> Option<Ext<F>> {
743        let (y, u, v, sign) = (self.y, self.u, self.v, self.sign);
744        let mut x = self.uv3.mul(pow);
745
746        let check = v.mul(&x.square());
747        let correct = check.ct_eq(&u);
748        let flipped = check.ct_eq(&u.neg());
749        if !bool::from(correct.or(flipped)) {
750            // No square root exists: the encoding is not a curve point.
751            return None;
752        }
753        // When only the flipped case matched, multiply by sqrt(-1).
754        let alt = x.mul(&F::SQRT_M1);
755        F::cmov(&mut x, &alt, flipped.and(correct.not()));
756
757        // x = 0 with a set sign bit is the one non-canonical encoding.
758        if bool::from(x.is_zero()) && bool::from(sign) {
759            return None;
760        }
761        // Match the requested sign.
762        let neg = x.neg();
763        let wrong_sign = Choice::from_u8(x.is_negative().unwrap_u8() ^ sign.unwrap_u8());
764        F::cmov(&mut x, &neg, wrong_sign);
765
766        Some(Ext {
767            x,
768            y,
769            z: F::ONE,
770            t: x.mul(&y),
771        })
772    }
773}
774
775/// Fill the basepoint tables now, for the field this CPU selects; see
776/// [`crate::prepare`].
777#[cfg(feature = "std")]
778pub(crate) fn prepare_tables() {
779    fn prepare<F: Curve>() {
780        basepoint_table::prepare::<F>();
781    }
782    dispatch!(prepare())
783}
784
785/// `scalar * B`, through the precomputed table where there is one.
786///
787/// Every basepoint multiplication in this module goes through here rather than
788/// calling `mul_scalar` on the basepoint directly, so the two paths cannot
789/// drift apart and a caller cannot accidentally take the slow one.
790fn mul_basepoint<F: Curve>(scalar: &[u8; 32]) -> Ext<F> {
791    #[cfg(feature = "std")]
792    {
793        basepoint_table::mul::<F>(scalar)
794    }
795    #[cfg(not(feature = "std"))]
796    {
797        mul_scalar_windowed(&basepoint::<F>(), scalar)
798    }
799}
800
801/// `scalar * B`, compressed, on the field this CPU selects: what signing and
802/// key derivation want, and the one place they choose the field.
803fn mul_basepoint_compressed(scalar: &[u8; 32]) -> [u8; 32] {
804    fn on<F: Curve>(scalar: &[u8; 32]) -> [u8; 32] {
805        mul_basepoint::<F>(scalar).compress()
806    }
807    dispatch!(on(scalar))
808}
809
810/// `[k]A + [s]B`, in one pass, variable time in both scalars.
811///
812/// Verification needs two scalar multiplications and then compares the
813/// results. Done separately that is two independent runs of doublings -- and
814/// the doublings are the whole cost, some two hundred and fifty-five of them
815/// against forty-odd additions. Run together they are shared: one chain of
816/// doublings, with each scalar contributing an addition at the positions where
817/// its own recoding is non-zero.
818///
819/// The basepoint half also stops paying for constant time here. `mul_basepoint`
820/// selects a table entry by reading all eight and moving conditionally, because
821/// a signing scalar is secret. Nothing in a verification is: the signature, the
822/// public key and the message are all in the clear, so the table is indexed
823/// directly and the window widened to eight, which is a table built once and
824/// about a third as many additions.
825///
826/// Both properties are why this is not the function signing calls.
827/// [`double_scalar_mul_vartime`], reachable from the benchmark.
828///
829/// The benchmark lives outside this workspace and cannot see private items,
830/// and comparing whole signatures cannot separate "our field arithmetic is
831/// slower" from "our scalar multiplication does more work". This is how that
832/// question gets answered rather than guessed at.
833#[cfg(feature = "bench-internals")]
834#[doc(hidden)]
835pub fn double_scalar_mul_vartime_for_bench(a: &Point, k: &[u8; 32], s: &[u8; 32]) -> Point {
836    fn on<F: Curve>(a: &Ext<Fe>, k: &[u8; 32], s: &[u8; 32]) -> Point {
837        Point(double_scalar_mul_vartime(&a.convert::<F>(), k, s).convert::<Fe>())
838    }
839    dispatch!(on(&a.0, k, s))
840}
841
842/// `scalar * p` in constant time, four bits at a time, for scalars below
843/// `2^255`.
844///
845/// What `no_std` signing uses in place of the precomputed table, and the same
846/// algorithm with the table built per call: the scalar becomes 64 signed
847/// radix-16 digits, `1..=8` times `p` are computed on the stack, and each digit
848/// selects one of them by reading all eight with conditional moves. That is
849/// 252 doublings and 64 additions, against 256 of each for
850/// [`Point::mul_scalar`]'s bit-at-a-time ladder, and eight entries of 160 bytes
851/// that are gone when it returns -- which is the constraint `no_std` exists
852/// for.
853///
854/// Every scalar reaching it is a clamped secret or a value reduced modulo the
855/// group order, which is what [`signed_digits`] needs. [`Point::mul_scalar`]
856/// remains the public operation and takes any 32 bytes.
857#[cfg(any(not(feature = "std"), test))]
858fn mul_scalar_windowed<F: EdField>(p: &Ext<F>, scalar: &[u8; 32]) -> Ext<F> {
859    let mut multiples = [*p; 8];
860    for i in 1..8 {
861        multiples[i] = multiples[i - 1].add(p);
862    }
863    let table: [Niels<F>; 8] = core::array::from_fn(|i| multiples[i].to_niels());
864
865    // `digit * p` for a digit in `[-8, 8]`, without indexing by the digit.
866    let select = |digit: i8| -> Niels<F> {
867        let negative = Choice::from_u8((digit as u8) >> 7);
868        let magnitude = ((digit as i16 ^ (digit as i16 >> 7)) - (digit as i16 >> 7)) as u8;
869        let mut out = Niels::IDENTITY;
870        for (i, entry) in table.iter().enumerate() {
871            out.cmov(entry, Choice::from_u8(u8::from(magnitude == (i as u8 + 1))));
872        }
873        out.conditional_negate(negative);
874        out
875    };
876
877    let digits = signed_digits(scalar);
878    let mut acc = Ext::IDENTITY.add_niels(&select(digits[63])).to_extended();
879    for i in (0..63).rev() {
880        // Times sixteen: three doublings that never need `T`, then one that
881        // does, since the addition after it reads `T`.
882        let mut q = acc.to_projective();
883        for _ in 0..3 {
884            q = q.double_projective();
885        }
886        acc = q.double().to_extended();
887        acc = acc.add_niels(&select(digits[i])).to_extended();
888    }
889    acc
890}
891
892/// `1, 3, 5 .. 15` times `p`, in Niels form.
893fn odd_multiples<F: EdField>(p: &Ext<F>) -> [Niels<F>; 8] {
894    let twice = p.double();
895    let mut odd = [*p; 8];
896    for i in 1..8 {
897        odd[i] = odd[i - 1].add(&twice);
898    }
899    core::array::from_fn(|i| odd[i].to_niels())
900}
901
902/// The scalar as 64 signed radix-16 digits, each in `[-8, 8]`.
903///
904/// A nibble above 8 becomes `nibble - 16` with a carry into the next digit,
905/// which is what keeps a table to the positive multiples.
906///
907/// Only the first 63 digits are recoded. The last one is left to absorb the
908/// final carry, because a carry *out* of the top would be a factor of `16^64`
909/// with nowhere to go -- silently dropping it would give the wrong point. That
910/// works because every scalar reaching here has its top byte at most 127: the
911/// clamped secret has bit 255 cleared by construction, and `r` and `s` are
912/// reduced modulo the group order and so are far smaller. The top nibble is
913/// then at most 7, one carry takes it to 8, and 8 is in range.
914///
915/// The first version of this recoded all 64 and dropped that carry. The
916/// agreement test below caught it.
917fn signed_digits(scalar: &[u8; 32]) -> [i8; 64] {
918    debug_assert!(
919        scalar[31] <= 127,
920        "the top digit can only absorb the final carry for scalars below 2^255"
921    );
922
923    let mut nibbles = [0i8; 64];
924    for (i, byte) in scalar.iter().enumerate() {
925        nibbles[i * 2] = (byte & 0x0f) as i8;
926        nibbles[i * 2 + 1] = (byte >> 4) as i8;
927    }
928
929    for i in 0..63 {
930        let carry = (nibbles[i] + 8) >> 4;
931        nibbles[i] -= carry << 4;
932        nibbles[i + 1] += carry;
933    }
934    nibbles
935}
936
937#[cfg(feature = "std")]
938fn double_scalar_mul_vartime<F: Curve>(a: &Ext<F>, k: &[u8; 32], s: &[u8; 32]) -> Ext<F> {
939    F::double_mul(a, k, s)
940}
941
942/// [`double_scalar_mul_vartime`] in the field's own arithmetic, one method
943/// call per operation.
944#[cfg(feature = "std")]
945fn double_scalar_mul_vartime_generic<F: Curve>(a: &Ext<F>, k: &[u8; 32], s: &[u8; 32]) -> Ext<F> {
946    basepoint_table::prepare::<F>();
947    shared_doublings(a, k, &wnaf(s, 8), |e, digit| {
948        let n = basepoint_table::odd_multiple::<F>((digit.unsigned_abs() as usize) / 2);
949        if digit > 0 {
950            e.add_affine_niels(n)
951        } else {
952            e.sub_affine_niels(n)
953        }
954    })
955}
956
957/// [`double_scalar_mul_vartime`] with no stored table: the basepoint's odd
958/// multiples are built per call, the same way `A`'s are, and read at width 5.
959///
960/// What `no_std` verification uses. It keeps the part that matters -- one chain
961/// of doublings shared by both scalars, where computing `[S]B` and `[k]A`
962/// separately would run two -- and gives up only the wider window, which is
963/// the part that needs storage.
964#[cfg(any(not(feature = "std"), test))]
965fn double_scalar_mul_vartime_no_table<F: EdField>(
966    a: &Ext<F>,
967    k: &[u8; 32],
968    s: &[u8; 32],
969) -> Ext<F> {
970    let odd_b = odd_multiples(&basepoint::<F>());
971    shared_doublings(a, k, &wnaf(s, 5), |e, digit| {
972        let n = &odd_b[(digit.unsigned_abs() as usize) / 2];
973        if digit > 0 {
974            e.add_niels(n)
975        } else {
976            e.sub_niels(n)
977        }
978    })
979}
980
981/// The loop both of those share: `[k]A` at width 5, plus whatever `add_b` adds
982/// at each non-zero digit of `naf_b`, over a single chain of doublings.
983#[inline(always)]
984fn shared_doublings<F: EdField>(
985    a: &Ext<F>,
986    k: &[u8; 32],
987    naf_b: &[i8; 258],
988    add_b: impl Fn(&Ext<F>, i8) -> Completed<F>,
989) -> Ext<F> {
990    // 1A, 3A, 5A .. 15A, in Niels form, built for this call.
991    let odd_a = odd_multiples(a);
992    let naf_a = wnaf(k, 5);
993
994    // Start at the highest position either recoding reaches, so the leading
995    // doublings of the identity are skipped.
996    let mut i = 257;
997    while i > 0 && naf_a[i] == 0 && naf_b[i] == 0 {
998        i -= 1;
999    }
1000
1001    // The accumulator is carried in whichever form the next step wants: a
1002    // doubling reads only X, Y and Z, and an addition is the only thing that
1003    // reads T. So a position with no addition pays three multiplications to
1004    // come out of the completed form instead of four.
1005    let mut acc = Ext::IDENTITY.to_projective();
1006    loop {
1007        // Nothing to add here, which is the common case: double straight back
1008        // to projective without materialising the completed form.
1009        if naf_a[i] == 0 && naf_b[i] == 0 {
1010            acc = acc.double_projective();
1011            if i == 0 {
1012                return acc.to_extended_from_projective();
1013            }
1014            i -= 1;
1015            continue;
1016        }
1017        let mut t = acc.double();
1018        if naf_a[i] != 0 {
1019            let e = t.to_extended();
1020            let n = &odd_a[(naf_a[i].unsigned_abs() as usize) / 2];
1021            t = if naf_a[i] > 0 {
1022                e.add_niels(n)
1023            } else {
1024                e.sub_niels(n)
1025            };
1026        }
1027        if naf_b[i] != 0 {
1028            t = add_b(&t.to_extended(), naf_b[i]);
1029        }
1030        if i == 0 {
1031            return t.to_extended();
1032        }
1033        acc = t.to_projective();
1034        i -= 1;
1035    }
1036}
1037
1038/// [`mul_basepoint`], reachable from the benchmark. See
1039/// [`double_scalar_mul_vartime_for_bench`].
1040#[cfg(feature = "bench-internals")]
1041#[doc(hidden)]
1042pub fn mul_basepoint_for_bench(scalar: &[u8; 32]) -> Point {
1043    fn on<F: Curve>(scalar: &[u8; 32]) -> Point {
1044        Point(mul_basepoint::<F>(scalar).convert::<Fe>())
1045    }
1046    dispatch!(on(scalar))
1047}
1048
1049/// The basepoint in `F`.
1050fn basepoint<F: EdField>() -> Ext<F> {
1051    Ext {
1052        x: F::from_limbs51(BASEPOINT_X),
1053        y: F::from_limbs51(BASEPOINT_Y),
1054        z: F::ONE,
1055        t: F::from_limbs51(BASEPOINT_T),
1056    }
1057}
1058
1059/// The basepoint in extended coordinates, `Z = 1` and `T = XY`.
1060///
1061/// It used to be decompressed from [`BASEPOINT_COMPRESSED`] on every call,
1062/// which is a field square root -- about a tenth of a signature on the `no_std`
1063/// path, where nothing caches it. The limbs were computed outside this crate
1064/// from `y = 4/5` and the curve equation, and
1065/// `the_basepoint_constant_is_the_decompressed_encoding` holds them to what
1066/// decompressing the RFC 8032 encoding gives.
1067const BASEPOINT_X: [u64; 5] = [
1068    1_738_742_601_995_546,
1069    1_146_398_526_822_698,
1070    2_070_867_633_025_821,
1071    562_264_141_797_630,
1072    587_772_402_128_613,
1073];
1074const BASEPOINT_Y: [u64; 5] = [
1075    1_801_439_850_948_184,
1076    1_351_079_888_211_148,
1077    450_359_962_737_049,
1078    900_719_925_474_099,
1079    1_801_439_850_948_198,
1080];
1081const BASEPOINT_T: [u64; 5] = [
1082    1_841_354_044_333_475,
1083    16_398_895_984_059,
1084    755_974_180_946_558,
1085    900_171_276_175_154,
1086    1_821_297_809_914_039,
1087];
1088
1089/// RFC 8032 Ed25519 (PureEdDSA over Curve25519 with SHA-512).
1090pub struct Ed25519;
1091
1092impl Algorithm for Ed25519 {
1093    const ID: &'static str = "ed25519";
1094    const NAME: &'static str = "Ed25519";
1095}
1096
1097/// Expand a 32-byte seed into the clamped scalar and the nonce prefix.
1098fn expand_seed(seed: &[u8]) -> ([u8; 32], [u8; 32]) {
1099    let h = Sha512::digest(seed);
1100    let mut a = [0u8; 32];
1101    let mut prefix = [0u8; 32];
1102    a.copy_from_slice(&h.as_ref()[..32]);
1103    prefix.copy_from_slice(&h.as_ref()[32..]);
1104    a[0] &= 248;
1105    a[31] &= 127;
1106    a[31] |= 64;
1107    (a, prefix)
1108}
1109
1110/// Width-`w` non-adjacent form of a 256-bit scalar.
1111///
1112/// Each non-zero digit is odd and lies in `[-(2^(w-1) - 1), 2^(w-1) - 1]`, and
1113/// no two non-zero digits are within `w` places of each other, which puts the
1114/// density near `1/(w+1)`. A wider window means fewer additions and a bigger
1115/// table: width 5 for an arbitrary point, whose table has to be built on the
1116/// spot, and width 8 for the basepoint, whose table is built once.
1117///
1118/// `w` must be at most 8, so that every digit fits an `i8`.
1119fn wnaf(scalar: &[u8; 32], w: u32) -> [i8; 258] {
1120    debug_assert!((2..=8).contains(&w), "window width out of range");
1121    let half = 1i64 << (w - 1);
1122    let full = 1i64 << w;
1123    let mask = (full - 1) as u64;
1124
1125    let mut naf = [0i8; 258];
1126    // Five limbs for a four-limb scalar. A negative digit adds to `k`, and for
1127    // a scalar near 2^256 that carries out of the top: on four limbs it wraps
1128    // to zero, the loop stops early, and the representation is silently short.
1129    // The scalars that reach this are reduced modulo the group order and could
1130    // not trigger it -- which is exactly the assumption that was wrong for the
1131    // NIST recoding, so the room is given rather than argued for.
1132    let mut k = [0u64; 5];
1133    for (i, limb) in k.iter_mut().take(4).enumerate() {
1134        let mut b = [0u8; 8];
1135        b.copy_from_slice(&scalar[i * 8..i * 8 + 8]);
1136        *limb = u64::from_le_bytes(b);
1137    }
1138
1139    let mut i = 0;
1140    while k.iter().any(|&x| x != 0) {
1141        if k[0] & 1 == 1 {
1142            let mut d = (k[0] & mask) as i64;
1143            if d >= half {
1144                d -= full;
1145            }
1146            naf[i] = d as i8;
1147            if d > 0 {
1148                sub_u64(&mut k, d as u64);
1149            } else {
1150                add_u64(&mut k, d.unsigned_abs());
1151            }
1152        }
1153        shr1(&mut k);
1154        i += 1;
1155    }
1156    naf
1157}
1158
1159/// `k -= v`, for `v` small enough not to borrow past the top.
1160fn sub_u64(k: &mut [u64; 5], v: u64) {
1161    let (d, mut borrow) = k[0].overflowing_sub(v);
1162    k[0] = d;
1163    for limb in k.iter_mut().skip(1) {
1164        if !borrow {
1165            break;
1166        }
1167        let (d, b) = limb.overflowing_sub(1);
1168        *limb = d;
1169        borrow = b;
1170    }
1171}
1172
1173/// `k += v`, for `v` small enough not to carry past the top.
1174fn add_u64(k: &mut [u64; 5], v: u64) {
1175    let (d, mut carry) = k[0].overflowing_add(v);
1176    k[0] = d;
1177    for limb in k.iter_mut().skip(1) {
1178        if !carry {
1179            break;
1180        }
1181        let (d, c) = limb.overflowing_add(1);
1182        *limb = d;
1183        carry = c;
1184    }
1185}
1186
1187/// `k >>= 1`.
1188fn shr1(k: &mut [u64; 5]) {
1189    for i in 0..4 {
1190        k[i] = (k[i] >> 1) | (k[i + 1] << 63);
1191    }
1192    k[4] >>= 1;
1193}
1194
1195/// `SHA-512(parts...)` reduced modulo the group order.
1196fn hash_to_scalar(parts: &[&[u8]]) -> [u8; 32] {
1197    let mut h = Sha512::new();
1198    for p in parts {
1199        h.update(p);
1200    }
1201    let digest = h.finalize();
1202    let mut wide = [0u8; 64];
1203    wide.copy_from_slice(digest.as_ref());
1204    scalar::reduce_wide(&wide)
1205}
1206
1207/// A signing key with its public key already derived.
1208///
1209/// # Why this exists
1210///
1211/// RFC 8032 signing needs the public key: it goes into the hash that produces
1212/// `k`. [`Ed25519::sign`] takes only the 32-byte seed, so it has to derive the
1213/// public key on every call -- a second basepoint multiplication, and with the
1214/// table in place that is most of what a signature now costs.
1215///
1216/// A key that is used more than once should derive it once. That is what a TLS
1217/// server does with a certificate key, and what dalek's `SigningKey` does,
1218/// which is why comparing `Ed25519::sign` against it was comparing two
1219/// different amounts of work.
1220///
1221/// The trait method still exists and still takes a seed. This changes nothing
1222/// for a caller signing once; it halves the cost for a caller signing twice.
1223pub struct Ed25519Key {
1224    /// The clamped scalar from the seed's hash.
1225    scalar: [u8; 32],
1226    /// The second half of that hash, which seeds the deterministic nonce.
1227    prefix: [u8; 32],
1228    /// `scalar * B`, compressed. Derived once, here.
1229    public: [u8; 32],
1230}
1231
1232impl Drop for Ed25519Key {
1233    fn drop(&mut self) {
1234        self.scalar.zeroize();
1235        self.prefix.zeroize();
1236        // `public` is public, and is left alone.
1237    }
1238}
1239
1240impl Ed25519Key {
1241    /// Expand a 32-byte seed and derive its public key.
1242    pub fn from_seed(seed: &[u8]) -> Result<Self> {
1243        ic_core::module::operational()?;
1244        ensure!(seed.len() == 32, InvalidLength, "ed25519 seed");
1245        let (scalar, prefix) = expand_seed(seed);
1246        let public = mul_basepoint_compressed(&scalar);
1247        Ok(Self {
1248            scalar,
1249            prefix,
1250            public,
1251        })
1252    }
1253
1254    /// The public key, already derived.
1255    pub fn public_key(&self) -> &[u8; 32] {
1256        &self.public
1257    }
1258
1259    /// Sign `message`, performing one basepoint multiplication rather than two.
1260    pub fn sign(&self, message: &[u8], signature: &mut [u8]) -> Result<()> {
1261        ic_core::module::operational()?;
1262        ensure!(
1263            signature.len() == 64,
1264            InvalidLength,
1265            "ed25519 signature buffer"
1266        );
1267
1268        // r = H(prefix || M), deterministic -- Ed25519 needs no RNG at signing
1269        // time, which removes an entire class of nonce-reuse failures.
1270        let mut r = hash_to_scalar(&[&self.prefix, message]);
1271        let big_r = mul_basepoint_compressed(&r);
1272
1273        let k = hash_to_scalar(&[&big_r, &self.public, message]);
1274        let s = scalar::mul_add(&k, &self.scalar, &r);
1275
1276        signature[..32].copy_from_slice(&big_r);
1277        signature[32..].copy_from_slice(&s);
1278        r.zeroize();
1279        Ok(())
1280    }
1281}
1282
1283impl SignatureScheme for Ed25519 {
1284    const PRIVATE_KEY_LEN: usize = 32;
1285    const PUBLIC_KEY_LEN: usize = 32;
1286    const SIGNATURE_LEN: usize = 64;
1287
1288    fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
1289        ic_core::module::operational()?;
1290        ensure!(private_key.len() == 32, InvalidLength, "ed25519 seed");
1291        ensure!(out.len() == 32, InvalidLength, "ed25519 public key buffer");
1292        let (mut a, mut prefix) = expand_seed(private_key);
1293        out.copy_from_slice(&mul_basepoint_compressed(&a));
1294        a.zeroize();
1295        prefix.zeroize();
1296        Ok(())
1297    }
1298
1299    fn sign(private_key: &[u8], message: &[u8], signature: &mut [u8]) -> Result<()> {
1300        ic_core::module::operational()?;
1301        ensure!(private_key.len() == 32, InvalidLength, "ed25519 seed");
1302        ensure!(
1303            signature.len() == 64,
1304            InvalidLength,
1305            "ed25519 signature buffer"
1306        );
1307
1308        // One shot: expand, derive the public key, sign, discard. A caller
1309        // signing more than once should hold an `Ed25519Key` instead and pay
1310        // the derivation once.
1311        Ed25519Key::from_seed(private_key)?.sign(message, signature)
1312    }
1313
1314    fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<()> {
1315        ic_core::module::operational()?;
1316        // One shot: the key and R are decompressed together, their
1317        // exponentiations interleaved. Only for well-formed lengths and a
1318        // canonical S; anything else takes the two-step path below, which
1319        // reports exactly the error it always has. A caller verifying more
1320        // than once against the same key should hold an `Ed25519VerifyKey`
1321        // and pay the key's decompression once.
1322        if public_key.len() == 32 && signature.len() == 64 {
1323            let mut pk = [0u8; 32];
1324            pk.copy_from_slice(public_key);
1325            let mut big_r = [0u8; 32];
1326            big_r.copy_from_slice(&signature[..32]);
1327            let mut s = [0u8; 32];
1328            s.copy_from_slice(&signature[32..]);
1329            if scalar::is_canonical(&s) {
1330                let k = hash_to_scalar(&[&big_r, &pk, message]);
1331                // The key's errors before R's, as the two-step path orders
1332                // them.
1333                return match dispatch!(check_one_shot(&pk, &big_r, &k, &s)) {
1334                    OneShot::Key(KeyDecode::Malformed) => Err(ic_core::err!(
1335                        MalformedEncoding,
1336                        "ed25519 public key is not on the curve or not canonical"
1337                    )),
1338                    OneShot::Key(_) => Err(ic_core::err!(
1339                        InvalidParameter,
1340                        "ed25519 public key has small order"
1341                    )),
1342                    OneShot::Signature(c) => c.into_result(),
1343                };
1344            }
1345        }
1346        Ed25519VerifyKey::from_bytes(public_key)?.verify(message, signature)
1347    }
1348}
1349
1350/// A public key with its point already recovered.
1351///
1352/// Verification needs the public key as a curve point, and decompressing one
1353/// is a field exponentiation -- about two microseconds, against the twenty a
1354/// verification takes. A key used more than once should not pay that more than
1355/// once, which is the same reason [`Ed25519Key`] exists on the signing side.
1356///
1357/// The point is stored negated, because the equation verification checks is
1358/// `[S]B + [k](-A) == R`, so that is the form every signature wants.
1359///
1360/// [`Ed25519::verify`] builds one of these and throws it away, which is the
1361/// right thing for a caller with one signature and the wrong thing for a
1362/// caller with many.
1363pub struct Ed25519VerifyKey {
1364    /// The compressed encoding, which the challenge hash needs verbatim.
1365    compressed: [u8; 32],
1366    /// `-A`, decompressed once, as its canonical affine coordinates: the form
1367    /// either field reads directly.
1368    neg_a: ([u8; 32], [u8; 32]),
1369}
1370
1371/// What decoding a public key found.
1372enum KeyDecode {
1373    /// Not on the curve, or not canonically encoded.
1374    Malformed,
1375    /// Of order dividing 8.
1376    SmallOrder,
1377    /// `-A`, as canonical affine `(x, y)`.
1378    Negated([u8; 32], [u8; 32]),
1379}
1380
1381/// Decode and check a public key on the field `F`.
1382fn decode_key<F: Curve>(bytes: &[u8; 32]) -> KeyDecode {
1383    match Ext::<F>::decompress(bytes) {
1384        None => KeyDecode::Malformed,
1385        Some(a) if a.has_small_order() => KeyDecode::SmallOrder,
1386        // Decompression leaves Z = 1, so X and Y are the affine coordinates.
1387        Some(a) => {
1388            let n = a.negate();
1389            KeyDecode::Negated(n.x.to_bytes(), n.y.to_bytes())
1390        }
1391    }
1392}
1393
1394/// What checking a signature's equation found.
1395#[derive(PartialEq, Eq, Debug)]
1396enum Check {
1397    /// R is not on the curve, or not canonically encoded.
1398    RMalformed,
1399    /// R is of order dividing 8.
1400    RSmallOrder,
1401    /// `[S]B + [k](-A) != R`.
1402    Mismatch,
1403    /// `[S]B + [k](-A) == R`.
1404    Holds,
1405}
1406
1407impl Check {
1408    fn into_result(self) -> Result<()> {
1409        match self {
1410            Check::RMalformed => Err(ic_core::err!(
1411                MalformedEncoding,
1412                "ed25519 signature R is not on the curve or not canonical"
1413            )),
1414            Check::RSmallOrder => Err(ic_core::err!(
1415                MalformedEncoding,
1416                "ed25519 signature R has small order"
1417            )),
1418            Check::Mismatch => Err(ic_core::err!(AuthenticationFailed, "ed25519")),
1419            Check::Holds => Ok(()),
1420        }
1421    }
1422}
1423
1424/// What one-shot verification found: a problem with the key, or the
1425/// signature's verdict.
1426enum OneShot {
1427    Key(KeyDecode),
1428    Signature(Check),
1429}
1430
1431/// One-shot verification on the field `F`: the key and R decompressed
1432/// together, then the key's checks, then the signature's.
1433fn check_one_shot<F: Curve>(
1434    pk: &[u8; 32],
1435    big_r: &[u8; 32],
1436    k: &[u8; 32],
1437    s: &[u8; 32],
1438) -> OneShot {
1439    let (a, r) = Ext::<F>::decompress_pair(pk, big_r);
1440    let a = match a {
1441        None => return OneShot::Key(KeyDecode::Malformed),
1442        Some(a) if a.has_small_order() => return OneShot::Key(KeyDecode::SmallOrder),
1443        Some(a) => a,
1444    };
1445    OneShot::Signature(equation_holds(&a.negate(), r, k, s))
1446}
1447
1448/// `[S]B + [k](-A) == R`, on the field `F`, everything public.
1449fn check_equation<F: Curve>(
1450    neg_a: &([u8; 32], [u8; 32]),
1451    big_r: &[u8; 32],
1452    k: &[u8; 32],
1453    s: &[u8; 32],
1454) -> Check {
1455    let a = Ext::from_affine(F::from_bytes(&neg_a.0), F::from_bytes(&neg_a.1));
1456    equation_holds(&a, Ext::<F>::decompress(big_r), k, s)
1457}
1458
1459/// `[S]B + [k](-A) == R` for `-A` and a decompressed R.
1460fn equation_holds<F: Curve>(
1461    neg_a: &Ext<F>,
1462    r_point: Option<Ext<F>>,
1463    k: &[u8; 32],
1464    s: &[u8; 32],
1465) -> Check {
1466    let r_point = match r_point {
1467        None => return Check::RMalformed,
1468        Some(p) => p,
1469    };
1470    // An R of small order is what a signature made without a private key
1471    // looks like; a correct signer's R is a multiple of the basepoint and
1472    // never has one. Refused as dalek's verify_strict and libsodium do.
1473    if r_point.has_small_order() {
1474        return Check::RSmallOrder;
1475    }
1476    // [S]B + [k](-A) == R, in one interleaved pass sharing a single chain of
1477    // doublings. Everything here is public, so neither multiplication is
1478    // constant time.
1479    #[cfg(feature = "std")]
1480    let lhs = double_scalar_mul_vartime(neg_a, k, s);
1481    #[cfg(not(feature = "std"))]
1482    let lhs = double_scalar_mul_vartime_no_table(neg_a, k, s);
1483    if lhs.eq_projective(&r_point) {
1484        Check::Holds
1485    } else {
1486        Check::Mismatch
1487    }
1488}
1489
1490impl Ed25519VerifyKey {
1491    /// Decompress `public_key`, rejecting anything not on the curve, not
1492    /// canonically encoded, or of small order.
1493    ///
1494    /// The last is not in RFC 8032, and is the check `verify_strict` in
1495    /// ed25519-dalek and libsodium's verifier also make. A key of order
1496    /// dividing 8 has no secret behind it worth the name: a signature under
1497    /// one can be made without any private key, for many messages at once,
1498    /// and the same signature can verify under several such keys. No key a
1499    /// correct signer derives has small order, so refusing them costs nothing
1500    /// honest.
1501    pub fn from_bytes(public_key: &[u8]) -> Result<Self> {
1502        ensure!(public_key.len() == 32, InvalidLength, "ed25519 public key");
1503        let mut compressed = [0u8; 32];
1504        compressed.copy_from_slice(public_key);
1505        match dispatch!(decode_key(&compressed)) {
1506            KeyDecode::Malformed => Err(ic_core::err!(
1507                MalformedEncoding,
1508                "ed25519 public key is not on the curve or not canonical"
1509            )),
1510            KeyDecode::SmallOrder => Err(ic_core::err!(
1511                InvalidParameter,
1512                "ed25519 public key has small order"
1513            )),
1514            KeyDecode::Negated(x, y) => Ok(Self {
1515                compressed,
1516                neg_a: (x, y),
1517            }),
1518        }
1519    }
1520
1521    /// The key as it was given.
1522    pub fn as_bytes(&self) -> &[u8; 32] {
1523        &self.compressed
1524    }
1525
1526    /// Verify `signature` over `message`.
1527    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<()> {
1528        ic_core::module::operational()?;
1529        ensure!(signature.len() == 64, InvalidLength, "ed25519 signature");
1530
1531        let mut big_r = [0u8; 32];
1532        big_r.copy_from_slice(&signature[..32]);
1533        let mut s = [0u8; 32];
1534        s.copy_from_slice(&signature[32..]);
1535
1536        // RFC 8032 section 5.1.7: reject a non-canonical S. Without this check
1537        // the signature is malleable, and any system that treats a signature
1538        // as a unique identifier becomes attackable.
1539        ensure!(
1540            scalar::is_canonical(&s),
1541            MalformedEncoding,
1542            "ed25519 signature S is not reduced"
1543        );
1544
1545        let k = hash_to_scalar(&[&big_r, &self.compressed, message]);
1546        dispatch!(check_equation(&self.neg_a, &big_r, &k, &s)).into_result()
1547    }
1548}
1549
1550impl SelfTest for Ed25519 {
1551    fn self_test() -> Result<()> {
1552        // RFC 8032 §7.1 test vector 1: the empty message.
1553        let mut seed = [0u8; 32];
1554        ic_core::codec::hex_decode(
1555            b"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
1556            &mut seed,
1557        )?;
1558        let mut want_pk = [0u8; 32];
1559        ic_core::codec::hex_decode(
1560            b"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
1561            &mut want_pk,
1562        )?;
1563        let mut want_sig = [0u8; 64];
1564        ic_core::codec::hex_decode(
1565            b"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
1566            &mut want_sig,
1567        )?;
1568
1569        let mut pk = [0u8; 32];
1570        <Self as SignatureScheme>::public_key(&seed, &mut pk)?;
1571        ensure!(
1572            ic_core::ct::verify(&want_pk, &pk),
1573            SelfTestFailed,
1574            "ed25519"
1575        );
1576
1577        let mut sig = [0u8; 64];
1578        <Self as SignatureScheme>::sign(&seed, b"", &mut sig)?;
1579        ensure!(
1580            ic_core::ct::verify(&want_sig, &sig),
1581            SelfTestFailed,
1582            "ed25519"
1583        );
1584
1585        <Self as SignatureScheme>::verify(&pk, b"", &sig)?;
1586
1587        // A corrupted signature must be rejected.
1588        sig[0] ^= 1;
1589        ensure!(
1590            <Self as SignatureScheme>::verify(&pk, b"", &sig).is_err(),
1591            SelfTestFailed,
1592            "ed25519"
1593        );
1594        Ok(())
1595    }
1596}
1597
1598#[cfg(test)]
1599mod tests {
1600    use super::*;
1601
1602    /// The curve on the portable field, which these tests were written for.
1603    type P = Ext<Fe>;
1604    const D: Fe = <Fe as EdField>::D;
1605    const D2: Fe = <Fe as EdField>::D2;
1606    const SQRT_M1: Fe = <Fe as EdField>::SQRT_M1;
1607
1608    fn basepoint() -> P {
1609        super::basepoint::<Fe>()
1610    }
1611
1612    fn mul_basepoint(s: &[u8; 32]) -> P {
1613        super::mul_basepoint::<Fe>(s)
1614    }
1615
1616    /// `-A` as the verify key holds it, as a point.
1617    fn neg_a(vk: &Ed25519VerifyKey) -> P {
1618        P::from_affine(Fe::from_bytes(&vk.neg_a.0), Fe::from_bytes(&vk.neg_a.1))
1619    }
1620    use ic_core::codec::{hex, unhex};
1621
1622    #[test]
1623    fn curve_constants_are_correct() {
1624        // d = -121665 / 121666
1625        let d = Fe::from_u64(121_665)
1626            .neg()
1627            .mul(&Fe::from_u64(121_666).invert());
1628        assert_eq!(hex(&D.to_bytes()), hex(&d.to_bytes()), "d");
1629        assert_eq!(hex(&D2.to_bytes()), hex(&d.add(&d).to_bytes()), "2d");
1630        // sqrt(-1) squares to -1.
1631        assert_eq!(
1632            hex(&SQRT_M1.square().to_bytes()),
1633            hex(&Fe::ONE.neg().to_bytes()),
1634            "sqrt(-1)"
1635        );
1636    }
1637
1638    #[test]
1639    fn basepoint_has_the_expected_coordinates() {
1640        let b = basepoint();
1641        // y = 4/5
1642        let expected_y = Fe::from_u64(4).mul(&Fe::from_u64(5).invert());
1643        let z_inv = b.z.invert();
1644        assert_eq!(
1645            hex(&b.y.mul(&z_inv).to_bytes()),
1646            hex(&expected_y.to_bytes())
1647        );
1648        assert_eq!(hex(&b.compress()), hex(&BASEPOINT_COMPRESSED));
1649    }
1650
1651    /// The dedicated doubling must agree with adding a point to itself.
1652    ///
1653    /// `add` is what RFC 8032's vectors validate, so it is the oracle here.
1654    /// The two formulas are different enough -- one reads `T`, the other does
1655    /// not -- that agreeing on the basepoint alone would not be convincing, so
1656    /// this walks a chain of multiples and doubles each one.
1657    #[test]
1658    fn doubling_agrees_with_adding_a_point_to_itself() {
1659        let mut p = basepoint();
1660        let mut checked = 0;
1661        for _ in 0..16 {
1662            assert_eq!(
1663                p.double().compress(),
1664                p.add(&p).compress(),
1665                "dedicated doubling and self-addition differ"
1666            );
1667            p = p.add(&basepoint());
1668            checked += 1;
1669        }
1670        assert_eq!(checked, 16, "the comparison did not run");
1671
1672        // The identity doubles to itself, which the formula has to get right
1673        // without a special case.
1674        assert_eq!(P::IDENTITY.double().compress(), P::IDENTITY.compress());
1675    }
1676
1677    /// Projective equality must agree with comparing compressed encodings.
1678    ///
1679    /// The two answer the same question by different routes -- one divides by
1680    /// Z, the other cross-multiplies -- so agreement is the argument. It has to
1681    /// hold for equal points given *different* representatives, which is the
1682    /// case the whole optimisation rests on, so the test scales one side by a
1683    /// factor and checks it still compares equal.
1684    #[test]
1685    fn projective_equality_agrees_with_compressed_equality() {
1686        let b = basepoint();
1687        let mut points = std::vec![P::IDENTITY, b];
1688        let mut p = b;
1689        for _ in 0..6 {
1690            p = p.double();
1691            points.push(p);
1692        }
1693
1694        let mut checked = 0;
1695        for (i, a) in points.iter().enumerate() {
1696            for (j, c) in points.iter().enumerate() {
1697                let projective = a.eq_projective(c);
1698                let compressed = a.compress() == c.compress();
1699                assert_eq!(
1700                    projective, compressed,
1701                    "projective and compressed equality differ for {i} vs {j}"
1702                );
1703                checked += 1;
1704            }
1705        }
1706        assert_eq!(checked, 64, "the comparison did not run");
1707
1708        // The case that matters: the same point with a different Z. Adding the
1709        // identity re-scales the representation without moving the point.
1710        let scaled = b.add(&P::IDENTITY);
1711        assert!(b.eq_projective(&scaled), "equal points with different Z");
1712        assert_eq!(b.compress(), scaled.compress());
1713    }
1714
1715    #[test]
1716    fn group_law_is_consistent() {
1717        let b = basepoint();
1718        // P + 0 == P
1719        assert_eq!(hex(&b.add(&P::IDENTITY).compress()), hex(&b.compress()));
1720        // 2P via doubling equals 2P via scalar multiplication.
1721        let mut two = [0u8; 32];
1722        two[0] = 2;
1723        assert_eq!(
1724            hex(&b.double().compress()),
1725            hex(&b.mul_scalar(&two).compress())
1726        );
1727        // (P + P) + P == 3P
1728        let mut three = [0u8; 32];
1729        three[0] = 3;
1730        assert_eq!(
1731            hex(&b.double().add(&b).compress()),
1732            hex(&b.mul_scalar(&three).compress())
1733        );
1734    }
1735
1736    #[test]
1737    fn order_of_the_basepoint_is_l() {
1738        // [L]B must be the identity.
1739        assert_eq!(
1740            hex(&basepoint().mul_scalar(&scalar::L).compress()),
1741            hex(&P::IDENTITY.compress())
1742        );
1743    }
1744
1745    #[test]
1746    fn compression_roundtrips() {
1747        let b = basepoint();
1748        for k in [1u8, 2, 3, 47, 200] {
1749            let mut s = [0u8; 32];
1750            s[0] = k;
1751            let p = b.mul_scalar(&s);
1752            let c = p.compress();
1753            let d = P::decompress(&c).expect("valid point");
1754            assert_eq!(hex(&d.compress()), hex(&c), "k = {k}");
1755        }
1756    }
1757
1758    /// The identity as a public key: under the cofactorless equation alone,
1759    /// `[S]B + [k](-A) == R` holds with `R = [S]B` for every message and
1760    /// every `S`, so anyone can sign anything. The equation is shown to hold,
1761    /// and the key is refused before it is consulted.
1762    #[test]
1763    fn a_signature_under_the_identity_key_is_refused() {
1764        let identity = P::IDENTITY.compress();
1765        let s = scalar::reduce(&[7u8; 32]);
1766        let r = mul_basepoint(&s).compress();
1767        let k = hash_to_scalar(&[&r, &identity, b"any message"]);
1768        let neg_a = P::IDENTITY.negate();
1769        let lhs = double_scalar_mul_vartime(&neg_a, &k, &s);
1770        assert!(
1771            lhs.eq_projective(&P::decompress(&r).unwrap()),
1772            "the forgery satisfies the equation"
1773        );
1774
1775        let err = Ed25519VerifyKey::from_bytes(&identity).err().unwrap();
1776        assert_eq!(err.kind(), ic_core::ErrorKind::InvalidParameter);
1777        let mut sig = [0u8; 64];
1778        sig[..32].copy_from_slice(&r);
1779        sig[32..].copy_from_slice(&s);
1780        assert!(Ed25519::verify(&identity, b"any message", &sig).is_err());
1781    }
1782
1783    /// A signature whose R is the identity, made with the real private key:
1784    /// `S = k*a` satisfies the equation, since `[k*a]B - [k]A` is the identity.
1785    /// A correct signer's R is never of small order; this one is refused.
1786    #[test]
1787    fn a_signature_with_an_identity_r_is_refused() {
1788        let key = Ed25519Key::from_seed(&[0x42u8; 32]).unwrap();
1789        let r = P::IDENTITY.compress();
1790        let message = b"identity R";
1791        let k = hash_to_scalar(&[&r, &key.public, message]);
1792        let s = scalar::mul_add(&k, &key.scalar, &[0u8; 32]);
1793        let mut sig = [0u8; 64];
1794        sig[..32].copy_from_slice(&r);
1795        sig[32..].copy_from_slice(&s);
1796
1797        let vk = Ed25519VerifyKey::from_bytes(&key.public).unwrap();
1798        let lhs = double_scalar_mul_vartime(&neg_a(&vk), &k, &s);
1799        assert!(
1800            lhs.eq_projective(&P::IDENTITY),
1801            "the signature satisfies the equation"
1802        );
1803        let err = vk.verify(message, &sig).unwrap_err();
1804        assert_eq!(err.kind(), ic_core::ErrorKind::MalformedEncoding);
1805    }
1806
1807    /// RFC 8032 section 5.1.3: a y field of p or more fails to decode, even
1808    /// where, reduced, it names a point of large order. y = 3 is one such:
1809    /// its canonical encoding decodes and its alias y + p does not.
1810    #[test]
1811    fn non_canonical_encodings_do_not_decode() {
1812        for sign in [0u8, 0x80] {
1813            let mut canonical = [0u8; 32];
1814            canonical[0] = 3;
1815            canonical[31] |= sign;
1816            let point = P::decompress(&canonical).expect("y = 3 is on the curve");
1817            assert!(!point.has_small_order());
1818            // p + 3 = 2^255 - 16, little-endian.
1819            let mut alias = [0xffu8; 32];
1820            alias[0] = 0xf0;
1821            alias[31] = 0x7f | sign;
1822            assert!(P::decompress(&alias).is_none(), "sign {sign:#x}");
1823        }
1824    }
1825
1826    #[test]
1827    fn decompression_rejects_non_curve_points() {
1828        // A y value with no corresponding x.
1829        let mut bad = [0u8; 32];
1830        bad[0] = 2;
1831        assert!(P::decompress(&bad).is_none());
1832    }
1833
1834    /// RFC 8032 §7.1 test vectors.
1835    #[test]
1836    fn rfc8032_vectors() {
1837        let cases: [(&str, &str, &str, &str); 3] = [
1838            (
1839                "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
1840                "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
1841                "",
1842                "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
1843            ),
1844            (
1845                "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
1846                "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
1847                "72",
1848                "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
1849            ),
1850            (
1851                "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
1852                "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
1853                "af82",
1854                "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
1855            ),
1856        ];
1857
1858        for (seed_hex, pk_hex, msg_hex, sig_hex) in cases {
1859            let seed = unhex(seed_hex).unwrap();
1860            let msg = unhex(msg_hex).unwrap();
1861
1862            let mut pk = [0u8; 32];
1863            Ed25519::public_key(&seed, &mut pk).unwrap();
1864            assert_eq!(hex(&pk), pk_hex, "public key for {seed_hex}");
1865
1866            let mut sig = [0u8; 64];
1867            Ed25519::sign(&seed, &msg, &mut sig).unwrap();
1868            assert_eq!(hex(&sig), sig_hex, "signature for {seed_hex}");
1869
1870            Ed25519::verify(&pk, &msg, &sig).unwrap();
1871        }
1872    }
1873
1874    #[test]
1875    fn verification_rejects_tampering() {
1876        let seed = [0x42u8; 32];
1877        let mut pk = [0u8; 32];
1878        Ed25519::public_key(&seed, &mut pk).unwrap();
1879        let mut sig = [0u8; 64];
1880        Ed25519::sign(&seed, b"authentic", &mut sig).unwrap();
1881        Ed25519::verify(&pk, b"authentic", &sig).unwrap();
1882
1883        // Wrong message.
1884        assert!(Ed25519::verify(&pk, b"forged", &sig).is_err());
1885        // Corrupted R.
1886        let mut bad = sig;
1887        bad[0] ^= 1;
1888        assert!(Ed25519::verify(&pk, b"authentic", &bad).is_err());
1889        // Corrupted S.
1890        let mut bad = sig;
1891        bad[40] ^= 1;
1892        assert!(Ed25519::verify(&pk, b"authentic", &bad).is_err());
1893        // Wrong public key.
1894        let mut other_pk = [0u8; 32];
1895        Ed25519::public_key(&[0x43u8; 32], &mut other_pk).unwrap();
1896        assert!(Ed25519::verify(&other_pk, b"authentic", &sig).is_err());
1897    }
1898
1899    /// A signature with `S >= L` must be rejected even though it would
1900    /// otherwise verify; this is the malleability check.
1901    #[test]
1902    fn rejects_non_canonical_s() {
1903        let seed = [0x42u8; 32];
1904        let mut pk = [0u8; 32];
1905        Ed25519::public_key(&seed, &mut pk).unwrap();
1906        let mut sig = [0u8; 64];
1907        Ed25519::sign(&seed, b"msg", &mut sig).unwrap();
1908
1909        // Add L to S. The verification equation still holds mod L, so only the
1910        // canonicality check can catch it.
1911        let mut carry = 0u16;
1912        for i in 0..32 {
1913            let t = sig[32 + i] as u16 + scalar::L[i] as u16 + carry;
1914            sig[32 + i] = t as u8;
1915            carry = t >> 8;
1916        }
1917        assert!(Ed25519::verify(&pk, b"msg", &sig).is_err());
1918    }
1919
1920    /// The cached key and the seed-only call must produce the same signature.
1921    ///
1922    /// They share a code path now, which is the point -- but that is the sort
1923    /// of thing a later refactor separates again, and the two would then differ
1924    /// only for callers who use one and verify with the other. RFC 8032's
1925    /// vectors exercise the trait method alone and would not notice.
1926    #[test]
1927    fn the_cached_key_signs_identically_to_the_seed() {
1928        let mut checked = 0;
1929        for seed in [[0x11u8; 32], [0x9du8; 32], [0xffu8; 32]] {
1930            for message in [&b""[..], &b"x"[..], &b"a longer message to sign"[..]] {
1931                let mut from_seed = [0u8; 64];
1932                Ed25519::sign(&seed, message, &mut from_seed).unwrap();
1933
1934                let key = Ed25519Key::from_seed(&seed).unwrap();
1935                let mut from_key = [0u8; 64];
1936                key.sign(message, &mut from_key).unwrap();
1937
1938                assert_eq!(from_seed, from_key, "the two signing paths diverged");
1939
1940                // And the cached public key is the one the trait derives.
1941                let mut derived = [0u8; 32];
1942                Ed25519::public_key(&seed, &mut derived).unwrap();
1943                assert_eq!(&derived, key.public_key());
1944
1945                // Both verify, so neither is consistently wrong.
1946                Ed25519::verify(&derived, message, &from_key).unwrap();
1947                checked += 1;
1948            }
1949        }
1950        assert_eq!(checked, 9, "the comparison did not run");
1951    }
1952
1953    /// Scalars that exercise the signed radix-16 recoding at its edges: digits
1954    /// of exactly 8, which carry; runs of 7 and 9 either side of that; the
1955    /// largest value the recoding accepts; the group order less one; and the
1956    /// shape of a clamped secret.
1957    fn windowed_scalars() -> std::vec::Vec<[u8; 32]> {
1958        let mut one = [0u8; 32];
1959        one[0] = 1;
1960        let mut eight = [0u8; 32];
1961        eight[0] = 8;
1962        let mut top = [0xffu8; 32];
1963        top[31] = 0x7f;
1964        let mut clamped = [0x9du8; 32];
1965        clamped[0] &= 248;
1966        clamped[31] &= 127;
1967        clamped[31] |= 64;
1968        let mut l_minus_1 = scalar::L;
1969        l_minus_1[0] -= 1;
1970        let mut out = std::vec![[0u8; 32], one, eight, top, clamped, l_minus_1];
1971        for fill in [0x88u8, 0x77, 0x99, 0x55, 0xaa] {
1972            let mut s = [fill; 32];
1973            s[31] &= 0x7f;
1974            out.push(s);
1975        }
1976        out
1977    }
1978
1979    /// The `no_std` signing path against the bit-at-a-time ladder, on the
1980    /// basepoint and on a point that is not.
1981    ///
1982    /// Tests run with `std`, where signing takes the precomputed table, so
1983    /// without this the windowed path would be compiled into embedded builds
1984    /// and executed by nothing.
1985    /// Every coordinate, not just the compressed form: a wrong `T` still
1986    /// compresses correctly, since compression reads only `X`, `Y` and `Z`,
1987    /// and would corrupt every addition that reads it.
1988    #[test]
1989    fn the_basepoint_constant_is_the_decompressed_encoding() {
1990        let decoded = P::decompress(&BASEPOINT_COMPRESSED).expect("the RFC 8032 basepoint");
1991        let zinv = decoded.z.invert();
1992        for (name, constant, from_encoding) in [
1993            ("x", basepoint().x, decoded.x.mul(&zinv)),
1994            ("y", basepoint().y, decoded.y.mul(&zinv)),
1995            ("t", basepoint().t, decoded.t.mul(&zinv)),
1996        ] {
1997            assert_eq!(
1998                constant.to_bytes(),
1999                from_encoding.to_bytes(),
2000                "{name} differs"
2001            );
2002        }
2003        assert_eq!(basepoint().z.to_bytes(), Fe::ONE.to_bytes());
2004        assert_eq!(basepoint().compress(), BASEPOINT_COMPRESSED);
2005    }
2006
2007    #[test]
2008    fn the_windowed_multiplication_agrees_with_the_ladder() {
2009        let b = basepoint();
2010        let mut seven = [0u8; 32];
2011        seven[0] = 7;
2012        let p = b.mul_scalar(&seven);
2013
2014        let mut checked = 0;
2015        for point in [b, p] {
2016            for scalar in windowed_scalars() {
2017                assert_eq!(
2018                    mul_scalar_windowed(&point, &scalar).compress(),
2019                    point.mul_scalar(&scalar).compress(),
2020                    "windowed and ladder differ for {scalar:02x?}"
2021                );
2022                checked += 1;
2023            }
2024        }
2025        assert!(checked >= 20, "only {checked} comparisons ran");
2026    }
2027
2028    /// The `no_std` verification path against the tabled one and against two
2029    /// separate ladders, which share no code with either.
2030    #[test]
2031    fn the_untabled_double_multiplication_agrees() {
2032        let b = basepoint();
2033        let mut checked = 0;
2034        for (i, k) in windowed_scalars().into_iter().enumerate() {
2035            let mut seed = [0u8; 32];
2036            seed[0] = 3 + i as u8;
2037            let a = b.mul_scalar(&seed);
2038            for s in [k, [0xffu8; 32], [0x9du8; 32]] {
2039                let untabled = double_scalar_mul_vartime_no_table(&a, &k, &s);
2040                let tabled = double_scalar_mul_vartime(&a, &k, &s);
2041                let ladders = a.mul_scalar(&k).add(&b.mul_scalar(&s));
2042                assert_eq!(
2043                    untabled.compress(),
2044                    tabled.compress(),
2045                    "k={k:02x?} s={s:02x?}"
2046                );
2047                assert_eq!(
2048                    untabled.compress(),
2049                    ladders.compress(),
2050                    "k={k:02x?} s={s:02x?}"
2051                );
2052                checked += 1;
2053            }
2054        }
2055        assert!(checked >= 30, "only {checked} comparisons ran");
2056    }
2057
2058    /// The recoding must represent the scalar, with the digits it promises.
2059    #[test]
2060    fn the_wnaf_digits_are_odd_sparse_and_faithful() {
2061        for scalar in [[1u8; 32], [0x9du8; 32], [0xffu8; 32], [0x55u8; 32]] {
2062            let naf = wnaf(&scalar, 5);
2063
2064            let mut previous_nonzero: Option<usize> = None;
2065            for (i, d) in naf.iter().enumerate() {
2066                if *d == 0 {
2067                    continue;
2068                }
2069                assert!(d % 2 != 0, "digit {d} at {i} is not odd");
2070                assert!((-15..=15).contains(d), "digit {d} at {i} is out of range");
2071                if let Some(j) = previous_nonzero {
2072                    assert!(i - j >= 5, "digits at {j} and {i} are adjacent");
2073                }
2074                previous_nonzero = Some(i);
2075            }
2076
2077            // And it evaluates back to the scalar, modulo a small prime that
2078            // has nothing to do with the curve.
2079            const M: u128 = 1_000_000_007;
2080            let mut from_digits = 0u128;
2081            let mut power = 1u128;
2082            for d in naf {
2083                let term = ((d as i128).rem_euclid(M as i128)) as u128;
2084                from_digits = (from_digits + term * power) % M;
2085                power = power * 2 % M;
2086            }
2087            let mut from_bytes = 0u128;
2088            let mut p = 1u128;
2089            for byte in scalar {
2090                from_bytes = (from_bytes + (byte as u128) * p) % M;
2091                p = p * 256 % M;
2092            }
2093            assert_eq!(from_digits, from_bytes, "recoding changed the value");
2094        }
2095    }
2096
2097    #[test]
2098    fn signing_is_deterministic() {
2099        let seed = [0x7fu8; 32];
2100        let mut a = [0u8; 64];
2101        let mut b = [0u8; 64];
2102        Ed25519::sign(&seed, b"same input", &mut a).unwrap();
2103        Ed25519::sign(&seed, b"same input", &mut b).unwrap();
2104        assert_eq!(a, b);
2105    }
2106
2107    #[test]
2108    fn rejects_wrong_lengths() {
2109        let mut out = [0u8; 32];
2110        assert!(Ed25519::public_key(&[0u8; 31], &mut out).is_err());
2111        assert!(Ed25519::sign(&[0u8; 32], b"", &mut [0u8; 63]).is_err());
2112        assert!(Ed25519::verify(&[0u8; 32], b"", &[0u8; 63]).is_err());
2113    }
2114
2115    #[test]
2116    fn self_test_passes() {
2117        Ed25519::self_test().unwrap();
2118    }
2119
2120    /// A few points on the curve, for the formula tests below.
2121    fn sample_points(n: usize) -> Vec<P> {
2122        let mut out = Vec::new();
2123        let mut p = basepoint();
2124        for _ in 0..n {
2125            out.push(p);
2126            p = p.double().add(&basepoint());
2127        }
2128        out
2129    }
2130
2131    /// The completed-coordinate doubling is the extended one.
2132    ///
2133    /// `Projective::double` produces four squarings and no multiplications,
2134    /// and the multiplications a doubling needs move into whichever conversion
2135    /// follows. That is only sound if the two routes agree, and the signs are
2136    /// where it would go wrong: the completed form this uses differs from the
2137    /// one the extended formula implies by a factor of -1 in two coordinates,
2138    /// which cancels projectively and would not cancel if one of them were
2139    /// dropped.
2140    #[test]
2141    fn the_completed_doubling_agrees_with_the_extended_one() {
2142        for p in sample_points(40) {
2143            let want = p.double();
2144            let got = p.to_projective().double().to_extended();
2145            assert!(got.eq_projective(&want), "doubling disagrees");
2146            // And through the projective form, which is the route a chain of
2147            // doublings actually takes.
2148            let chained = p.to_projective().double().to_projective().double();
2149            let twice = p.double().double();
2150            assert!(chained.to_extended().eq_projective(&twice), "two doublings");
2151        }
2152    }
2153
2154    /// Niels addition is the nine-multiplication addition.
2155    #[test]
2156    fn niels_addition_agrees_with_the_general_one() {
2157        let pts = sample_points(20);
2158        for p in &pts {
2159            for q in &pts {
2160                let want = p.add(q);
2161                let got = p.add_niels(&q.to_niels()).to_extended();
2162                assert!(got.eq_projective(&want), "add_niels disagrees");
2163
2164                let want_sub = p.add(&q.negate());
2165                let got_sub = p.sub_niels(&q.to_niels()).to_extended();
2166                assert!(got_sub.eq_projective(&want_sub), "sub_niels disagrees");
2167            }
2168        }
2169    }
2170
2171    /// Affine-Niels addition is the general addition.
2172    ///
2173    /// It drops the `Z` multiply on the assumption that the stored point has
2174    /// `Z = 1`, which `to_affine_niels` arranges by inverting. If that
2175    /// inversion or the `2d·x·y` were wrong the result would still be a point
2176    /// on the curve, just the wrong one, so it is checked against the addition
2177    /// the published vectors validate.
2178    #[test]
2179    fn affine_niels_addition_agrees_with_the_general_one() {
2180        let pts = sample_points(20);
2181        for p in &pts {
2182            for q in &pts {
2183                let want = p.add(q);
2184                let got = p.add_affine_niels(&q.to_affine_niels()).to_extended();
2185                assert!(got.eq_projective(&want), "add_affine_niels disagrees");
2186
2187                // And the negated form, which the table's sign handling uses.
2188                let mut n = q.to_affine_niels();
2189                n.conditional_negate(1);
2190                let want_neg = p.add(&q.negate());
2191                let got_neg = p.add_affine_niels(&n).to_extended();
2192                assert!(got_neg.eq_projective(&want_neg), "negated form disagrees");
2193            }
2194        }
2195    }
2196
2197    /// Where verification's time actually goes.
2198    ///
2199    /// Ignored: it is a measurement, not an assertion. Run it with
2200    /// `cargo test -p ic-ec --release -- --ignored --nocapture where_verify_spends`
2201    /// before changing anything here, because the answer decided what was
2202    /// worth doing and a guess would not have.
2203    #[test]
2204    #[ignore = "diagnostic, not a test"]
2205    fn where_verify_spends_its_time() {
2206        use std::time::Instant;
2207
2208        let seed = [7u8; 32];
2209        let key = Ed25519Key::from_seed(&seed).unwrap();
2210        let msg = b"benchmark message";
2211        let mut sig = [0u8; 64];
2212        key.sign(msg, &mut sig).unwrap();
2213        let pk = *key.public_key();
2214
2215        let mut big_r = [0u8; 32];
2216        big_r.copy_from_slice(&sig[..32]);
2217        let mut s_sc = [0u8; 32];
2218        s_sc.copy_from_slice(&sig[32..]);
2219
2220        let n = 2000;
2221        let time = |label: &str, f: &mut dyn FnMut()| {
2222            let mut best = f64::INFINITY;
2223            for _ in 0..5 {
2224                let t = Instant::now();
2225                for _ in 0..n {
2226                    f();
2227                }
2228                let e = t.elapsed().as_secs_f64() / n as f64 * 1e6;
2229                if e < best {
2230                    best = e;
2231                }
2232            }
2233            println!("  {label:<34} {best:>9.2} us");
2234            best
2235        };
2236
2237        let a_point = P::decompress(&pk).unwrap();
2238        let k = hash_to_scalar(&[&big_r, &pk, msg]);
2239
2240        println!(
2241            "
2242ed25519 verify, cost breakdown:"
2243        );
2244        let d = time("decompress (x2 per verify)", &mut || {
2245            core::hint::black_box(P::decompress(&pk));
2246        });
2247        let h = time("hash_to_scalar", &mut || {
2248            core::hint::black_box(hash_to_scalar(&[&big_r, &pk, msg]));
2249        });
2250        let b = time("mul_basepoint (const time)", &mut || {
2251            core::hint::black_box(mul_basepoint(&s_sc));
2252        });
2253        let v = time("double_scalar_mul_vartime", &mut || {
2254            core::hint::black_box(double_scalar_mul_vartime(&a_point.negate(), &k, &s_sc));
2255        });
2256        time("  of which: wnaf(k,5)+wnaf(s,8)", &mut || {
2257            core::hint::black_box(wnaf(&k, 5));
2258            core::hint::black_box(wnaf(&s_sc, 8));
2259        });
2260        time("  of which: odd_a table build", &mut || {
2261            let twice = a_point.double();
2262            let mut odd = [a_point; 8];
2263            for i in 1..8 {
2264                odd[i] = odd[i - 1].add(&twice);
2265            }
2266            let t: [Niels<Fe>; 8] = core::array::from_fn(|i| odd[i].to_niels());
2267            core::hint::black_box(t);
2268        });
2269        time("  of which: 255 doublings", &mut || {
2270            let mut p = a_point;
2271            for _ in 0..255 {
2272                p = p.double();
2273            }
2274            core::hint::black_box(p);
2275        });
2276        time("  of which: 79 additions", &mut || {
2277            let mut p = a_point;
2278            for _ in 0..79 {
2279                p = p.add(&a_point);
2280            }
2281            core::hint::black_box(p);
2282        });
2283        time("compress (one inversion)", &mut || {
2284            core::hint::black_box(a_point.compress());
2285        });
2286        println!(
2287            "  {:<34} {:>9.2} us",
2288            "-- accounted for",
2289            2.0 * d + h + b + v
2290        );
2291
2292        // One level down: if the point ops are slow, the field ops are why.
2293        println!(
2294            "
2295field and point primitives, nanoseconds:"
2296        );
2297        let nn = 200_000;
2298        let ns = |label: &str, f: &mut dyn FnMut()| {
2299            let mut best = f64::INFINITY;
2300            for _ in 0..5 {
2301                let t = Instant::now();
2302                for _ in 0..nn {
2303                    f();
2304                }
2305                let e = t.elapsed().as_secs_f64() / nn as f64 * 1e9;
2306                if e < best {
2307                    best = e;
2308                }
2309            }
2310            println!("  {label:<34} {best:>9.2} ns");
2311        };
2312        let fx = a_point.x;
2313        let fy = a_point.y;
2314        ns("Fe::mul", &mut || {
2315            core::hint::black_box(core::hint::black_box(&fx).mul(core::hint::black_box(&fy)));
2316        });
2317        ns("Fe::square", &mut || {
2318            core::hint::black_box(core::hint::black_box(&fx).square());
2319        });
2320        ns("Fe::add", &mut || {
2321            core::hint::black_box(core::hint::black_box(&fx).add(core::hint::black_box(&fy)));
2322        });
2323        ns("Fe::sub", &mut || {
2324            core::hint::black_box(core::hint::black_box(&fx).sub(core::hint::black_box(&fy)));
2325        });
2326        ns("Fe::neg", &mut || {
2327            core::hint::black_box(core::hint::black_box(&fx).neg());
2328        });
2329        let proj = a_point.to_projective();
2330        let comp = proj.double();
2331        let an = a_point.to_affine_niels();
2332        ns("Projective::double  (4S)", &mut || {
2333            core::hint::black_box(core::hint::black_box(&proj).double());
2334        });
2335        ns("Projective::double_projective", &mut || {
2336            core::hint::black_box(core::hint::black_box(proj).double_projective());
2337        });
2338        ns("Completed::to_projective (3M)", &mut || {
2339            core::hint::black_box(core::hint::black_box(&comp).to_projective());
2340        });
2341        ns("Completed::to_extended (4M)", &mut || {
2342            core::hint::black_box(core::hint::black_box(&comp).to_extended());
2343        });
2344        ns("P::add_affine_niels (3M)", &mut || {
2345            core::hint::black_box(
2346                core::hint::black_box(&a_point).add_affine_niels(core::hint::black_box(&an)),
2347            );
2348        });
2349        ns("P::double", &mut || {
2350            core::hint::black_box(core::hint::black_box(&a_point).double());
2351        });
2352        ns("P::add", &mut || {
2353            core::hint::black_box(
2354                core::hint::black_box(&a_point).add(core::hint::black_box(&a_point)),
2355            );
2356        });
2357    }
2358
2359    /// How many point operations a verification actually performs.
2360    #[test]
2361    #[ignore = "diagnostic, not a test"]
2362    fn count_the_point_operations() {
2363        let mut doublings = 0usize;
2364        let mut adds_a = 0usize;
2365        let mut adds_b = 0usize;
2366        let mut state = 0x1234_5678_9abc_def0u64;
2367        let trials = 200;
2368        for _ in 0..trials {
2369            let mut kb = [0u8; 32];
2370            for c in kb.chunks_exact_mut(8) {
2371                state ^= state >> 12;
2372                state ^= state << 25;
2373                state ^= state >> 27;
2374                c.copy_from_slice(&state.wrapping_mul(0x2545_f491_4f6c_dd1d).to_le_bytes());
2375            }
2376            kb[31] &= 0x0f;
2377            let na = wnaf(&kb, 5);
2378            let nb = wnaf(&kb, 8);
2379            let mut i = 257;
2380            while i > 0 && na[i] == 0 && nb[i] == 0 {
2381                i -= 1;
2382            }
2383            doublings += i + 1;
2384            adds_a += na.iter().filter(|d| **d != 0).count();
2385            adds_b += nb.iter().filter(|d| **d != 0).count();
2386        }
2387        let d = doublings as f64 / trials as f64;
2388        let aa = adds_a as f64 / trials as f64;
2389        let ab = adds_b as f64 / trials as f64;
2390        println!(
2391            "
2392  per double-scalar multiplication, averaged over {trials} scalars:"
2393        );
2394        println!("    doublings                  {d:>8.1}");
2395        println!("    additions, w=5 table (A)   {aa:>8.1}");
2396        println!("    additions, w=8 table (B)   {ab:>8.1}");
2397        println!("    additions, building A      {:>8.1}", 8.0);
2398        println!("    ---");
2399        println!("    total additions            {:>8.1}", aa + ab + 8.0);
2400        println!(
2401            "    field muls, at 4M+4S per doubling and 9M per addition: {:>6.0}",
2402            d * 8.0 + (aa + ab + 8.0) * 9.0
2403        );
2404        println!(
2405            "    the same at dalek's 3M+4S and 7M:                      {:>6.0}",
2406            d * 7.0 + (aa + ab + 8.0) * 7.0
2407        );
2408    }
2409}