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