// -*- mode: rust; -*- // // This file is part of curve25519-dalek. // Copyright (c) 2016-2021 isis lovecruft // Copyright (c) 2016-2020 Henry de Valence // See LICENSE for licensing information. // // Authors: // - isis agora lovecruft // - Henry de Valence //! Group operations for Curve25519, in Edwards form. //! //! ## Encoding and Decoding //! //! Encoding is done by converting to and from a `CompressedEdwardsY` //! struct, which is a typed wrapper around `[u8; 32]`. //! //! ## Equality Testing //! //! The `EdwardsPoint` struct implements the [`subtle::ConstantTimeEq`] //! trait for constant-time equality checking, and also uses this to //! ensure `Eq` equality checking runs in constant time. //! //! ## Cofactor-related functions //! //! The order of the group of points on the curve \\(\mathcal E\\) //! is \\(|\mathcal E| = 8\ell \\), so its structure is \\( \mathcal //! E = \mathcal E\[8\] \times \mathcal E[\ell]\\). The torsion //! subgroup \\( \mathcal E\[8\] \\) consists of eight points of small //! order. Technically, all of \\(\mathcal E\\) is torsion, but we //! use the word only to refer to the small \\(\mathcal E\[8\]\\) part, not //! the large prime-order \\(\mathcal E[\ell]\\) part. //! //! To test if a point is in \\( \mathcal E\[8\] \\), use //! [`EdwardsPoint::is_small_order`]. //! //! To test if a point is in \\( \mathcal E[\ell] \\), use //! [`EdwardsPoint::is_torsion_free`]. //! //! To multiply by the cofactor, use [`EdwardsPoint::mul_by_cofactor`]. //! //! To avoid dealing with cofactors entirely, consider using Ristretto. //! //! ## Scalars //! //! Scalars are represented by the [`Scalar`] struct. To construct a scalar, see //! [`Scalar::from_canonical_bytes`] or [`Scalar::from_bytes_mod_order_wide`]. //! //! ## Scalar Multiplication //! //! Scalar multiplication on Edwards points is provided by: //! //! * the `*` operator between a `Scalar` and a `EdwardsPoint`, which //! performs constant-time variable-base scalar multiplication; //! //! * the `*` operator between a `Scalar` and a //! `EdwardsBasepointTable`, which performs constant-time fixed-base //! scalar multiplication; //! //! * an implementation of the //! [`MultiscalarMul`](../traits/trait.MultiscalarMul.html) trait for //! constant-time variable-base multiscalar multiplication; //! //! * an implementation of the //! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html) //! trait for variable-time variable-base multiscalar multiplication; //! //! ## Implementation //! //! The Edwards arithmetic is implemented using the “extended twisted //! coordinates” of Hisil, Wong, Carter, and Dawson, and the //! corresponding complete formulas. For more details, //! see the [`curve_models` submodule][curve_models] //! of the internal documentation. //! //! ## Validity Checking //! //! There is no function for checking whether a point is valid. //! Instead, the `EdwardsPoint` struct is guaranteed to hold a valid //! point on the curve. //! //! We use the Rust type system to make invalid points //! unrepresentable: `EdwardsPoint` objects can only be created via //! successful decompression of a compressed point, or else by //! operations on other (valid) `EdwardsPoint`s. //! //! [curve_models]: https://docs.rs/curve25519-dalek/latest/curve25519-dalek/backend/serial/curve_models/index.html // We allow non snake_case names because coordinates in projective space are // traditionally denoted by the capitalisation of their respective // counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my // affine and projective cakes and eat both of them too. #![allow(non_snake_case)] mod affine; use cfg_if::cfg_if; use core::array::TryFromSliceError; use core::borrow::Borrow; use core::fmt::Debug; use core::iter::Sum; use core::ops::{Add, Neg, Sub}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; #[cfg(feature = "digest")] use digest::{ FixedOutput, HashMarker, array::typenum::U64, block_api::BlockSizeUser, consts::True, typenum::IsGreater, }; #[cfg(feature = "group")] use { group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup}, rand_core::RngCore as GroupRngCore, subtle::CtOption, }; #[cfg(feature = "rand_core")] use rand_core::{CryptoRng, RngCore}; use subtle::Choice; use subtle::ConditionallySelectable; use subtle::ConstantTimeEq; #[cfg(feature = "zeroize")] use zeroize::Zeroize; use crate::constants; use crate::field::FieldElement; use crate::scalar::{Scalar, clamp_integer}; use crate::montgomery::MontgomeryPoint; use crate::backend::serial::curve_models::AffineNielsPoint; use crate::backend::serial::curve_models::CompletedPoint; use crate::backend::serial::curve_models::ProjectiveNielsPoint; use crate::backend::serial::curve_models::ProjectivePoint; #[cfg(feature = "precomputed-tables")] use crate::window::{ LookupTableRadix16, LookupTableRadix32, LookupTableRadix64, LookupTableRadix128, LookupTableRadix256, }; #[cfg(feature = "precomputed-tables")] use crate::traits::BasepointTable; use crate::traits::ValidityCheck; use crate::traits::{Identity, IsIdentity}; use affine::AffinePoint; #[cfg(feature = "alloc")] use crate::traits::MultiscalarMul; #[cfg(feature = "alloc")] use crate::traits::{VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul}; #[cfg(feature = "alloc")] use alloc::vec::Vec; // ------------------------------------------------------------------------ // Compressed points // ------------------------------------------------------------------------ /// In "Edwards y" / "Ed25519" format, the curve point \\((x,y)\\) is /// determined by the \\(y\\)-coordinate and the sign of \\(x\\). /// /// The first 255 bits of a `CompressedEdwardsY` represent the /// \\(y\\)-coordinate. The high bit of the 32nd byte gives the sign of \\(x\\). #[allow(clippy::derived_hash_with_manual_eq)] #[derive(Copy, Clone, Hash)] pub struct CompressedEdwardsY(pub [u8; 32]); impl ConstantTimeEq for CompressedEdwardsY { fn ct_eq(&self, other: &CompressedEdwardsY) -> Choice { self.as_bytes().ct_eq(other.as_bytes()) } } impl Eq for CompressedEdwardsY {} impl PartialEq for CompressedEdwardsY { fn eq(&self, other: &Self) -> bool { self.ct_eq(other).into() } } impl Debug for CompressedEdwardsY { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "CompressedEdwardsY: {:?}", self.as_bytes()) } } impl CompressedEdwardsY { /// View this `CompressedEdwardsY` as an array of bytes. pub const fn as_bytes(&self) -> &[u8; 32] { &self.0 } /// Copy this `CompressedEdwardsY` to an array of bytes. pub const fn to_bytes(&self) -> [u8; 32] { self.0 } /// Attempt to decompress to an `EdwardsPoint`. /// /// Returns `None` if the input is not the \\(y\\)-coordinate of a /// curve point. pub fn decompress(&self) -> Option { let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self); if is_valid_y_coord.into() { Some(decompress::step_2(self, X, Y, Z)) } else { None } } } mod decompress { use super::*; #[rustfmt::skip] // keep alignment of explanatory comments pub(super) fn step_1( repr: &CompressedEdwardsY, ) -> (Choice, FieldElement, FieldElement, FieldElement) { let Y = FieldElement::from_bytes(repr.as_bytes()); let Z = FieldElement::ONE; let YY = Y.square(); let u = &YY - &Z; // u = y²-1 let v = &(&YY * &constants::EDWARDS_D) + &Z; // v = dy²+1 let (is_valid_y_coord, X) = FieldElement::sqrt_ratio_i(&u, &v); (is_valid_y_coord, X, Y, Z) } #[rustfmt::skip] pub(super) fn step_2( repr: &CompressedEdwardsY, mut X: FieldElement, Y: FieldElement, Z: FieldElement, ) -> EdwardsPoint { // FieldElement::sqrt_ratio_i always returns the nonnegative square root, // so we negate according to the supplied sign bit. let compressed_sign_bit = Choice::from(repr.as_bytes()[31] >> 7); // AENEAS-COMPAT: negate-then-conditional-assign instead of // `X.conditional_negate(...)` — semantically identical and still // constant-time, but avoids subtle's `ConditionallyNegatable` // blanket impl which breaks the verification toolchain (the same // documented rewrite as in `FieldElement::sqrt_ratio_i`). let X_neg = -&X; X.conditional_assign(&X_neg, compressed_sign_bit); EdwardsPoint { X, Y, Z, T: &X * &Y, } } } impl TryFrom<&[u8]> for CompressedEdwardsY { type Error = TryFromSliceError; fn try_from(slice: &[u8]) -> Result { Self::from_slice(slice) } } // ------------------------------------------------------------------------ // Serde support // ------------------------------------------------------------------------ // Serializes to and from `EdwardsPoint` directly, doing compression // and decompression internally. This means that users can create // structs containing `EdwardsPoint`s and use Serde's derived // serializers to serialize those structures. #[cfg(feature = "digest")] use constants::ED25519_SQRTAM2; #[cfg(feature = "serde")] use serde::de::Visitor; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "serde")] impl Serialize for EdwardsPoint { fn serialize(&self, serializer: S) -> Result where S: Serializer, { use serde::ser::SerializeTuple; let mut tup = serializer.serialize_tuple(32)?; for byte in self.compress().as_bytes().iter() { tup.serialize_element(byte)?; } tup.end() } } #[cfg(feature = "serde")] impl Serialize for CompressedEdwardsY { fn serialize(&self, serializer: S) -> Result where S: Serializer, { use serde::ser::SerializeTuple; let mut tup = serializer.serialize_tuple(32)?; for byte in self.as_bytes().iter() { tup.serialize_element(byte)?; } tup.end() } } #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for EdwardsPoint { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct EdwardsPointVisitor; impl<'de> Visitor<'de> for EdwardsPointVisitor { type Value = EdwardsPoint; fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a valid point in Edwards y + sign format") } fn visit_seq(self, mut seq: A) -> Result where A: serde::de::SeqAccess<'de>, { let mut bytes = [0u8; 32]; #[allow(clippy::needless_range_loop)] for i in 0..32 { bytes[i] = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; } CompressedEdwardsY(bytes) .decompress() .ok_or_else(|| serde::de::Error::custom("decompression failed")) } } deserializer.deserialize_tuple(32, EdwardsPointVisitor) } } #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for CompressedEdwardsY { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct CompressedEdwardsYVisitor; impl<'de> Visitor<'de> for CompressedEdwardsYVisitor { type Value = CompressedEdwardsY; fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("32 bytes of data") } fn visit_seq(self, mut seq: A) -> Result where A: serde::de::SeqAccess<'de>, { let mut bytes = [0u8; 32]; #[allow(clippy::needless_range_loop)] for i in 0..32 { bytes[i] = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?; } Ok(CompressedEdwardsY(bytes)) } } deserializer.deserialize_tuple(32, CompressedEdwardsYVisitor) } } // ------------------------------------------------------------------------ // Internal point representations // ------------------------------------------------------------------------ /// An `EdwardsPoint` represents a point on the Edwards form of Curve25519. #[derive(Copy, Clone)] #[allow(missing_docs)] pub struct EdwardsPoint { pub(crate) X: FieldElement, pub(crate) Y: FieldElement, pub(crate) Z: FieldElement, pub(crate) T: FieldElement, } // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ impl Identity for CompressedEdwardsY { fn identity() -> CompressedEdwardsY { CompressedEdwardsY([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]) } } impl Default for CompressedEdwardsY { fn default() -> CompressedEdwardsY { CompressedEdwardsY::identity() } } impl CompressedEdwardsY { /// Construct a `CompressedEdwardsY` from a slice of bytes. /// /// # Errors /// /// Returns [`TryFromSliceError`] if the input `bytes` slice does not have /// a length of 32. pub fn from_slice(bytes: &[u8]) -> Result { bytes.try_into().map(CompressedEdwardsY) } } impl Identity for EdwardsPoint { fn identity() -> EdwardsPoint { EdwardsPoint { X: FieldElement::ZERO, Y: FieldElement::ONE, Z: FieldElement::ONE, T: FieldElement::ZERO, } } } impl Default for EdwardsPoint { fn default() -> EdwardsPoint { EdwardsPoint::identity() } } // ------------------------------------------------------------------------ // Zeroize implementations for wiping points from memory // ------------------------------------------------------------------------ #[cfg(feature = "zeroize")] impl Zeroize for CompressedEdwardsY { /// Reset this `CompressedEdwardsY` to the compressed form of the identity element. fn zeroize(&mut self) { self.0.zeroize(); self.0[0] = 1; } } #[cfg(feature = "zeroize")] impl Zeroize for EdwardsPoint { /// Reset this `EdwardsPoint` to the identity element. fn zeroize(&mut self) { self.X.zeroize(); self.Y = FieldElement::ONE; self.Z = FieldElement::ONE; self.T.zeroize(); } } // ------------------------------------------------------------------------ // Validity checks (for debugging, not CT) // ------------------------------------------------------------------------ impl ValidityCheck for EdwardsPoint { fn is_valid(&self) -> bool { let point_on_curve = self.as_projective().is_valid(); let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T); point_on_curve && on_segre_image } } // ------------------------------------------------------------------------ // Constant-time assignment // ------------------------------------------------------------------------ impl ConditionallySelectable for EdwardsPoint { fn conditional_select(a: &EdwardsPoint, b: &EdwardsPoint, choice: Choice) -> EdwardsPoint { EdwardsPoint { X: FieldElement::conditional_select(&a.X, &b.X, choice), Y: FieldElement::conditional_select(&a.Y, &b.Y, choice), Z: FieldElement::conditional_select(&a.Z, &b.Z, choice), T: FieldElement::conditional_select(&a.T, &b.T, choice), } } } // ------------------------------------------------------------------------ // Equality // ------------------------------------------------------------------------ impl ConstantTimeEq for EdwardsPoint { fn ct_eq(&self, other: &EdwardsPoint) -> Choice { // We would like to check that the point (X/Z, Y/Z) is equal to // the point (X'/Z', Y'/Z') without converting into affine // coordinates (x, y) and (x', y'), which requires two inversions. // We have that X = xZ and X' = x'Z'. Thus, x = x' is equivalent to // (xZ)Z' = (x'Z')Z, and similarly for the y-coordinate. (&self.X * &other.Z).ct_eq(&(&other.X * &self.Z)) & (&self.Y * &other.Z).ct_eq(&(&other.Y * &self.Z)) } } impl PartialEq for EdwardsPoint { fn eq(&self, other: &EdwardsPoint) -> bool { self.ct_eq(other).into() } } impl Eq for EdwardsPoint {} // ------------------------------------------------------------------------ // Point conversions // ------------------------------------------------------------------------ impl EdwardsPoint { /// Convert to a ProjectiveNielsPoint pub(crate) fn as_projective_niels(&self) -> ProjectiveNielsPoint { ProjectiveNielsPoint { Y_plus_X: &self.Y + &self.X, Y_minus_X: &self.Y - &self.X, Z: self.Z, T2d: &self.T * &constants::EDWARDS_D2, } } /// Convert the representation of this point from extended /// coordinates to projective coordinates. /// /// Free. pub(crate) const fn as_projective(&self) -> ProjectivePoint { ProjectivePoint { X: self.X, Y: self.Y, Z: self.Z, } } /// Dehomogenize to a `AffineNielsPoint`. /// Mainly for testing. pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint { let recip = self.Z.invert(); let x = &self.X * &recip; let y = &self.Y * &recip; let xy2d = &(&x * &y) * &constants::EDWARDS_D2; AffineNielsPoint { y_plus_x: &y + &x, y_minus_x: &y - &x, xy2d, } } /// Dehomogenize to `AffinePoint`. pub(crate) fn to_affine(self) -> AffinePoint { let recip = self.Z.invert(); let x = &self.X * &recip; let y = &self.Y * &recip; AffinePoint { x, y } } /// Convert this `EdwardsPoint` on the Edwards model to the /// corresponding `MontgomeryPoint` on the Montgomery model. /// /// This function has one exceptional case; the identity point of /// the Edwards curve is sent to the 2-torsion point \\((0,0)\\) /// on the Montgomery curve. /// /// Note that this is a one-way conversion, since the Montgomery /// model does not retain sign information. pub fn to_montgomery(&self) -> MontgomeryPoint { // We have u = (1+y)/(1-y) = (Z+Y)/(Z-Y). // // The denominator is zero only when y=1, the identity point of // the Edwards curve. Since 0.invert() = 0, in this case we // compute the 2-torsion point (0,0). let U = &self.Z + &self.Y; let W = &self.Z - &self.Y; let u = &U * &W.invert(); MontgomeryPoint(u.to_bytes()) } /// Converts a large batch of points to Edwards at once. This has the same /// behavior on identity elements as [`Self::to_montgomery`]. #[cfg(feature = "alloc")] pub fn to_montgomery_batch(eds: &[Self]) -> Vec { // Do the same thing as the above function. u = (1+y)/(1-y) = (Z+Y)/(Z-Y). // We will do this in a batch, ie compute (Z-Y) for all the input // points, then invert them all at once // Compute the denominators in a batch let mut denominators = eds.iter().map(|p| &p.Z - &p.Y).collect::>(); FieldElement::invert_batch_alloc(&mut denominators); // Now compute the Montgomery u coordinate for every point let mut ret = Vec::with_capacity(eds.len()); for (ed, d) in eds.iter().zip(denominators.iter()) { let u = &(&ed.Z + &ed.Y) * d; ret.push(MontgomeryPoint(u.to_bytes())); } ret } /// Compress this point to `CompressedEdwardsY` format. pub fn compress(&self) -> CompressedEdwardsY { self.to_affine().compress() } /// Compress several `EdwardsPoint`s into `CompressedEdwardsY` format, using a batch inversion /// for a significant speedup. #[cfg(feature = "alloc")] pub fn compress_batch(inputs: &[EdwardsPoint]) -> Vec { let mut zs = inputs.iter().map(|input| input.Z).collect::>(); FieldElement::invert_batch_alloc(&mut zs); inputs .iter() .zip(&zs) .map(|(input, recip)| { let x = &input.X * recip; let y = &input.Y * recip; AffinePoint { x, y }.compress() }) .collect() } #[cfg(feature = "digest")] // The function `map_to_curve` calculates an [EdwardsPoint] from a [FieldElement]. fn map_to_curve(fe: FieldElement) -> EdwardsPoint { let c1 = ED25519_SQRTAM2; // 1. (xMn, xMd, yMn, yMd) = map_to_curve_elligator2_curve25519(u) let (xMn, xMd, yMn, yMd) = crate::montgomery::elligator_encode(&fe); // 2. xn = xMn * yMd let xn = &xMn * &yMd; // 3. xn = xn * c1 let xn = &xn * &c1; // 4. xd = xMd * yMn let xd = &xMd * &yMn; // 5. yn = xMn - xMd let yn = &xMn - &xMd; // 6. yd = xMn + xMd let yd = &xMn + &xMd; // 7. tv1 = xd * yd let tv1 = &xd * &yd; // 8. e = tv1 == 0 let e = tv1.ct_eq(&FieldElement::ZERO); // 9. xn = CMOV(xn, 0, e) let xn = FieldElement::conditional_select(&xn, &FieldElement::ZERO, e); // 10. xd = CMOV(xd, 1, e) let xd = FieldElement::conditional_select(&xd, &FieldElement::ONE, e); // 11. yn = CMOV(yn, 1, e) let yn = FieldElement::conditional_select(&yn, &FieldElement::ONE, e); // 12. yd = CMOV(yd, 1, e) let yd = FieldElement::conditional_select(&yd, &FieldElement::ONE, e); // 13. return (xn, xd, yn, yd) EdwardsPoint { X: &xn * &yd, Y: &xd * &yn, Z: &xd * &yd, T: &xn * &yn, } } #[cfg(feature = "digest")] /// Perform encode to curve per RFC 9380, with explicit hash function and domain separator /// `domain_sep`, using the Twisted Edwards Elligator 2 method. The input is the concatenation /// of the elements of `bytes`. Likewise for the domain separator with `domain_sep`. At least /// one element of `domain_sep`, MUST be nonempty, and the concatenation MUST NOT exceed 255 /// bytes. /// /// The specification names SHA-512 as an example of a secure hash to use with this function, /// but you may use any 512-bit hash within reason (see the /// [`spec`](https://www.rfc-editor.org/rfc/rfc9380.html#section-5.2) for details). /// /// # Warning /// `encode_to_curve` is a nonuniform encoding from byte strings to points in `G`. That is, /// the distribution of its output is not uniformly random in `G`: the set of possible outputs /// of encode_to_curve is only a fraction of the points in `G`, and some points in this set /// are more likely to be output than others. /// /// If your application needs the distribution of the output to be statistically close to /// uniform in `G`, use [Self::hash_to_curve] instead. /// /// # Panics /// Panics if `domain_sep.collect().len() == 0` or `> 255` pub fn encode_to_curve(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint where D: BlockSizeUser + Default + FixedOutput + HashMarker, D::BlockSize: IsGreater, { // For reference see // https://www.rfc-editor.org/rfc/rfc9380.html#name-elligator-2-method-2 let fe = FieldElement::hash_to_field::(bytes, domain_sep); let Q = Self::map_to_curve(fe[0]); Q.mul_by_cofactor() } #[cfg(feature = "digest")] /// Perform a hash to curve per RFC 9380, with explicit hash function and domain separator /// `domain_sep`, using the Twisted Edwards Elligator 2 method. The input is the concatenation /// of the elements of `bytes`. Likewise for the domain separator with `domain_sep`. At least /// one element of `domain_sep`, MUST be nonempty, and the concatenation MUST NOT exceed /// 255 bytes. /// /// The specification names SHA-512 as an example of a secure hash to use with this function, /// but you may use any 512-bit hash within reason (see the /// [`spec`](https://www.rfc-editor.org/rfc/rfc9380.html#section-5.2) for details). /// /// # Panics /// Panics if `domain_sep.collect().len() == 0` or `> 255` pub fn hash_to_curve(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint where D: BlockSizeUser + Default + FixedOutput + HashMarker, D::BlockSize: IsGreater, { // For reference see // https://www.rfc-editor.org/rfc/rfc9380.html#name-elligator-2-method-2 let fe = FieldElement::hash_to_field::(bytes, domain_sep); let Q0 = Self::map_to_curve(fe[0]); let Q1 = Self::map_to_curve(fe[1]); let R = Q0 + Q1; R.mul_by_cofactor() } /// Return an `EdwardsPoint` chosen uniformly at random using a user-provided RNG. /// /// # Inputs /// /// * `rng`: any RNG which implements `CryptoRng` and `RngCore` /// /// # Returns /// /// A random `EdwardsPoint`. /// /// # Implementation /// /// Uses rejection sampling, generating a random `CompressedEdwardsY` and then attempting point /// decompression, rejecting invalid points. #[cfg(feature = "rand_core")] pub fn random(rng: &mut R) -> Self { let mut repr = CompressedEdwardsY([0u8; 32]); loop { rng.fill_bytes(&mut repr.0); if let Some(p) = repr.decompress() { if !IsIdentity::is_identity(&p) { break p; } } } } } // ------------------------------------------------------------------------ // Doubling // ------------------------------------------------------------------------ impl EdwardsPoint { /// Add this point to itself. pub(crate) fn double(&self) -> EdwardsPoint { self.as_projective().double().as_extended() } } // ------------------------------------------------------------------------ // Addition and Subtraction // ------------------------------------------------------------------------ impl<'a> Add<&'a EdwardsPoint> for &EdwardsPoint { type Output = EdwardsPoint; fn add(self, other: &'a EdwardsPoint) -> EdwardsPoint { (self + &other.as_projective_niels()).as_extended() } } define_add_variants!( LHS = EdwardsPoint, RHS = EdwardsPoint, Output = EdwardsPoint ); impl<'a> AddAssign<&'a EdwardsPoint> for EdwardsPoint { fn add_assign(&mut self, _rhs: &'a EdwardsPoint) { *self = (self as &EdwardsPoint) + _rhs; } } define_add_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint); impl<'a> Sub<&'a EdwardsPoint> for &EdwardsPoint { type Output = EdwardsPoint; fn sub(self, other: &'a EdwardsPoint) -> EdwardsPoint { (self - &other.as_projective_niels()).as_extended() } } define_sub_variants!( LHS = EdwardsPoint, RHS = EdwardsPoint, Output = EdwardsPoint ); impl<'a> SubAssign<&'a EdwardsPoint> for EdwardsPoint { fn sub_assign(&mut self, _rhs: &'a EdwardsPoint) { *self = (self as &EdwardsPoint) - _rhs; } } define_sub_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint); impl Sum for EdwardsPoint where T: Borrow, { fn sum(iter: I) -> Self where I: Iterator, { iter.fold(EdwardsPoint::identity(), |acc, item| acc + item.borrow()) } } // ------------------------------------------------------------------------ // Negation // ------------------------------------------------------------------------ impl Neg for &EdwardsPoint { type Output = EdwardsPoint; fn neg(self) -> EdwardsPoint { EdwardsPoint { X: -(&self.X), Y: self.Y, Z: self.Z, T: -(&self.T), } } } impl Neg for EdwardsPoint { type Output = EdwardsPoint; fn neg(self) -> EdwardsPoint { -&self } } // ------------------------------------------------------------------------ // Scalar multiplication // ------------------------------------------------------------------------ impl<'a> MulAssign<&'a Scalar> for EdwardsPoint { fn mul_assign(&mut self, scalar: &'a Scalar) { let result = (self as &EdwardsPoint) * scalar; *self = result; } } define_mul_assign_variants!(LHS = EdwardsPoint, RHS = Scalar); define_mul_variants!(LHS = EdwardsPoint, RHS = Scalar, Output = EdwardsPoint); define_mul_variants!(LHS = Scalar, RHS = EdwardsPoint, Output = EdwardsPoint); impl<'a> Mul<&'a Scalar> for &EdwardsPoint { type Output = EdwardsPoint; /// Scalar multiplication: compute `scalar * self`. /// /// For scalar multiplication of a basepoint, /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, scalar: &'a Scalar) -> EdwardsPoint { crate::backend::variable_base_mul(self, scalar) } } impl<'a> Mul<&'a EdwardsPoint> for &Scalar { type Output = EdwardsPoint; /// Scalar multiplication: compute `scalar * self`. /// /// For scalar multiplication of a basepoint, /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, point: &'a EdwardsPoint) -> EdwardsPoint { point * self } } impl EdwardsPoint { /// Fixed-base scalar multiplication by the Ed25519 base point. /// /// Uses precomputed basepoint tables when the `precomputed-tables` feature /// is enabled, trading off increased code size for ~4x better performance. pub fn mul_base(scalar: &Scalar) -> Self { #[cfg(not(feature = "precomputed-tables"))] { scalar * constants::ED25519_BASEPOINT_POINT } #[cfg(feature = "precomputed-tables")] { scalar * constants::ED25519_BASEPOINT_TABLE } } /// Multiply this point by `clamp_integer(bytes)`. For a description of clamping, see /// [`clamp_integer`]. pub fn mul_clamped(self, bytes: [u8; 32]) -> Self { // We have to construct a Scalar that is not reduced mod l, which breaks scalar invariant // #2. But #2 is not necessary for correctness of variable-base multiplication. All that // needs to hold is invariant #1, i.e., the scalar is less than 2^255. This is guaranteed // by clamping. // Further, we don't do any reduction or arithmetic with this clamped value, so there's no // issues arising from the fact that the curve point is not necessarily in the prime-order // subgroup. let s = Scalar { bytes: clamp_integer(bytes), }; s * self } /// Multiply the basepoint by `clamp_integer(bytes)`. For a description of clamping, see /// [`clamp_integer`]. pub fn mul_base_clamped(bytes: [u8; 32]) -> Self { // See reasoning in Self::mul_clamped why it is OK to make an unreduced Scalar here. We // note that fixed-base multiplication is also defined for all values of `bytes` less than // 2^255. let s = Scalar { bytes: clamp_integer(bytes), }; Self::mul_base(&s) } } // ------------------------------------------------------------------------ // Multiscalar Multiplication impls // ------------------------------------------------------------------------ // These use the iterator's size hint and the target settings to // forward to a specific backend implementation. #[cfg(feature = "alloc")] impl MultiscalarMul for EdwardsPoint { type Point = EdwardsPoint; fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, { // Sanity-check lengths of input iterators let mut scalars = scalars.into_iter(); let mut points = points.into_iter(); // Lower and upper bounds on iterators let (s_lo, s_hi) = scalars.by_ref().size_hint(); let (p_lo, p_hi) = points.by_ref().size_hint(); // They should all be equal assert_eq!(s_lo, p_lo); assert_eq!(s_hi, Some(s_lo)); assert_eq!(p_hi, Some(p_lo)); // Now we know there's a single size. When we do // size-dependent algorithm dispatch, use this as the hint. let _size = s_lo; crate::backend::straus_multiscalar_mul(scalars, points) } } #[cfg(feature = "alloc")] impl VartimeMultiscalarMul for EdwardsPoint { type Point = EdwardsPoint; fn optional_multiscalar_mul(scalars: I, points: J) -> Option where I: IntoIterator, I::Item: Borrow, J: IntoIterator>, { // Sanity-check lengths of input iterators let mut scalars = scalars.into_iter(); let mut points = points.into_iter(); // Lower and upper bounds on iterators let (s_lo, s_hi) = scalars.by_ref().size_hint(); let (p_lo, p_hi) = points.by_ref().size_hint(); // They should all be equal assert_eq!(s_lo, p_lo); assert_eq!(s_hi, Some(s_lo)); assert_eq!(p_hi, Some(p_lo)); // Now we know there's a single size. // Use this as the hint to decide which algorithm to use. let size = s_lo; if size < 190 { crate::backend::straus_optional_multiscalar_mul(scalars, points) } else { crate::backend::pippenger_optional_multiscalar_mul(scalars, points) } } } /// Precomputation for variable-time multiscalar multiplication with `EdwardsPoint`s. // This wraps the inner implementation in a facade type so that we can // decouple stability of the inner type from the stability of the // outer type. #[cfg(feature = "alloc")] pub struct VartimeEdwardsPrecomputation(crate::backend::VartimePrecomputedStraus); #[cfg(feature = "alloc")] impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation { type Point = EdwardsPoint; fn new(static_points: I) -> Self where I: IntoIterator, I::Item: Borrow, { Self(crate::backend::VartimePrecomputedStraus::new(static_points)) } fn len(&self) -> usize { self.0.len() } fn is_empty(&self) -> bool { self.0.is_empty() } fn optional_mixed_multiscalar_mul( &self, static_scalars: I, dynamic_scalars: J, dynamic_points: K, ) -> Option where I: IntoIterator, I::Item: Borrow, J: IntoIterator, J::Item: Borrow, K: IntoIterator>, { self.0 .optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points) } } impl EdwardsPoint { /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. pub fn vartime_double_scalar_mul_basepoint( a: &Scalar, A: &EdwardsPoint, b: &Scalar, ) -> EdwardsPoint { crate::backend::vartime_double_base_mul(a, A, b) } /// Compute \\(a_1 A_1 + a_2 A_2 + b B\\) in variable time, where \\(B\\) is the Ed25519 basepoint. /// /// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\), /// and falls back to general scalar multiplication for full-width scalars. /// /// # Example /// /// ``` /// use curve25519::scalar::Scalar; /// use curve25519::constants::ED25519_BASEPOINT_POINT; /// use curve25519::edwards::EdwardsPoint; /// /// let a1 = Scalar::from(123u64); /// let a2 = Scalar::from(456u64); /// let b = Scalar::from(789u64); /// /// let A1 = &ED25519_BASEPOINT_POINT * &Scalar::from(2u64); /// let A2 = &ED25519_BASEPOINT_POINT * &Scalar::from(3u64); /// /// // Compute a1*A1 + a2*A2 + b*B efficiently /// let result = EdwardsPoint::vartime_triple_scalar_mul_basepoint(&a1, &A1, &a2, &A2, &b); /// ``` #[allow(non_snake_case)] pub fn vartime_triple_scalar_mul_basepoint( a1: &Scalar, A1: &EdwardsPoint, a2: &Scalar, A2: &EdwardsPoint, b: &Scalar, ) -> EdwardsPoint { crate::backend::vartime_triple_base_mul_128_128_256(a1, A1, a2, A2, b) } } #[cfg(feature = "precomputed-tables")] macro_rules! impl_basepoint_table { (Name = $name:ident, LookupTable = $table:ident, Point = $point:ty, Radix = $radix:expr, Additions = $adds:expr) => { /// A precomputed table of multiples of a basepoint, for accelerating /// fixed-base scalar multiplication. One table, for the Ed25519 /// basepoint, is provided in the [`constants`] module. /// /// The basepoint tables are reasonably large, so they should probably be boxed. /// /// The sizes for the tables and the number of additions required for one scalar /// multiplication are as follows: /// /// * [`EdwardsBasepointTableRadix16`]: 30KB, 64A /// (this is the default size, and is used for /// [`constants::ED25519_BASEPOINT_TABLE`]) /// * [`EdwardsBasepointTableRadix64`]: 120KB, 43A /// * [`EdwardsBasepointTableRadix128`]: 240KB, 37A /// * [`EdwardsBasepointTableRadix256`]: 480KB, 33A /// /// # Why 33 additions for radix-256? /// /// Normally, the radix-256 tables would allow for only 32 additions per scalar /// multiplication. However, due to the fact that standardised definitions of /// legacy protocols—such as x25519—require allowing unreduced 255-bit scalars /// invariants, when converting such an unreduced scalar's representation to /// radix-\\(2^{8}\\), we cannot guarantee the carry bit will fit in the last /// coefficient (the coefficients are `i8`s). When, \\(w\\), the power-of-2 of /// the radix, is \\(w < 8\\), we can fold the final carry onto the last /// coefficient, \\(d\\), because \\(d < 2^{w/2}\\), so /// $$ /// d + carry \cdot 2^{w} = d + 1 \cdot 2^{w} < 2^{w+1} < 2^{8} /// $$ /// When \\(w = 8\\), we can't fit \\(carry \cdot 2^{w}\\) into an `i8`, so we /// add the carry bit onto an additional coefficient. #[derive(Clone)] #[repr(transparent)] pub struct $name(pub(crate) [$table; 32]); impl BasepointTable for $name { type Point = $point; /// Create a table of precomputed multiples of `basepoint`. fn create(basepoint: &$point) -> $name { // XXX use init_with let mut table = $name([$table::default(); 32]); let mut P = *basepoint; for i in 0..32 { // P = (2w)^i * B table.0[i] = $table::from(&P); P = P.mul_by_pow_2($radix + $radix); } table } /// Get the basepoint for this table as an `EdwardsPoint`. fn basepoint(&self) -> $point { // self.0[0].select(1) = 1*(16^2)^0*B // but as an `AffineNielsPoint`, so add identity to convert to extended. (&<$point>::identity() + &self.0[0].select(1)).as_extended() } /// The computation uses Pippeneger's algorithm, as described for the /// specific case of radix-16 on page 13 of the Ed25519 paper. /// /// # Piggenger's Algorithm Generalised /// /// Write the scalar \\(a\\) in radix-\\(w\\), where \\(w\\) is a power of /// 2, with coefficients in \\([\frac{-w}{2},\frac{w}{2})\\), i.e., /// $$ /// a = a\_0 + a\_1 w\^1 + \cdots + a\_{x} w\^{x}, /// $$ /// with /// $$ /// \begin{aligned} /// \frac{-w}{2} \leq a_i < \frac{w}{2} /// &&\cdots&& /// \frac{-w}{2} \leq a\_{x} \leq \frac{w}{2} /// \end{aligned} /// $$ /// and the number of additions, \\(x\\), is given by /// \\(x = \lceil \frac{256}{w} \rceil\\). Then /// $$ /// a B = a\_0 B + a\_1 w\^1 B + \cdots + a\_{x-1} w\^{x-1} B. /// $$ /// Grouping even and odd coefficients gives /// $$ /// \begin{aligned} /// a B = \quad a\_0 w\^0 B +& a\_2 w\^2 B + \cdots + a\_{x-2} w\^{x-2} B \\\\ /// + a\_1 w\^1 B +& a\_3 w\^3 B + \cdots + a\_{x-1} w\^{x-1} B \\\\ /// = \quad(a\_0 w\^0 B +& a\_2 w\^2 B + \cdots + a\_{x-2} w\^{x-2} B) \\\\ /// + w(a\_1 w\^0 B +& a\_3 w\^2 B + \cdots + a\_{x-1} w\^{x-2} B). \\\\ /// \end{aligned} /// $$ /// For each \\(i = 0 \ldots 31\\), we create a lookup table of /// $$ /// [w\^{2i} B, \ldots, \frac{w}{2}\cdot w\^{2i} B], /// $$ /// and use it to select \\( y \cdot w\^{2i} \cdot B \\) in constant time. /// /// The radix-\\(w\\) representation requires that the scalar is bounded /// by \\(2\^{255}\\), which is always the case. /// /// The above algorithm is trivially generalised to other powers-of-2 radices. fn mul_base(&self, scalar: &Scalar) -> $point { let a = scalar.as_radix_2w($radix); let tables = &self.0; let mut P = <$point>::identity(); for i in (0..$adds).filter(|x| x % 2 == 1) { P = (&P + &tables[i / 2].select(a[i])).as_extended(); } P = P.mul_by_pow_2($radix); for i in (0..$adds).filter(|x| x % 2 == 0) { P = (&P + &tables[i / 2].select(a[i])).as_extended(); } P } } impl<'a, 'b> Mul<&'b Scalar> for &'a $name { type Output = $point; /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by /// computing the multiple \\(aB\\) of this basepoint \\(B\\). fn mul(self, scalar: &'b Scalar) -> $point { // delegate to a private function so that its documentation appears in internal docs self.mul_base(scalar) } } impl<'a, 'b> Mul<&'a $name> for &'b Scalar { type Output = $point; /// Construct an `EdwardsPoint` from a `Scalar` \\(a\\) by /// computing the multiple \\(aB\\) of this basepoint \\(B\\). fn mul(self, basepoint_table: &'a $name) -> $point { basepoint_table * self } } impl Debug for $name { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?}([\n", stringify!($name))?; for i in 0..32 { write!(f, "\t{:?},\n", &self.0[i])?; } write!(f, "])") } } }; } // End macro_rules! impl_basepoint_table // The number of additions required is ceil(256/w) where w is the radix representation. cfg_if! { if #[cfg(feature = "precomputed-tables")] { impl_basepoint_table! { Name = EdwardsBasepointTable, LookupTable = LookupTableRadix16, Point = EdwardsPoint, Radix = 4, Additions = 64 } impl_basepoint_table! { Name = EdwardsBasepointTableRadix32, LookupTable = LookupTableRadix32, Point = EdwardsPoint, Radix = 5, Additions = 52 } impl_basepoint_table! { Name = EdwardsBasepointTableRadix64, LookupTable = LookupTableRadix64, Point = EdwardsPoint, Radix = 6, Additions = 43 } impl_basepoint_table! { Name = EdwardsBasepointTableRadix128, LookupTable = LookupTableRadix128, Point = EdwardsPoint, Radix = 7, Additions = 37 } impl_basepoint_table! { Name = EdwardsBasepointTableRadix256, LookupTable = LookupTableRadix256, Point = EdwardsPoint, Radix = 8, Additions = 33 } /// A type-alias for [`EdwardsBasepointTable`] because the latter is /// used as a constructor in the [`constants`] module. // // Same as for `LookupTableRadix16`, we have to define `EdwardsBasepointTable` // first, because it's used as a constructor, and then provide a type alias for // it. pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable; } } #[cfg(feature = "precomputed-tables")] macro_rules! impl_basepoint_table_conversions { (LHS = $lhs:ty, RHS = $rhs:ty) => { impl<'a> From<&'a $lhs> for $rhs { fn from(table: &'a $lhs) -> $rhs { <$rhs>::create(&table.basepoint()) } } impl<'a> From<&'a $rhs> for $lhs { fn from(table: &'a $rhs) -> $lhs { <$lhs>::create(&table.basepoint()) } } }; } cfg_if! { if #[cfg(feature = "precomputed-tables")] { // Conversions from radix 16 impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix32 } impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix64 } impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix128 } impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix16, RHS = EdwardsBasepointTableRadix256 } // Conversions from radix 32 impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix32, RHS = EdwardsBasepointTableRadix64 } impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix32, RHS = EdwardsBasepointTableRadix128 } impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix32, RHS = EdwardsBasepointTableRadix256 } // Conversions from radix 64 impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix64, RHS = EdwardsBasepointTableRadix128 } impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix64, RHS = EdwardsBasepointTableRadix256 } // Conversions from radix 128 impl_basepoint_table_conversions! { LHS = EdwardsBasepointTableRadix128, RHS = EdwardsBasepointTableRadix256 } } } impl EdwardsPoint { /// Multiply by the cofactor: return \\(\[8\]P\\). pub fn mul_by_cofactor(&self) -> EdwardsPoint { self.mul_by_pow_2(3) } /// Compute \\([2\^k] P \\) by successive doublings. Requires \\( k > 0 \\). pub(crate) fn mul_by_pow_2(&self, k: u32) -> EdwardsPoint { debug_assert!(k > 0); let mut r: CompletedPoint; let mut s = self.as_projective(); for _ in 0..(k - 1) { r = s.double(); s = r.as_projective(); } // Unroll last iteration so we can go directly as_extended() s.double().as_extended() } /// Determine if this point is of small order. /// /// # Return /// /// * `true` if `self` is in the torsion subgroup \\( \mathcal E\[8\] \\); /// * `false` if `self` is not in the torsion subgroup \\( \mathcal E\[8\] \\). /// /// # Example /// /// ``` /// use curve25519::constants; /// /// // Generator of the prime-order subgroup /// let P = constants::ED25519_BASEPOINT_POINT; /// // Generator of the torsion subgroup /// let Q = constants::EIGHT_TORSION[1]; /// /// // P has large order /// assert_eq!(P.is_small_order(), false); /// /// // Q has small order /// assert_eq!(Q.is_small_order(), true); /// ``` pub fn is_small_order(&self) -> bool { self.mul_by_cofactor().is_identity() } /// Determine if this point is “torsion-free”, i.e., is contained in /// the prime-order subgroup. /// /// # Return /// /// * `true` if `self` has zero torsion component and is in the /// prime-order subgroup; /// * `false` if `self` has a nonzero torsion component and is not /// in the prime-order subgroup. /// /// # Example /// /// ``` /// use curve25519::constants; /// /// // Generator of the prime-order subgroup /// let P = constants::ED25519_BASEPOINT_POINT; /// // Generator of the torsion subgroup /// let Q = constants::EIGHT_TORSION[1]; /// /// // P is torsion-free /// assert_eq!(P.is_torsion_free(), true); /// /// // P + Q is not torsion-free /// assert_eq!((P+Q).is_torsion_free(), false); /// ``` pub fn is_torsion_free(&self) -> bool { (self * constants::BASEPOINT_ORDER).is_identity() } } // ------------------------------------------------------------------------ // Debug traits // ------------------------------------------------------------------------ impl Debug for EdwardsPoint { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, "EdwardsPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}", &self.X, &self.Y, &self.Z, &self.T ) } } // ------------------------------------------------------------------------ // group traits // ------------------------------------------------------------------------ // Use the full trait path to avoid Group::identity overlapping Identity::identity in the // rest of the module (e.g. tests). #[cfg(feature = "group")] impl group::Group for EdwardsPoint { type Scalar = Scalar; fn random(mut rng: impl GroupRngCore) -> Self { let mut repr = CompressedEdwardsY([0u8; 32]); loop { rng.fill_bytes(&mut repr.0); if let Some(p) = repr.decompress() { if !IsIdentity::is_identity(&p) { break p; } } } } fn identity() -> Self { Identity::identity() } fn generator() -> Self { constants::ED25519_BASEPOINT_POINT } fn is_identity(&self) -> Choice { self.ct_eq(&Identity::identity()) } fn double(&self) -> Self { self.double() } } #[cfg(feature = "group")] impl GroupEncoding for EdwardsPoint { type Repr = [u8; 32]; fn from_bytes(bytes: &Self::Repr) -> CtOption { let repr = CompressedEdwardsY(*bytes); let (is_valid_y_coord, X, Y, Z) = decompress::step_1(&repr); CtOption::new(decompress::step_2(&repr, X, Y, Z), is_valid_y_coord) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { // Just use the checked API; there are no checks we can skip. Self::from_bytes(bytes) } fn to_bytes(&self) -> Self::Repr { self.compress().to_bytes() } } /// A `SubgroupPoint` represents a point on the Edwards form of Curve25519, that is /// guaranteed to be in the prime-order subgroup. #[cfg(feature = "group")] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct SubgroupPoint(EdwardsPoint); #[cfg(feature = "group")] impl From for EdwardsPoint { fn from(p: SubgroupPoint) -> Self { p.0 } } #[cfg(feature = "group")] impl Neg for SubgroupPoint { type Output = Self; fn neg(self) -> Self::Output { SubgroupPoint(-self.0) } } #[cfg(feature = "group")] impl Add<&SubgroupPoint> for &SubgroupPoint { type Output = SubgroupPoint; fn add(self, other: &SubgroupPoint) -> SubgroupPoint { SubgroupPoint(self.0 + other.0) } } #[cfg(feature = "group")] define_add_variants!( LHS = SubgroupPoint, RHS = SubgroupPoint, Output = SubgroupPoint ); #[cfg(feature = "group")] impl Add<&SubgroupPoint> for &EdwardsPoint { type Output = EdwardsPoint; fn add(self, other: &SubgroupPoint) -> EdwardsPoint { self + other.0 } } #[cfg(feature = "group")] define_add_variants!( LHS = EdwardsPoint, RHS = SubgroupPoint, Output = EdwardsPoint ); #[cfg(feature = "group")] impl AddAssign<&SubgroupPoint> for SubgroupPoint { fn add_assign(&mut self, rhs: &SubgroupPoint) { self.0 += rhs.0 } } #[cfg(feature = "group")] define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint); #[cfg(feature = "group")] impl AddAssign<&SubgroupPoint> for EdwardsPoint { fn add_assign(&mut self, rhs: &SubgroupPoint) { *self += rhs.0 } } #[cfg(feature = "group")] define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint); #[cfg(feature = "group")] impl Sub<&SubgroupPoint> for &SubgroupPoint { type Output = SubgroupPoint; fn sub(self, other: &SubgroupPoint) -> SubgroupPoint { SubgroupPoint(self.0 - other.0) } } #[cfg(feature = "group")] define_sub_variants!( LHS = SubgroupPoint, RHS = SubgroupPoint, Output = SubgroupPoint ); #[cfg(feature = "group")] impl Sub<&SubgroupPoint> for &EdwardsPoint { type Output = EdwardsPoint; fn sub(self, other: &SubgroupPoint) -> EdwardsPoint { self - other.0 } } #[cfg(feature = "group")] define_sub_variants!( LHS = EdwardsPoint, RHS = SubgroupPoint, Output = EdwardsPoint ); #[cfg(feature = "group")] impl SubAssign<&SubgroupPoint> for SubgroupPoint { fn sub_assign(&mut self, rhs: &SubgroupPoint) { self.0 -= rhs.0; } } #[cfg(feature = "group")] define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint); #[cfg(feature = "group")] impl SubAssign<&SubgroupPoint> for EdwardsPoint { fn sub_assign(&mut self, rhs: &SubgroupPoint) { *self -= rhs.0; } } #[cfg(feature = "group")] define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint); #[cfg(feature = "group")] impl Sum for SubgroupPoint where T: Borrow, { fn sum(iter: I) -> Self where I: Iterator, { use group::Group; iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow()) } } #[cfg(feature = "group")] impl Mul<&Scalar> for &SubgroupPoint { type Output = SubgroupPoint; /// Scalar multiplication: compute `scalar * self`. /// /// For scalar multiplication of a basepoint, /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, scalar: &Scalar) -> SubgroupPoint { SubgroupPoint(self.0 * scalar) } } #[cfg(feature = "group")] define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint); #[cfg(feature = "group")] impl Mul<&SubgroupPoint> for &Scalar { type Output = SubgroupPoint; /// Scalar multiplication: compute `scalar * self`. /// /// For scalar multiplication of a basepoint, /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, point: &SubgroupPoint) -> SubgroupPoint { point * self } } #[cfg(feature = "group")] define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint); #[cfg(feature = "group")] impl MulAssign<&Scalar> for SubgroupPoint { fn mul_assign(&mut self, scalar: &Scalar) { self.0 *= scalar; } } #[cfg(feature = "group")] define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar); #[cfg(feature = "group")] impl ConstantTimeEq for SubgroupPoint { fn ct_eq(&self, other: &SubgroupPoint) -> Choice { self.0.ct_eq(&other.0) } } #[cfg(feature = "group")] impl ConditionallySelectable for SubgroupPoint { fn conditional_select(a: &SubgroupPoint, b: &SubgroupPoint, choice: Choice) -> SubgroupPoint { SubgroupPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice)) } } #[cfg(all(feature = "group", feature = "zeroize"))] impl Zeroize for SubgroupPoint { fn zeroize(&mut self) { self.0.zeroize(); } } #[cfg(feature = "group")] impl group::Group for SubgroupPoint { type Scalar = Scalar; fn random(mut rng: impl GroupRngCore) -> Self { use group::ff::Field; // This will almost never loop, but `Group::random` is documented as returning a // non-identity element. let s = loop { let s: Scalar = Field::random(&mut rng); if !s.is_zero_vartime() { break s; } }; // This gives an element of the prime-order subgroup. Self::generator() * s } fn identity() -> Self { SubgroupPoint(Identity::identity()) } fn generator() -> Self { SubgroupPoint(EdwardsPoint::generator()) } fn is_identity(&self) -> Choice { self.0.ct_eq(&Identity::identity()) } fn double(&self) -> Self { SubgroupPoint(self.0.double()) } } #[cfg(feature = "group")] impl GroupEncoding for SubgroupPoint { type Repr = ::Repr; fn from_bytes(bytes: &Self::Repr) -> CtOption { EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup()) } fn to_bytes(&self) -> Self::Repr { self.0.compress().to_bytes() } } #[cfg(feature = "group")] impl PrimeGroup for SubgroupPoint {} #[cfg(feature = "group")] impl CofactorGroup for EdwardsPoint { type Subgroup = SubgroupPoint; fn clear_cofactor(&self) -> Self::Subgroup { SubgroupPoint(self.mul_by_cofactor()) } fn into_subgroup(self) -> CtOption { CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self)) } fn is_torsion_free(&self) -> Choice { (self * constants::BASEPOINT_ORDER).ct_eq(&Self::identity()) } } // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ #[cfg(test)] mod test { use super::*; use rand::Rng; #[cfg(feature = "precomputed-tables")] use crate::constants::ED25519_BASEPOINT_TABLE; /// X coordinate of the basepoint. /// = 15112221349535400772501151409588531511454012693041857206046113283949847762202 static BASE_X_COORD_BYTES: [u8; 32] = [ 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21, ]; /// Compressed Edwards Y form of 2*basepoint. static BASE2_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([ 0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0xe, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39, 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd, 0x60, 0x22, ]); /// Compressed Edwards Y form of 16*basepoint. static BASE16_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([ 0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a, 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96, 0x96, 0x70, ]); /// 4493907448824000747700850167940867464579944529806937181821189941592931634714 pub static A_SCALAR: Scalar = Scalar { bytes: [ 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09, ], }; /// 2506056684125797857694181776241676200180934651973138769173342316833279714961 pub static B_SCALAR: Scalar = Scalar { bytes: [ 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0, 0xb3, 0x2e, 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, 0x56, 0xa7, 0xd4, 0xaa, 0xb8, 0x60, 0x8a, 0x05, ], }; /// A_SCALAR * basepoint, computed with ed25519.py pub static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([ 0xea, 0x27, 0xe2, 0x60, 0x53, 0xdf, 0x1b, 0x59, 0x56, 0xf1, 0x4d, 0x5d, 0xec, 0x3c, 0x34, 0xc3, 0x84, 0xa2, 0x69, 0xb7, 0x4c, 0xc3, 0x80, 0x3e, 0xa8, 0xe2, 0xe7, 0xc9, 0x42, 0x5e, 0x40, 0xa5, ]); /// A_SCALAR * (A_TIMES_BASEPOINT) + B_SCALAR * BASEPOINT /// computed with ed25519.py static DOUBLE_SCALAR_MULT_RESULT: CompressedEdwardsY = CompressedEdwardsY([ 0x7d, 0xfd, 0x6c, 0x45, 0xaf, 0x6d, 0x6e, 0x0e, 0xba, 0x20, 0x37, 0x1a, 0x23, 0x64, 0x59, 0xc4, 0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85, 0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13, 0x2b, 0x42, ]); /// Test round-trip decompression for the basepoint. #[test] fn basepoint_decompression_compression() { let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES); let bp = constants::ED25519_BASEPOINT_COMPRESSED .decompress() .expect("basepoint should decompress"); assert!(bp.is_valid()); // Check that decompression actually gives the correct X coordinate assert_eq!(base_X, bp.X); assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED); } /// Test sign handling in decompression #[test] fn decompression_sign_handling() { // Manually set the high bit of the last byte to flip the sign let mut minus_basepoint_bytes = *constants::ED25519_BASEPOINT_COMPRESSED.as_bytes(); minus_basepoint_bytes[31] |= 1 << 7; let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes) .decompress() .expect("minus basepoint should decompress"); // Test projective coordinates exactly since we know they should // only differ by a flipped sign. assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT_POINT.X)); assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT_POINT.Y); assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT_POINT.Z); assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT_POINT.T)); } /// Test that computing 1*basepoint gives the correct basepoint. #[cfg(feature = "precomputed-tables")] #[test] fn basepoint_mult_one_vs_basepoint() { let bp = ED25519_BASEPOINT_TABLE * &Scalar::ONE; let compressed = bp.compress(); assert_eq!(compressed, constants::ED25519_BASEPOINT_COMPRESSED); } /// Test that `EdwardsBasepointTable::basepoint()` gives the correct basepoint. #[cfg(feature = "precomputed-tables")] #[test] fn basepoint_table_basepoint_function_correct() { let bp = ED25519_BASEPOINT_TABLE.basepoint(); assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED); } /// Test `impl Add for EdwardsPoint` /// using basepoint + basepoint versus the 2*basepoint constant. #[test] fn basepoint_plus_basepoint_vs_basepoint2() { let bp = constants::ED25519_BASEPOINT_POINT; let bp_added = bp + bp; assert_eq!(bp_added.compress(), BASE2_CMPRSSD); } /// Test `impl Add for EdwardsPoint` /// using the basepoint, basepoint2 constants #[test] fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() { let bp = constants::ED25519_BASEPOINT_POINT; let bp_added = (&bp + &bp.as_projective_niels()).as_extended(); assert_eq!(bp_added.compress(), BASE2_CMPRSSD); } /// Test `impl Add for EdwardsPoint` /// using the basepoint, basepoint2 constants #[test] fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() { let bp = constants::ED25519_BASEPOINT_POINT; let bp_affine_niels = bp.as_affine_niels(); let bp_added = (&bp + &bp_affine_niels).as_extended(); assert_eq!(bp_added.compress(), BASE2_CMPRSSD); } /// Check that equality of `EdwardsPoints` handles projective /// coordinates correctly. #[test] fn extended_point_equality_handles_scaling() { let mut two_bytes = [0u8; 32]; two_bytes[0] = 2; let id1 = EdwardsPoint::identity(); let id2 = EdwardsPoint { X: FieldElement::ZERO, Y: FieldElement::from_bytes(&two_bytes), Z: FieldElement::from_bytes(&two_bytes), T: FieldElement::ZERO, }; assert!(bool::from(id1.ct_eq(&id2))); } /// Sanity check for conversion to precomputed points #[cfg(feature = "precomputed-tables")] #[test] fn to_affine_niels_clears_denominators() { // construct a point as aB so it has denominators (ie. Z != 1) let aB = ED25519_BASEPOINT_TABLE * &A_SCALAR; let aB_affine_niels = aB.as_affine_niels(); let also_aB = (&EdwardsPoint::identity() + &aB_affine_niels).as_extended(); assert_eq!(aB.compress(), also_aB.compress()); } /// Test mul_base versus a known scalar multiple from ed25519.py #[test] fn basepoint_mult_vs_ed25519py() { let aB = EdwardsPoint::mul_base(&A_SCALAR); assert_eq!(aB.compress(), A_TIMES_BASEPOINT); } /// Test that multiplication by the basepoint order kills the basepoint #[test] fn basepoint_mult_by_basepoint_order() { let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER); assert!(should_be_id.is_identity()); } /// Test precomputed basepoint mult #[cfg(feature = "precomputed-tables")] #[test] fn test_precomputed_basepoint_mult() { let aB_1 = ED25519_BASEPOINT_TABLE * &A_SCALAR; let aB_2 = constants::ED25519_BASEPOINT_POINT * A_SCALAR; assert_eq!(aB_1.compress(), aB_2.compress()); } /// Test scalar_mul versus a known scalar multiple from ed25519.py #[test] fn scalar_mul_vs_ed25519py() { let aB = constants::ED25519_BASEPOINT_POINT * A_SCALAR; assert_eq!(aB.compress(), A_TIMES_BASEPOINT); } /// Test basepoint.double() versus the 2*basepoint constant. #[test] fn basepoint_double_vs_basepoint2() { assert_eq!( constants::ED25519_BASEPOINT_POINT.double().compress(), BASE2_CMPRSSD ); } /// Test that computing 2*basepoint is the same as basepoint.double() #[test] fn basepoint_mult_two_vs_basepoint2() { let two = Scalar::from(2u64); let bp2 = EdwardsPoint::mul_base(&two); assert_eq!(bp2.compress(), BASE2_CMPRSSD); } /// Test that all the basepoint table types compute the same results. #[cfg(feature = "precomputed-tables")] #[test] fn basepoint_tables() { let P = &constants::ED25519_BASEPOINT_POINT; let a = A_SCALAR; let table_radix16 = EdwardsBasepointTableRadix16::create(P); let table_radix32 = EdwardsBasepointTableRadix32::create(P); let table_radix64 = EdwardsBasepointTableRadix64::create(P); let table_radix128 = EdwardsBasepointTableRadix128::create(P); let table_radix256 = EdwardsBasepointTableRadix256::create(P); let aP = (ED25519_BASEPOINT_TABLE * &a).compress(); let aP16 = (&table_radix16 * &a).compress(); let aP32 = (&table_radix32 * &a).compress(); let aP64 = (&table_radix64 * &a).compress(); let aP128 = (&table_radix128 * &a).compress(); let aP256 = (&table_radix256 * &a).compress(); assert_eq!(aP, aP16); assert_eq!(aP16, aP32); assert_eq!(aP32, aP64); assert_eq!(aP64, aP128); assert_eq!(aP128, aP256); } /// Check unreduced scalar multiplication by the basepoint tables is the same no matter what /// radix the table is. #[cfg(feature = "precomputed-tables")] #[test] fn basepoint_tables_unreduced_scalar() { let P = &constants::ED25519_BASEPOINT_POINT; let a = crate::scalar::test::LARGEST_UNREDUCED_SCALAR; let table_radix16 = EdwardsBasepointTableRadix16::create(P); let table_radix32 = EdwardsBasepointTableRadix32::create(P); let table_radix64 = EdwardsBasepointTableRadix64::create(P); let table_radix128 = EdwardsBasepointTableRadix128::create(P); let table_radix256 = EdwardsBasepointTableRadix256::create(P); let aP = (ED25519_BASEPOINT_TABLE * &a).compress(); let aP16 = (&table_radix16 * &a).compress(); let aP32 = (&table_radix32 * &a).compress(); let aP64 = (&table_radix64 * &a).compress(); let aP128 = (&table_radix128 * &a).compress(); let aP256 = (&table_radix256 * &a).compress(); assert_eq!(aP, aP16); assert_eq!(aP16, aP32); assert_eq!(aP32, aP64); assert_eq!(aP64, aP128); assert_eq!(aP128, aP256); } /// Check that converting to projective and then back to extended round-trips. #[test] fn basepoint_projective_extended_round_trip() { assert_eq!( constants::ED25519_BASEPOINT_POINT .as_projective() .as_extended() .compress(), constants::ED25519_BASEPOINT_COMPRESSED ); } /// Test computing 16*basepoint vs mul_by_pow_2(4) #[test] fn basepoint16_vs_mul_by_pow_2_4() { let bp16 = constants::ED25519_BASEPOINT_POINT.mul_by_pow_2(4); assert_eq!(bp16.compress(), BASE16_CMPRSSD); } /// Check that mul_base_clamped and mul_clamped agree #[test] fn mul_base_clamped() { let mut csprng = rand::thread_rng(); // Make a random curve point in the curve. Give it torsion to make things interesting. #[cfg(feature = "precomputed-tables")] let random_point = { let mut b = [0u8; 32]; csprng.fill(&mut b); EdwardsPoint::mul_base_clamped(b) + constants::EIGHT_TORSION[1] }; // Make a basepoint table from the random point. We'll use this with mul_base_clamped #[cfg(feature = "precomputed-tables")] let random_table = EdwardsBasepointTableRadix256::create(&random_point); // Now test scalar mult. agreement on the default basepoint as well as random_point // Test that mul_base_clamped and mul_clamped agree on a large integer. Even after // clamping, this integer is not reduced mod l. let a_bytes = [0xff; 32]; assert_eq!( EdwardsPoint::mul_base_clamped(a_bytes), constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes) ); #[cfg(feature = "precomputed-tables")] assert_eq!( random_table.mul_base_clamped(a_bytes), random_point.mul_clamped(a_bytes) ); // Test agreement on random integers for _ in 0..100 { // This will be reduced mod l with probability l / 2^256 ≈ 6.25% let mut a_bytes = [0u8; 32]; csprng.fill(&mut a_bytes); assert_eq!( EdwardsPoint::mul_base_clamped(a_bytes), constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes) ); #[cfg(feature = "precomputed-tables")] assert_eq!( random_table.mul_base_clamped(a_bytes), random_point.mul_clamped(a_bytes) ); } } #[test] #[cfg(feature = "alloc")] fn impl_sum() { // Test that sum works for non-empty iterators let BASE = constants::ED25519_BASEPOINT_POINT; let s1 = Scalar::from(999u64); let P1 = BASE * s1; let s2 = Scalar::from(333u64); let P2 = BASE * s2; let arr = [P1, P2]; let sum: EdwardsPoint = arr.iter().sum(); assert_eq!(sum, P1 + P2); // Test that sum works for the empty iterator let empty_array: [EdwardsPoint; 0] = []; let sum: EdwardsPoint = empty_array.iter().sum(); assert_eq!(sum, EdwardsPoint::identity()); // Test that sum works on owning iterators let s = Scalar::from(2u64); let mapped = arr.iter().map(|x| x * s); let sum: EdwardsPoint = mapped.sum(); assert_eq!(sum, P1 * s + P2 * s); } /// Test that the conditional assignment trait works for AffineNielsPoints. #[test] fn conditional_assign_for_affine_niels_point() { let id = AffineNielsPoint::identity(); let mut p1 = AffineNielsPoint::identity(); let bp = constants::ED25519_BASEPOINT_POINT.as_affine_niels(); p1.conditional_assign(&bp, Choice::from(0)); assert_eq!(p1, id); p1.conditional_assign(&bp, Choice::from(1)); assert_eq!(p1, bp); } #[test] fn is_small_order() { // The basepoint has large prime order assert!(!constants::ED25519_BASEPOINT_POINT.is_small_order()); // constants::EIGHT_TORSION has all points of small order. for torsion_point in &constants::EIGHT_TORSION { assert!(torsion_point.is_small_order()); } } #[test] fn compressed_identity() { assert_eq!( EdwardsPoint::identity().compress(), CompressedEdwardsY::identity() ); #[cfg(feature = "alloc")] { let compressed = EdwardsPoint::compress_batch(&[EdwardsPoint::identity()]); assert_eq!(&compressed, &[CompressedEdwardsY::identity()]); } } #[cfg(all(feature = "alloc", feature = "rand_core"))] #[test] fn compress_batch() { let mut rng = rand::thread_rng(); // TODO(tarcieri): proptests? // Make some points deterministically then randomly let mut points = (1u64..16) .map(|n| constants::ED25519_BASEPOINT_POINT * Scalar::from(n)) .collect::>(); points.extend(core::iter::repeat_with(|| EdwardsPoint::random(&mut rng)).take(100)); let compressed = EdwardsPoint::compress_batch(&points); // Check that the batch-compressed points match the individually compressed ones for (point, compressed) in points.iter().zip(&compressed) { assert_eq!(&point.compress(), compressed); } } #[test] fn is_identity() { assert!(EdwardsPoint::identity().is_identity()); assert!(!constants::ED25519_BASEPOINT_POINT.is_identity()); } /// Rust's debug builds have overflow and underflow trapping, /// and enable `debug_assert!()`. This performs many scalar /// multiplications to attempt to trigger possible overflows etc. /// /// For instance, the `u64` `Mul` implementation for /// `FieldElements` requires the input `Limb`s to be bounded by /// 2^54, but we cannot enforce this dynamically at runtime, or /// statically at compile time (until Rust gets type-level /// integers, at which point we can encode "bits of headroom" into /// the type system and prove correctness). #[test] fn monte_carlo_overflow_underflow_debug_assert_test() { let mut P = constants::ED25519_BASEPOINT_POINT; // N.B. each scalar_mul does 1407 field mults, 1024 field squarings, // so this does ~ 1M of each operation. for _ in 0..1_000 { P *= &A_SCALAR; } } #[test] fn scalarmult_extended_point_works_both_ways() { let G: EdwardsPoint = constants::ED25519_BASEPOINT_POINT; let s: Scalar = A_SCALAR; let P1 = G * s; let P2 = s * G; assert!(P1.compress().to_bytes() == P2.compress().to_bytes()); } // A single iteration of a consistency check for MSM. #[cfg(all(feature = "alloc", feature = "rand_core"))] fn multiscalar_consistency_iter(n: usize) { let mut rng = rand::thread_rng(); // Construct random coefficients x0, ..., x_{n-1}, // followed by some extra hardcoded ones. let xs = (0..n).map(|_| Scalar::random(&mut rng)).collect::>(); let check = xs.iter().map(|xi| xi * xi).sum::(); // Construct points G_i = x_i * B let Gs = xs.iter().map(EdwardsPoint::mul_base).collect::>(); // Compute H1 = (consttime) let H1 = EdwardsPoint::multiscalar_mul(&xs, &Gs); // Compute H2 = (vartime) let H2 = EdwardsPoint::vartime_multiscalar_mul(&xs, &Gs); // Compute H3 = = sum(xi^2) * B let H3 = EdwardsPoint::mul_base(&check); assert_eq!(H1, H3); assert_eq!(H2, H3); } // Use different multiscalar sizes to hit different internal // parameters. #[test] #[cfg(all(feature = "alloc", feature = "rand_core"))] fn multiscalar_consistency_n_100() { let iters = 50; for _ in 0..iters { multiscalar_consistency_iter(100); } } #[test] #[cfg(all(feature = "alloc", feature = "rand_core"))] fn multiscalar_consistency_n_250() { let iters = 50; for _ in 0..iters { multiscalar_consistency_iter(250); } } #[test] #[cfg(all(feature = "alloc", feature = "rand_core"))] fn multiscalar_consistency_n_500() { let iters = 50; for _ in 0..iters { multiscalar_consistency_iter(500); } } #[test] #[cfg(all(feature = "alloc", feature = "rand_core"))] fn multiscalar_consistency_n_1000() { let iters = 50; for _ in 0..iters { multiscalar_consistency_iter(1000); } } #[test] #[cfg(all(feature = "alloc", feature = "rand_core"))] fn batch_to_montgomery() { let mut rng = rand::thread_rng(); let scalars = (0..128) .map(|_| Scalar::random(&mut rng)) .collect::>(); let points = scalars .iter() .map(EdwardsPoint::mul_base) .collect::>(); let single_monts = points .iter() .map(EdwardsPoint::to_montgomery) .collect::>(); for i in [0, 1, 2, 3, 10, 50, 128] { let invs = EdwardsPoint::to_montgomery_batch(&points[..i]); assert_eq!(&invs, &single_monts[..i]); } } #[test] #[cfg(all(feature = "alloc", feature = "rand_core"))] fn vartime_precomputed_vs_nonprecomputed_multiscalar() { let mut rng = rand::thread_rng(); let static_scalars = (0..128) .map(|_| Scalar::random(&mut rng)) .collect::>(); let dynamic_scalars = (0..128) .map(|_| Scalar::random(&mut rng)) .collect::>(); let check_scalar: Scalar = static_scalars .iter() .chain(dynamic_scalars.iter()) .map(|s| s * s) .sum(); let static_points = static_scalars .iter() .map(EdwardsPoint::mul_base) .collect::>(); let dynamic_points = dynamic_scalars .iter() .map(EdwardsPoint::mul_base) .collect::>(); let precomputation = VartimeEdwardsPrecomputation::new(static_points.iter()); assert_eq!(precomputation.len(), 128); assert!(!precomputation.is_empty()); let P = precomputation.vartime_mixed_multiscalar_mul( &static_scalars, &dynamic_scalars, &dynamic_points, ); use crate::traits::VartimeMultiscalarMul; let Q = EdwardsPoint::vartime_multiscalar_mul( static_scalars.iter().chain(dynamic_scalars.iter()), static_points.iter().chain(dynamic_points.iter()), ); let R = EdwardsPoint::mul_base(&check_scalar); assert_eq!(P.compress(), R.compress()); assert_eq!(Q.compress(), R.compress()); } mod vartime { use super::super::*; use super::{A_SCALAR, A_TIMES_BASEPOINT, B_SCALAR, DOUBLE_SCALAR_MULT_RESULT}; /// Test double_scalar_mul_vartime vs ed25519.py #[test] fn double_scalar_mul_basepoint_vs_ed25519py() { let A = A_TIMES_BASEPOINT .decompress() .expect("test point should decompress"); let result = EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR); assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT); } #[test] fn triple_scalar_mul_basepoint_accepts_full_width_scalars() { let mut a1_bytes = [0u8; 32]; a1_bytes[0] = 7; a1_bytes[16] = 1; let a1 = Scalar::from_canonical_bytes(a1_bytes).unwrap(); let mut a2_bytes = [0u8; 32]; a2_bytes[0] = 11; a2_bytes[24] = 1; let a2 = Scalar::from_canonical_bytes(a2_bytes).unwrap(); let b = B_SCALAR; let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(17u64); let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(19u64); let result = EdwardsPoint::vartime_triple_scalar_mul_basepoint(&a1, &A1, &a2, &A2, &b); let expected = (a1 * A1) + (a2 * A2) + EdwardsPoint::mul_base(&b); assert_eq!(result, expected); } #[test] #[cfg(feature = "alloc")] fn multiscalar_mul_vs_ed25519py() { let A = A_TIMES_BASEPOINT .decompress() .expect("test point should decompress"); let result = EdwardsPoint::vartime_multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT], ); assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT); } #[test] #[cfg(feature = "alloc")] fn multiscalar_mul_vartime_vs_consttime() { let A = A_TIMES_BASEPOINT .decompress() .expect("test point should decompress"); let result_vartime = EdwardsPoint::vartime_multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT], ); let result_consttime = EdwardsPoint::multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT], ); assert_eq!(result_vartime.compress(), result_consttime.compress()); } } #[test] #[cfg(feature = "serde")] fn serde_bincode_basepoint_roundtrip() { use bincode; let encoded = bincode::serialize(&constants::ED25519_BASEPOINT_POINT).unwrap(); let enc_compressed = bincode::serialize(&constants::ED25519_BASEPOINT_COMPRESSED).unwrap(); assert_eq!(encoded, enc_compressed); // Check that the encoding is 32 bytes exactly assert_eq!(encoded.len(), 32); let dec_uncompressed: EdwardsPoint = bincode::deserialize(&encoded).unwrap(); let dec_compressed: CompressedEdwardsY = bincode::deserialize(&encoded).unwrap(); assert_eq!(dec_uncompressed, constants::ED25519_BASEPOINT_POINT); assert_eq!(dec_compressed, constants::ED25519_BASEPOINT_COMPRESSED); // Check that the encoding itself matches the usual one let raw_bytes = constants::ED25519_BASEPOINT_COMPRESSED.as_bytes(); let bp: EdwardsPoint = bincode::deserialize(raw_bytes).unwrap(); assert_eq!(bp, constants::ED25519_BASEPOINT_POINT); } // Hash-to-curve test vectors from // https://www.rfc-editor.org/rfc/rfc9380.html#appendix-J.5.2 // These are of the form (input_msg, output_x, output_y) #[cfg(all(feature = "alloc", feature = "digest"))] const RFC_ENCODE_TO_CURVE_KAT: &[(&[u8], &str, &str)] = &[ ( b"", "1ff2b70ecf862799e11b7ae744e3489aa058ce805dd323a936375a84695e76da", "222e314d04a4d5725e9f2aff9fb2a6b69ef375a1214eb19021ceab2d687f0f9b", ), ( b"abc", "5f13cc69c891d86927eb37bd4afc6672360007c63f68a33ab423a3aa040fd2a8", "67732d50f9a26f73111dd1ed5dba225614e538599db58ba30aaea1f5c827fa42", ), ( b"abcdef0123456789", "1dd2fefce934ecfd7aae6ec998de088d7dd03316aa1847198aecf699ba6613f1", "2f8a6c24dd1adde73909cada6a4a137577b0f179d336685c4a955a0a8e1a86fb", ), ( b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "35fbdc5143e8a97afd3096f2b843e07df72e15bfca2eaf6879bf97c5d3362f73", "2af6ff6ef5ebba128b0774f4296cb4c2279a074658b083b8dcca91f57a603450", ), ( b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "6e5e1f37e99345887fc12111575fc1c3e36df4b289b8759d23af14d774b66bff", "2c90c3d39eb18ff291d33441b35f3262cdd307162cc97c31bfcc7a4245891a37" ) ]; #[cfg(all(feature = "alloc", feature = "digest"))] fn hex_str_to_fe(hex_str: &str) -> FieldElement { let mut bytes = hex::decode(hex_str).expect("RFC test vector hex must decode"); bytes.reverse(); let bytes: [u8; 32] = bytes .try_into() .expect("RFC test vector field element must be 32 bytes"); FieldElement::from_bytes(&bytes) } #[test] #[cfg(all(feature = "alloc", feature = "digest"))] fn elligator_encode_to_curve_test_vectors() { let dst = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_"; for (index, vector) in RFC_ENCODE_TO_CURVE_KAT.iter().enumerate() { let input = vector.0; let expected_output = { let x = hex_str_to_fe(vector.1); let y = hex_str_to_fe(vector.2); AffinePoint { x, y }.to_edwards() }; let computed = EdwardsPoint::encode_to_curve::(&[input], &[dst]); assert_eq!(computed, expected_output, "Failed in test {}", index); } } // Hash-to-curve test vectors from // https://www.rfc-editor.org/rfc/rfc9380.html#appendix-J.5.1 // These are of the form (input_msg, output_x, output_y) #[cfg(all(feature = "alloc", feature = "digest"))] const RFC_HASH_TO_CURVE_KAT: &[(&[u8], &str, &str)] = &[ ( b"", "3c3da6925a3c3c268448dcabb47ccde5439559d9599646a8260e47b1e4822fc6", "09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21", ), ( b"abc", "608040b42285cc0d72cbb3985c6b04c935370c7361f4b7fbdb1ae7f8c1a8ecad", "1a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531", ), ( b"abcdef0123456789", "6d7fabf47a2dc03fe7d47f7dddd21082c5fb8f86743cd020f3fb147d57161472", "53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6", ), ( b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", "5fb0b92acedd16f3bcb0ef83f5c7b7a9466b5f1e0d8d217421878ea3686f8524", "2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7", ), ( b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "0efcfde5898a839b00997fbe40d2ebe950bc81181afbd5cd6b9618aa336c1e8c", "6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995" ) ]; #[test] #[cfg(all(feature = "alloc", feature = "digest"))] fn elligator_hash_to_curve_test_vectors() { let dst = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_"; for (index, vector) in RFC_HASH_TO_CURVE_KAT.iter().enumerate() { let input = vector.0; let expected_output = { let x = hex_str_to_fe(vector.1); let y = hex_str_to_fe(vector.2); AffinePoint { x, y }.to_edwards() }; let computed = EdwardsPoint::hash_to_curve::(&[input], &[dst]); assert_eq!(expected_output, computed, "Failed in test {}", index); } } }