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