rhdl_ed25519_scalable_core/host.rs
1//! Host-side reference arithmetic for the scalable datapath.
2//!
3//! These functions run on the build/test host, never in hardware. They
4//! generate ROM contents (the fixed-base table and constants) and give the
5//! microcode interpreter bit-exact semantics: every field value is canonical,
6//! strictly below `p = 2^255 - 19`, exactly as the RHDL multiplier and
7//! add/subtract units produce.
8//!
9//! The module also holds the reference curve model ([`Ext`], [`Niels`]),
10//! point encoding and strict decoding, the small-order encoding list, and the
11//! mixed-radix scalar recoding ([`recode`]) with its digit layout
12//! ([`DIGITS`], [`digit_offset`], [`table_row`]). None of it is constant
13//! time; it only computes reference values.
14
15use std::sync::OnceLock;
16
17/// Canonical field element modulo `2^255 - 19`, four little-endian words.
18///
19/// Every operation returns a fully reduced value, so `==` on the words is
20/// field equality and the low bit is the Ed25519 sign.
21#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
22pub struct Fp(pub [u64; 4]);
23
24/// The field prime `p = 2^255 - 19` as four little-endian words.
25const P: [u64; 4] = [
26 0xffff_ffff_ffff_ffed,
27 0xffff_ffff_ffff_ffff,
28 0xffff_ffff_ffff_ffff,
29 0x7fff_ffff_ffff_ffff,
30];
31
32/// `a >= b` for four-word little-endian integers.
33fn ge(a: &[u64; 4], b: &[u64; 4]) -> bool {
34 // Compare from the most significant word down.
35 for i in (0..4).rev() {
36 if a[i] != b[i] {
37 return a[i] > b[i];
38 }
39 }
40 true
41}
42
43/// `a - b mod 2^256` for four-word little-endian integers.
44fn sub_raw(a: &[u64; 4], b: &[u64; 4]) -> [u64; 4] {
45 let mut out = [0u64; 4];
46 let mut borrow = 0u64;
47 for i in 0..4 {
48 // Borrow out of this word if either subtraction wrapped.
49 let (d, b1) = a[i].overflowing_sub(b[i]);
50 let (d, b2) = d.overflowing_sub(borrow);
51 out[i] = d;
52 borrow = u64::from(b1 || b2);
53 }
54 out
55}
56
57impl Fp {
58 /// Zero.
59 pub const ZERO: Fp = Fp([0; 4]);
60 /// One.
61 pub const ONE: Fp = Fp([1, 0, 0, 0]);
62
63 /// Reduce an arbitrary 512-bit little-endian value.
64 fn reduce512(w: [u64; 8]) -> Fp {
65 // 2^256 = 38 (mod p).
66 // Fold the high 256 bits into the low ones: acc[i] = w[i] + 38 w[i+4]
67 // (acc[4] stays zero).
68 let mut acc = [0u128; 5];
69 for i in 0..4 {
70 acc[i] += u128::from(w[i]) + u128::from(w[i + 4]) * 38;
71 }
72 // Propagate carries into 64-bit limbs; limbs[4] is small (below 40).
73 let mut limbs = [0u64; 5];
74 let mut carry = 0u128;
75 for i in 0..4 {
76 let v = acc[i] + carry;
77 limbs[i] = v as u64;
78 carry = v >> 64;
79 }
80 limbs[4] = carry as u64;
81 // Fold bit 255 and above: value = lo255 + top * 2^255, 2^255 = 19.
82 let mut v = [limbs[0], limbs[1], limbs[2], limbs[3]];
83 let top = (u128::from(limbs[4]) << 1) | u128::from(v[3] >> 63);
84 v[3] &= 0x7fff_ffff_ffff_ffff;
85 let mut c = top * 19;
86 for limb in &mut v {
87 let s = u128::from(*limb) + c;
88 *limb = s as u64;
89 c = s >> 64;
90 }
91 // The value is now below 2^255 + a small amount; subtract p until
92 // canonical.
93 let mut out = Fp(v);
94 while ge(&out.0, &P) {
95 out = Fp(sub_raw(&out.0, &P));
96 }
97 out
98 }
99
100 /// Canonical value from 32 little-endian bytes (reduced modulo `p`).
101 ///
102 /// Bit 255 is included in the value, so callers decoding a point must
103 /// clear the sign bit first.
104 pub fn from_bytes(bytes: &[u8; 32]) -> Fp {
105 let mut w = [0u64; 8];
106 for i in 0..4 {
107 w[i] = u64::from_le_bytes(bytes[i * 8..i * 8 + 8].try_into().unwrap());
108 }
109 Fp::reduce512(w)
110 }
111
112 /// Canonical little-endian encoding.
113 pub fn to_bytes(self) -> [u8; 32] {
114 let mut out = [0u8; 32];
115 for i in 0..4 {
116 out[i * 8..i * 8 + 8].copy_from_slice(&self.0[i].to_le_bytes());
117 }
118 out
119 }
120
121 /// Sum modulo `p`.
122 pub fn add(self, o: Fp) -> Fp {
123 // 257-bit sum, reduced by the general path.
124 let mut w = [0u64; 8];
125 let mut carry = 0u128;
126 for i in 0..4 {
127 let s = u128::from(self.0[i]) + u128::from(o.0[i]) + carry;
128 w[i] = s as u64;
129 carry = s >> 64;
130 }
131 w[4] = carry as u64;
132 Fp::reduce512(w)
133 }
134
135 /// Difference modulo `p`.
136 pub fn sub(self, o: Fp) -> Fp {
137 // -o as p - o, except that -0 is 0 (p itself is not canonical).
138 let neg = if o == Fp::ZERO {
139 Fp::ZERO
140 } else {
141 Fp(sub_raw(&P, &o.0))
142 };
143 self.add(neg)
144 }
145
146 /// Product modulo `p`.
147 pub fn mul(self, o: Fp) -> Fp {
148 // Schoolbook 256 x 256 -> 512-bit product, one 64-bit word per entry
149 // of `w` (u128 entries hold intermediate sums).
150 let mut w = [0u128; 8];
151 for i in 0..4 {
152 let mut carry = 0u128;
153 for j in 0..4 {
154 let t = w[i + j] + u128::from(self.0[i]) * u128::from(o.0[j]) + carry;
155 w[i + j] = t & u128::from(u64::MAX);
156 carry = t >> 64;
157 }
158 w[i + 4] += carry;
159 }
160 Fp::reduce512(w.map(|x| x as u64))
161 }
162
163 /// Square modulo `p`.
164 pub fn square(self) -> Fp {
165 self.mul(self)
166 }
167
168 /// `self^(2^k)`.
169 pub fn pow2k(self, k: u32) -> Fp {
170 let mut x = self;
171 for _ in 0..k {
172 x = x.square();
173 }
174 x
175 }
176
177 /// Multiplicative inverse (zero maps to zero).
178 ///
179 /// Fermat's little theorem, `self^(p-2)`, by plain square-and-multiply.
180 pub fn invert(self) -> Fp {
181 // p - 2 = 2^255 - 21.
182 let mut result = Fp::ONE;
183 let e: [u64; 4] = sub_raw(&P, &[2, 0, 0, 0]);
184 // Exponent bits from the top (bit 254) down.
185 for i in (0..255).rev() {
186 result = result.square();
187 if (e[i / 64] >> (i % 64)) & 1 == 1 {
188 result = result.mul(self);
189 }
190 }
191 result
192 }
193
194 /// Negation modulo `p`.
195 pub fn neg(self) -> Fp {
196 Fp::ZERO.sub(self)
197 }
198
199 /// Least-significant bit of the canonical value (Ed25519 "negative").
200 pub fn is_negative(self) -> bool {
201 self.0[0] & 1 == 1
202 }
203
204 /// Fifteen 17-bit digits of the canonical value, least significant first.
205 ///
206 /// This is the digit layout of the RHDL field units (15 x 17 = 255 bits).
207 pub fn digits17(self) -> [u32; 15] {
208 let mut out = [0u32; 15];
209 for (k, digit) in out.iter_mut().enumerate() {
210 let mut v = 0u32;
211 for b in 0..17 {
212 // Absolute bit index of bit `b` of digit `k`.
213 let bit = 17 * k + b;
214 v |= (((self.0[bit / 64] >> (bit % 64)) & 1) as u32) << b;
215 }
216 *digit = v;
217 }
218 out
219 }
220
221 /// Inverse of [`Self::digits17`]; the digits must encode a value below `p`.
222 ///
223 /// The value is not reduced, so out-of-range digits give a non-canonical
224 /// `Fp`.
225 pub fn from_digits17(d: &[u32; 15]) -> Fp {
226 let mut w = [0u64; 4];
227 for (k, digit) in d.iter().enumerate() {
228 for b in 0..17 {
229 if (digit >> b) & 1 == 1 {
230 let bit = 17 * k + b;
231 w[bit / 64] |= 1 << (bit % 64);
232 }
233 }
234 }
235 Fp(w)
236 }
237
238 /// A small integer as a field element (canonical because `v < p`).
239 fn from_u64(v: u64) -> Fp {
240 Fp([v, 0, 0, 0])
241 }
242}
243
244/// Edwards curve constant `d = -121665/121666`.
245pub fn edwards_d() -> Fp {
246 Fp::from_u64(121665)
247 .neg()
248 .mul(Fp::from_u64(121666).invert())
249}
250
251/// `2d`, the constant the addition formulas and the Niels form use.
252pub fn edwards_d2() -> Fp {
253 let d = edwards_d();
254 d.add(d)
255}
256
257/// `sqrt(-1)`, i.e. `2^((p-1)/4)`.
258///
259/// 2 is a non-square modulo `p`, so `2^((p-1)/2) = -1` and its square root
260/// `2^((p-1)/4)` is a square root of `-1`.
261pub fn sqrt_m1() -> Fp {
262 // (p - 1)/4 = 2^253 - 5.
263 let mut result = Fp::ONE;
264 let two = Fp::from_u64(2);
265 let e: [u64; 4] = [
266 0xffff_ffff_ffff_fffb,
267 0xffff_ffff_ffff_ffff,
268 0xffff_ffff_ffff_ffff,
269 0x1fff_ffff_ffff_ffff,
270 ];
271 // Square-and-multiply from bit 252 down.
272 for i in (0..253).rev() {
273 result = result.square();
274 if (e[i / 64] >> (i % 64)) & 1 == 1 {
275 result = result.mul(two);
276 }
277 }
278 result
279}
280
281/// Extended twisted-Edwards point `(X:Y:Z:T)`.
282///
283/// Affine `x = X/Z`, `y = Y/Z`, on `-x^2 + y^2 = 1 + d x^2 y^2`.
284#[derive(Copy, Clone, Debug, PartialEq, Eq)]
285pub struct Ext {
286 /// X.
287 pub x: Fp,
288 /// Y.
289 pub y: Fp,
290 /// Z.
291 pub z: Fp,
292 /// T = XY/Z.
293 pub t: Fp,
294}
295
296/// Affine Niels coordinates `(y+x, y-x, 2dxy)`.
297///
298/// The form the tables store: a mixed addition with it needs no `Z` and no
299/// multiply by `2d`, and negation only swaps two coordinates and negates the
300/// third.
301#[derive(Copy, Clone, Debug, PartialEq, Eq)]
302pub struct Niels {
303 /// `y + x`.
304 pub ypx: Fp,
305 /// `y - x`.
306 pub ymx: Fp,
307 /// `2dxy`.
308 pub xy2d: Fp,
309}
310
311impl Niels {
312 /// Niels form of the identity `(0, 1)`: `(1, 1, 0)`.
313 pub fn identity() -> Self {
314 Niels {
315 ypx: Fp::ONE,
316 ymx: Fp::ONE,
317 xy2d: Fp::ZERO,
318 }
319 }
320 /// Negated point: `-(x, y) = (-x, y)` swaps `y+x` and `y-x` and negates
321 /// `2dxy`.
322 pub fn neg(self) -> Self {
323 Niels {
324 ypx: self.ymx,
325 ymx: self.ypx,
326 xy2d: self.xy2d.neg(),
327 }
328 }
329}
330
331impl Ext {
332 /// Negated point `(-X:Y:Z:-T)`.
333 pub fn neg(self) -> Ext {
334 Ext {
335 x: self.x.neg(),
336 y: self.y,
337 z: self.z,
338 t: self.t.neg(),
339 }
340 }
341
342 /// Identity `(0:1:1:0)`.
343 pub fn identity() -> Self {
344 Ext {
345 x: Fp::ZERO,
346 y: Fp::ONE,
347 z: Fp::ONE,
348 t: Fp::ZERO,
349 }
350 }
351
352 /// Ed25519 basepoint: `y = 4/5`, `x` non-negative (even).
353 pub fn basepoint() -> Self {
354 let y = Fp::from_u64(4).mul(Fp::from_u64(5).invert());
355 let x = recover_x(y, false).expect("basepoint");
356 Ext {
357 x,
358 y,
359 z: Fp::ONE,
360 t: x.mul(y),
361 }
362 }
363
364 /// Complete unified addition (Hisil et al., a = -1).
365 ///
366 /// Complete means it is correct for every pair of inputs, including
367 /// doubling and the identity, which keeps this reference model simple.
368 pub fn add(self, o: Ext) -> Ext {
369 let d2 = edwards_d2();
370 let a = self.y.sub(self.x).mul(o.y.sub(o.x));
371 let b = self.y.add(self.x).mul(o.y.add(o.x));
372 let c = self.t.mul(d2).mul(o.t);
373 let d = self.z.add(self.z).mul(o.z);
374 let e = b.sub(a);
375 let f = d.sub(c);
376 let g = d.add(c);
377 let h = b.add(a);
378 Ext {
379 x: e.mul(f),
380 y: g.mul(h),
381 z: f.mul(g),
382 t: e.mul(h),
383 }
384 }
385
386 /// Doubling, through the complete addition.
387 pub fn double(self) -> Ext {
388 self.add(self)
389 }
390
391 /// Affine coordinates `(X/Z, Y/Z)`.
392 pub fn affine(self) -> (Fp, Fp) {
393 let zi = self.z.invert();
394 (self.x.mul(zi), self.y.mul(zi))
395 }
396
397 /// Standard 32-byte compressed encoding: `y` little endian with the sign
398 /// of `x` in bit 255.
399 pub fn compress(self) -> [u8; 32] {
400 let (x, y) = self.affine();
401 let mut s = y.to_bytes();
402 // Bit 255 of a canonical y is zero, so XOR sets it.
403 s[31] ^= u8::from(x.is_negative()) << 7;
404 s
405 }
406
407 /// Affine Niels form.
408 pub fn niels(self) -> Niels {
409 let (x, y) = self.affine();
410 Niels {
411 ypx: y.add(x),
412 ymx: y.sub(x),
413 xy2d: edwards_d2().mul(x).mul(y),
414 }
415 }
416
417 /// Scalar multiplication by a little-endian 256-bit integer (host only).
418 ///
419 /// Double-and-add from the top bit, not constant time.
420 pub fn mul_bytes(self, k: &[u8; 32]) -> Ext {
421 let mut acc = Ext::identity();
422 for i in (0..256).rev() {
423 acc = acc.double();
424 if (k[i / 8] >> (i % 8)) & 1 == 1 {
425 acc = acc.add(self);
426 }
427 }
428 acc
429 }
430}
431
432/// The `x` with the given sign for which `(x, y)` is on the curve, or
433/// `None` if `y` has no such `x`.
434///
435/// From the curve equation, `x^2 = (y^2 - 1) / (d y^2 + 1)`. Returns
436/// `Some(0)` for `x = 0` even when `negative` is set; [`decode_point`]
437/// rejects that encoding.
438fn recover_x(y: Fp, negative: bool) -> Option<Fp> {
439 let d = edwards_d();
440 let yy = y.square();
441 // x^2 = u / v.
442 let u = yy.sub(Fp::ONE);
443 let v = d.mul(yy).add(Fp::ONE);
444 let x2 = u.mul(v.invert());
445 // x = x2^((p+3)/8), (p+3)/8 = 2^252 - 2.
446 // Because p = 5 (mod 8), this is a square root of x2 or of -x2.
447 let mut x = Fp::ONE;
448 let e: [u64; 4] = [
449 0xffff_ffff_ffff_fffe,
450 0xffff_ffff_ffff_ffff,
451 0xffff_ffff_ffff_ffff,
452 0x0fff_ffff_ffff_ffff,
453 ];
454 for i in (0..252).rev() {
455 x = x.square();
456 if (e[i / 64] >> (i % 64)) & 1 == 1 {
457 x = x.mul(x2);
458 }
459 }
460 // A root of -x2: multiply by sqrt(-1) to get a root of x2.
461 if x.square() != x2 {
462 x = x.mul(sqrt_m1());
463 }
464 // Still not a root: x2 is not a square, so no point has this y.
465 if x.square() != x2 {
466 return None;
467 }
468 // Pick the root whose low bit matches the requested sign.
469 if x.is_negative() != negative {
470 x = x.neg();
471 }
472 Some(x)
473}
474
475/// Digits of the fixed-base recoding (one table per digit).
476pub const DIGITS: usize = 54;
477/// The low `WIDE_DIGITS` digits are signed radix 32, the rest radix 16.
478///
479/// 39 * 5 + 15 * 4 = 255 bits. The split balances the machine's slots: a
480/// radix-32 lookup scans 8 entry pairs and a radix-16 one 4, every digit
481/// costs one 7-multiply mixed addition, and with this split scans and
482/// multiplies both total about 375 per fixed-base multiplication.
483pub const WIDE_DIGITS: usize = 39;
484/// Table rows (entry pairs) over all digits: 39 * 8 + 15 * 4 = 372.
485pub const TABLE_ROWS: usize = WIDE_DIGITS * 8 + (DIGITS - WIDE_DIGITS) * 4;
486
487/// Bit width of digit `i`: 5 for radix 32, 4 for radix 16.
488pub const fn digit_width(i: usize) -> u32 {
489 if i < WIDE_DIGITS { 5 } else { 4 }
490}
491
492/// Bit offset of digit `i`: the sum of the widths of digits `0..i`.
493pub const fn digit_offset(i: usize) -> u32 {
494 if i < WIDE_DIGITS {
495 5 * i as u32
496 } else {
497 5 * WIDE_DIGITS as u32 + 4 * (i - WIDE_DIGITS) as u32
498 }
499}
500
501/// Table entries of digit `i` (magnitudes `1..=entries`).
502///
503/// A signed width-`w` digit has magnitude at most `2^(w-1)`: 16 entries for
504/// radix 32, 8 for radix 16. Zero needs no entry (it selects the identity).
505pub const fn digit_entries(i: usize) -> usize {
506 1 << (digit_width(i) - 1)
507}
508
509/// First table row (entry pair) of digit `i`.
510///
511/// Rows are packed digit after digit: 8 per radix-32 digit, 4 per radix-16
512/// digit.
513pub const fn table_row(i: usize) -> usize {
514 if i < WIDE_DIGITS {
515 8 * i
516 } else {
517 8 * WIDE_DIGITS + 4 * (i - WIDE_DIGITS)
518 }
519}
520
521/// `table[i][e-1] = e * 2^offset(i) * B` in affine Niels form.
522///
523/// Built once on first use (it takes many host-side inversions) and shared.
524pub fn basepoint_table() -> &'static [Vec<Niels>] {
525 static TABLE: OnceLock<Vec<Vec<Niels>>> = OnceLock::new();
526 TABLE.get_or_init(|| point_table(Ext::basepoint()))
527}
528
529/// `table[i][e-1] = e * 2^offset(i) * P` in affine Niels form: the
530/// fixed-base table of any point (the base point, or a verification key).
531pub fn point_table(p: Ext) -> Vec<Vec<Niels>> {
532 // 2^offset(i) * P for the current digit.
533 let mut base = p;
534 let mut table = Vec::with_capacity(DIGITS);
535 for i in 0..DIGITS {
536 let mut row = Vec::with_capacity(digit_entries(i));
537 // e * base, for e = 1, 2, ...
538 let mut multiple = base;
539 for _ in 0..digit_entries(i) {
540 row.push(multiple.niels());
541 multiple = multiple.add(base);
542 }
543 table.push(row);
544 // Advance to the next digit's base.
545 for _ in 0..digit_width(i) {
546 base = base.double();
547 }
548 }
549 table
550}
551
552/// Whether a 32-byte encoding has a canonical `y` (below `p`, sign bit
553/// ignored).
554pub fn canonical_y(bytes: &[u8; 32]) -> bool {
555 let mut y = *bytes;
556 y[31] &= 0x7f;
557 // Compare with p = 2^255 - 19, most significant byte first.
558 let mut p = [0xffu8; 32];
559 p[0] = 0xed;
560 p[31] = 0x7f;
561 for i in (0..32).rev() {
562 if y[i] != p[i] {
563 return y[i] < p[i];
564 }
565 }
566 // y == p is not canonical.
567 false
568}
569
570/// Strict point decoding: canonical `y`, a square root exists, and not the
571/// `x = 0` encoding with the sign bit set. Same rules as the strict
572/// verifier of `rhdl_ed25519_fast_core`.
573pub fn decode_point(bytes: &[u8; 32]) -> Option<Ext> {
574 if !canonical_y(bytes) {
575 return None;
576 }
577 // Bit 255 is the sign of x; the rest is y.
578 let negative = bytes[31] >> 7 == 1;
579 let mut yb = *bytes;
580 yb[31] &= 0x7f;
581 let y = Fp::from_bytes(&yb);
582 let x = recover_x(y, negative)?;
583 // "Negative zero" has no point of its own; reject it.
584 if x == Fp::ZERO && negative {
585 return None;
586 }
587 Some(Ext {
588 x,
589 y,
590 z: Fp::ONE,
591 t: x.mul(y),
592 })
593}
594
595/// The eight encodings of low-order points that strict verification
596/// rejects (the list `rhdl_ed25519_fast_core` checks).
597///
598/// These are the canonical encodings of the eight points of the torsion
599/// subgroup (order dividing 8), compared as four little-endian 64-bit
600/// words. With a small-order key, `[k]A` takes at most eight values, so a
601/// signature can be forged without the secret key; strict verifiers refuse
602/// these encodings.
603pub fn small_order_encoding(bytes: &[u8; 32]) -> bool {
604 const WORDS: [[u64; 4]; 8] = [
605 // y = 1: the identity (order 1).
606 [1, 0, 0, 0],
607 // An order-8 point, x non-negative.
608 [0x4fd84d3d706a17c7, 0x0f67100d760b3cba, 0xc6cc392cfa53202a, 0x7a03ac9277fdc74e],
609 // y = 0 with the sign bit set: the order-4 point whose x is odd.
610 [0, 0, 0, 0x8000_0000_0000_0000],
611 // An order-8 point, x non-negative.
612 [0xb027b2c28f95e826, 0xf098eff289f4c345, 0x3933c6d305acdfd5, 0x05fc536d880238b1],
613 // y = p - 1 = -1: the point (0, -1) (order 2).
614 [0xffff_ffff_ffff_ffec, 0xffff_ffff_ffff_ffff, 0xffff_ffff_ffff_ffff, 0x7fff_ffff_ffff_ffff],
615 // The previous order-8 y with the sign bit set.
616 [0xb027b2c28f95e826, 0xf098eff289f4c345, 0x3933c6d305acdfd5, 0x85fc536d880238b1],
617 // y = 0 with the sign bit clear: the order-4 point whose x is even.
618 [0, 0, 0, 0],
619 // The first order-8 y with the sign bit set.
620 [0x4fd84d3d706a17c7, 0x0f67100d760b3cba, 0xc6cc392cfa53202a, 0xfa03ac9277fdc74e],
621 ];
622 let words: [u64; 4] = std::array::from_fn(|k| u64::from_le_bytes(bytes[8 * k..8 * k + 8].try_into().unwrap()));
623 WORDS.contains(&words)
624}
625
626/// Signed mixed-radix recoding into [`DIGITS`] digits (the rest of the
627/// array is zero): digit `i` lies in `[-2^(w-1), 2^(w-1)]` for its width `w`.
628///
629/// The scalar must be below `2^253` (every scalar the signer multiplies is
630/// reduced modulo `l`), which keeps the top digit in `[0, 4]`.
631///
632/// Signed digits halve the table: magnitudes run to `2^(w-1)` instead of
633/// `2^w - 1`, and the sign is applied by negating the selected entry. The
634/// array has 64 slots to match the machine's per-scalar digit slots.
635pub fn recode(scalar: &[u8; 32]) -> [i8; 64] {
636 assert!(scalar[31] < 32, "scalar must be below 2^253");
637 // Bit `k` of the scalar.
638 let bit = |k: u32| -> i32 { ((scalar[(k / 8) as usize] >> (k % 8)) & 1) as i32 };
639 let mut d = [0i8; 64];
640 // Carry out of the previous digit (0 or 1).
641 let mut carry = 0;
642 for i in 0..DIGITS {
643 let w = digit_width(i);
644 // The digit's w bits plus the incoming carry, in 0..=2^w.
645 let mut v = carry;
646 for b in 0..w {
647 v += bit(digit_offset(i) + b) << b;
648 }
649 // Values from 2^(w-1) up become v - 2^w (in [-2^(w-1), 0]) and carry
650 // 1 into the next digit. The top digit keeps its value: the scalar
651 // bound keeps it at most 4.
652 if i + 1 < DIGITS && v >= 1 << (w - 1) {
653 d[i] = (v - (1 << w)) as i8;
654 carry = 1;
655 } else {
656 d[i] = v as i8;
657 carry = 0;
658 }
659 }
660 d
661}
662
663/// Ed25519 group order `l = 2^252 + 27742317777372353535851937790883648493`,
664/// little endian.
665pub const L_BYTES: [u8; 32] = [
666 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
667 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10,
668];