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 <isis@patternsinthevoid.net>
10// - Henry de Valence <hdevalence@hdevalence.ca>
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, MONTGOMERY_A, MONTGOMERY_A_NEG};
58use crate::edwards::{CompressedEdwardsY, EdwardsPoint};
59use crate::field::FieldElement;
60use crate::scalar::{clamp_integer, Scalar};
61
62use crate::traits::Identity;
63
64use subtle::Choice;
65use subtle::ConditionallySelectable;
66use subtle::ConstantTimeEq;
67
68#[cfg(feature = "zeroize")]
69use zeroize::Zeroize;
70
71/// Holds the \\(u\\)-coordinate of a point on the Montgomery form of
72/// Curve25519 or its twist.
73#[derive(Copy, Clone, Debug, Default)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub struct MontgomeryPoint(pub [u8; 32]);
76
77/// Equality of `MontgomeryPoint`s is defined mod p.
78impl ConstantTimeEq for MontgomeryPoint {
79    fn ct_eq(&self, other: &MontgomeryPoint) -> Choice {
80        let self_fe = FieldElement::from_bytes(&self.0);
81        let other_fe = FieldElement::from_bytes(&other.0);
82
83        self_fe.ct_eq(&other_fe)
84    }
85}
86
87impl ConditionallySelectable for MontgomeryPoint {
88    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
89        Self(<[u8; 32]>::conditional_select(&a.0, &b.0, choice))
90    }
91}
92
93impl PartialEq for MontgomeryPoint {
94    fn eq(&self, other: &MontgomeryPoint) -> bool {
95        self.ct_eq(other).into()
96    }
97}
98
99impl Eq for MontgomeryPoint {}
100
101// Equal MontgomeryPoints must hash to the same value. So we have to get them into a canonical
102// encoding first
103impl Hash for MontgomeryPoint {
104    fn hash<H: Hasher>(&self, state: &mut H) {
105        // Do a round trip through a `FieldElement`. `as_bytes` is guaranteed to give a canonical
106        // 32-byte encoding
107        let canonical_bytes = FieldElement::from_bytes(&self.0).to_bytes();
108        canonical_bytes.hash(state);
109    }
110}
111
112impl Identity for MontgomeryPoint {
113    /// Return the group identity element, which has order 4.
114    fn identity() -> MontgomeryPoint {
115        MontgomeryPoint([0u8; 32])
116    }
117}
118
119#[cfg(feature = "zeroize")]
120impl Zeroize for MontgomeryPoint {
121    fn zeroize(&mut self) {
122        self.0.zeroize();
123    }
124}
125
126impl MontgomeryPoint {
127    /// Fixed-base scalar multiplication (i.e. multiplication by the base point).
128    pub fn mul_base(scalar: &Scalar) -> Self {
129        EdwardsPoint::mul_base(scalar).to_montgomery()
130    }
131
132    /// Multiply this point by `clamp_integer(bytes)`. For a description of clamping, see
133    /// [`clamp_integer`].
134    pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
135        // We have to construct a Scalar that is not reduced mod l, which breaks scalar invariant
136        // #2. But #2 is not necessary for correctness of variable-base multiplication. All that
137        // needs to hold is invariant #1, i.e., the scalar is less than 2^255. This is guaranteed
138        // by clamping.
139        // Further, we don't do any reduction or arithmetic with this clamped value, so there's no
140        // issues arising from the fact that the curve point is not necessarily in the prime-order
141        // subgroup.
142        let s = Scalar {
143            bytes: clamp_integer(bytes),
144        };
145        s * self
146    }
147
148    /// Multiply the basepoint by `clamp_integer(bytes)`. For a description of clamping, see
149    /// [`clamp_integer`].
150    pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
151        // See reasoning in Self::mul_clamped why it is OK to make an unreduced Scalar here. We
152        // note that fixed-base multiplication is also defined for all values of `bytes` less than
153        // 2^255.
154        let s = Scalar {
155            bytes: clamp_integer(bytes),
156        };
157        Self::mul_base(&s)
158    }
159
160    /// Given `self` \\( = u\_0(P) \\), and a big-endian bit representation of an integer
161    /// \\(n\\), return \\( u\_0(\[n\]P) \\). This is constant time in the length of `bits`.
162    ///
163    /// **NOTE:** You probably do not want to use this function. Almost every protocol built on
164    /// Curve25519 uses _clamped multiplication_, explained
165    /// [here](https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/).
166    /// When in doubt, use [`Self::mul_clamped`].
167    #[cfg(not(verify))]
168    pub fn mul_bits_be(&self, bits: impl Iterator<Item = bool>) -> MontgomeryPoint {
169        // Algorithm 8 of Costello-Smith 2017
170        let affine_u = FieldElement::from_bytes(&self.0);
171        let mut x0 = ProjectivePoint::identity();
172        let mut x1 = ProjectivePoint {
173            U: affine_u,
174            W: FieldElement::ONE,
175        };
176
177        // Go through the bits from most to least significant, using a sliding window of 2
178        let mut prev_bit = false;
179        for cur_bit in bits {
180            let choice: u8 = (prev_bit != cur_bit) as u8;
181
182            debug_assert!(choice == 0 || choice == 1);
183
184            ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
185            differential_add_and_double(&mut x0, &mut x1, &affine_u);
186
187            prev_bit = cur_bit;
188        }
189        // The final value of prev_bit above is scalar.bits()[0], i.e., the LSB of scalar
190        ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(prev_bit as u8));
191        // Don't leave the bit in the stack
192        #[cfg(feature = "zeroize")]
193        prev_bit.zeroize();
194
195        x0.as_affine()
196    }
197
198    /// View this `MontgomeryPoint` as an array of bytes.
199    pub const fn as_bytes(&self) -> &[u8; 32] {
200        &self.0
201    }
202
203    /// Convert this `MontgomeryPoint` to an array of bytes.
204    pub const fn to_bytes(&self) -> [u8; 32] {
205        self.0
206    }
207
208    /// Attempt to convert to an `EdwardsPoint`, using the supplied
209    /// choice of sign for the `EdwardsPoint`.
210    ///
211    /// # Inputs
212    ///
213    /// * `sign`: a `u8` donating the desired sign of the resulting
214    ///   `EdwardsPoint`.  `0` denotes positive and `1` negative.
215    ///
216    /// # Return
217    ///
218    /// * `Some(EdwardsPoint)` if `self` is the \\(u\\)-coordinate of a
219    ///   point on (the Montgomery form of) Curve25519;
220    ///
221    /// * `None` if `self` is the \\(u\\)-coordinate of a point on the
222    ///   twist of (the Montgomery form of) Curve25519;
223    ///
224    pub fn to_edwards(&self, sign: u8) -> Option<EdwardsPoint> {
225        // To decompress the Montgomery u coordinate to an
226        // `EdwardsPoint`, we apply the birational map to obtain the
227        // Edwards y coordinate, then do Edwards decompression.
228        //
229        // The birational map is y = (u-1)/(u+1).
230        //
231        // The exceptional points are the zeros of the denominator,
232        // i.e., u = -1.
233        //
234        // But when u = -1, v^2 = u*(u^2+486662*u+1) = 486660.
235        //
236        // Since this is nonsquare mod p, u = -1 corresponds to a point
237        // on the twist, not the curve, so we can reject it early.
238
239        let u = FieldElement::from_bytes(&self.0);
240
241        if u == FieldElement::MINUS_ONE {
242            return None;
243        }
244
245        let one = FieldElement::ONE;
246
247        let y = &(&u - &one) * &(&u + &one).invert();
248
249        let mut y_bytes = y.to_bytes();
250        y_bytes[31] ^= sign << 7;
251
252        CompressedEdwardsY(y_bytes).decompress()
253    }
254}
255
256/// Perform the Elligator2 mapping to a Montgomery point. Returns a Montgomery point and a `Choice`
257/// determining whether eps is a square. This is required by the standard to determine the
258/// sign of the v coordinate.
259///
260/// See <https://www.rfc-editor.org/rfc/rfc9380.html#name-elligator-2-method>
261//
262#[allow(unused)]
263pub(crate) fn elligator_encode(r_0: &FieldElement) -> (MontgomeryPoint, Choice) {
264    let one = FieldElement::ONE;
265    let d_1 = &one + &r_0.square2(); /* 2r^2 */
266
267    let d = &MONTGOMERY_A_NEG * &(d_1.invert()); /* A/(1+2r^2) */
268
269    let d_sq = &d.square();
270    let au = &MONTGOMERY_A * &d;
271
272    let inner = &(d_sq + &au) + &one;
273    let eps = &d * &inner; /* eps = d^3 + Ad^2 + d */
274
275    let (eps_is_sq, _eps) = FieldElement::sqrt_ratio_i(&eps, &one);
276
277    let zero = FieldElement::ZERO;
278    let Atemp = FieldElement::conditional_select(&MONTGOMERY_A, &zero, eps_is_sq); /* 0, or A if nonsquare*/
279    let mut u = &d + &Atemp; /* d, or d+A if nonsquare */
280    let u_neg = -&u;
281    u.conditional_assign(&u_neg, !eps_is_sq); /* d, or -d-A if nonsquare */
282
283    (MontgomeryPoint(u.to_bytes()), eps_is_sq)
284}
285
286/// A `ProjectivePoint` holds a point on the projective line
287/// \\( \mathbb P(\mathbb F\_p) \\), which we identify with the Kummer
288/// line of the Montgomery curve.
289#[derive(Copy, Clone, Debug)]
290struct ProjectivePoint {
291    pub U: FieldElement,
292    pub W: FieldElement,
293}
294
295#[cfg_attr(verify, aeneas::rename("IdentityMontgomeryProjectivePoint"))]
296impl Identity for ProjectivePoint {
297    fn identity() -> ProjectivePoint {
298        ProjectivePoint {
299            U: FieldElement::ONE,
300            W: FieldElement::ZERO,
301        }
302    }
303}
304
305impl Default for ProjectivePoint {
306    fn default() -> ProjectivePoint {
307        ProjectivePoint::identity()
308    }
309}
310
311impl ConditionallySelectable for ProjectivePoint {
312    fn conditional_select(
313        a: &ProjectivePoint,
314        b: &ProjectivePoint,
315        choice: Choice,
316    ) -> ProjectivePoint {
317        ProjectivePoint {
318            U: FieldElement::conditional_select(&a.U, &b.U, choice),
319            W: FieldElement::conditional_select(&a.W, &b.W, choice),
320        }
321    }
322}
323
324impl ProjectivePoint {
325    /// Dehomogenize this point to affine coordinates.
326    ///
327    /// # Return
328    ///
329    /// * \\( u = U / W \\) if \\( W \neq 0 \\);
330    /// * \\( 0 \\) if \\( W \eq 0 \\);
331    pub fn as_affine(&self) -> MontgomeryPoint {
332        let u = &self.U * &self.W.invert();
333        MontgomeryPoint(u.to_bytes())
334    }
335}
336
337/// Perform the double-and-add step of the Montgomery ladder.
338///
339/// Given projective points
340/// \\( (U\_P : W\_P) = u(P) \\),
341/// \\( (U\_Q : W\_Q) = u(Q) \\),
342/// and the affine difference
343/// \\(      u\_{P-Q} = u(P-Q) \\), set
344/// $$
345///     (U\_P : W\_P) \gets u(\[2\]P)
346/// $$
347/// and
348/// $$
349///     (U\_Q : W\_Q) \gets u(P + Q).
350/// $$
351#[rustfmt::skip] // keep alignment of explanatory comments
352fn differential_add_and_double(
353    P: &mut ProjectivePoint,
354    Q: &mut ProjectivePoint,
355    affine_PmQ: &FieldElement,
356) {
357    let t0 = &P.U + &P.W;
358    let t1 = &P.U - &P.W;
359    let t2 = &Q.U + &Q.W;
360    let t3 = &Q.U - &Q.W;
361
362    let t4 = t0.square();   // (U_P + W_P)^2 = U_P^2 + 2 U_P W_P + W_P^2
363    let t5 = t1.square();   // (U_P - W_P)^2 = U_P^2 - 2 U_P W_P + W_P^2
364
365    let t6 = &t4 - &t5;     // 4 U_P W_P
366
367    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
368    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
369
370    let t9  = &t7 + &t8;    // 2 (U_P U_Q - W_P W_Q)
371    let t10 = &t7 - &t8;    // 2 (W_P U_Q - U_P W_Q)
372
373    let t11 =  t9.square(); // 4 (U_P U_Q - W_P W_Q)^2
374    let t12 = t10.square(); // 4 (W_P U_Q - U_P W_Q)^2
375
376    let t13 = &APLUS2_OVER_FOUR * &t6; // (A + 2) U_P U_Q
377
378    let t14 = &t4 * &t5;    // ((U_P + W_P)(U_P - W_P))^2 = (U_P^2 - W_P^2)^2
379    let t15 = &t13 + &t5;   // (U_P - W_P)^2 + (A + 2) U_P W_P
380
381    let t16 = &t6 * &t15;   // 4 (U_P W_P) ((U_P - W_P)^2 + (A + 2) U_P W_P)
382
383    let t17 = affine_PmQ * &t12; // U_D * 4 (W_P U_Q - U_P W_Q)^2
384    let t18 = t11;               // W_D * 4 (U_P U_Q - W_P W_Q)^2
385
386    P.U = t14;  // U_{P'} = (U_P + W_P)^2 (U_P - W_P)^2
387    P.W = t16;  // W_{P'} = (4 U_P W_P) ((U_P - W_P)^2 + ((A + 2)/4) 4 U_P W_P)
388    Q.U = t18;  // U_{Q'} = W_D * 4 (U_P U_Q - W_P W_Q)^2
389    Q.W = t17;  // W_{Q'} = U_D * 4 (W_P U_Q - U_P W_Q)^2
390}
391
392define_mul_assign_variants!(LHS = MontgomeryPoint, RHS = Scalar);
393
394define_mul_variants!(
395    LHS = MontgomeryPoint,
396    RHS = Scalar,
397    Output = MontgomeryPoint
398);
399define_mul_variants!(
400    LHS = Scalar,
401    RHS = MontgomeryPoint,
402    Output = MontgomeryPoint
403);
404
405/// Multiply this `MontgomeryPoint` by a `Scalar`.
406///
407/// NOTE: This implementation avoids using Scalar::bits_le() and iterator adapters
408/// (.rev(), .skip()) because Charon/Aeneas cannot extract the Iterator trait machinery.
409/// Instead, we inline the bit iteration using a while loop.
410impl Mul<&Scalar> for &MontgomeryPoint {
411    type Output = MontgomeryPoint;
412
413    /// Given `self` \\( = u\_0(P) \\), and a `Scalar` \\(n\\), return \\( u\_0(\[n\]P) \\)
414    fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
415        // Algorithm 8 of Costello-Smith 2017 (inlined from mul_bits_be)
416        let affine_u = FieldElement::from_bytes(&self.0);
417        let mut x0 = ProjectivePoint::identity();
418        let mut x1 = ProjectivePoint {
419            U: affine_u,
420            W: FieldElement::ONE,
421        };
422
423        // Go through the bits from most to least significant, using a sliding window of 2.
424        // We multiply by the integer representation of the given Scalar. By scalar invariant #1,
425        // the MSB (bit 255) is 0, so we can skip it and start from bit 254.
426        let scalar_bytes = scalar.as_bytes();
427        let mut prev_bit = false;
428        let mut i: isize = 254;
429        while i >= 0 {
430            let byte_idx = (i >> 3) as usize; // i / 8
431            let bit_idx = (i & 7) as usize; // i % 8
432            let cur_bit = ((scalar_bytes[byte_idx] >> bit_idx) & 1u8) == 1u8;
433
434            let choice: u8 = (prev_bit != cur_bit) as u8;
435
436            debug_assert!(choice == 0 || choice == 1);
437
438            ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
439            differential_add_and_double(&mut x0, &mut x1, &affine_u);
440
441            prev_bit = cur_bit;
442            i -= 1;
443        }
444        // The final value of prev_bit above is scalar.bits()[0], i.e., the LSB of scalar
445        ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(prev_bit as u8));
446        // Don't leave the bit in the stack
447        #[cfg(feature = "zeroize")]
448        prev_bit.zeroize();
449
450        x0.as_affine()
451    }
452}
453
454impl MulAssign<&Scalar> for MontgomeryPoint {
455    fn mul_assign(&mut self, scalar: &Scalar) {
456        *self = (self as &MontgomeryPoint) * scalar;
457    }
458}
459
460impl Mul<&MontgomeryPoint> for &Scalar {
461    type Output = MontgomeryPoint;
462
463    fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint {
464        point * self
465    }
466}
467
468// ------------------------------------------------------------------------
469// Tests
470// ------------------------------------------------------------------------
471
472#[cfg(test)]
473mod test {
474    use super::*;
475    use crate::constants;
476
477    #[cfg(feature = "alloc")]
478    use alloc::vec::Vec;
479
480    use rand_core::{CryptoRng, RngCore};
481
482    #[test]
483    fn identity_in_different_coordinates() {
484        let id_projective = ProjectivePoint::identity();
485        let id_montgomery = id_projective.as_affine();
486
487        assert!(id_montgomery == MontgomeryPoint::identity());
488    }
489
490    #[test]
491    fn identity_in_different_models() {
492        assert!(EdwardsPoint::identity().to_montgomery() == MontgomeryPoint::identity());
493    }
494
495    #[test]
496    #[cfg(feature = "serde")]
497    fn serde_bincode_basepoint_roundtrip() {
498        use bincode;
499
500        let encoded = bincode::serialize(&constants::X25519_BASEPOINT).unwrap();
501        let decoded: MontgomeryPoint = bincode::deserialize(&encoded).unwrap();
502
503        assert_eq!(encoded.len(), 32);
504        assert_eq!(decoded, constants::X25519_BASEPOINT);
505
506        let raw_bytes = constants::X25519_BASEPOINT.as_bytes();
507        let bp: MontgomeryPoint = bincode::deserialize(raw_bytes).unwrap();
508        assert_eq!(bp, constants::X25519_BASEPOINT);
509    }
510
511    /// Test Montgomery -> Edwards on the X/Ed25519 basepoint
512    #[test]
513    fn basepoint_montgomery_to_edwards() {
514        // sign bit = 0 => basepoint
515        assert_eq!(
516            constants::ED25519_BASEPOINT_POINT,
517            constants::X25519_BASEPOINT.to_edwards(0).unwrap()
518        );
519        // sign bit = 1 => minus basepoint
520        assert_eq!(
521            -constants::ED25519_BASEPOINT_POINT,
522            constants::X25519_BASEPOINT.to_edwards(1).unwrap()
523        );
524    }
525
526    /// Test Edwards -> Montgomery on the X/Ed25519 basepoint
527    #[test]
528    fn basepoint_edwards_to_montgomery() {
529        assert_eq!(
530            constants::ED25519_BASEPOINT_POINT.to_montgomery(),
531            constants::X25519_BASEPOINT
532        );
533    }
534
535    /// Check that Montgomery -> Edwards fails for points on the twist.
536    #[test]
537    fn montgomery_to_edwards_rejects_twist() {
538        let one = FieldElement::ONE;
539
540        // u = 2 corresponds to a point on the twist.
541        let two = MontgomeryPoint((&one + &one).to_bytes());
542
543        assert!(two.to_edwards(0).is_none());
544
545        // u = -1 corresponds to a point on the twist, but should be
546        // checked explicitly because it's an exceptional point for the
547        // birational map.  For instance, libsignal will accept it.
548        let minus_one = MontgomeryPoint((-&one).to_bytes());
549
550        assert!(minus_one.to_edwards(0).is_none());
551    }
552
553    #[test]
554    fn eq_defined_mod_p() {
555        let mut u18_bytes = [0u8; 32];
556        u18_bytes[0] = 18;
557        let u18 = MontgomeryPoint(u18_bytes);
558        let u18_unred = MontgomeryPoint([255; 32]);
559
560        assert_eq!(u18, u18_unred);
561    }
562
563    /// Returns a random point on the prime-order subgroup
564    fn rand_prime_order_point(mut rng: impl RngCore + CryptoRng) -> EdwardsPoint {
565        let s: Scalar = Scalar::random(&mut rng);
566        EdwardsPoint::mul_base(&s)
567    }
568
569    /// Given a bytestring that's little-endian at the byte level, return an iterator over all the
570    /// bits, in little-endian order.
571    fn bytestring_bits_le(x: &[u8]) -> impl DoubleEndedIterator<Item = bool> + Clone + '_ {
572        let bitlen = x.len() * 8;
573        (0..bitlen).map(|i| {
574            // As i runs from 0..256, the bottom 3 bits index the bit, while the upper bits index
575            // the byte. Since self.bytes is little-endian at the byte level, this iterator is
576            // little-endian on the bit level
577            ((x[i >> 3] >> (i & 7)) & 1u8) == 1
578        })
579    }
580
581    #[test]
582    fn montgomery_ladder_matches_edwards_scalarmult() {
583        let mut csprng = rand_core::OsRng;
584
585        for _ in 0..100 {
586            let p_edwards = rand_prime_order_point(csprng);
587            let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
588
589            let s: Scalar = Scalar::random(&mut csprng);
590            let expected = s * p_edwards;
591            let result = s * p_montgomery;
592
593            assert_eq!(result, expected.to_montgomery())
594        }
595    }
596
597    // Tests that, on the prime-order subgroup, MontgomeryPoint::mul_bits_be is the same as
598    // multiplying by the Scalar representation of the same bits
599    #[test]
600    fn montgomery_mul_bits_be() {
601        let mut csprng = rand_core::OsRng;
602
603        for _ in 0..100 {
604            // Make a random prime-order point P
605            let p_edwards = rand_prime_order_point(csprng);
606            let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
607
608            // Make a random integer b
609            let mut bigint = [0u8; 64];
610            csprng.fill_bytes(&mut bigint[..]);
611            let bigint_bits_be = bytestring_bits_le(&bigint).rev();
612
613            // Check that bP is the same whether calculated as scalar-times-edwards or
614            // integer-times-montgomery.
615            let expected = Scalar::from_bytes_mod_order_wide(&bigint) * p_edwards;
616            let result = p_montgomery.mul_bits_be(bigint_bits_be);
617            assert_eq!(result, expected.to_montgomery())
618        }
619    }
620
621    // Tests that MontgomeryPoint::mul_bits_be is consistent on any point, even ones that might be
622    // on the curve's twist. Specifically, this tests that b₁(b₂P) == b₂(b₁P) for random
623    // integers b₁, b₂ and random (curve or twist) point P.
624    #[test]
625    fn montgomery_mul_bits_be_twist() {
626        let mut csprng = rand_core::OsRng;
627
628        for _ in 0..100 {
629            // Make a random point P on the curve or its twist
630            let p_montgomery = {
631                let mut buf = [0u8; 32];
632                csprng.fill_bytes(&mut buf);
633                MontgomeryPoint(buf)
634            };
635
636            // Compute two big integers b₁ and b₂
637            let mut bigint1 = [0u8; 64];
638            let mut bigint2 = [0u8; 64];
639            csprng.fill_bytes(&mut bigint1[..]);
640            csprng.fill_bytes(&mut bigint2[..]);
641
642            // Compute b₁P and b₂P
643            let bigint1_bits_be = bytestring_bits_le(&bigint1).rev();
644            let bigint2_bits_be = bytestring_bits_le(&bigint2).rev();
645            let prod1 = p_montgomery.mul_bits_be(bigint1_bits_be.clone());
646            let prod2 = p_montgomery.mul_bits_be(bigint2_bits_be.clone());
647
648            // Check that b₁(b₂P) == b₂(b₁P)
649            assert_eq!(
650                prod1.mul_bits_be(bigint2_bits_be),
651                prod2.mul_bits_be(bigint1_bits_be)
652            );
653        }
654    }
655
656    /// Check that mul_base_clamped and mul_clamped agree
657    #[test]
658    fn mul_base_clamped() {
659        let mut csprng = rand_core::OsRng;
660
661        // Test agreement on a large integer. Even after clamping, this is not reduced mod l.
662        let a_bytes = [0xff; 32];
663        assert_eq!(
664            MontgomeryPoint::mul_base_clamped(a_bytes),
665            constants::X25519_BASEPOINT.mul_clamped(a_bytes)
666        );
667
668        // Test agreement on random integers
669        for _ in 0..100 {
670            // This will be reduced mod l with probability l / 2^256 ≈ 6.25%
671            let mut a_bytes = [0u8; 32];
672            csprng.fill_bytes(&mut a_bytes);
673
674            assert_eq!(
675                MontgomeryPoint::mul_base_clamped(a_bytes),
676                constants::X25519_BASEPOINT.mul_clamped(a_bytes)
677            );
678        }
679    }
680
681    #[cfg(feature = "alloc")]
682    const ELLIGATOR_CORRECT_OUTPUT: [u8; 32] = [
683        0x5f, 0x35, 0x20, 0x00, 0x1c, 0x6c, 0x99, 0x36, 0xa3, 0x12, 0x06, 0xaf, 0xe7, 0xc7, 0xac,
684        0x22, 0x4e, 0x88, 0x61, 0x61, 0x9b, 0xf9, 0x88, 0x72, 0x44, 0x49, 0x15, 0x89, 0x9d, 0x95,
685        0xf4, 0x6e,
686    ];
687
688    #[test]
689    #[cfg(feature = "alloc")]
690    fn montgomery_elligator_correct() {
691        let bytes: Vec<u8> = (0u8..32u8).collect();
692        let bits_in: [u8; 32] = (&bytes[..]).try_into().expect("Range invariant broken");
693
694        let fe = FieldElement::from_bytes(&bits_in);
695        let (eg, _) = elligator_encode(&fe);
696        assert_eq!(eg.to_bytes(), ELLIGATOR_CORRECT_OUTPUT);
697    }
698
699    #[test]
700    fn montgomery_elligator_zero_zero() {
701        let zero = [0u8; 32];
702        let fe = FieldElement::from_bytes(&zero);
703        let (eg, _) = elligator_encode(&fe);
704        assert_eq!(eg.to_bytes(), zero);
705    }
706}