mirror of
https://github.com/saymrwulf/anza-cryptography-source.git
synced 2026-09-03 20:13:46 +00:00
Same documented rewrite as FieldElement::sqrt_ratio_i: semantically identical and still constant-time, but avoids subtle's ConditionallyNegatable blanket impl, which the verification toolchain cannot translate. Unblocks extracting decompress for the phase-2 full point-level lift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2632 lines
89 KiB
Rust
2632 lines
89 KiB
Rust
// -*- 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 <isis@patternsinthevoid.net>
|
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
|
|
//! 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<EdwardsPoint> {
|
|
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<CompressedEdwardsY, TryFromSliceError> {
|
|
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
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<D>(deserializer: D) -> Result<Self, D::Error>
|
|
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<A>(self, mut seq: A) -> Result<EdwardsPoint, A::Error>
|
|
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<D>(deserializer: D) -> Result<Self, D::Error>
|
|
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<A>(self, mut seq: A) -> Result<CompressedEdwardsY, A::Error>
|
|
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<CompressedEdwardsY, TryFromSliceError> {
|
|
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<MontgomeryPoint> {
|
|
// 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::<Vec<_>>();
|
|
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<CompressedEdwardsY> {
|
|
let mut zs = inputs.iter().map(|input| input.Z).collect::<Vec<_>>();
|
|
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<D>(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint
|
|
where
|
|
D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
|
|
D::BlockSize: IsGreater<D::OutputSize, Output = True>,
|
|
{
|
|
// For reference see
|
|
// https://www.rfc-editor.org/rfc/rfc9380.html#name-elligator-2-method-2
|
|
|
|
let fe = FieldElement::hash_to_field::<D, 1>(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<D>(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint
|
|
where
|
|
D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
|
|
D::BlockSize: IsGreater<D::OutputSize, Output = True>,
|
|
{
|
|
// For reference see
|
|
// https://www.rfc-editor.org/rfc/rfc9380.html#name-elligator-2-method-2
|
|
|
|
let fe = FieldElement::hash_to_field::<D, 2>(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<R: CryptoRng + RngCore + ?Sized>(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<T> Sum<T> for EdwardsPoint
|
|
where
|
|
T: Borrow<EdwardsPoint>,
|
|
{
|
|
fn sum<I>(iter: I) -> Self
|
|
where
|
|
I: Iterator<Item = T>,
|
|
{
|
|
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<I, J>(scalars: I, points: J) -> EdwardsPoint
|
|
where
|
|
I: IntoIterator,
|
|
I::Item: Borrow<Scalar>,
|
|
J: IntoIterator,
|
|
J::Item: Borrow<EdwardsPoint>,
|
|
{
|
|
// 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<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
|
|
where
|
|
I: IntoIterator,
|
|
I::Item: Borrow<Scalar>,
|
|
J: IntoIterator<Item = Option<EdwardsPoint>>,
|
|
{
|
|
// 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<I>(static_points: I) -> Self
|
|
where
|
|
I: IntoIterator,
|
|
I::Item: Borrow<Self::Point>,
|
|
{
|
|
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<I, J, K>(
|
|
&self,
|
|
static_scalars: I,
|
|
dynamic_scalars: J,
|
|
dynamic_points: K,
|
|
) -> Option<Self::Point>
|
|
where
|
|
I: IntoIterator,
|
|
I::Item: Borrow<Scalar>,
|
|
J: IntoIterator,
|
|
J::Item: Borrow<Scalar>,
|
|
K: IntoIterator<Item = Option<Self::Point>>,
|
|
{
|
|
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<AffineNielsPoint>; 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<Self> {
|
|
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<Self> {
|
|
// 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<SubgroupPoint> 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<T> Sum<T> for SubgroupPoint
|
|
where
|
|
T: Borrow<SubgroupPoint>,
|
|
{
|
|
fn sum<I>(iter: I) -> Self
|
|
where
|
|
I: Iterator<Item = T>,
|
|
{
|
|
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 = <EdwardsPoint as GroupEncoding>::Repr;
|
|
|
|
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
|
EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
|
|
}
|
|
|
|
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
|
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<Self::Subgroup> {
|
|
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<EdwardsPoint> 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<ProjectiveNielsPoint> 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<AffineNielsPoint> 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::<Vec<_>>();
|
|
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::<Vec<_>>();
|
|
let check = xs.iter().map(|xi| xi * xi).sum::<Scalar>();
|
|
|
|
// Construct points G_i = x_i * B
|
|
let Gs = xs.iter().map(EdwardsPoint::mul_base).collect::<Vec<_>>();
|
|
|
|
// Compute H1 = <xs, Gs> (consttime)
|
|
let H1 = EdwardsPoint::multiscalar_mul(&xs, &Gs);
|
|
// Compute H2 = <xs, Gs> (vartime)
|
|
let H2 = EdwardsPoint::vartime_multiscalar_mul(&xs, &Gs);
|
|
// Compute H3 = <xs, Gs> = 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::<Vec<_>>();
|
|
|
|
let points = scalars
|
|
.iter()
|
|
.map(EdwardsPoint::mul_base)
|
|
.collect::<Vec<_>>();
|
|
|
|
let single_monts = points
|
|
.iter()
|
|
.map(EdwardsPoint::to_montgomery)
|
|
.collect::<Vec<_>>();
|
|
|
|
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::<Vec<_>>();
|
|
|
|
let dynamic_scalars = (0..128)
|
|
.map(|_| Scalar::random(&mut rng))
|
|
.collect::<Vec<_>>();
|
|
|
|
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::<Vec<_>>();
|
|
let dynamic_points = dynamic_scalars
|
|
.iter()
|
|
.map(EdwardsPoint::mul_base)
|
|
.collect::<Vec<_>>();
|
|
|
|
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::<sha2::Sha512>(&[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::<sha2::Sha512>(&[input], &[dst]);
|
|
|
|
assert_eq!(expected_output, computed, "Failed in test {}", index);
|
|
}
|
|
}
|
|
}
|