#![allow(deprecated)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std #![doc = include_str!("../README.md")] #![allow(clippy::redundant_closure_call)] use core::{ borrow::Borrow, ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}, iter::{Iterator, Sum, Product}, hash::{Hash, Hasher}, }; use zeroize::Zeroize; use subtle::{ConstantTimeEq, ConditionallySelectable}; use rand_core::RngCore; use digest::{consts::U64, Digest, HashMarker}; use subtle::{Choice, CtOption}; pub use curve25519_dalek as dalek; use dalek::{ constants::{self, BASEPOINT_ORDER}, scalar::Scalar as DScalar, edwards::{EdwardsPoint as DEdwardsPoint, EdwardsBasepointTable, CompressedEdwardsY}, ristretto::{RistrettoPoint as DRistrettoPoint, RistrettoBasepointTable, CompressedRistretto}, }; pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE}; use ::ciphersuite::group::{ ff::{Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes}, Group, GroupEncoding, prime::PrimeGroup, }; mod ciphersuite; pub use crate::ciphersuite::{Ed25519, Ristretto}; use core::hint::black_box; fn u8_from_bool(bit_ref: &mut bool) -> u8 { let bit_ref = black_box(bit_ref); let mut bit = black_box(*bit_ref); #[allow(clippy::cast_lossless)] let res = black_box(bit as u8); bit.zeroize(); debug_assert!((res | 1) == 1); bit_ref.zeroize(); res } // Convert a boolean to a Choice in a *presumably* constant time manner fn choice(mut value: bool) -> Choice { Choice::from(u8_from_bool(&mut value)) } macro_rules! deref_borrow { ($Source: ident, $Target: ident) => { impl Deref for $Source { type Target = $Target; fn deref(&self) -> &Self::Target { &self.0 } } impl Borrow<$Target> for $Source { fn borrow(&self) -> &$Target { &self.0 } } impl Borrow<$Target> for &$Source { fn borrow(&self) -> &$Target { &self.0 } } }; } macro_rules! constant_time { ($Value: ident, $Inner: ident) => { impl ConstantTimeEq for $Value { fn ct_eq(&self, other: &Self) -> Choice { self.0.ct_eq(&other.0) } } impl ConditionallySelectable for $Value { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { $Value($Inner::conditional_select(&a.0, &b.0, choice)) } } }; } pub(crate) use constant_time; macro_rules! math_op { ( $Value: ident, $Other: ident, $Op: ident, $op_fn: ident, $Assign: ident, $assign_fn: ident, $function: expr ) => { impl $Op<$Other> for $Value { type Output = $Value; fn $op_fn(self, other: $Other) -> Self::Output { Self($function(self.0, other.0)) } } impl $Assign<$Other> for $Value { fn $assign_fn(&mut self, other: $Other) { self.0 = $function(self.0, other.0); } } impl<'a> $Op<&'a $Other> for $Value { type Output = $Value; fn $op_fn(self, other: &'a $Other) -> Self::Output { Self($function(self.0, other.0)) } } impl<'a> $Assign<&'a $Other> for $Value { fn $assign_fn(&mut self, other: &'a $Other) { self.0 = $function(self.0, other.0); } } }; } pub(crate) use math_op; macro_rules! math { ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => { math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add); math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub); math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul); }; } pub(crate) use math; macro_rules! math_neg { ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => { math!($Value, $Factor, $add, $sub, $mul); impl Neg for $Value { type Output = Self; fn neg(self) -> Self::Output { Self(-self.0) } } }; } /// Wrapper around the dalek Scalar type. #[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)] pub struct Scalar(pub DScalar); deref_borrow!(Scalar, DScalar); constant_time!(Scalar, DScalar); math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul); macro_rules! from_wrapper { ($uint: ident) => { impl From<$uint> for Scalar { fn from(a: $uint) -> Scalar { Scalar(DScalar::from(a)) } } }; } from_wrapper!(u8); from_wrapper!(u16); from_wrapper!(u32); from_wrapper!(u64); from_wrapper!(u128); impl Scalar { pub fn pow(&self, other: Scalar) -> Scalar { let mut table = [Scalar::ONE; 16]; table[1] = *self; for i in 2 .. 16 { table[i] = table[i - 1] * self; } let mut res = Scalar::ONE; let mut bits = 0; for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() { bits <<= 1; let mut bit = u8_from_bool(&mut bit); bits |= bit; bit.zeroize(); if ((i + 1) % 4) == 0 { if i != 3 { for _ in 0 .. 4 { res *= res; } } let mut scale_by = Scalar::ONE; #[allow(clippy::needless_range_loop)] for i in 0 .. 16 { #[allow(clippy::cast_possible_truncation)] // Safe since 0 .. 16 { scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8))); } } res *= scale_by; bits = 0; } } res } /// Perform wide reduction on a 64-byte array to create a Scalar without bias. pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar { Self(DScalar::from_bytes_mod_order_wide(bytes)) } /// Derive a Scalar without bias from a digest via wide reduction. pub fn from_hash + HashMarker>(hash: D) -> Scalar { let mut output = [0u8; 64]; output.copy_from_slice(&hash.finalize()); let res = Scalar(DScalar::from_bytes_mod_order_wide(&output)); output.zeroize(); res } } impl Field for Scalar { const ZERO: Scalar = Scalar(DScalar::ZERO); const ONE: Scalar = Scalar(DScalar::ONE); fn random(rng: impl RngCore) -> Self { Self(::random(rng)) } fn square(&self) -> Self { Self(self.0.square()) } fn double(&self) -> Self { Self(self.0.double()) } fn invert(&self) -> CtOption { ::invert(&self.0).map(Self) } fn sqrt(&self) -> CtOption { self.0.sqrt().map(Self) } fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { let (choice, res) = DScalar::sqrt_ratio(num, div); (choice, Self(res)) } } impl PrimeField for Scalar { type Repr = [u8; 32]; const MODULUS: &'static str = ::MODULUS; const NUM_BITS: u32 = ::NUM_BITS; const CAPACITY: u32 = ::CAPACITY; const TWO_INV: Scalar = Scalar(::TWO_INV); const MULTIPLICATIVE_GENERATOR: Scalar = Scalar(::MULTIPLICATIVE_GENERATOR); const S: u32 = ::S; const ROOT_OF_UNITY: Scalar = Scalar(::ROOT_OF_UNITY); const ROOT_OF_UNITY_INV: Scalar = Scalar(::ROOT_OF_UNITY_INV); const DELTA: Scalar = Scalar(::DELTA); fn from_repr(bytes: [u8; 32]) -> CtOption { ::from_repr(bytes).map(Scalar) } fn to_repr(&self) -> [u8; 32] { self.0.to_repr() } fn is_odd(&self) -> Choice { self.0.is_odd() } fn from_u128(num: u128) -> Self { Scalar(DScalar::from_u128(num)) } } impl PrimeFieldBits for Scalar { type ReprBits = [u8; 32]; fn to_le_bits(&self) -> FieldBits { self.to_repr().into() } fn char_le_bits() -> FieldBits { BASEPOINT_ORDER.to_bytes().into() } } impl FromUniformBytes<64> for Scalar { fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { Self::from_bytes_mod_order_wide(bytes) } } impl Sum for Scalar { fn sum>(iter: I) -> Scalar { Self(DScalar::sum(iter)) } } impl<'a> Sum<&'a Scalar> for Scalar { fn sum>(iter: I) -> Scalar { Self(DScalar::sum(iter)) } } impl Product for Scalar { fn product>(iter: I) -> Scalar { Self(DScalar::product(iter)) } } impl<'a> Product<&'a Scalar> for Scalar { fn product>(iter: I) -> Scalar { Self(DScalar::product(iter)) } } macro_rules! dalek_group { ( $Point: ident, $DPoint: ident, $torsion_free: expr, $Table: ident, $DCompressed: ident, $BASEPOINT_POINT: ident, $BASEPOINT_TABLE: ident ) => { /// Wrapper around the dalek Point type. /// /// All operations will be restricted to a prime-order subgroup (equivalent to the group itself /// in the case of Ristretto). The exposure of the internal element does allow bypassing this /// however, which may lead to undefined/computationally-unsafe behavior, and is entirely at /// the user's risk. #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] pub struct $Point(pub $DPoint); deref_borrow!($Point, $DPoint); constant_time!($Point, $DPoint); math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul); /// The basepoint for this curve. pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT); impl Sum<$Point> for $Point { fn sum>(iter: I) -> $Point { Self($DPoint::sum(iter)) } } impl<'a> Sum<&'a $Point> for $Point { fn sum>(iter: I) -> $Point { Self($DPoint::sum(iter)) } } impl Group for $Point { type Scalar = Scalar; fn random(mut rng: impl RngCore) -> Self { loop { let mut bytes = [0; 32]; rng.fill_bytes(&mut bytes); let Some(point) = Option::<$Point>::from($Point::from_bytes(&bytes)) else { continue; }; // Ban identity, per the trait specification if !bool::from(point.is_identity()) { return point; } } } fn identity() -> Self { Self($DPoint::identity()) } fn generator() -> Self { $BASEPOINT_POINT } fn is_identity(&self) -> Choice { self.0.ct_eq(&$DPoint::identity()) } fn double(&self) -> Self { Self(self.0.double()) } } impl GroupEncoding for $Point { type Repr = [u8; 32]; fn from_bytes(bytes: &Self::Repr) -> CtOption { let decompressed = $DCompressed(*bytes).decompress(); // TODO: Same note on unwrap_or as above let point = decompressed.unwrap_or($DPoint::identity()); CtOption::new( $Point(point), choice(black_box(decompressed).is_some()) & choice($torsion_free(point)), ) } fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { $Point::from_bytes(bytes) } fn to_bytes(&self) -> Self::Repr { self.0.to_bytes() } } impl PrimeGroup for $Point {} impl Mul for &$Table { type Output = $Point; fn mul(self, b: Scalar) -> $Point { $Point(&b.0 * self) } } // Support being used as a key in a table // While it is expensive as a key, due to the field operations required, there's frequently // use cases for public key -> value lookups #[allow(unknown_lints, renamed_and_removed_lints)] #[allow(clippy::derived_hash_with_manual_eq, clippy::derive_hash_xor_eq)] impl Hash for $Point { fn hash(&self, state: &mut H) { self.to_bytes().hash(state); } } }; } dalek_group!( EdwardsPoint, DEdwardsPoint, |point: DEdwardsPoint| point.is_torsion_free(), EdwardsBasepointTable, CompressedEdwardsY, ED25519_BASEPOINT_POINT, ED25519_BASEPOINT_TABLE ); impl EdwardsPoint { pub fn mul_by_cofactor(&self) -> EdwardsPoint { EdwardsPoint(self.0.mul_by_cofactor()) } } dalek_group!( RistrettoPoint, DRistrettoPoint, |_| true, RistrettoBasepointTable, CompressedRistretto, RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_TABLE ); #[test] fn test_ed25519_group() { ff_group_tests::group::test_prime_group_bits::<_, EdwardsPoint>(&mut rand_core::OsRng); } #[test] fn test_ristretto_group() { ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng); } type ThirtyTwoArray = [u8; 32]; prime_field::odd_prime_field_with_specific_repr!( FieldElement, "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", "02", false, crate::ThirtyTwoArray ); impl FieldElement { /// Create a FieldElement from a `crypto_bigint::U256`. /// /// This will reduce the `U256` by the modulus, into a member of the field. #[deprecated] pub const fn from_u256(u256: &crypto_bigint_05::U256) -> Self { const MODULUS: crypto_bigint::U256 = crypto_bigint::U256::from_be_hex( "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", ); let mut u256 = crypto_bigint::U256::from_words(*u256.as_words()); loop { let result = FieldElement::from_bytes(&u256.to_le_bytes()); if let Some(result) = result { return result; } u256 = u256.wrapping_sub(&MODULUS); } } /// Create a `FieldElement` from the reduction of a 512-bit number. /// /// The bytes are interpreted in little-endian format. #[deprecated] pub fn wide_reduce(value: [u8; 64]) -> Self { >::from_uniform_bytes(&value) } }