mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Downstream the eVRF libraries from FCMP++
Also adds no-std support to secq256k1 and embedwards25519.
This commit is contained in:
@@ -3,35 +3,39 @@ name = "ec-divisors"
|
||||
version = "0.1.0"
|
||||
description = "A library for calculating elliptic curve divisors"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/evrf/divisors"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/divisors"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["ciphersuite", "ff", "group"]
|
||||
edition = "2021"
|
||||
rust-version = "1.71"
|
||||
rust-version = "1.69"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] }
|
||||
std-shims = { path = "../../../common/std-shims", version = "^0.1.1", default-features = false }
|
||||
|
||||
subtle = { version = "2", default-features = false, features = ["std"] }
|
||||
ff = { version = "0.13", default-features = false, features = ["std", "bits"] }
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||
|
||||
subtle = { version = "2", default-features = false }
|
||||
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||
group = { version = "0.13", default-features = false }
|
||||
|
||||
hex = { version = "0.4", optional = true }
|
||||
dalek-ff-group = { path = "../../dalek-ff-group", features = ["std"], optional = true }
|
||||
pasta_curves = { version = "0.5", default-features = false, features = ["bits", "alloc"], optional = true }
|
||||
hex = { version = "0.4", default-features = false, optional = true }
|
||||
dalek-ff-group = { path = "../../dalek-ff-group", default-features = false, optional = true }
|
||||
pasta_curves = { version = "0.5", git = "https://github.com/kayabaNerve/pasta_curves.git", rev = "a46b5be95cacbff54d06aad8d3bbcba42e05d616", default-features = false, features = ["bits", "alloc"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_core = { version = "0.6", features = ["getrandom"] }
|
||||
|
||||
hex = "0.4"
|
||||
dalek-ff-group = { path = "../../dalek-ff-group", features = ["std"] }
|
||||
pasta_curves = { version = "0.5", default-features = false, features = ["bits", "alloc"] }
|
||||
pasta_curves = { version = "0.5", git = "https://github.com/kayabaNerve/pasta_curves.git", rev = "a46b5be95cacbff54d06aad8d3bbcba42e05d616", default-features = false, features = ["bits", "alloc"] }
|
||||
|
||||
[features]
|
||||
ed25519 = ["hex", "dalek-ff-group"]
|
||||
std = ["std-shims/std", "zeroize/std", "subtle/std", "ff/std", "dalek-ff-group?/std"]
|
||||
ed25519 = ["hex/alloc", "dalek-ff-group"]
|
||||
pasta = ["pasta_curves"]
|
||||
default = ["std"]
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![deny(missing_docs)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std_shims::{vec, vec::Vec};
|
||||
|
||||
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConditionallySelectable};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
@@ -18,7 +21,7 @@ pub use poly::Poly;
|
||||
mod tests;
|
||||
|
||||
/// A curve usable with this library.
|
||||
pub trait DivisorCurve: Group + ConstantTimeEq + ConditionallySelectable {
|
||||
pub trait DivisorCurve: Group + ConstantTimeEq + ConditionallySelectable + Zeroize {
|
||||
/// An element of the field this curve is defined over.
|
||||
type FieldElement: Zeroize + PrimeField + ConditionallySelectable;
|
||||
|
||||
@@ -54,6 +57,8 @@ pub trait DivisorCurve: Group + ConstantTimeEq + ConditionallySelectable {
|
||||
/// Convert a point to its x and y coordinates.
|
||||
///
|
||||
/// Returns None if passed the point at infinity.
|
||||
///
|
||||
/// This function may run in time variable to if the point is the identity.
|
||||
fn to_xy(point: Self) -> Option<(Self::FieldElement, Self::FieldElement)>;
|
||||
}
|
||||
|
||||
@@ -271,8 +276,16 @@ pub struct ScalarDecomposition<F: Zeroize + PrimeFieldBits> {
|
||||
}
|
||||
|
||||
impl<F: Zeroize + PrimeFieldBits> ScalarDecomposition<F> {
|
||||
/// Decompose a scalar.
|
||||
pub fn new(scalar: F) -> Self {
|
||||
/// Decompose a non-zero scalar.
|
||||
///
|
||||
/// Returns `None` if the scalar is zero.
|
||||
///
|
||||
/// This function is constant time if the scalar is non-zero.
|
||||
pub fn new(scalar: F) -> Option<Self> {
|
||||
if bool::from(scalar.is_zero()) {
|
||||
None?;
|
||||
}
|
||||
|
||||
/*
|
||||
We need the sum of the coefficients to equal F::NUM_BITS. The scalar's bits will be less than
|
||||
F::NUM_BITS. Accordingly, we need to increment the sum of the coefficients without
|
||||
@@ -400,7 +413,12 @@ impl<F: Zeroize + PrimeFieldBits> ScalarDecomposition<F> {
|
||||
}
|
||||
debug_assert!(bool::from(decomposition.iter().sum::<u64>().ct_eq(&num_bits)));
|
||||
|
||||
ScalarDecomposition { scalar, decomposition }
|
||||
Some(ScalarDecomposition { scalar, decomposition })
|
||||
}
|
||||
|
||||
/// The scalar.
|
||||
pub fn scalar(&self) -> &F {
|
||||
&self.scalar
|
||||
}
|
||||
|
||||
/// The decomposition of the scalar.
|
||||
@@ -414,7 +432,7 @@ impl<F: Zeroize + PrimeFieldBits> ScalarDecomposition<F> {
|
||||
///
|
||||
/// This function executes in constant time with regards to the scalar.
|
||||
///
|
||||
/// This function MAY panic if this scalar is zero.
|
||||
/// This function MAY panic if the generator is the point at infinity.
|
||||
pub fn scalar_mul_divisor<C: Zeroize + DivisorCurve<Scalar = F>>(
|
||||
&self,
|
||||
mut generator: C,
|
||||
@@ -430,37 +448,19 @@ impl<F: Zeroize + PrimeFieldBits> ScalarDecomposition<F> {
|
||||
divisor_points[0] = -generator * self.scalar;
|
||||
|
||||
// Write the decomposition
|
||||
let mut write_to: u32 = 1;
|
||||
let mut write_above: u64 = 0;
|
||||
for coefficient in &self.decomposition {
|
||||
let mut coefficient = *coefficient;
|
||||
// Iterate over the maximum amount of iters for this value to be constant time regardless of
|
||||
// any branch prediction algorithms
|
||||
for _ in 0 .. <C::Scalar as PrimeField>::NUM_BITS {
|
||||
// Write the generator to the slot we're supposed to
|
||||
/*
|
||||
Without this loop, we'd increment this dependent on the distribution within the
|
||||
decomposition. If the distribution is bottom-heavy, we won't access the tail of
|
||||
`divisor_points` for a while, risking it being ejected out of the cache (causing a cache
|
||||
miss which may not occur with a top-heavy distribution which quickly moves to the tail).
|
||||
|
||||
This is O(log2(NUM_BITS) ** 3) though, as this the third loop, which is horrific.
|
||||
*/
|
||||
for i in 1 ..= <C::Scalar as PrimeField>::NUM_BITS {
|
||||
divisor_points[i as usize] =
|
||||
<_>::conditional_select(&divisor_points[i as usize], &generator, i.ct_eq(&write_to));
|
||||
}
|
||||
// If the coefficient isn't zero, increment write_to (so we don't overwrite this generator
|
||||
// when it should be there)
|
||||
let coefficient_not_zero = !coefficient.ct_eq(&0);
|
||||
write_to = <_>::conditional_select(&write_to, &(write_to + 1), coefficient_not_zero);
|
||||
// Subtract one from the coefficient, if it's not zero and won't underflow
|
||||
coefficient =
|
||||
<_>::conditional_select(&coefficient, &coefficient.wrapping_sub(1), coefficient_not_zero);
|
||||
// Write the generator to every slot except the slots we have already written to.
|
||||
for i in 1 ..= (<C::Scalar as PrimeField>::NUM_BITS as u64) {
|
||||
divisor_points[i as usize].conditional_assign(&generator, i.ct_gt(&write_above));
|
||||
}
|
||||
|
||||
// Increase the next write start by the coefficient.
|
||||
write_above += coefficient;
|
||||
generator = generator.double();
|
||||
}
|
||||
|
||||
// Create a divisor out of all points except the last point which is solely scratch
|
||||
// Create a divisor out of the points
|
||||
let res = new_divisor(&divisor_points).unwrap();
|
||||
divisor_points.zeroize();
|
||||
res
|
||||
@@ -511,6 +511,7 @@ mod pasta {
|
||||
|
||||
#[cfg(any(test, feature = "ed25519"))]
|
||||
mod ed25519 {
|
||||
use subtle::{Choice, ConditionallySelectable};
|
||||
use group::{
|
||||
ff::{Field, PrimeField},
|
||||
Group, GroupEncoding,
|
||||
@@ -558,9 +559,13 @@ mod ed25519 {
|
||||
((D * edwards_y_sq) + Self::FieldElement::ONE).invert().unwrap())
|
||||
.sqrt()
|
||||
.unwrap();
|
||||
if u8::from(bool::from(edwards_x.is_odd())) != x_is_odd {
|
||||
edwards_x = -edwards_x;
|
||||
}
|
||||
|
||||
// Negate the x coordinate if the sign doesn't match
|
||||
edwards_x = <_>::conditional_select(
|
||||
&edwards_x,
|
||||
&-edwards_x,
|
||||
edwards_x.is_odd() ^ Choice::from(x_is_odd),
|
||||
);
|
||||
|
||||
// Calculate the x and y coordinates for Wei25519
|
||||
let edwards_y_plus_one = Self::FieldElement::ONE + edwards_y;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::ops::{Add, Neg, Sub, Mul, Rem};
|
||||
use std_shims::{vec, vec::Vec};
|
||||
|
||||
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConditionallySelectable};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -257,7 +258,7 @@ impl<F: From<u64> + Zeroize + PrimeField> Poly<F> {
|
||||
self.zero_coefficient = F::ZERO;
|
||||
|
||||
// Move the x coefficients
|
||||
std::mem::swap(&mut self.yx_coefficients[power_of_y - 1], &mut self.x_coefficients);
|
||||
core::mem::swap(&mut self.yx_coefficients[power_of_y - 1], &mut self.x_coefficients);
|
||||
self.x_coefficients = vec![];
|
||||
|
||||
self
|
||||
@@ -564,7 +565,7 @@ impl<F: From<u64> + Zeroize + PrimeField> Poly<F> {
|
||||
quotient = conditional_select_poly(
|
||||
quotient,
|
||||
// If the dividing coefficient was for y**0 x**0, we return the poly scaled by its inverse
|
||||
self.clone() * denominator_dividing_coefficient_inv,
|
||||
self * denominator_dividing_coefficient_inv,
|
||||
denominator_dividing_coefficient.ct_eq(&CoefficientIndex { y_pow: 0, x_pow: 0 }),
|
||||
);
|
||||
remainder = conditional_select_poly(
|
||||
|
||||
Reference in New Issue
Block a user