mirror of
https://github.com/saymrwulf/pasta_curves-source.git
synced 2026-09-10 21:00:35 +00:00
commit
275dad22ad
11 changed files with 360 additions and 152 deletions
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
|
|
@ -188,3 +188,26 @@ jobs:
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: -- --check
|
args: -- --check
|
||||||
|
|
||||||
|
no-std:
|
||||||
|
name: Check no-std target ${{ matrix.target }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- thumbv6m-none-eabi
|
||||||
|
- wasm32-unknown-unknown
|
||||||
|
- wasm32-wasi
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- run: rustup target add ${{ matrix.target }}
|
||||||
|
- name: Build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --verbose --target ${{ matrix.target }} --no-default-features
|
||||||
|
|
|
||||||
19
Cargo.toml
19
Cargo.toml
|
|
@ -25,6 +25,7 @@ rand_xorshift = "0.3"
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "hashtocurve"
|
name = "hashtocurve"
|
||||||
harness = false
|
harness = false
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "fp"
|
name = "fp"
|
||||||
|
|
@ -37,16 +38,20 @@ harness = false
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "point"
|
name = "point"
|
||||||
harness = false
|
harness = false
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
subtle = "2.3"
|
blake2b_simd = { version = "0.5", default-features = false }
|
||||||
ff = "0.11"
|
ff = { version = "0.11", default-features = false }
|
||||||
group = "0.11"
|
group = { version = "0.11", default-features = false }
|
||||||
rand = "0.8"
|
rand = { version = "0.8", default-features = false }
|
||||||
blake2b_simd = "0.5"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
|
subtle = { version = "2.3", default-features = false }
|
||||||
|
|
||||||
|
# std dependencies
|
||||||
|
lazy_static = { version = "1.4.0", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["bits"]
|
default = ["bits", "std"]
|
||||||
bits = ["ff/bits"]
|
bits = ["ff/bits"]
|
||||||
|
std = ["group/alloc", "lazy_static", "rand/getrandom"]
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,16 @@ pub use ff::Field;
|
||||||
mod curves;
|
mod curves;
|
||||||
mod fields;
|
mod fields;
|
||||||
|
|
||||||
|
pub(crate) use fields::*;
|
||||||
|
|
||||||
pub use curves::*;
|
pub use curves::*;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub use fields::*;
|
pub use fields::*;
|
||||||
|
|
||||||
/// This represents an element of a group with basic operations that can be
|
/// This represents an element of a group with basic operations that can be
|
||||||
/// performed. This allows an FFT implementation (for example) to operate
|
/// performed. This allows an FFT implementation (for example) to operate
|
||||||
/// generically over either a field or elliptic curve group.
|
/// generically over either a field or elliptic curve group.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub trait Group: Copy + Clone + Send + Sync + 'static {
|
pub trait Group: Copy + Clone + Send + Sync + 'static {
|
||||||
/// The group is assumed to be of prime order $p$. `Scalar` is the
|
/// The group is assumed to be of prime order $p$. `Scalar` is the
|
||||||
/// associated scalar field of size $p$.
|
/// associated scalar field of size $p$.
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,29 @@
|
||||||
//! This module contains the `Curve`/`CurveAffine` abstractions that allow us to
|
//! This module contains the `Curve`/`CurveAffine` abstractions that allow us to
|
||||||
//! write code that generalizes over a pair of groups.
|
//! write code that generalizes over a pair of groups.
|
||||||
|
|
||||||
use core::cmp;
|
#[cfg(feature = "std")]
|
||||||
use core::ops::{Add, Mul, Sub};
|
|
||||||
use group::prime::{PrimeCurve, PrimeCurveAffine};
|
use group::prime::{PrimeCurve, PrimeCurveAffine};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use super::{FieldExt, Group};
|
use super::{FieldExt, Group};
|
||||||
|
|
||||||
use std::io::{self, Read, Write};
|
#[cfg(feature = "std")]
|
||||||
|
use std::{
|
||||||
|
boxed::Box,
|
||||||
|
cmp,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
ops::{Add, Mul, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
/// This trait is a common interface for dealing with elements of an elliptic
|
/// This trait is a common interface for dealing with elements of an elliptic
|
||||||
/// curve group in a "projective" form, where that arithmetic is usually more
|
/// curve group in a "projective" form, where that arithmetic is usually more
|
||||||
/// efficient.
|
/// efficient.
|
||||||
|
///
|
||||||
|
/// Currently requires the `std` feature flag because of `hash_to_curve`, and
|
||||||
|
/// `CurveAffine::{read, write}`.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub trait CurveExt:
|
pub trait CurveExt:
|
||||||
PrimeCurve<Affine = <Self as CurveExt>::AffineExt>
|
PrimeCurve<Affine = <Self as CurveExt>::AffineExt>
|
||||||
+ group::Group<Scalar = <Self as CurveExt>::ScalarExt>
|
+ group::Group<Scalar = <Self as CurveExt>::ScalarExt>
|
||||||
|
|
@ -81,6 +92,7 @@ pub trait CurveExt:
|
||||||
|
|
||||||
/// This trait is the affine counterpart to `Curve` and is used for
|
/// This trait is the affine counterpart to `Curve` and is used for
|
||||||
/// serialization, storage in memory, and inspection of $x$ and $y$ coordinates.
|
/// serialization, storage in memory, and inspection of $x$ and $y$ coordinates.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub trait CurveAffine:
|
pub trait CurveAffine:
|
||||||
PrimeCurveAffine<
|
PrimeCurveAffine<
|
||||||
Scalar = <Self as CurveAffine>::ScalarExt,
|
Scalar = <Self as CurveAffine>::ScalarExt,
|
||||||
|
|
@ -135,12 +147,14 @@ pub trait CurveAffine:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The affine coordinates of a point on an elliptic curve.
|
/// The affine coordinates of a point on an elliptic curve.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct Coordinates<C: CurveAffine> {
|
pub struct Coordinates<C: CurveAffine> {
|
||||||
pub(crate) x: C::Base,
|
pub(crate) x: C::Base,
|
||||||
pub(crate) y: C::Base,
|
pub(crate) y: C::Base,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<C: CurveAffine> Coordinates<C> {
|
impl<C: CurveAffine> Coordinates<C> {
|
||||||
/// Returns the x-coordinate.
|
/// Returns the x-coordinate.
|
||||||
///
|
///
|
||||||
|
|
@ -171,6 +185,7 @@ impl<C: CurveAffine> Coordinates<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<C: CurveAffine> ConditionallySelectable for Coordinates<C> {
|
impl<C: CurveAffine> ConditionallySelectable for Coordinates<C> {
|
||||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||||
Coordinates {
|
Coordinates {
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,28 @@
|
||||||
//! code that generalizes over a pair of fields.
|
//! code that generalizes over a pair of fields.
|
||||||
|
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
|
|
||||||
use static_assertions::const_assert;
|
use static_assertions::const_assert;
|
||||||
use std::assert;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use subtle::{Choice, CtOption};
|
use subtle::{Choice, CtOption};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use super::Group;
|
use super::Group;
|
||||||
|
|
||||||
use std::io::{self, Read, Write};
|
#[cfg(feature = "std")]
|
||||||
|
use std::{
|
||||||
|
assert,
|
||||||
|
boxed::Box,
|
||||||
|
convert::TryInto,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
marker::PhantomData,
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
|
||||||
const_assert!(size_of::<usize>() >= 4);
|
const_assert!(size_of::<usize>() >= 4);
|
||||||
|
|
||||||
/// This trait is a common interface for dealing with elements of a finite
|
/// This trait is a common interface for dealing with elements of a finite
|
||||||
/// field.
|
/// field.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub trait FieldExt: ff::PrimeField + From<bool> + Ord + Group<Scalar = Self> {
|
pub trait FieldExt: ff::PrimeField + From<bool> + Ord + Group<Scalar = Self> {
|
||||||
/// Modulus of the field written as a string for display purposes
|
/// Modulus of the field written as a string for display purposes
|
||||||
const MODULUS: &'static str;
|
const MODULUS: &'static str;
|
||||||
|
|
@ -130,7 +138,58 @@ pub trait FieldExt: ff::PrimeField + From<bool> + Ord + Group<Scalar = Self> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tonelli–Shanks' square-root algorithm for `p mod 16 = 1`.
|
||||||
|
///
|
||||||
|
/// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||||
|
///
|
||||||
|
/// `tm1d2` should be set to `(t - 1) // 2`, where `t = (modulus - 1) >> F::S`.
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub(crate) fn sqrt_tonelli_shanks<F: ff::PrimeField, S: AsRef<[u64]>>(
|
||||||
|
f: &F,
|
||||||
|
tm1d2: S,
|
||||||
|
) -> CtOption<F> {
|
||||||
|
use subtle::{ConditionallySelectable, ConstantTimeEq};
|
||||||
|
|
||||||
|
// w = self^((t - 1) // 2)
|
||||||
|
let w = f.pow_vartime(tm1d2);
|
||||||
|
|
||||||
|
let mut v = F::S;
|
||||||
|
let mut x = w * f;
|
||||||
|
let mut b = x * w;
|
||||||
|
|
||||||
|
// Initialize z as the 2^S root of unity.
|
||||||
|
let mut z = F::root_of_unity();
|
||||||
|
|
||||||
|
for max_v in (1..=F::S).rev() {
|
||||||
|
let mut k = 1;
|
||||||
|
let mut tmp = b.square();
|
||||||
|
let mut j_less_than_v: Choice = 1.into();
|
||||||
|
|
||||||
|
for j in 2..max_v {
|
||||||
|
let tmp_is_one = tmp.ct_eq(&F::one());
|
||||||
|
let squared = F::conditional_select(&tmp, &z, tmp_is_one).square();
|
||||||
|
tmp = F::conditional_select(&squared, &tmp, tmp_is_one);
|
||||||
|
let new_z = F::conditional_select(&z, &squared, tmp_is_one);
|
||||||
|
j_less_than_v &= !j.ct_eq(&v);
|
||||||
|
k = u32::conditional_select(&j, &k, tmp_is_one);
|
||||||
|
z = F::conditional_select(&z, &new_z, j_less_than_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = x * z;
|
||||||
|
x = F::conditional_select(&result, &x, b.ct_eq(&F::one()));
|
||||||
|
z = z.square();
|
||||||
|
b *= z;
|
||||||
|
v = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
CtOption::new(
|
||||||
|
x,
|
||||||
|
(x * x).ct_eq(f), // Only return Some if it's the square root.
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parameters for a perfect hash function used in square root computation.
|
/// Parameters for a perfect hash function used in square root computation.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SqrtHasher<F: FieldExt> {
|
struct SqrtHasher<F: FieldExt> {
|
||||||
hash_xor: u32,
|
hash_xor: u32,
|
||||||
|
|
@ -138,6 +197,7 @@ struct SqrtHasher<F: FieldExt> {
|
||||||
marker: PhantomData<F>,
|
marker: PhantomData<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<F: FieldExt> SqrtHasher<F> {
|
impl<F: FieldExt> SqrtHasher<F> {
|
||||||
/// Returns a perfect hash of x for use with SqrtTables::inv.
|
/// Returns a perfect hash of x for use with SqrtTables::inv.
|
||||||
fn hash(&self, x: &F) -> usize {
|
fn hash(&self, x: &F) -> usize {
|
||||||
|
|
@ -150,6 +210,7 @@ impl<F: FieldExt> SqrtHasher<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tables used for square root computation.
|
/// Tables used for square root computation.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SqrtTables<F: FieldExt> {
|
pub struct SqrtTables<F: FieldExt> {
|
||||||
hasher: SqrtHasher<F>,
|
hasher: SqrtHasher<F>,
|
||||||
|
|
@ -160,9 +221,12 @@ pub struct SqrtTables<F: FieldExt> {
|
||||||
g3: Box<[F; 129]>,
|
g3: Box<[F; 129]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl<F: FieldExt> SqrtTables<F> {
|
impl<F: FieldExt> SqrtTables<F> {
|
||||||
/// Build tables given parameters for the perfect hash.
|
/// Build tables given parameters for the perfect hash.
|
||||||
pub fn new(hash_xor: u32, hash_mod: usize) -> Self {
|
pub fn new(hash_xor: u32, hash_mod: usize) -> Self {
|
||||||
|
use std::vec;
|
||||||
|
|
||||||
let hasher = SqrtHasher {
|
let hasher = SqrtHasher {
|
||||||
hash_xor,
|
hash_xor,
|
||||||
hash_mod,
|
hash_mod,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@
|
||||||
//! groups.
|
//! groups.
|
||||||
|
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::fmt::Debug;
|
use core::fmt;
|
||||||
use core::iter::Sum;
|
use core::iter::Sum;
|
||||||
use core::ops::{Add, Mul, Neg, Sub};
|
use core::ops::{Add, Mul, Neg, Sub};
|
||||||
use ff::Field;
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::boxed::Box;
|
||||||
|
|
||||||
|
use ff::{Field, PrimeField};
|
||||||
use group::{
|
use group::{
|
||||||
cofactor::{CofactorCurve, CofactorGroup},
|
cofactor::{CofactorCurve, CofactorGroup},
|
||||||
prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
|
prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
|
||||||
|
|
@ -15,6 +19,8 @@ use rand::RngCore;
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||||
|
|
||||||
use super::{Fp, Fq};
|
use super::{Fp, Fq};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use crate::arithmetic::{Coordinates, CurveAffine, CurveExt, FieldExt, Group};
|
use crate::arithmetic::{Coordinates, CurveAffine, CurveExt, FieldExt, Group};
|
||||||
|
|
||||||
macro_rules! new_curve_impl {
|
macro_rules! new_curve_impl {
|
||||||
|
|
@ -47,8 +53,8 @@ macro_rules! new_curve_impl {
|
||||||
infinity: Choice,
|
infinity: Choice,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for $name_affine {
|
impl fmt::Debug for $name_affine {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
if self.infinity.into() {
|
if self.infinity.into() {
|
||||||
write!(f, "Infinity")
|
write!(f, "Infinity")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -68,7 +74,7 @@ macro_rules! new_curve_impl {
|
||||||
let x3 = x.square() * x;
|
let x3 = x.square() * x;
|
||||||
let y = (x3 + $name::curve_constant_b()).sqrt();
|
let y = (x3 + $name::curve_constant_b()).sqrt();
|
||||||
if let Some(y) = Option::<$base>::from(y) {
|
if let Some(y) = Option::<$base>::from(y) {
|
||||||
let sign = y.to_bytes()[0] & 1;
|
let sign = y.is_odd().unwrap_u8();
|
||||||
let y = if ysign ^ sign == 0 { y } else { -y };
|
let y = if ysign ^ sign == 0 { y } else { -y };
|
||||||
|
|
||||||
let p = $name_affine {
|
let p = $name_affine {
|
||||||
|
|
@ -96,6 +102,7 @@ macro_rules! new_curve_impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl group::WnafGroup for $name {
|
impl group::WnafGroup for $name {
|
||||||
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
|
fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
|
||||||
// Copied from bls12_381::g1, should be updated.
|
// Copied from bls12_381::g1, should be updated.
|
||||||
|
|
@ -115,6 +122,7 @@ macro_rules! new_curve_impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl CurveExt for $name {
|
impl CurveExt for $name {
|
||||||
type ScalarExt = $scalar;
|
type ScalarExt = $scalar;
|
||||||
type Base = $base;
|
type Base = $base;
|
||||||
|
|
@ -465,7 +473,7 @@ macro_rules! new_curve_impl {
|
||||||
//
|
//
|
||||||
// NOTE: We skip the leading bit because it's always unset.
|
// NOTE: We skip the leading bit because it's always unset.
|
||||||
for bit in other
|
for bit in other
|
||||||
.to_bytes()
|
.to_repr()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||||
|
|
@ -576,7 +584,7 @@ macro_rules! new_curve_impl {
|
||||||
//
|
//
|
||||||
// NOTE: We skip the leading bit because it's always unset.
|
// NOTE: We skip the leading bit because it's always unset.
|
||||||
for bit in other
|
for bit in other
|
||||||
.to_bytes()
|
.to_repr()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||||
|
|
@ -646,11 +654,11 @@ macro_rules! new_curve_impl {
|
||||||
let ysign = Choice::from(tmp[31] >> 7);
|
let ysign = Choice::from(tmp[31] >> 7);
|
||||||
tmp[31] &= 0b0111_1111;
|
tmp[31] &= 0b0111_1111;
|
||||||
|
|
||||||
$base::from_bytes(&tmp).and_then(|x| {
|
$base::from_repr(tmp).and_then(|x| {
|
||||||
CtOption::new(Self::identity(), x.is_zero() & (!ysign)).or_else(|| {
|
CtOption::new(Self::identity(), x.is_zero() & (!ysign)).or_else(|| {
|
||||||
let x3 = x.square() * x;
|
let x3 = x.square() * x;
|
||||||
(x3 + $name::curve_constant_b()).sqrt().and_then(|y| {
|
(x3 + $name::curve_constant_b()).sqrt().and_then(|y| {
|
||||||
let sign = Choice::from(y.to_bytes()[0] & 1);
|
let sign = y.is_odd();
|
||||||
|
|
||||||
let y = $base::conditional_select(&y, &-y, ysign ^ sign);
|
let y = $base::conditional_select(&y, &-y, ysign ^ sign);
|
||||||
|
|
||||||
|
|
@ -678,14 +686,15 @@ macro_rules! new_curve_impl {
|
||||||
[0; 32]
|
[0; 32]
|
||||||
} else {
|
} else {
|
||||||
let (x, y) = (self.x, self.y);
|
let (x, y) = (self.x, self.y);
|
||||||
let sign = (y.to_bytes()[0] & 1) << 7;
|
let sign = y.is_odd().unwrap_u8() << 7;
|
||||||
let mut xbytes = x.to_bytes();
|
let mut xbytes = x.to_repr();
|
||||||
xbytes[31] |= sign;
|
xbytes[31] |= sign;
|
||||||
xbytes
|
xbytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl CurveAffine for $name_affine {
|
impl CurveAffine for $name_affine {
|
||||||
type ScalarExt = $scalar;
|
type ScalarExt = $scalar;
|
||||||
type Base = $base;
|
type Base = $base;
|
||||||
|
|
@ -769,6 +778,7 @@ macro_rules! new_curve_impl {
|
||||||
impl_binops_multiplicative!($name, $scalar);
|
impl_binops_multiplicative!($name, $scalar);
|
||||||
impl_binops_multiplicative_mixed!($name_affine, $scalar, $name);
|
impl_binops_multiplicative_mixed!($name_affine, $scalar, $name);
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl Group for $name {
|
impl Group for $name {
|
||||||
type Scalar = $scalar;
|
type Scalar = $scalar;
|
||||||
|
|
||||||
|
|
@ -869,6 +879,7 @@ macro_rules! impl_projective_curve_specific {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
macro_rules! impl_projective_curve_ext {
|
macro_rules! impl_projective_curve_ext {
|
||||||
($name:ident, $iso:ident, $base:ident, special_a0_b5) => {
|
($name:ident, $iso:ident, $base:ident, special_a0_b5) => {
|
||||||
fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box<dyn Fn(&[u8]) -> Self + 'a> {
|
fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box<dyn Fn(&[u8]) -> Self + 'a> {
|
||||||
|
|
|
||||||
162
src/fields/fp.rs
162
src/fields/fp.rs
|
|
@ -1,14 +1,21 @@
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::{Add, Mul, Neg, Sub};
|
use core::ops::{Add, Mul, Neg, Sub};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
use ff::PrimeField;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
#[cfg(feature = "bits")]
|
#[cfg(feature = "bits")]
|
||||||
use ff::{FieldBits, PrimeFieldBits};
|
use ff::{FieldBits, PrimeFieldBits};
|
||||||
|
|
||||||
use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtTables};
|
use crate::arithmetic::{adc, mac, sbb};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use crate::arithmetic::{FieldExt, Group, SqrtTables};
|
||||||
|
|
||||||
/// This represents an element of $\mathbb{F}_p$ where
|
/// This represents an element of $\mathbb{F}_p$ where
|
||||||
///
|
///
|
||||||
|
|
@ -23,7 +30,7 @@ pub struct Fp(pub(crate) [u64; 4]);
|
||||||
|
|
||||||
impl fmt::Debug for Fp {
|
impl fmt::Debug for Fp {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let tmp = self.to_bytes();
|
let tmp = self.to_repr();
|
||||||
write!(f, "0x")?;
|
write!(f, "0x")?;
|
||||||
for &b in tmp.iter().rev() {
|
for &b in tmp.iter().rev() {
|
||||||
write!(f, "{:02x}", b)?;
|
write!(f, "{:02x}", b)?;
|
||||||
|
|
@ -64,23 +71,23 @@ impl PartialEq for Fp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::Ord for Fp {
|
impl core::cmp::Ord for Fp {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
let left = self.to_bytes();
|
let left = self.to_repr();
|
||||||
let right = other.to_bytes();
|
let right = other.to_repr();
|
||||||
left.iter()
|
left.iter()
|
||||||
.zip(right.iter())
|
.zip(right.iter())
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
|
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
|
||||||
std::cmp::Ordering::Equal => None,
|
core::cmp::Ordering::Equal => None,
|
||||||
res => Some(res),
|
res => Some(res),
|
||||||
})
|
})
|
||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
.unwrap_or(core::cmp::Ordering::Equal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::PartialOrd for Fp {
|
impl core::cmp::PartialOrd for Fp {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -217,6 +224,7 @@ const ROOT_OF_UNITY: Fp = Fp::from_raw([
|
||||||
/// GENERATOR^{2^s} where t * 2^s + 1 = p
|
/// GENERATOR^{2^s} where t * 2^s + 1 = p
|
||||||
/// with t odd. In other words, this
|
/// with t odd. In other words, this
|
||||||
/// is a t root of unity.
|
/// is a t root of unity.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
const DELTA: Fp = Fp::from_raw([
|
const DELTA: Fp = Fp::from_raw([
|
||||||
0x6a6ccd20dd7b9ba2,
|
0x6a6ccd20dd7b9ba2,
|
||||||
0xf5e4f3f13eee5636,
|
0xf5e4f3f13eee5636,
|
||||||
|
|
@ -437,16 +445,17 @@ impl Fp {
|
||||||
|
|
||||||
impl From<Fp> for [u8; 32] {
|
impl From<Fp> for [u8; 32] {
|
||||||
fn from(value: Fp) -> [u8; 32] {
|
fn from(value: Fp) -> [u8; 32] {
|
||||||
value.to_bytes()
|
value.to_repr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Fp> for [u8; 32] {
|
impl<'a> From<&'a Fp> for [u8; 32] {
|
||||||
fn from(value: &'a Fp) -> [u8; 32] {
|
fn from(value: &'a Fp) -> [u8; 32] {
|
||||||
value.to_bytes()
|
value.to_repr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl Group for Fp {
|
impl Group for Fp {
|
||||||
type Scalar = Fp;
|
type Scalar = Fp;
|
||||||
|
|
||||||
|
|
@ -466,10 +475,16 @@ impl Group for Fp {
|
||||||
|
|
||||||
impl ff::Field for Fp {
|
impl ff::Field for Fp {
|
||||||
fn random(mut rng: impl RngCore) -> Self {
|
fn random(mut rng: impl RngCore) -> Self {
|
||||||
let mut random_bytes = [0; 64];
|
Self::from_u512([
|
||||||
rng.fill_bytes(&mut random_bytes[..]);
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
Self::from_bytes_wide(&random_bytes)
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
|
|
@ -491,8 +506,22 @@ impl ff::Field for Fp {
|
||||||
|
|
||||||
/// Computes the square root of this element, if it exists.
|
/// Computes the square root of this element, if it exists.
|
||||||
fn sqrt(&self) -> CtOption<Self> {
|
fn sqrt(&self) -> CtOption<Self> {
|
||||||
let (is_square, res) = self.sqrt_alt();
|
#[cfg(feature = "std")]
|
||||||
CtOption::new(res, is_square)
|
{
|
||||||
|
let (is_square, res) = FP_TABLES.sqrt_alt(self);
|
||||||
|
CtOption::new(res, is_square)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
crate::arithmetic::sqrt_tonelli_shanks(
|
||||||
|
self,
|
||||||
|
&[
|
||||||
|
0x04a6_7c8d_cc96_9876,
|
||||||
|
0x0000_0000_1123_4c7e,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_2000_0000,
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the multiplicative inverse of this element,
|
/// Computes the multiplicative inverse of this element,
|
||||||
|
|
@ -535,15 +564,47 @@ impl ff::PrimeField for Fp {
|
||||||
const S: u32 = S;
|
const S: u32 = S;
|
||||||
|
|
||||||
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||||
Self::from_bytes(&repr)
|
let mut tmp = Fp([0, 0, 0, 0]);
|
||||||
|
|
||||||
|
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
|
||||||
|
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
|
||||||
|
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
|
||||||
|
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
|
||||||
|
|
||||||
|
// Try to subtract the modulus
|
||||||
|
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
|
||||||
|
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
|
||||||
|
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
|
||||||
|
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
|
||||||
|
|
||||||
|
// If the element is smaller than MODULUS then the
|
||||||
|
// subtraction will underflow, producing a borrow value
|
||||||
|
// of 0xffff...ffff. Otherwise, it'll be zero.
|
||||||
|
let is_some = (borrow as u8) & 1;
|
||||||
|
|
||||||
|
// Convert to Montgomery form by computing
|
||||||
|
// (a.R^0 * R^2) / R = a.R
|
||||||
|
tmp *= &R2;
|
||||||
|
|
||||||
|
CtOption::new(tmp, Choice::from(is_some))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_repr(&self) -> Self::Repr {
|
fn to_repr(&self) -> Self::Repr {
|
||||||
self.to_bytes()
|
// Turn into canonical form by computing
|
||||||
|
// (a.R) / R = a
|
||||||
|
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
|
||||||
|
|
||||||
|
let mut res = [0; 32];
|
||||||
|
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
|
||||||
|
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
|
||||||
|
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
|
||||||
|
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_odd(&self) -> Choice {
|
fn is_odd(&self) -> Choice {
|
||||||
Choice::from(self.to_bytes()[0] & 1)
|
Choice::from(self.to_repr()[0] & 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multiplicative_generator() -> Self {
|
fn multiplicative_generator() -> Self {
|
||||||
|
|
@ -551,7 +612,7 @@ impl ff::PrimeField for Fp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_of_unity() -> Self {
|
fn root_of_unity() -> Self {
|
||||||
Self::ROOT_OF_UNITY
|
ROOT_OF_UNITY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -602,11 +663,13 @@ impl PrimeFieldBits for Fp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta.
|
// The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta.
|
||||||
static ref FP_TABLES: SqrtTables<Fp> = SqrtTables::new(0x11BE, 1098);
|
static ref FP_TABLES: SqrtTables<Fp> = SqrtTables::new(0x11BE, 1098);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl FieldExt for Fp {
|
impl FieldExt for Fp {
|
||||||
const MODULUS: &'static str =
|
const MODULUS: &'static str =
|
||||||
"0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001";
|
"0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001";
|
||||||
|
|
@ -660,48 +723,12 @@ impl FieldExt for Fp {
|
||||||
Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0])
|
Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to convert a little-endian byte representation of
|
|
||||||
/// a scalar into a `Fp`, failing if the input is not canonical.
|
|
||||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fp> {
|
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fp> {
|
||||||
let mut tmp = Fp([0, 0, 0, 0]);
|
<Self as ff::PrimeField>::from_repr(*bytes)
|
||||||
|
|
||||||
tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
|
|
||||||
tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
|
|
||||||
tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
|
|
||||||
tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
|
|
||||||
|
|
||||||
// Try to subtract the modulus
|
|
||||||
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
|
|
||||||
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
|
|
||||||
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
|
|
||||||
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
|
|
||||||
|
|
||||||
// If the element is smaller than MODULUS then the
|
|
||||||
// subtraction will underflow, producing a borrow value
|
|
||||||
// of 0xffff...ffff. Otherwise, it'll be zero.
|
|
||||||
let is_some = (borrow as u8) & 1;
|
|
||||||
|
|
||||||
// Convert to Montgomery form by computing
|
|
||||||
// (a.R^0 * R^2) / R = a.R
|
|
||||||
tmp *= &R2;
|
|
||||||
|
|
||||||
CtOption::new(tmp, Choice::from(is_some))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an element of `Fp` into a byte representation in
|
|
||||||
/// little-endian byte order.
|
|
||||||
fn to_bytes(&self) -> [u8; 32] {
|
fn to_bytes(&self) -> [u8; 32] {
|
||||||
// Turn into canonical form by computing
|
<Self as ff::PrimeField>::to_repr(self)
|
||||||
// (a.R) / R = a
|
|
||||||
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
|
|
||||||
|
|
||||||
let mut res = [0; 32];
|
|
||||||
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
|
|
||||||
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
|
|
||||||
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
|
|
||||||
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a 512-bit little endian integer into
|
/// Converts a 512-bit little endian integer into
|
||||||
|
|
@ -764,8 +791,8 @@ impl FieldExt for Fp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
use ff::{Field, PrimeField};
|
use ff::Field;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inv() {
|
fn test_inv() {
|
||||||
|
|
@ -782,6 +809,7 @@ fn test_inv() {
|
||||||
assert_eq!(inv, INV);
|
assert_eq!(inv, INV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rescue() {
|
fn test_rescue() {
|
||||||
// NB: TWO_INV is standing in as a "random" field element
|
// NB: TWO_INV is standing in as a "random" field element
|
||||||
|
|
@ -793,6 +821,7 @@ fn test_rescue() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sqrt() {
|
fn test_sqrt() {
|
||||||
// NB: TWO_INV is standing in as a "random" field element
|
// NB: TWO_INV is standing in as a "random" field element
|
||||||
|
|
@ -800,6 +829,7 @@ fn test_sqrt() {
|
||||||
assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV);
|
assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pow_by_t_minus1_over2() {
|
fn test_pow_by_t_minus1_over2() {
|
||||||
// NB: TWO_INV is standing in as a "random" field element
|
// NB: TWO_INV is standing in as a "random" field element
|
||||||
|
|
@ -807,6 +837,7 @@ fn test_pow_by_t_minus1_over2() {
|
||||||
assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &Fp::T_MINUS1_OVER2));
|
assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &Fp::T_MINUS1_OVER2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sqrt_ratio_and_alt() {
|
fn test_sqrt_ratio_and_alt() {
|
||||||
// (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field
|
// (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field
|
||||||
|
|
@ -853,6 +884,7 @@ fn test_sqrt_ratio_and_alt() {
|
||||||
assert!(v == expected);
|
assert!(v == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zeta() {
|
fn test_zeta() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -868,6 +900,7 @@ fn test_zeta() {
|
||||||
assert!(c == Fp::one());
|
assert!(c == Fp::one());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_root_of_unity() {
|
fn test_root_of_unity() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -876,16 +909,19 @@ fn test_root_of_unity() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inv_root_of_unity() {
|
fn test_inv_root_of_unity() {
|
||||||
assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap());
|
assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inv_2() {
|
fn test_inv_2() {
|
||||||
assert_eq!(Fp::TWO_INV, Fp::from(2).invert().unwrap());
|
assert_eq!(Fp::TWO_INV, Fp::from(2).invert().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delta() {
|
fn test_delta() {
|
||||||
assert_eq!(Fp::DELTA, GENERATOR.pow(&[1u64 << Fp::S, 0, 0, 0]));
|
assert_eq!(Fp::DELTA, GENERATOR.pow(&[1u64 << Fp::S, 0, 0, 0]));
|
||||||
|
|
|
||||||
162
src/fields/fq.rs
162
src/fields/fq.rs
|
|
@ -1,14 +1,21 @@
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::{Add, Mul, Neg, Sub};
|
use core::ops::{Add, Mul, Neg, Sub};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
use ff::PrimeField;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
#[cfg(feature = "bits")]
|
#[cfg(feature = "bits")]
|
||||||
use ff::{FieldBits, PrimeFieldBits};
|
use ff::{FieldBits, PrimeFieldBits};
|
||||||
|
|
||||||
use crate::arithmetic::{adc, mac, sbb, FieldExt, Group, SqrtTables};
|
use crate::arithmetic::{adc, mac, sbb};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use crate::arithmetic::{FieldExt, Group, SqrtTables};
|
||||||
|
|
||||||
/// This represents an element of $\mathbb{F}_q$ where
|
/// This represents an element of $\mathbb{F}_q$ where
|
||||||
///
|
///
|
||||||
|
|
@ -23,7 +30,7 @@ pub struct Fq(pub(crate) [u64; 4]);
|
||||||
|
|
||||||
impl fmt::Debug for Fq {
|
impl fmt::Debug for Fq {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let tmp = self.to_bytes();
|
let tmp = self.to_repr();
|
||||||
write!(f, "0x")?;
|
write!(f, "0x")?;
|
||||||
for &b in tmp.iter().rev() {
|
for &b in tmp.iter().rev() {
|
||||||
write!(f, "{:02x}", b)?;
|
write!(f, "{:02x}", b)?;
|
||||||
|
|
@ -64,23 +71,23 @@ impl PartialEq for Fq {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::Ord for Fq {
|
impl core::cmp::Ord for Fq {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
let left = self.to_bytes();
|
let left = self.to_repr();
|
||||||
let right = other.to_bytes();
|
let right = other.to_repr();
|
||||||
left.iter()
|
left.iter()
|
||||||
.zip(right.iter())
|
.zip(right.iter())
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
|
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
|
||||||
std::cmp::Ordering::Equal => None,
|
core::cmp::Ordering::Equal => None,
|
||||||
res => Some(res),
|
res => Some(res),
|
||||||
})
|
})
|
||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
.unwrap_or(core::cmp::Ordering::Equal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::PartialOrd for Fq {
|
impl core::cmp::PartialOrd for Fq {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -217,6 +224,7 @@ const ROOT_OF_UNITY: Fq = Fq::from_raw([
|
||||||
/// GENERATOR^{2^s} where t * 2^s + 1 = q
|
/// GENERATOR^{2^s} where t * 2^s + 1 = q
|
||||||
/// with t odd. In other words, this
|
/// with t odd. In other words, this
|
||||||
/// is a t root of unity.
|
/// is a t root of unity.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
const DELTA: Fq = Fq::from_raw([
|
const DELTA: Fq = Fq::from_raw([
|
||||||
0x8494392472d1683c,
|
0x8494392472d1683c,
|
||||||
0xe3ac3376541d1140,
|
0xe3ac3376541d1140,
|
||||||
|
|
@ -437,16 +445,17 @@ impl Fq {
|
||||||
|
|
||||||
impl From<Fq> for [u8; 32] {
|
impl From<Fq> for [u8; 32] {
|
||||||
fn from(value: Fq) -> [u8; 32] {
|
fn from(value: Fq) -> [u8; 32] {
|
||||||
value.to_bytes()
|
value.to_repr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Fq> for [u8; 32] {
|
impl<'a> From<&'a Fq> for [u8; 32] {
|
||||||
fn from(value: &'a Fq) -> [u8; 32] {
|
fn from(value: &'a Fq) -> [u8; 32] {
|
||||||
value.to_bytes()
|
value.to_repr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl Group for Fq {
|
impl Group for Fq {
|
||||||
type Scalar = Fq;
|
type Scalar = Fq;
|
||||||
|
|
||||||
|
|
@ -466,10 +475,16 @@ impl Group for Fq {
|
||||||
|
|
||||||
impl ff::Field for Fq {
|
impl ff::Field for Fq {
|
||||||
fn random(mut rng: impl RngCore) -> Self {
|
fn random(mut rng: impl RngCore) -> Self {
|
||||||
let mut random_bytes = [0; 64];
|
Self::from_u512([
|
||||||
rng.fill_bytes(&mut random_bytes[..]);
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
Self::from_bytes_wide(&random_bytes)
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
rng.next_u64(),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zero() -> Self {
|
fn zero() -> Self {
|
||||||
|
|
@ -491,8 +506,22 @@ impl ff::Field for Fq {
|
||||||
|
|
||||||
/// Computes the square root of this element, if it exists.
|
/// Computes the square root of this element, if it exists.
|
||||||
fn sqrt(&self) -> CtOption<Self> {
|
fn sqrt(&self) -> CtOption<Self> {
|
||||||
let (is_square, res) = self.sqrt_alt();
|
#[cfg(feature = "std")]
|
||||||
CtOption::new(res, is_square)
|
{
|
||||||
|
let (is_square, res) = FQ_TABLES.sqrt_alt(self);
|
||||||
|
CtOption::new(res, is_square)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
crate::arithmetic::sqrt_tonelli_shanks(
|
||||||
|
self,
|
||||||
|
&[
|
||||||
|
0x04ca_546e_c623_7590,
|
||||||
|
0x0000_0000_1123_4c7e,
|
||||||
|
0x0000_0000_0000_0000,
|
||||||
|
0x0000_0000_2000_0000,
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the multiplicative inverse of this element,
|
/// Computes the multiplicative inverse of this element,
|
||||||
|
|
@ -535,15 +564,47 @@ impl ff::PrimeField for Fq {
|
||||||
const S: u32 = S;
|
const S: u32 = S;
|
||||||
|
|
||||||
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||||
Self::from_bytes(&repr)
|
let mut tmp = Fq([0, 0, 0, 0]);
|
||||||
|
|
||||||
|
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
|
||||||
|
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
|
||||||
|
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
|
||||||
|
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
|
||||||
|
|
||||||
|
// Try to subtract the modulus
|
||||||
|
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
|
||||||
|
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
|
||||||
|
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
|
||||||
|
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
|
||||||
|
|
||||||
|
// If the element is smaller than MODULUS then the
|
||||||
|
// subtraction will underflow, producing a borrow value
|
||||||
|
// of 0xffff...ffff. Otherwise, it'll be zero.
|
||||||
|
let is_some = (borrow as u8) & 1;
|
||||||
|
|
||||||
|
// Convert to Montgomery form by computing
|
||||||
|
// (a.R^0 * R^2) / R = a.R
|
||||||
|
tmp *= &R2;
|
||||||
|
|
||||||
|
CtOption::new(tmp, Choice::from(is_some))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_repr(&self) -> Self::Repr {
|
fn to_repr(&self) -> Self::Repr {
|
||||||
self.to_bytes()
|
// Turn into canonical form by computing
|
||||||
|
// (a.R) / R = a
|
||||||
|
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
|
||||||
|
|
||||||
|
let mut res = [0; 32];
|
||||||
|
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
|
||||||
|
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
|
||||||
|
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
|
||||||
|
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_odd(&self) -> Choice {
|
fn is_odd(&self) -> Choice {
|
||||||
Choice::from(self.to_bytes()[0] & 1)
|
Choice::from(self.to_repr()[0] & 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multiplicative_generator() -> Self {
|
fn multiplicative_generator() -> Self {
|
||||||
|
|
@ -551,7 +612,7 @@ impl ff::PrimeField for Fq {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_of_unity() -> Self {
|
fn root_of_unity() -> Self {
|
||||||
Self::ROOT_OF_UNITY
|
ROOT_OF_UNITY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -602,11 +663,13 @@ impl PrimeFieldBits for Fq {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta.
|
// The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta.
|
||||||
static ref FQ_TABLES: SqrtTables<Fq> = SqrtTables::new(0x116A9E, 1206);
|
static ref FQ_TABLES: SqrtTables<Fq> = SqrtTables::new(0x116A9E, 1206);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
impl FieldExt for Fq {
|
impl FieldExt for Fq {
|
||||||
const MODULUS: &'static str =
|
const MODULUS: &'static str =
|
||||||
"0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001";
|
"0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001";
|
||||||
|
|
@ -660,48 +723,12 @@ impl FieldExt for Fq {
|
||||||
Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0])
|
Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to convert a little-endian byte representation of
|
|
||||||
/// a scalar into a `Fq`, failing if the input is not canonical.
|
|
||||||
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fq> {
|
fn from_bytes(bytes: &[u8; 32]) -> CtOption<Fq> {
|
||||||
let mut tmp = Fq([0, 0, 0, 0]);
|
<Self as ff::PrimeField>::from_repr(*bytes)
|
||||||
|
|
||||||
tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
|
|
||||||
tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
|
|
||||||
tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
|
|
||||||
tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
|
|
||||||
|
|
||||||
// Try to subtract the modulus
|
|
||||||
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
|
|
||||||
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
|
|
||||||
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
|
|
||||||
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
|
|
||||||
|
|
||||||
// If the element is smaller than MODULUS then the
|
|
||||||
// subtraction will underflow, producing a borrow value
|
|
||||||
// of 0xffff...ffff. Otherwise, it'll be zero.
|
|
||||||
let is_some = (borrow as u8) & 1;
|
|
||||||
|
|
||||||
// Convert to Montgomery form by computing
|
|
||||||
// (a.R^0 * R^2) / R = a.R
|
|
||||||
tmp *= &R2;
|
|
||||||
|
|
||||||
CtOption::new(tmp, Choice::from(is_some))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an element of `Fq` into a byte representation in
|
|
||||||
/// little-endian byte order.
|
|
||||||
fn to_bytes(&self) -> [u8; 32] {
|
fn to_bytes(&self) -> [u8; 32] {
|
||||||
// Turn into canonical form by computing
|
<Self as ff::PrimeField>::to_repr(self)
|
||||||
// (a.R) / R = a
|
|
||||||
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
|
|
||||||
|
|
||||||
let mut res = [0; 32];
|
|
||||||
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
|
|
||||||
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
|
|
||||||
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
|
|
||||||
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a 512-bit little endian integer into
|
/// Converts a 512-bit little endian integer into
|
||||||
|
|
@ -764,8 +791,8 @@ impl FieldExt for Fq {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
use ff::{Field, PrimeField};
|
use ff::Field;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inv() {
|
fn test_inv() {
|
||||||
|
|
@ -782,6 +809,7 @@ fn test_inv() {
|
||||||
assert_eq!(inv, INV);
|
assert_eq!(inv, INV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rescue() {
|
fn test_rescue() {
|
||||||
// NB: TWO_INV is standing in as a "random" field element
|
// NB: TWO_INV is standing in as a "random" field element
|
||||||
|
|
@ -793,6 +821,7 @@ fn test_rescue() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sqrt() {
|
fn test_sqrt() {
|
||||||
// NB: TWO_INV is standing in as a "random" field element
|
// NB: TWO_INV is standing in as a "random" field element
|
||||||
|
|
@ -800,6 +829,7 @@ fn test_sqrt() {
|
||||||
assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV);
|
assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pow_by_t_minus1_over2() {
|
fn test_pow_by_t_minus1_over2() {
|
||||||
// NB: TWO_INV is standing in as a "random" field element
|
// NB: TWO_INV is standing in as a "random" field element
|
||||||
|
|
@ -807,6 +837,7 @@ fn test_pow_by_t_minus1_over2() {
|
||||||
assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &Fq::T_MINUS1_OVER2));
|
assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &Fq::T_MINUS1_OVER2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sqrt_ratio_and_alt() {
|
fn test_sqrt_ratio_and_alt() {
|
||||||
// (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field
|
// (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field
|
||||||
|
|
@ -853,6 +884,7 @@ fn test_sqrt_ratio_and_alt() {
|
||||||
assert!(v == expected);
|
assert!(v == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zeta() {
|
fn test_zeta() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -867,6 +899,7 @@ fn test_zeta() {
|
||||||
assert!(c == Fq::one());
|
assert!(c == Fq::one());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_root_of_unity() {
|
fn test_root_of_unity() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -875,16 +908,19 @@ fn test_root_of_unity() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inv_root_of_unity() {
|
fn test_inv_root_of_unity() {
|
||||||
assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap());
|
assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inv_2() {
|
fn test_inv_2() {
|
||||||
assert_eq!(Fq::TWO_INV, Fq::from(2).invert().unwrap());
|
assert_eq!(Fq::TWO_INV, Fq::from(2).invert().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_delta() {
|
fn test_delta() {
|
||||||
assert_eq!(Fq::DELTA, GENERATOR.pow(&[1u64 << Fq::S, 0, 0, 0]));
|
assert_eq!(Fq::DELTA, GENERATOR.pow(&[1u64 << Fq::S, 0, 0, 0]));
|
||||||
|
|
|
||||||
10
src/lib.rs
10
src/lib.rs
|
|
@ -1,5 +1,6 @@
|
||||||
//! Implementation of the Pallas / Vesta curve cycle.
|
//! Implementation of the Pallas / Vesta curve cycle.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![allow(unknown_lints)]
|
#![allow(unknown_lints)]
|
||||||
#![allow(clippy::op_ref, clippy::same_item_push, clippy::upper_case_acronyms)]
|
#![allow(clippy::op_ref, clippy::same_item_push, clippy::upper_case_acronyms)]
|
||||||
|
|
@ -8,21 +9,28 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
|
#[cfg(any(feature = "std", test))]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
mod curves;
|
mod curves;
|
||||||
mod fields;
|
mod fields;
|
||||||
|
|
||||||
pub mod arithmetic;
|
pub mod arithmetic;
|
||||||
mod hashtocurve;
|
|
||||||
pub mod pallas;
|
pub mod pallas;
|
||||||
pub mod vesta;
|
pub mod vesta;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod hashtocurve;
|
||||||
|
|
||||||
pub use curves::*;
|
pub use curves::*;
|
||||||
pub use fields::*;
|
pub use fields::*;
|
||||||
|
|
||||||
pub extern crate group;
|
pub extern crate group;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_endo_consistency() {
|
fn test_endo_consistency() {
|
||||||
use crate::arithmetic::{CurveExt, FieldExt};
|
use crate::arithmetic::{CurveExt, FieldExt};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ pub type Point = Ep;
|
||||||
/// A Pallas point in the affine coordinate space (or the point at infinity).
|
/// A Pallas point in the affine coordinate space (or the point at infinity).
|
||||||
pub type Affine = EpAffine;
|
pub type Affine = EpAffine;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn test_iso_map() {
|
fn test_iso_map() {
|
||||||
|
|
@ -64,6 +65,7 @@ fn test_iso_map() {
|
||||||
assert!(p2 == p.double());
|
assert!(p2 == p.double());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iso_map_identity() {
|
fn test_iso_map_identity() {
|
||||||
use crate::arithmetic::CurveExt;
|
use crate::arithmetic::CurveExt;
|
||||||
|
|
@ -98,6 +100,7 @@ fn test_iso_map_identity() {
|
||||||
assert!(bool::from(p.is_identity()));
|
assert!(bool::from(p.is_identity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_map_to_curve_simple_swu() {
|
fn test_map_to_curve_simple_swu() {
|
||||||
use crate::arithmetic::CurveExt;
|
use crate::arithmetic::CurveExt;
|
||||||
|
|
@ -132,6 +135,7 @@ fn test_map_to_curve_simple_swu() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hash_to_curve() {
|
fn test_hash_to_curve() {
|
||||||
use crate::arithmetic::CurveExt;
|
use crate::arithmetic::CurveExt;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ pub type Point = Eq;
|
||||||
/// A Vesta point in the affine coordinate space (or the point at infinity).
|
/// A Vesta point in the affine coordinate space (or the point at infinity).
|
||||||
pub type Affine = EqAffine;
|
pub type Affine = EqAffine;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_map_to_curve_simple_swu() {
|
fn test_map_to_curve_simple_swu() {
|
||||||
use crate::arithmetic::CurveExt;
|
use crate::arithmetic::CurveExt;
|
||||||
|
|
@ -48,6 +49,7 @@ fn test_map_to_curve_simple_swu() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hash_to_curve() {
|
fn test_hash_to_curve() {
|
||||||
use crate::arithmetic::CurveExt;
|
use crate::arithmetic::CurveExt;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue