literate_crypto/pubkey/ecc/
num.rs

1use {
2    crate::{
3        ecc::{Curve, Point},
4        util,
5    },
6    docext::docext,
7    std::{cmp, iter, mem, ops},
8};
9
10/// Number used for modular arithmetic. Internally stored in little-endian
11/// (least-significant byte first) format.
12#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
13pub struct Num([u64; Self::WIDTH]);
14
15impl Num {
16    pub const ZERO: Num = Num([0, 0, 0, 0]);
17    pub const ONE: Num = Num([1, 0, 0, 0]);
18    pub const TWO: Num = Num([2, 0, 0, 0]);
19    pub const THREE: Num = Num([3, 0, 0, 0]);
20    pub const SEVEN: Num = Num([7, 0, 0, 0]);
21
22    /// The size of this number in 64-bit words.
23    pub const WIDTH: usize = 4;
24    /// The size of this number in bits.
25    pub const BITS: usize = Self::WIDTH * u64::BITS as usize;
26    /// The size of this number in bytes.
27    pub const BYTES: usize = Self::BITS / 8;
28
29    pub const fn from_le_words(n: [u64; Self::WIDTH]) -> Self {
30        Self(n)
31    }
32
33    pub fn from_le_bytes(b: [u8; Self::BYTES]) -> Self {
34        const S: usize = mem::size_of::<u64>();
35        Self::from_le_words([
36            u64::from_le_bytes(b[..S].try_into().unwrap()),
37            u64::from_le_bytes(b[S..2 * S].try_into().unwrap()),
38            u64::from_le_bytes(b[2 * S..3 * S].try_into().unwrap()),
39            u64::from_le_bytes(b[3 * S..4 * S].try_into().unwrap()),
40        ])
41    }
42
43    pub fn to_le_bytes(&self) -> [u8; Self::BYTES] {
44        let mut result = [0u8; Self::BYTES];
45        result
46            .iter_mut()
47            .zip(self.0.iter().flat_map(|n| n.to_le_bytes()))
48            .for_each(|(a, b)| *a = b);
49        result
50    }
51
52    /// Modular addition with modulus `p`.
53    #[must_use]
54    pub fn add(&self, n: Self, p: Self) -> Self {
55        let (n, carry) = add(self.0, n.0);
56        if carry.0 {
57            // To account for the carry bit, extend n by a single most significant byte
58            // equal to 1, then do the reduction.
59            let mut ext = [0; Self::WIDTH + 1];
60            ext.iter_mut()
61                .zip(n.into_iter().chain(iter::once(1)))
62                .for_each(|(a, b)| *a = b);
63            Self(reduce(ext, p.0))
64        } else {
65            Self(reduce(n, p.0))
66        }
67    }
68
69    /// Modular subtraction with modulus `p`.
70    #[must_use]
71    pub fn sub(self, n: Self, p: Self) -> Self {
72        let (n, borrow) = sub(self.0, n.0);
73        if borrow.0 {
74            // If there was a borrow, then the result is negative, so add MOD to
75            // make it positive. This addition is guaranteed to result in a carry, and the
76            // carry and borrow bits "cancel" each other out. Note that adding
77            // MOD in a prime field modulus MOD is a no-op, and also note that
78            // self and rhs are both already reduced modulus MOD before the
79            // subtraction.
80            let (add, carry) = add(n, p.0);
81            assert!(carry.0);
82            Self(add)
83        } else {
84            Self(n)
85        }
86    }
87
88    /// Modular multiplication with modulus `p`.
89    #[must_use]
90    pub fn mul(self, n: Self, p: Self) -> Self {
91        // Same as multiplication on paper.
92        let mut prod = [0; Self::WIDTH * 2];
93        for (i, a) in self.0.into_iter().enumerate() {
94            let mut carry = 0u128;
95            for (j, b) in n.0.into_iter().enumerate() {
96                let m = prod[i + j] as u128 + a as u128 * b as u128 + carry;
97                // The upper u64::BITS are the carry part.
98                carry = (m & ((u64::MAX as u128) << u64::BITS)) >> u64::BITS;
99                // The lower u64::BITS are the digit to store at i + j.
100                prod[i + j] = u64::try_from(m & u64::MAX as u128).unwrap();
101            }
102            // The final carry becomes the next digit over.
103            prod[i + Self::WIDTH] = u64::try_from(carry).unwrap();
104        }
105        Self(reduce(prod, p.0))
106    }
107
108    /// Modular equality with modulus `p`.
109    pub fn eq(self, n: Self, p: Self) -> bool {
110        reduce(self.0, p.0) == reduce(n.0, p.0)
111    }
112
113    /// Reduction modulo `p`.
114    pub fn reduce(self, p: Self) -> Self {
115        Self(reduce(self.0, p.0))
116    }
117
118    /// Get the modular multiplicative inverse of the number by using the
119    /// extended Euclidean algorithm. Returns `None` for [`Num::ZERO`],
120    /// since 0 has no inverse.
121    ///
122    /// The non-extended Euclidean algorithm computes the greatest common
123    /// divisor $gcd(a, b)$ given $a, b, a \leq b$. It relies on the following
124    /// fact: $gcd(a, b) = gcd(b - \lfloor \frac{b}{a} \rfloor a, a)$. This fact
125    /// allows the algorithm to successively reduce the values of $a$ and
126    /// $b$ until one is eventually equal to zero, and the other is equal to
127    /// the greatest common divisor. The algorithm operates as follows:
128    ///
129    /// - Set $u = a, v = b$. The algorithm maintains the invariant that $u \leq
130    ///   v$.
131    /// - Iteratively update $u$ and $v$. First, get the quotient $q = \lfloor
132    ///   \frac{v}{u} \rfloor$, then set the new values $v' = u, u' = v - qu$.
133    ///   Note that $v - qu$ is the remainder from dividing $v$ by $u$. Call
134    ///   this remainder $r$, so that $u = r$.
135    /// - Terminate when $u = 0$. $v$ is the greatest common divisor.
136    ///
137    /// To extend the algorithm above, apply Bezout's identity. This identity
138    /// states that, given two integers $a$ and $b$ with greatest common
139    /// divisor $d$, there exist integers $x$ and $y$ such that $ax + by =
140    /// d$.
141    ///
142    /// The extended algorithm will represent $u$ and $v$ as
143    ///
144    /// $$
145    /// u = x_1a + y_1b \\
146    /// v = x_2a + y_2b
147    /// $$
148    ///
149    /// Since $u$ should be initialized to $a$, and $v$ should
150    /// be initialized to $b$, the initial values for $x_{1, 2}$ and $y_{1,
151    /// 2}$ are $x_1 = 1, y_1 = 0, x_2 = 0, y_2 = 1$.
152    ///
153    /// The rest of the algorithm is exactly the same, except that apart from
154    /// updating $u$ and $v$ like the regular Euclidean algorithm, the
155    /// extended Euclidean algorithm also updates $x_{1, 2}$ and $y_{1, 2}$.
156    /// This is done as follows:
157    ///
158    /// $$
159    /// x_2' = x_1 \\
160    /// x_1' = x_2 - qx_1 \\
161    /// y_2' = y_1 \\
162    /// y_1' = y_2 - qy_1
163    /// $$
164    ///
165    /// Where $q = \lfloor \frac{v}{u} \rfloor$ is the quotient and $r = v - qu$
166    /// is the remainder, same as in the non-extended Euclidean algorithm. It is
167    /// not difficult to verify that the values for $x_{1, 2}'$ and $y_{1, 2}'$
168    /// are correct. Namely, it must be true that $v' = u$ and $u' = r$ as in
169    /// the non-extended Euclidean algorithm. This can be shown with a few
170    /// substitutions:
171    ///
172    /// $$
173    /// v' = x_2'a + y_2'b \\
174    /// v' = x_1a + y_1 b \\
175    /// v' = u
176    /// $$
177    ///
178    /// And
179    ///
180    /// $$
181    /// u' = x_1'a + y_1'b \\
182    /// u' = (x_2 - qx_1)a + (y_2 - qy_1)b \\
183    /// u' = ax_2 + by_2 - (ax_1 + by_1)q \\
184    /// u' = v - qu
185    /// $$
186    ///
187    /// So $v' = u, u' = v - qu$ as expected. The algorithm terminates when $u =
188    /// 0$, at which point $x_2$, $y_2$, and $v$ are the result of the
189    /// algorithm.
190    ///
191    /// Finally, the above can be used to get a multiplicative inverse. If $b$
192    /// (or $a$) is prime, the result of the algorithm will be $v = 1$ because
193    /// the greatest common divisor between a prime number and any other
194    /// number is 1.
195    ///
196    /// $$
197    /// v = 1 = x_2a + y_2b
198    /// $$
199    ///
200    /// If the operations are done in a prime field with order $b$, then
201    ///
202    /// $$
203    /// y_2b \equiv 0 \pmod b \implies v \equiv x_2a \equiv 1 \pmod b
204    /// $$
205    ///
206    /// This means that $x_2$ is the multiplicative inverse of $a$
207    /// in the prime field with order $b$. Finally, since $y_1$ and $y_2$ are
208    /// not used, they can be omitted from the algorithm as a small
209    /// optimization.
210    #[docext]
211    #[must_use]
212    pub fn inv(&self, p: Self) -> Option<Self> {
213        if *self == Self::ZERO {
214            return None;
215        }
216
217        let mut u = reduce(self.0, p.0);
218        let mut v = p.0;
219        let mut x1 = Self::ONE;
220        let mut x2 = Self::ZERO;
221        while u != Self::ZERO.0 {
222            let (q, r) = div(v, u);
223            v = u;
224            u = r.0;
225            let x = x2.sub(Self(q).mul(x1, p), p);
226            x2 = x1;
227            x1 = x;
228        }
229        Some(x2)
230    }
231
232    /// Get the bit at the given index. The rightmost (least significant) bit is
233    /// at index 0.
234    pub fn get_bit(&self, i: usize) -> bool {
235        get_bit(self.0, i)
236    }
237}
238
239impl cmp::PartialOrd for Num {
240    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
241        Some(self.cmp(other))
242    }
243}
244
245impl cmp::Ord for Num {
246    fn cmp(&self, other: &Self) -> cmp::Ordering {
247        // Compare the digits in most-significant-first order.
248        for (a, b) in self.0.iter().zip(other.0.iter()).rev() {
249            match a.cmp(b) {
250                cmp::Ordering::Less => return cmp::Ordering::Less,
251                cmp::Ordering::Equal => {}
252                cmp::Ordering::Greater => return cmp::Ordering::Greater,
253            }
254        }
255        cmp::Ordering::Equal
256    }
257}
258
259/// Flag to indicate if a subtraction resulted in a borrow.
260#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261struct Borrow(bool);
262
263/// Flag to indicate if an addition resulted in a carry.
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265struct Carry(bool);
266
267/// The remainder left after a division.
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269struct Rem<const N: usize>([u64; N]);
270
271/// Subtract two numbers.
272#[must_use]
273fn sub<const N: usize>(a: [u64; N], b: [u64; N]) -> ([u64; N], Borrow) {
274    // The easiest way to understand this code is to do subtractions on paper and
275    // watch how digits are borrowed from. Some examples to go through might be:
276    // 591 - 202, where the middle digit is borrowed from;
277    // 201 - 192, where the first two digits are borrowed from;
278    // 201 - 292, where the first two digits are borrowed from and the result
279    // includes a borrow bit.
280    // The only difference is that in this implementation digits range from 0 to
281    // u64::MAX, whereas on paper they range from 0 to 9, and they are stored in
282    // LSB-first order, whereas on paper they are MSB-first.
283    let mut borrow = false;
284    let mut result = [0; N];
285    for ((a, b), r) in a.iter().zip(&b).zip(result.iter_mut()) {
286        let (sub, overflow) = a.overflowing_sub(*b);
287        *r = sub;
288        if overflow {
289            if borrow {
290                // If the subtraction overflowed, and this digit was borrowed from, then
291                // subtract the borrow from the result. It is impossible for this subtraction to
292                // overflow, because the result must be at least 1 due to the previous
293                // subtraction having overflowed.
294                *r -= 1;
295            }
296            // The subtraction overflowed, so borrow from the next digit.
297            borrow = true;
298        } else {
299            // There was no overflow. Subtract the borrow bit.
300            let (sub, overflow) = r.overflowing_sub(borrow as u64);
301            *r = sub;
302            if overflow {
303                // If subtracting the borrow bit overflowed, then the next
304                // digit must be borrowed from. Don't clear the borrow bit.
305            } else {
306                // If subtracting the borrow bit did not overflow, then the
307                // borrow bit was either already clear or it was
308                // successfully "used", and hence can be cleared.
309                borrow = false;
310            }
311        }
312    }
313    (result, Borrow(borrow))
314}
315
316/// Add two numbers.
317#[must_use]
318fn add<const N: usize>(a: [u64; N], b: [u64; N]) -> ([u64; N], Carry) {
319    // Same as addition on paper.
320    let mut carry = false;
321    let mut result = [0; N];
322    for ((a, b), r) in a.iter().zip(&b).zip(result.iter_mut()) {
323        let (add, overflow) = a.overflowing_add(*b);
324        *r = add;
325        if carry {
326            // If the carry bit is set, increment the result by one. If this operation
327            // overflows, set the carry bit. If it doesn't overflow, then the
328            // carry bit was successfully "used", so clear it.
329            let (add, overflow) = r.overflowing_add(1);
330            *r = add;
331            carry = overflow;
332        }
333        if overflow {
334            // If the original addition overflowed, there was a carry. This is true
335            // regardless of the current state of the carry bit.
336            carry = true;
337        }
338    }
339    (result, Carry(carry))
340}
341
342/// Divide two numbers.
343#[must_use]
344fn div<const N: usize>(n: [u64; N], d: [u64; N]) -> ([u64; N], Rem<N>) {
345    // This is an implementation of long division. It's the same as long division
346    // done on paper, except it's done in base 2 instead of base 10. The easiest
347    // way to understand the algorithm is to do an example on paper in base ten,
348    // e.g. 587 / 342, and see how the base 2 algorithm below corresponds to the
349    // base 10 algorithm done on paper.
350    //
351    // The long division algorithm can be roughly explained in words as follows:
352    // keep track of a running remainder. For each digit of the dividend, append
353    // the digit to the running remainder. Count how many times the divisor can
354    // be subtracted from the running remainder, do the subtractions, and append
355    // the count to the result as a single digit. Note that the count may be zero.
356    // The algorithm finishes when there are no more digits in the dividend,
357    // resulting in a quotient and a remainder.
358    let mut q = [0; N];
359    let mut r = [0; N];
360    for i in (0..N * u64::BITS as usize).rev() {
361        r = shl(r);
362        if get_bit(n, i) {
363            r = set_bit(r, 0);
364        }
365        let (sub, borrow) = sub(r, d);
366        if !borrow.0 {
367            // The subtraction didn't require a borrow, which means that r >= d, i.e. the
368            // subtraction was successful.
369            r = sub;
370            // Because this is long division in base 2, only a 1 or a 0 can be appended
371            // to the result. In case of successful division, a 1 is appended, and at most
372            // one subtraction is made to the running remainder. This is different from long
373            // division in base 10, where any digit from 0 to 9 can be appended,
374            // and at most nine subtractions could be made (although in practice
375            // a human does not subtract 9 times, instead he divides two small numbers in
376            // his head).
377            q = set_bit(q, i);
378        }
379    }
380    (q, Rem(r))
381}
382
383/// Reduce a number modulo another number.
384#[must_use]
385fn reduce<const N: usize, const P: usize>(n: [u64; N], p: [u64; P]) -> [u64; P] {
386    assert!(N >= P);
387    let (_div, rem) = div(n, util::resize(p));
388    util::resize(rem.0)
389}
390
391/// Shift all of the bits left by one.
392#[must_use]
393fn shl<const N: usize>(n: [u64; N]) -> [u64; N] {
394    let mut res = [0; N];
395    let mut msb = false;
396    for (i, digit) in n.into_iter().enumerate() {
397        res[i] = digit.wrapping_shl(1);
398        // If the most significant bit was shifted out of the previous digit, the next
399        // digit should have the least significant bit set after the shift.
400        if msb {
401            res[i] |= 1;
402        }
403        msb = digit & (1 << (u64::BITS - 1)) != 0;
404    }
405    res
406}
407
408/// Get the bit at the given index. The rightmost (least significant) bit is at
409/// index 0.
410#[must_use]
411fn get_bit<const N: usize>(n: [u64; N], i: usize) -> bool {
412    let digit = i / u64::BITS as usize;
413    let i = i % u64::BITS as usize;
414    n[digit] & (1 << i) != 0
415}
416
417/// Set the bit at the given index. Note that the rightmost bit is at index
418/// 0, the leftmost at index 255.
419#[must_use]
420fn set_bit<const N: usize>(mut n: [u64; N], i: usize) -> [u64; N] {
421    let digit = i / u64::BITS as usize;
422    let i = i % u64::BITS as usize;
423    n[digit] |= 1 << i;
424    n
425}
426
427/// Multiply the point by a scalar.
428///
429/// This uses the _square-and-multiply_ method. For example, to calculate
430/// $x^{19}$, start with $y = x$ and multiply $y$ with itself, resulting in $y =
431/// y \cdot y = x^2$. Then, multiply $y$ with itself again, resulting in
432/// $y = y \cdot y = x^4$. Repeat this until it can no longer be done, at which
433/// point $y = x^{16}$ and there have been four multiplications thus far.
434/// Finally, multiply $y$ with $x$ three more times to get the desired result.
435///
436/// With this method, $x^{19}$ was calculated in only seven multiplications,
437/// compared to the naive algorithm which would execute 19 multiplications.
438///
439/// In the case of elliptic curve points, the "square" is equivalent to
440/// doubling, and "multiply" is equivalent to addition.
441#[docext]
442impl<C: Curve> ops::Mul<Point<C>> for Num {
443    type Output = Point<C>;
444
445    fn mul(self, rhs: Point<C>) -> Self::Output {
446        rhs.scale(self)
447    }
448}