Skip to main content

curve25519_dalek/
montgomery.rs

1// -*- mode: rust; -*-
2//
3// This file is part of curve25519-dalek.
4// Copyright (c) 2016-2021 isis lovecruft
5// Copyright (c) 2016-2019 Henry de Valence
6// See LICENSE for licensing information.
7//
8// Authors:
9// - isis agora lovecruft <[email protected]>
10// - Henry de Valence <[email protected]>
11
12//! Scalar multiplication on the Montgomery form of Curve25519.
13//!
14//! To avoid notational confusion with the Edwards code, we use
15//! variables \\( u, v \\) for the Montgomery curve, so that “Montgomery
16//! \\(u\\)” here corresponds to “Montgomery \\(x\\)” elsewhere.
17//!
18//! Montgomery arithmetic works not on the curve itself, but on the
19//! \\(u\\)-line, which discards sign information and unifies the curve
20//! and its quadratic twist.  See [_Montgomery curves and their
21//! arithmetic_][costello-smith] by Costello and Smith for more details.
22//!
23//! The `MontgomeryPoint` struct contains the affine \\(u\\)-coordinate
24//! \\(u\_0(P)\\) of a point \\(P\\) on either the curve or the twist.
25//! Here the map \\(u\_0 : \mathcal M \rightarrow \mathbb F\_p \\) is
26//! defined by \\(u\_0((u,v)) = u\\); \\(u\_0(\mathcal O) = 0\\).  See
27//! section 5.4 of Costello-Smith for more details.
28//!
29//! # Scalar Multiplication
30//!
31//! Scalar multiplication on `MontgomeryPoint`s is provided by the `*`
32//! operator, which implements the Montgomery ladder.
33//!
34//! # Edwards Conversion
35//!
36//! The \\(2\\)-to-\\(1\\) map from the Edwards model to the Montgomery
37//! \\(u\\)-line is provided by `EdwardsPoint::to_montgomery()`.
38//!
39//! To lift a `MontgomeryPoint` to an `EdwardsPoint`, use
40//! `MontgomeryPoint::to_edwards()`, which takes a sign parameter.
41//! This function rejects `MontgomeryPoints` which correspond to points
42//! on the twist.
43//!
44//! [costello-smith]: https://eprint.iacr.org/2017/212.pdf
45
46// We allow non snake_case names because coordinates in projective space are
47// traditionally denoted by the capitalisation of their respective
48// counterparts in affine space.  Yeah, you heard me, rustc, I'm gonna have my
49// affine and projective cakes and eat both of them too.
50#![allow(non_snake_case)]
51
52use core::{
53    hash::{Hash, Hasher},
54    ops::{Mul, MulAssign},
55};
56
57use crate::constants::APLUS2_OVER_FOUR;
58#[cfg(feature = "digest")]
59use crate::constants::{MONTGOMERY_A, MONTGOMERY_A_NEG, SQRT_M1};
60use crate::edwards::{CompressedEdwardsY, EdwardsPoint};
61use crate::field::FieldElement;
62use crate::scalar::{Scalar, clamp_integer};
63
64use crate::traits::Identity;
65
66use subtle::Choice;
67use subtle::ConditionallySelectable;
68use subtle::ConstantTimeEq;
69
70#[cfg(feature = "zeroize")]
71use zeroize::Zeroize;
72
73// We need the const 2^((p+3)/8) for elligator_encode. These defs are checked in tests::consts()
74#[cfg(all(curve25519_dalek_bits = "32", feature = "digest"))]
75const FE_C2: FieldElement = FieldElement::from_limbs([
76    34513073, 25610706, 9377949, 3500415, 12389472, 33281959, 41962654, 31548777, 326685, 11406482,
77]);
78#[cfg(all(curve25519_dalek_bits = "64", feature = "digest"))]
79const FE_C2: FieldElement = FieldElement::from_limbs([
80    1718705420411057,
81    234908883556509,
82    2233514472574048,
83    2117202627021982,
84    765476049583133,
85]);
86
87/// Holds the \\(u\\)-coordinate of a point on the Montgomery form of
88/// Curve25519 or its twist.
89#[derive(Copy, Clone, Debug, Default)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct MontgomeryPoint(pub [u8; 32]);
92
93/// Equality of `MontgomeryPoint`s is defined mod p.
94impl ConstantTimeEq for MontgomeryPoint {
95    fn ct_eq(&self, other: &MontgomeryPoint) -> Choice {
96        let self_fe = FieldElement::from_bytes(&self.0);
97        let other_fe = FieldElement::from_bytes(&other.0);
98
99        self_fe.ct_eq(&other_fe)
100    }
101}
102
103impl ConditionallySelectable for MontgomeryPoint {
104    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
105        Self(<[u8; 32]>::conditional_select(&a.0, &b.0, choice))
106    }
107}
108
109impl PartialEq for MontgomeryPoint {
110    fn eq(&self, other: &MontgomeryPoint) -> bool {
111        self.ct_eq(other).into()
112    }
113}
114
115impl Eq for MontgomeryPoint {}
116
117// Equal MontgomeryPoints must hash to the same value. So we have to get them into a canonical
118// encoding first
119impl Hash for MontgomeryPoint {
120    fn hash<H: Hasher>(&self, state: &mut H) {
121        // Do a round trip through a `FieldElement`. `as_bytes` is guaranteed to give a canonical
122        // 32-byte encoding
123        let canonical_bytes = FieldElement::from_bytes(&self.0).to_bytes();
124        canonical_bytes.hash(state);
125    }
126}
127
128impl Identity for MontgomeryPoint {
129    /// Return the group identity element, which has order 4.
130    fn identity() -> MontgomeryPoint {
131        MontgomeryPoint([0u8; 32])
132    }
133}
134
135#[cfg(feature = "zeroize")]
136impl Zeroize for MontgomeryPoint {
137    fn zeroize(&mut self) {
138        self.0.zeroize();
139    }
140}
141
142impl MontgomeryPoint {
143    /// Fixed-base scalar multiplication (i.e. multiplication by the base point).
144    pub fn mul_base(scalar: &Scalar) -> Self {
145        EdwardsPoint::mul_base(scalar).to_montgomery()
146    }
147
148    /// Multiply this point by `clamp_integer(bytes)`. For a description of clamping, see
149    /// [`clamp_integer`].
150    pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
151        // We have to construct a Scalar that is not reduced mod l, which breaks scalar invariant
152        // #2. But #2 is not necessary for correctness of variable-base multiplication. All that
153        // needs to hold is invariant #1, i.e., the scalar is less than 2^255. This is guaranteed
154        // by clamping.
155        // Further, we don't do any reduction or arithmetic with this clamped value, so there's no
156        // issues arising from the fact that the curve point is not necessarily in the prime-order
157        // subgroup.
158        let s = Scalar {
159            bytes: clamp_integer(bytes),
160        };
161        s * self
162    }
163
164    /// Multiply the basepoint by `clamp_integer(bytes)`. For a description of clamping, see
165    /// [`clamp_integer`].
166    pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
167        // See reasoning in Self::mul_clamped why it is OK to make an unreduced Scalar here. We
168        // note that fixed-base multiplication is also defined for all values of `bytes` less than
169        // 2^255.
170        let s = Scalar {
171            bytes: clamp_integer(bytes),
172        };
173        Self::mul_base(&s)
174    }
175
176    /// Given `self` \\( = u\_0(P) \\), and a big-endian bit representation of an integer
177    /// \\(n\\), return \\( u\_0(\[n\]P) \\). This is constant time in the length of `bits`.
178    ///
179    /// **NOTE:** You probably do not want to use this function. Almost every protocol built on
180    /// Curve25519 uses _clamped multiplication_, explained
181    /// [here](https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/).
182    /// When in doubt, use [`Self::mul_clamped`].
183    pub fn mul_bits_be(&self, bits: impl Iterator<Item = bool>) -> MontgomeryPoint {
184        // Algorithm 8 of Costello-Smith 2017
185        let affine_u = FieldElement::from_bytes(&self.0);
186        let mut x0 = ProjectivePoint::identity();
187        let mut x1 = ProjectivePoint {
188            U: affine_u,
189            W: FieldElement::ONE,
190        };
191
192        // Go through the bits from most to least significant, using a sliding window of 2
193        let mut prev_bit = false;
194        for cur_bit in bits {
195            let choice: u8 = (prev_bit ^ cur_bit) as u8;
196
197            debug_assert!(choice == 0 || choice == 1);
198
199            ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
200            differential_add_and_double(&mut x0, &mut x1, &affine_u);
201
202            prev_bit = cur_bit;
203        }
204        // The final value of prev_bit above is scalar.bits()[0], i.e., the LSB of scalar
205        ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(prev_bit as u8));
206        // Don't leave the bit in the stack
207        #[cfg(feature = "zeroize")]
208        prev_bit.zeroize();
209
210        x0.as_affine()
211    }
212
213    /// View this `MontgomeryPoint` as an array of bytes.
214    pub const fn as_bytes(&self) -> &[u8; 32] {
215        &self.0
216    }
217
218    /// Convert this `MontgomeryPoint` to an array of bytes.
219    pub const fn to_bytes(&self) -> [u8; 32] {
220        self.0
221    }
222
223    /// Attempt to convert to an `EdwardsPoint`, using the supplied
224    /// choice of sign for the `EdwardsPoint`.
225    ///
226    /// # Inputs
227    ///
228    /// * `sign`: a `u8` donating the desired sign of the resulting
229    ///   `EdwardsPoint`.  `0` denotes positive and `1` negative.
230    ///
231    /// # Return
232    ///
233    /// * `Some(EdwardsPoint)` if `self` is the \\(u\\)-coordinate of a
234    ///   point on (the Montgomery form of) Curve25519;
235    ///
236    /// * `None` if `self` is the \\(u\\)-coordinate of a point on the
237    ///   twist of (the Montgomery form of) Curve25519;
238    ///
239    pub fn to_edwards(&self, sign: u8) -> Option<EdwardsPoint> {
240        // To decompress the Montgomery u coordinate to an
241        // `EdwardsPoint`, we apply the birational map to obtain the
242        // Edwards y coordinate, then do Edwards decompression.
243        //
244        // The birational map is y = (u-1)/(u+1).
245        //
246        // The exceptional points are the zeros of the denominator,
247        // i.e., u = -1.
248        //
249        // But when u = -1, v^2 = u*(u^2+486662*u+1) = 486660.
250        //
251        // Since this is nonsquare mod p, u = -1 corresponds to a point
252        // on the twist, not the curve, so we can reject it early.
253
254        let u = FieldElement::from_bytes(&self.0);
255
256        if u == FieldElement::MINUS_ONE {
257            return None;
258        }
259
260        let one = FieldElement::ONE;
261
262        let y = &(&u - &one) * &(&u + &one).invert();
263
264        let mut y_bytes = y.to_bytes();
265        y_bytes[31] ^= sign << 7;
266
267        CompressedEdwardsY(y_bytes).decompress()
268    }
269}
270
271#[cfg(feature = "digest")]
272/// Perform the Elligator2 mapping to a tuple `(xn, xd, yn, yd)` such that
273/// `(xn / xd, yn / yd)` is a point on curve25519.
274///
275/// See <https://www.rfc-editor.org/rfc/rfc9380.html#name-elligator-2-method>
276pub(crate) fn elligator_encode(
277    u: &FieldElement,
278) -> (FieldElement, FieldElement, FieldElement, FieldElement) {
279    // We follow https://www.rfc-editor.org/rfc/rfc9380.html#appendix-G.2.1
280
281    use core::ops::Neg;
282    let one = FieldElement::ONE;
283    let c2 = FE_C2;
284
285    // 1.  tv1 = u^2
286    // 2.  tv1 = 2 * tv1
287    let tv1 = u.square2();
288    // 3.   xd = tv1 + 1
289    let xd = &one + &tv1;
290    // 4.  x1n = -J
291    let x1n = MONTGOMERY_A_NEG;
292    // 5.  tv2 = xd^2
293    let tv2 = xd.square();
294    // 6.  gxd = tv2 * xd
295    let gxd = &tv2 * &xd;
296    // 7.  gx1 = J * tv1
297    let gx1 = &MONTGOMERY_A * &tv1;
298    // 8.  gx1 = gx1 * x1n
299    let gx1 = &gx1 * &x1n;
300    // 9.  gx1 = gx1 + tv2
301    let gx1 = &gx1 + &tv2;
302    // 10. gx1 = gx1 * x1n
303    let gx1 = &gx1 * &x1n;
304    // 11. tv3 = gxd^2
305    let tv3 = gxd.square();
306    // 12. tv2 = tv3^2
307    let tv2 = tv3.square();
308    // 13. tv3 = tv3 * gxd
309    let tv3 = &tv3 * &gxd;
310    // 14. tv3 = tv3 * gx1
311    let tv3 = &tv3 * &gx1;
312    // 15. tv2 = tv2 * tv3
313    let tv2 = &tv2 * &tv3;
314    // 16. y11 = tv2^c4
315    let y11 = tv2.pow_p58();
316    // 17. y11 = y11 * tv3
317    let y11 = &y11 * &tv3;
318    // 18. y12 = y11 * c3
319    let y12 = &y11 * &SQRT_M1;
320    // 19. tv2 = y11^2
321    let tv2 = y11.square();
322    // 20. tv2 = tv2 * gxd
323    let tv2 = &tv2 * &gxd;
324    // 21.  e1 = tv2 == gx1
325    let e1 = tv2.ct_eq(&gx1);
326    // 22.  y1 = CMOV(y12, y11, e1)
327    let y1 = FieldElement::conditional_select(&y12, &y11, e1);
328    // 23. x2n = x1n * tv1
329    let x2n = &x1n * &tv1;
330    // 24. y21 = y11 * u
331    let y21 = &y11 * u;
332    // 25. y21 = y21 * c2
333    let y21 = &y21 * &c2;
334    // 26. y22 = y21 * c3
335    let y22 = &y21 * &SQRT_M1;
336    // 27. gx2 = gx1 * tv1
337    let gx2 = &gx1 * &tv1;
338    // 28. tv2 = y21^2
339    let tv2 = y21.square();
340    // 29. tv2 = tv2 * gxd
341    let tv2 = &tv2 * &gxd;
342    // 30.  e2 = tv2 == gx2
343    let e2 = tv2.ct_eq(&gx2);
344    // 31.  y2 = CMOV(y22, y21, e2)
345    let y2 = FieldElement::conditional_select(&y22, &y21, e2);
346    // 32. tv2 = y1^2
347    let tv2 = y1.square();
348    // 33. tv2 = tv2 * gxd
349    let tv2 = &tv2 * &gxd;
350    // 34.  e3 = tv2 == gx1
351    let e3 = tv2.ct_eq(&gx1);
352    // 35.  xn = CMOV(x2n, x1n, e3)
353    let xn = FieldElement::conditional_select(&x2n, &x1n, e3);
354    // 36.   y = CMOV(y2, y1, e3)
355    let y = FieldElement::conditional_select(&y2, &y1, e3);
356    // 37.  e4 = sgn0(y) == 1
357    let e4 = y.is_negative();
358    // 38.   y = CMOV(y, -y, e3 XOR e4)
359    let y = FieldElement::conditional_select(&y, &y.neg(), e3 ^ e4);
360    // 39. return (xn, xd, y, 1)
361
362    (xn, xd, y, one)
363}
364
365/// A `ProjectivePoint` holds a point on the projective line
366/// \\( \mathbb P(\mathbb F\_p) \\), which we identify with the Kummer
367/// line of the Montgomery curve.
368#[derive(Copy, Clone, Debug)]
369struct ProjectivePoint {
370    pub U: FieldElement,
371    pub W: FieldElement,
372}
373
374impl Identity for ProjectivePoint {
375    fn identity() -> ProjectivePoint {
376        ProjectivePoint {
377            U: FieldElement::ONE,
378            W: FieldElement::ZERO,
379        }
380    }
381}
382
383impl Default for ProjectivePoint {
384    fn default() -> ProjectivePoint {
385        ProjectivePoint::identity()
386    }
387}
388
389impl ConditionallySelectable for ProjectivePoint {
390    fn conditional_select(
391        a: &ProjectivePoint,
392        b: &ProjectivePoint,
393        choice: Choice,
394    ) -> ProjectivePoint {
395        ProjectivePoint {
396            U: FieldElement::conditional_select(&a.U, &b.U, choice),
397            W: FieldElement::conditional_select(&a.W, &b.W, choice),
398        }
399    }
400}
401
402impl ProjectivePoint {
403    /// Dehomogenize this point to affine coordinates.
404    ///
405    /// # Return
406    ///
407    /// * \\( u = U / W \\) if \\( W \neq 0 \\);
408    /// * \\( 0 \\) if \\( W \eq 0 \\);
409    pub fn as_affine(&self) -> MontgomeryPoint {
410        let u = &self.U * &self.W.invert();
411        MontgomeryPoint(u.to_bytes())
412    }
413}
414
415/// Perform the double-and-add step of the Montgomery ladder.
416///
417/// Given projective points
418/// \\( (U\_P : W\_P) = u(P) \\),
419/// \\( (U\_Q : W\_Q) = u(Q) \\),
420/// and the affine difference
421/// \\(      u\_{P-Q} = u(P-Q) \\), set
422/// $$
423///     (U\_P : W\_P) \gets u(\[2\]P)
424/// $$
425/// and
426/// $$
427///     (U\_Q : W\_Q) \gets u(P + Q).
428/// $$
429#[rustfmt::skip] // keep alignment of explanatory comments
430fn differential_add_and_double(
431    P: &mut ProjectivePoint,
432    Q: &mut ProjectivePoint,
433    affine_PmQ: &FieldElement,
434) {
435    let t0 = &P.U + &P.W;
436    let t1 = &P.U - &P.W;
437    let t2 = &Q.U + &Q.W;
438    let t3 = &Q.U - &Q.W;
439
440    let t4 = t0.square();   // (U_P + W_P)^2 = U_P^2 + 2 U_P W_P + W_P^2
441    let t5 = t1.square();   // (U_P - W_P)^2 = U_P^2 - 2 U_P W_P + W_P^2
442
443    let t6 = &t4 - &t5;     // 4 U_P W_P
444
445    let t7 = &t0 * &t3;     // (U_P + W_P) (U_Q - W_Q) = U_P U_Q + W_P U_Q - U_P W_Q - W_P W_Q
446    let t8 = &t1 * &t2;     // (U_P - W_P) (U_Q + W_Q) = U_P U_Q - W_P U_Q + U_P W_Q - W_P W_Q
447
448    let t9  = &t7 + &t8;    // 2 (U_P U_Q - W_P W_Q)
449    let t10 = &t7 - &t8;    // 2 (W_P U_Q - U_P W_Q)
450
451    let t11 =  t9.square(); // 4 (U_P U_Q - W_P W_Q)^2
452    let t12 = t10.square(); // 4 (W_P U_Q - U_P W_Q)^2
453
454    let t13 = &APLUS2_OVER_FOUR * &t6; // (A + 2) U_P U_Q
455
456    let t14 = &t4 * &t5;    // ((U_P + W_P)(U_P - W_P))^2 = (U_P^2 - W_P^2)^2
457    let t15 = &t13 + &t5;   // (U_P - W_P)^2 + (A + 2) U_P W_P
458
459    let t16 = &t6 * &t15;   // 4 (U_P W_P) ((U_P - W_P)^2 + (A + 2) U_P W_P)
460
461    let t17 = affine_PmQ * &t12; // U_D * 4 (W_P U_Q - U_P W_Q)^2
462    let t18 = t11;               // W_D * 4 (U_P U_Q - W_P W_Q)^2
463
464    P.U = t14;  // U_{P'} = (U_P + W_P)^2 (U_P - W_P)^2
465    P.W = t16;  // W_{P'} = (4 U_P W_P) ((U_P - W_P)^2 + ((A + 2)/4) 4 U_P W_P)
466    Q.U = t18;  // U_{Q'} = W_D * 4 (U_P U_Q - W_P W_Q)^2
467    Q.W = t17;  // W_{Q'} = U_D * 4 (W_P U_Q - U_P W_Q)^2
468}
469
470define_mul_assign_variants!(LHS = MontgomeryPoint, RHS = Scalar);
471
472define_mul_variants!(
473    LHS = MontgomeryPoint,
474    RHS = Scalar,
475    Output = MontgomeryPoint
476);
477define_mul_variants!(
478    LHS = Scalar,
479    RHS = MontgomeryPoint,
480    Output = MontgomeryPoint
481);
482
483/// Multiply this `MontgomeryPoint` by a `Scalar`.
484impl Mul<&Scalar> for &MontgomeryPoint {
485    type Output = MontgomeryPoint;
486
487    /// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\)
488    fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
489        // We multiply by the integer representation of the given Scalar. By scalar invariant #1,
490        // the MSB is 0, so we can skip it.
491        self.mul_bits_be(scalar.bits_le().rev().skip(1))
492    }
493}
494
495impl MulAssign<&Scalar> for MontgomeryPoint {
496    fn mul_assign(&mut self, scalar: &Scalar) {
497        *self = (self as &MontgomeryPoint) * scalar;
498    }
499}
500
501impl Mul<&MontgomeryPoint> for &Scalar {
502    type Output = MontgomeryPoint;
503
504    fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint {
505        point * self
506    }
507}
508
509// ------------------------------------------------------------------------
510// Tests
511// ------------------------------------------------------------------------
512
513#[cfg(test)]
514mod test {
515    use super::*;
516    use crate::constants;
517    use getrandom::{
518        SysRng,
519        rand_core::{Rng, TryRng, UnwrapErr},
520    };
521
522    #[cfg(feature = "rand_core")]
523    use rand_core::CryptoRng;
524
525    #[test]
526    fn identity_in_different_coordinates() {
527        let id_projective = ProjectivePoint::identity();
528        let id_montgomery = id_projective.as_affine();
529
530        assert!(id_montgomery == MontgomeryPoint::identity());
531    }
532
533    #[test]
534    fn identity_in_different_models() {
535        assert!(EdwardsPoint::identity().to_montgomery() == MontgomeryPoint::identity());
536    }
537
538    #[test]
539    #[cfg(feature = "serde")]
540    fn serde_postcard_basepoint_roundtrip() {
541        let encoded = postcard::to_allocvec(&constants::X25519_BASEPOINT).unwrap();
542        let decoded: MontgomeryPoint = postcard::from_bytes(&encoded).unwrap();
543
544        assert_eq!(encoded.len(), 32);
545        assert_eq!(decoded, constants::X25519_BASEPOINT);
546
547        // Check that the encoding itself matches the usual one.
548        // serde::Deserialize on fixed-size arrays calls tuple deserialization. postcard
549        // (de)serializes tuples by just doing each element and that's it.
550        let raw_bytes = constants::X25519_BASEPOINT.as_bytes();
551        let bp: MontgomeryPoint = postcard::from_bytes(raw_bytes).unwrap();
552        assert_eq!(bp, constants::X25519_BASEPOINT);
553    }
554
555    /// Test Montgomery -> Edwards on the X/Ed25519 basepoint
556    #[test]
557    fn basepoint_montgomery_to_edwards() {
558        // sign bit = 0 => basepoint
559        assert_eq!(
560            constants::ED25519_BASEPOINT_POINT,
561            constants::X25519_BASEPOINT.to_edwards(0).unwrap()
562        );
563        // sign bit = 1 => minus basepoint
564        assert_eq!(
565            -constants::ED25519_BASEPOINT_POINT,
566            constants::X25519_BASEPOINT.to_edwards(1).unwrap()
567        );
568    }
569
570    /// Test Edwards -> Montgomery on the X/Ed25519 basepoint
571    #[test]
572    fn basepoint_edwards_to_montgomery() {
573        assert_eq!(
574            constants::ED25519_BASEPOINT_POINT.to_montgomery(),
575            constants::X25519_BASEPOINT
576        );
577    }
578
579    /// Check that Montgomery -> Edwards fails for points on the twist.
580    #[test]
581    fn montgomery_to_edwards_rejects_twist() {
582        let one = FieldElement::ONE;
583
584        // u = 2 corresponds to a point on the twist.
585        let two = MontgomeryPoint((&one + &one).to_bytes());
586
587        assert!(two.to_edwards(0).is_none());
588
589        // u = -1 corresponds to a point on the twist, but should be
590        // checked explicitly because it's an exceptional point for the
591        // birational map.  For instance, libsignal will accept it.
592        let minus_one = MontgomeryPoint((-&one).to_bytes());
593
594        assert!(minus_one.to_edwards(0).is_none());
595    }
596
597    #[test]
598    fn eq_defined_mod_p() {
599        let mut u18_bytes = [0u8; 32];
600        u18_bytes[0] = 18;
601        let u18 = MontgomeryPoint(u18_bytes);
602        let u18_unred = MontgomeryPoint([255; 32]);
603
604        assert_eq!(u18, u18_unred);
605    }
606
607    /// Returns a random point on the prime-order subgroup
608    #[cfg(feature = "rand_core")]
609    fn rand_prime_order_point<R: CryptoRng + ?Sized>(rng: &mut R) -> EdwardsPoint {
610        let s: Scalar = Scalar::random(rng);
611        EdwardsPoint::mul_base(&s)
612    }
613
614    /// Given a bytestring that's little-endian at the byte level, return an iterator over all the
615    /// bits, in little-endian order.
616    fn bytestring_bits_le(x: &[u8]) -> impl DoubleEndedIterator<Item = bool> + Clone + '_ {
617        let bitlen = x.len() * 8;
618        (0..bitlen).map(|i| {
619            // As i runs from 0..256, the bottom 3 bits index the bit, while the upper bits index
620            // the byte. Since self.bytes is little-endian at the byte level, this iterator is
621            // little-endian on the bit level
622            ((x[i >> 3] >> (i & 7)) & 1u8) == 1
623        })
624    }
625
626    #[cfg(feature = "rand_core")]
627    #[test]
628    fn montgomery_ladder_matches_edwards_scalarmult() {
629        let mut csprng = UnwrapErr(SysRng);
630
631        for _ in 0..100 {
632            let p_edwards = rand_prime_order_point(&mut csprng);
633            let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
634
635            let s: Scalar = Scalar::random(&mut csprng);
636            let expected = s * p_edwards;
637            let result = s * p_montgomery;
638
639            assert_eq!(result, expected.to_montgomery())
640        }
641    }
642
643    // Tests that, on the prime-order subgroup, MontgomeryPoint::mul_bits_be is the same as
644    // multiplying by the Scalar representation of the same bits
645    #[cfg(feature = "rand_core")]
646    #[test]
647    fn montgomery_mul_bits_be() {
648        let mut csprng = UnwrapErr(SysRng);
649
650        for _ in 0..100 {
651            // Make a random prime-order point P
652            let p_edwards = rand_prime_order_point(&mut csprng);
653            let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
654
655            // Make a random integer b
656            let mut bigint = [0u8; 64];
657            csprng.fill_bytes(&mut bigint[..]);
658            let bigint_bits_be = bytestring_bits_le(&bigint).rev();
659
660            // Check that bP is the same whether calculated as scalar-times-edwards or
661            // integer-times-montgomery.
662            let expected = Scalar::from_bytes_mod_order_wide(&bigint) * p_edwards;
663            let result = p_montgomery.mul_bits_be(bigint_bits_be);
664            assert_eq!(result, expected.to_montgomery())
665        }
666    }
667
668    // Tests that MontgomeryPoint::mul_bits_be is consistent on any point, even ones that might be
669    // on the curve's twist. Specifically, this tests that b₁(b₂P) == b₂(b₁P) for random
670    // integers b₁, b₂ and random (curve or twist) point P.
671    #[test]
672    fn montgomery_mul_bits_be_twist() {
673        let mut csprng = UnwrapErr(SysRng);
674
675        for _ in 0..100 {
676            // Make a random point P on the curve or its twist
677            let p_montgomery = {
678                let mut buf = [0u8; 32];
679                csprng.fill_bytes(&mut buf);
680                MontgomeryPoint(buf)
681            };
682
683            // Compute two big integers b₁ and b₂
684            let mut bigint1 = [0u8; 64];
685            let mut bigint2 = [0u8; 64];
686            csprng.fill_bytes(&mut bigint1[..]);
687            csprng.fill_bytes(&mut bigint2[..]);
688
689            // Compute b₁P and b₂P
690            let bigint1_bits_be = bytestring_bits_le(&bigint1).rev();
691            let bigint2_bits_be = bytestring_bits_le(&bigint2).rev();
692            let prod1 = p_montgomery.mul_bits_be(bigint1_bits_be.clone());
693            let prod2 = p_montgomery.mul_bits_be(bigint2_bits_be.clone());
694
695            // Check that b₁(b₂P) == b₂(b₁P)
696            assert_eq!(
697                prod1.mul_bits_be(bigint2_bits_be),
698                prod2.mul_bits_be(bigint1_bits_be)
699            );
700        }
701    }
702
703    /// Check that mul_base_clamped and mul_clamped agree
704    #[test]
705    fn mul_base_clamped() {
706        let mut csprng = UnwrapErr(SysRng);
707
708        // Test agreement on a large integer. Even after clamping, this is not reduced mod l.
709        let a_bytes = [0xff; 32];
710        assert_eq!(
711            MontgomeryPoint::mul_base_clamped(a_bytes),
712            constants::X25519_BASEPOINT.mul_clamped(a_bytes)
713        );
714
715        // Test agreement on random integers
716        for _ in 0..100 {
717            // This will be reduced mod l with probability l / 2^256 ≈ 6.25%
718            let mut a_bytes = [0u8; 32];
719            csprng.try_fill_bytes(&mut a_bytes).unwrap();
720
721            assert_eq!(
722                MontgomeryPoint::mul_base_clamped(a_bytes),
723                constants::X25519_BASEPOINT.mul_clamped(a_bytes)
724            );
725        }
726    }
727
728    #[cfg(feature = "alloc")]
729    #[cfg(feature = "digest")]
730    const ELLIGATOR_CORRECT_OUTPUT: [u8; 32] = [
731        0x5f, 0x35, 0x20, 0x00, 0x1c, 0x6c, 0x99, 0x36, 0xa3, 0x12, 0x06, 0xaf, 0xe7, 0xc7, 0xac,
732        0x22, 0x4e, 0x88, 0x61, 0x61, 0x9b, 0xf9, 0x88, 0x72, 0x44, 0x49, 0x15, 0x89, 0x9d, 0x95,
733        0xf4, 0x6e,
734    ];
735
736    #[test]
737    #[cfg(feature = "alloc")]
738    #[cfg(feature = "digest")]
739    fn montgomery_elligator_correct() {
740        use alloc::vec::Vec;
741        let bytes: Vec<u8> = (0u8..32u8).collect();
742        let bits_in: [u8; 32] = (&bytes[..]).try_into().expect("Range invariant broken");
743
744        let fe = FieldElement::from_bytes(&bits_in);
745        let (un, ud, ..) = elligator_encode(&fe);
746        let u = &un * &ud.invert();
747        assert_eq!(u.to_bytes(), ELLIGATOR_CORRECT_OUTPUT);
748    }
749
750    #[test]
751    #[cfg(feature = "digest")]
752    fn montgomery_elligator_zero_zero() {
753        let zero = [0u8; 32];
754        let fe = FieldElement::from_bytes(&zero);
755        let (un, ud, ..) = elligator_encode(&fe);
756        let u = &un * &ud.invert();
757        assert_eq!(u.to_bytes(), zero);
758    }
759
760    // Check that FE_C2 is correctly defined
761    #[test]
762    #[cfg(feature = "digest")]
763    fn c2() {
764        let one = FieldElement::ONE;
765        let two = &one + &one;
766        // c2 = 2^((p+3)/8) = 2^((p-5)/8 + 8/8) = 2*2^((p-5)/8)
767        let c2 = &two * &(two.pow_p58());
768
769        assert_eq!(c2, FE_C2);
770    }
771}