#![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] #[allow(unused_imports)] use std_shims::prelude::*; use prime_field::{ subtle::{Choice, CtOption}, zeroize::Zeroize, }; use ciphersuite::group::{ff::PrimeField, Group, GroupEncoding}; pub use curve25519_dalek::Scalar as FieldElement; use short_weierstrass::{ShortWeierstrass, Affine, Projective}; prime_field::odd_prime_field!( Scalar, "0fffffffffffffffffffffffffffffffe53f4debb78ff96877063f0306eef96b", "0a", false ); #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] pub struct Embedwards25519; #[allow(deprecated)] // No other way to construct arbitrary `FieldElement` at compile-time :/ impl ShortWeierstrass for Embedwards25519 { type FieldElement = FieldElement; const A: FieldElement = FieldElement::from_bits(hex_literal::hex!( "ead3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010" )); const B: FieldElement = FieldElement::from_bits(hex_literal::hex!( "5f07603a853f20370b682036210d463e64903a23ea669d07ca26cfc13f594209" )); const PRIME_ORDER: bool = true; const GENERATOR: Affine = Affine::from_xy_unchecked( FieldElement::ONE, FieldElement::from_bits(hex_literal::hex!( "2e4118080a484a3dfbafe2199a0e36b7193581d676c0dadfa376b0265616020c" )), ); type Scalar = Scalar; type Repr = [u8; 32]; // Use an all-zero encoding for the identity as `0` isn't the `x` coordinate of an on-curve point const IDENTITY: [u8; 32] = [0; 32]; fn encode_compressed(x: Self::FieldElement, odd_y: Choice) -> Self::Repr { // The LE `x` coordinate, with if `y` is odd in the unused 256th bit let mut res = [0; 32]; res.copy_from_slice(x.to_repr().as_ref()); res[31] |= odd_y.unwrap_u8() << 7; res } fn decode_compressed(bytes: &Self::Repr) -> (::Repr, Choice) { // Extract and clear the sign bit let mut bytes = *bytes; let odd_y = Choice::from(bytes[31] >> 7); bytes[31] &= u8::MAX >> 1; // Copy from the point's representation to the field's let mut repr = ::Repr::default(); { let repr: &mut [u8] = repr.as_mut(); repr.copy_from_slice(&bytes); } (repr, odd_y) } // No points have a torsion element as this a prime-order curve fn has_torsion_element(_point: Projective) -> Choice { 0.into() } } pub type Point = Projective; impl ciphersuite::WrappedGroup for Embedwards25519 { type F = Scalar; type G = Point; fn generator() -> Self::G { ::generator() } } impl ciphersuite::Id for Embedwards25519 { const ID: &[u8] = b"embedwards25519"; } impl ciphersuite::WithPreferredHash for Embedwards25519 { type H = blake2::Blake2b512; } impl ciphersuite::GroupCanonicalEncoding for Embedwards25519 { fn from_canonical_bytes(bytes: &::Repr) -> CtOption { Self::G::from_bytes(bytes) } } #[cfg(feature = "alloc")] impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Embedwards25519 { type ScalarBits = typenum::U<{ ::NUM_BITS as usize }>; } #[test] fn test_curve() { ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng); } #[test] fn generator() { assert_eq!( ::generator(), Point::from_bytes(&hex_literal::hex!( "0100000000000000000000000000000000000000000000000000000000000000" )) .unwrap() ); } #[test] fn zero_x_is_off_curve() { assert!(bool::from( Affine::::decompress(FieldElement::ZERO, 1.into()).is_none() )); } // Checks random won't infinitely loop #[test] fn random() { Point::random(&mut rand_core::OsRng); }