mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 14:39:25 +00:00
Add implementation of secq256k1
This commit is contained in:
39
crypto/evrf/secq256k1/Cargo.toml
Normal file
39
crypto/evrf/secq256k1/Cargo.toml
Normal file
@@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "secq256k1"
|
||||
version = "0.1.0"
|
||||
description = "An implementation of the curve secp256k1 cycles with"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/evrf/secq256k1"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["secp256k1", "secq256k1", "group"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
rustversion = "1"
|
||||
hex-literal = { version = "0.4", default-features = false }
|
||||
|
||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||
|
||||
zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] }
|
||||
subtle = { version = "^2.4", default-features = false, features = ["std"] }
|
||||
|
||||
ff = { version = "0.13", default-features = false, features = ["std", "bits"] }
|
||||
group = { version = "0.13", default-features = false }
|
||||
|
||||
generic-array = { version = "0.14", default-features = false }
|
||||
crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] }
|
||||
|
||||
k256 = { version = "0.13", default-features = false, features = ["arithmetic"] }
|
||||
|
||||
ec-divisors = { path = "../divisors" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4"
|
||||
|
||||
rand_core = { version = "0.6", features = ["std"] }
|
||||
|
||||
ff-group-tests = { path = "../../ff-group-tests" }
|
||||
21
crypto/evrf/secq256k1/LICENSE
Normal file
21
crypto/evrf/secq256k1/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2024 Luke Parker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
3
crypto/evrf/secq256k1/README.md
Normal file
3
crypto/evrf/secq256k1/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# secq256k1
|
||||
|
||||
An implementation of the curve secp256k1 cycles with.
|
||||
290
crypto/evrf/secq256k1/src/backend.rs
Normal file
290
crypto/evrf/secq256k1/src/backend.rs
Normal file
@@ -0,0 +1,290 @@
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// Use black_box when possible
|
||||
#[rustversion::since(1.66)]
|
||||
use core::hint::black_box;
|
||||
#[rustversion::before(1.66)]
|
||||
fn black_box<T>(val: T) -> T {
|
||||
val
|
||||
}
|
||||
|
||||
pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||
let bit_ref = black_box(bit_ref);
|
||||
|
||||
let mut bit = black_box(*bit_ref);
|
||||
let res = black_box(bit as u8);
|
||||
bit.zeroize();
|
||||
debug_assert!((res | 1) == 1);
|
||||
|
||||
bit_ref.zeroize();
|
||||
res
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! from_wrapper {
|
||||
($wrapper: ident, $inner: ident, $uint: ident) => {
|
||||
impl From<$uint> for $wrapper {
|
||||
fn from(a: $uint) -> $wrapper {
|
||||
Self(Residue::new(&$inner::from(a)))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! field {
|
||||
(
|
||||
$FieldName: ident,
|
||||
$ResidueType: ident,
|
||||
|
||||
$MODULUS_STR: ident,
|
||||
$MODULUS: ident,
|
||||
$WIDE_MODULUS: ident,
|
||||
|
||||
$MULTIPLICATIVE_GENERATOR: literal,
|
||||
$S: literal,
|
||||
$ROOT_OF_UNITY: literal,
|
||||
$DELTA: literal,
|
||||
) => {
|
||||
use core::{
|
||||
ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
||||
iter::{Sum, Product},
|
||||
};
|
||||
|
||||
use subtle::{Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallySelectable};
|
||||
use rand_core::RngCore;
|
||||
|
||||
use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus};
|
||||
|
||||
use ff::{Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic};
|
||||
|
||||
use $crate::backend::u8_from_bool;
|
||||
|
||||
fn reduce(x: U512) -> U256 {
|
||||
U256::from_le_slice(&x.rem(&NonZero::new($WIDE_MODULUS).unwrap()).to_le_bytes()[.. 32])
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for $FieldName {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0.ct_eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for $FieldName {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
$FieldName(Residue::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
math_op!($FieldName, $FieldName, Add, add, AddAssign, add_assign, |x: $ResidueType, y| x
|
||||
.add(&y));
|
||||
math_op!($FieldName, $FieldName, Sub, sub, SubAssign, sub_assign, |x: $ResidueType, y| x
|
||||
.sub(&y));
|
||||
math_op!($FieldName, $FieldName, Mul, mul, MulAssign, mul_assign, |x: $ResidueType, y| x
|
||||
.mul(&y));
|
||||
|
||||
from_wrapper!($FieldName, U256, u8);
|
||||
from_wrapper!($FieldName, U256, u16);
|
||||
from_wrapper!($FieldName, U256, u32);
|
||||
from_wrapper!($FieldName, U256, u64);
|
||||
from_wrapper!($FieldName, U256, u128);
|
||||
|
||||
impl Neg for $FieldName {
|
||||
type Output = $FieldName;
|
||||
fn neg(self) -> $FieldName {
|
||||
Self(self.0.neg())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a $FieldName {
|
||||
type Output = $FieldName;
|
||||
fn neg(self) -> Self::Output {
|
||||
(*self).neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl $FieldName {
|
||||
/// Perform an exponentation.
|
||||
pub fn pow(&self, other: $FieldName) -> $FieldName {
|
||||
let mut table = [Self(Residue::ONE); 16];
|
||||
table[1] = *self;
|
||||
for i in 2 .. 16 {
|
||||
table[i] = table[i - 1] * self;
|
||||
}
|
||||
|
||||
let mut res = Self(Residue::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(bit.deref_mut());
|
||||
bits |= bit;
|
||||
bit.zeroize();
|
||||
|
||||
if ((i + 1) % 4) == 0 {
|
||||
if i != 3 {
|
||||
for _ in 0 .. 4 {
|
||||
res *= res;
|
||||
}
|
||||
}
|
||||
|
||||
let mut factor = table[0];
|
||||
for (j, candidate) in table[1 ..].iter().enumerate() {
|
||||
let j = j + 1;
|
||||
factor = Self::conditional_select(&factor, &candidate, usize::from(bits).ct_eq(&j));
|
||||
}
|
||||
res *= factor;
|
||||
bits = 0;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for $FieldName {
|
||||
const ZERO: Self = Self(Residue::ZERO);
|
||||
const ONE: Self = Self(Residue::ONE);
|
||||
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
let mut bytes = [0; 64];
|
||||
rng.fill_bytes(&mut bytes);
|
||||
$FieldName(Residue::new(&reduce(U512::from_le_slice(bytes.as_ref()))))
|
||||
}
|
||||
|
||||
fn square(&self) -> Self {
|
||||
Self(self.0.square())
|
||||
}
|
||||
fn double(&self) -> Self {
|
||||
*self + self
|
||||
}
|
||||
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
let res = self.0.invert();
|
||||
CtOption::new(Self(res.0), res.1.into())
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
// (p + 1) // 4, as valid since p % 4 == 3
|
||||
let mod_plus_one_div_four = $MODULUS.saturating_add(&U256::ONE).wrapping_div(&(4u8.into()));
|
||||
let res = self.pow(Self($ResidueType::new_checked(&mod_plus_one_div_four).unwrap()));
|
||||
CtOption::new(res, res.square().ct_eq(self))
|
||||
}
|
||||
|
||||
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
|
||||
sqrt_ratio_generic(num, div)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeField for $FieldName {
|
||||
type Repr = [u8; 32];
|
||||
|
||||
const MODULUS: &'static str = $MODULUS_STR;
|
||||
|
||||
const NUM_BITS: u32 = 256;
|
||||
const CAPACITY: u32 = 255;
|
||||
|
||||
const TWO_INV: Self = $FieldName($ResidueType::new(&U256::from_u8(2)).invert().0);
|
||||
|
||||
const MULTIPLICATIVE_GENERATOR: Self =
|
||||
Self(Residue::new(&U256::from_u8($MULTIPLICATIVE_GENERATOR)));
|
||||
const S: u32 = $S;
|
||||
|
||||
const ROOT_OF_UNITY: Self = $FieldName(Residue::new(&U256::from_be_hex($ROOT_OF_UNITY)));
|
||||
const ROOT_OF_UNITY_INV: Self = Self(Self::ROOT_OF_UNITY.0.invert().0);
|
||||
|
||||
const DELTA: Self = $FieldName(Residue::new(&U256::from_be_hex($DELTA)));
|
||||
|
||||
fn from_repr(bytes: Self::Repr) -> CtOption<Self> {
|
||||
let res = U256::from_le_slice(&bytes);
|
||||
CtOption::new($FieldName(Residue::new(&res)), res.ct_lt(&$MODULUS))
|
||||
}
|
||||
fn to_repr(&self) -> Self::Repr {
|
||||
let mut repr = [0; 32];
|
||||
repr.copy_from_slice(&self.0.retrieve().to_le_bytes());
|
||||
repr
|
||||
}
|
||||
|
||||
fn is_odd(&self) -> Choice {
|
||||
self.0.retrieve().is_odd()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeFieldBits for $FieldName {
|
||||
type ReprBits = [u8; 32];
|
||||
|
||||
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
||||
self.to_repr().into()
|
||||
}
|
||||
|
||||
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||
let mut repr = [0; 32];
|
||||
repr.copy_from_slice(&MODULUS.to_le_bytes());
|
||||
repr.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum<$FieldName> for $FieldName {
|
||||
fn sum<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
||||
let mut res = $FieldName::ZERO;
|
||||
for item in iter {
|
||||
res += item;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a $FieldName> for $FieldName {
|
||||
fn sum<I: Iterator<Item = &'a $FieldName>>(iter: I) -> $FieldName {
|
||||
iter.cloned().sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl Product<$FieldName> for $FieldName {
|
||||
fn product<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
||||
let mut res = $FieldName::ONE;
|
||||
for item in iter {
|
||||
res *= item;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Product<&'a $FieldName> for $FieldName {
|
||||
fn product<I: Iterator<Item = &'a $FieldName>>(iter: I) -> $FieldName {
|
||||
iter.cloned().product()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
13
crypto/evrf/secq256k1/src/lib.rs
Normal file
13
crypto/evrf/secq256k1/src/lib.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
#[macro_use]
|
||||
mod backend;
|
||||
|
||||
mod scalar;
|
||||
pub use scalar::Scalar;
|
||||
|
||||
pub use k256::Scalar as FieldElement;
|
||||
|
||||
mod point;
|
||||
pub use point::Point;
|
||||
414
crypto/evrf/secq256k1/src/point.rs
Normal file
414
crypto/evrf/secq256k1/src/point.rs
Normal file
@@ -0,0 +1,414 @@
|
||||
use core::{
|
||||
ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
||||
iter::Sum,
|
||||
};
|
||||
|
||||
use rand_core::RngCore;
|
||||
|
||||
use zeroize::Zeroize;
|
||||
use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable};
|
||||
|
||||
use generic_array::{typenum::U33, GenericArray};
|
||||
|
||||
use group::{
|
||||
ff::{Field, PrimeField, PrimeFieldBits},
|
||||
Group, GroupEncoding,
|
||||
prime::PrimeGroup,
|
||||
};
|
||||
|
||||
use crate::{backend::u8_from_bool, Scalar, FieldElement};
|
||||
|
||||
fn recover_y(x: FieldElement) -> CtOption<FieldElement> {
|
||||
// x**3 + B since a = 0
|
||||
((x.square() * x) + FieldElement::from(7u64)).sqrt()
|
||||
}
|
||||
|
||||
/// Point.
|
||||
#[derive(Clone, Copy, Debug, Zeroize)]
|
||||
#[repr(C)]
|
||||
pub struct Point {
|
||||
x: FieldElement, // / Z
|
||||
y: FieldElement, // / Z
|
||||
z: FieldElement,
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Point {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
let x1 = self.x * other.z;
|
||||
let x2 = other.x * self.z;
|
||||
|
||||
let y1 = self.y * other.z;
|
||||
let y2 = other.y * self.z;
|
||||
|
||||
(self.x.is_zero() & other.x.is_zero()) | (x1.ct_eq(&x2) & y1.ct_eq(&y2))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Point {
|
||||
fn eq(&self, other: &Point) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Point {}
|
||||
|
||||
impl ConditionallySelectable for Point {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Point {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Point;
|
||||
#[allow(non_snake_case)]
|
||||
fn add(self, other: Self) -> Self {
|
||||
// add-2015-rcb
|
||||
|
||||
let a = FieldElement::ZERO;
|
||||
let B = FieldElement::from(7u64);
|
||||
let b3 = B + B + B;
|
||||
|
||||
let X1 = self.x;
|
||||
let Y1 = self.y;
|
||||
let Z1 = self.z;
|
||||
let X2 = other.x;
|
||||
let Y2 = other.y;
|
||||
let Z2 = other.z;
|
||||
|
||||
let t0 = X1 * X2;
|
||||
let t1 = Y1 * Y2;
|
||||
let t2 = Z1 * Z2;
|
||||
let t3 = X1 + Y1;
|
||||
let t4 = X2 + Y2;
|
||||
let t3 = t3 * t4;
|
||||
let t4 = t0 + t1;
|
||||
let t3 = t3 - t4;
|
||||
let t4 = X1 + Z1;
|
||||
let t5 = X2 + Z2;
|
||||
let t4 = t4 * t5;
|
||||
let t5 = t0 + t2;
|
||||
let t4 = t4 - t5;
|
||||
let t5 = Y1 + Z1;
|
||||
let X3 = Y2 + Z2;
|
||||
let t5 = t5 * X3;
|
||||
let X3 = t1 + t2;
|
||||
let t5 = t5 - X3;
|
||||
let Z3 = a * t4;
|
||||
let X3 = b3 * t2;
|
||||
let Z3 = X3 + Z3;
|
||||
let X3 = t1 - Z3;
|
||||
let Z3 = t1 + Z3;
|
||||
let Y3 = X3 * Z3;
|
||||
let t1 = t0 + t0;
|
||||
let t1 = t1 + t0;
|
||||
let t2 = a * t2;
|
||||
let t4 = b3 * t4;
|
||||
let t1 = t1 + t2;
|
||||
let t2 = t0 - t2;
|
||||
let t2 = a * t2;
|
||||
let t4 = t4 + t2;
|
||||
let t0 = t1 * t4;
|
||||
let Y3 = Y3 + t0;
|
||||
let t0 = t5 * t4;
|
||||
let X3 = t3 * X3;
|
||||
let X3 = X3 - t0;
|
||||
let t0 = t3 * t1;
|
||||
let Z3 = t5 * Z3;
|
||||
let Z3 = Z3 + t0;
|
||||
Point { x: X3, y: Y3, z: Z3 }
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Point {
|
||||
fn add_assign(&mut self, other: Point) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Point> for Point {
|
||||
type Output = Point;
|
||||
fn add(self, other: &Point) -> Point {
|
||||
self + *other
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&Point> for Point {
|
||||
fn add_assign(&mut self, other: &Point) {
|
||||
*self += *other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Point {
|
||||
type Output = Point;
|
||||
fn neg(self) -> Self {
|
||||
Point { x: self.x, y: -self.y, z: self.z }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Point {
|
||||
type Output = Point;
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn sub(self, other: Self) -> Self {
|
||||
self + other.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Point {
|
||||
fn sub_assign(&mut self, other: Point) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<&Point> for Point {
|
||||
type Output = Point;
|
||||
fn sub(self, other: &Point) -> Point {
|
||||
self - *other
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<&Point> for Point {
|
||||
fn sub_assign(&mut self, other: &Point) {
|
||||
*self -= *other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Group for Point {
|
||||
type Scalar = Scalar;
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
loop {
|
||||
let mut bytes = GenericArray::default();
|
||||
rng.fill_bytes(bytes.as_mut());
|
||||
let opt = Self::from_bytes(&bytes);
|
||||
if opt.is_some().into() {
|
||||
return opt.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
fn identity() -> Self {
|
||||
Point { x: FieldElement::ZERO, y: FieldElement::ONE, z: FieldElement::ZERO }
|
||||
}
|
||||
fn generator() -> Self {
|
||||
Point {
|
||||
x: FieldElement::from_repr(
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000000000000000000000000001")
|
||||
.into(),
|
||||
)
|
||||
.unwrap(),
|
||||
y: FieldElement::from_repr(
|
||||
hex_literal::hex!("0C7C97045A2074634909ABDF82C9BD0248916189041F2AF0C1B800D1FFC278C0")
|
||||
.into(),
|
||||
)
|
||||
.unwrap(),
|
||||
z: FieldElement::ONE,
|
||||
}
|
||||
}
|
||||
fn is_identity(&self) -> Choice {
|
||||
self.z.ct_eq(&FieldElement::ZERO)
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
fn double(&self) -> Self {
|
||||
// dbl-2007-bl
|
||||
|
||||
let a = FieldElement::ZERO;
|
||||
|
||||
let X1 = self.x;
|
||||
let Y1 = self.y;
|
||||
let Z1 = self.z;
|
||||
|
||||
let XX = X1 * X1;
|
||||
let ZZ = Z1 * Z1;
|
||||
let w = (a * ZZ) + XX.double() + XX;
|
||||
let s = (Y1 * Z1).double();
|
||||
let ss = s * s;
|
||||
let sss = s * ss;
|
||||
let R = Y1 * s;
|
||||
let RR = R * R;
|
||||
let B = X1 + R;
|
||||
let B = (B * B) - XX - RR;
|
||||
let h = (w * w) - B.double();
|
||||
let X3 = h * s;
|
||||
let Y3 = w * (B - h) - RR.double();
|
||||
let Z3 = sss;
|
||||
|
||||
let res = Self { x: X3, y: Y3, z: Z3 };
|
||||
// If self is identity, res will not be well-formed
|
||||
// Accordingly, we return self if self was the identity
|
||||
Self::conditional_select(&res, self, self.is_identity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum<Point> for Point {
|
||||
fn sum<I: Iterator<Item = Point>>(iter: I) -> Point {
|
||||
let mut res = Self::identity();
|
||||
for i in iter {
|
||||
res += i;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a Point> for Point {
|
||||
fn sum<I: Iterator<Item = &'a Point>>(iter: I) -> Point {
|
||||
Point::sum(iter.cloned())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Scalar> for Point {
|
||||
type Output = Point;
|
||||
fn mul(self, mut other: Scalar) -> Point {
|
||||
// Precompute the optimal amount that's a multiple of 2
|
||||
let mut table = [Point::identity(); 16];
|
||||
table[1] = self;
|
||||
for i in 2 .. 16 {
|
||||
table[i] = table[i - 1] + self;
|
||||
}
|
||||
|
||||
let mut res = Self::identity();
|
||||
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(bit.deref_mut());
|
||||
bits |= bit;
|
||||
bit.zeroize();
|
||||
|
||||
if ((i + 1) % 4) == 0 {
|
||||
if i != 3 {
|
||||
for _ in 0 .. 4 {
|
||||
res = res.double();
|
||||
}
|
||||
}
|
||||
|
||||
let mut term = table[0];
|
||||
for (j, candidate) in table[1 ..].iter().enumerate() {
|
||||
let j = j + 1;
|
||||
term = Self::conditional_select(&term, candidate, usize::from(bits).ct_eq(&j));
|
||||
}
|
||||
res += term;
|
||||
bits = 0;
|
||||
}
|
||||
}
|
||||
other.zeroize();
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<Scalar> for Point {
|
||||
fn mul_assign(&mut self, other: Scalar) {
|
||||
*self = *self * other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<&Scalar> for Point {
|
||||
type Output = Point;
|
||||
fn mul(self, other: &Scalar) -> Point {
|
||||
self * *other
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<&Scalar> for Point {
|
||||
fn mul_assign(&mut self, other: &Scalar) {
|
||||
*self *= *other;
|
||||
}
|
||||
}
|
||||
|
||||
impl GroupEncoding for Point {
|
||||
type Repr = GenericArray<u8, U33>;
|
||||
|
||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
// Extract and clear the sign bit
|
||||
let sign = Choice::from(bytes[0] & 1);
|
||||
|
||||
// Parse x, recover y
|
||||
FieldElement::from_repr(*GenericArray::from_slice(&bytes[1 ..])).and_then(|x| {
|
||||
let is_identity = x.is_zero();
|
||||
|
||||
let y = recover_y(x).map(|mut y| {
|
||||
y.conditional_negate(y.is_odd().ct_eq(&!sign));
|
||||
y
|
||||
});
|
||||
|
||||
// If this the identity, set y to 1
|
||||
let y =
|
||||
CtOption::conditional_select(&y, &CtOption::new(FieldElement::ONE, 1.into()), is_identity);
|
||||
// Create the point if we have a y solution
|
||||
let point = y.map(|y| Point { x, y, z: FieldElement::ONE });
|
||||
|
||||
let not_negative_zero = !(is_identity & sign);
|
||||
// Only return the point if it isn't -0 and the sign byte wasn't malleated
|
||||
CtOption::conditional_select(
|
||||
&CtOption::new(Point::identity(), 0.into()),
|
||||
&point,
|
||||
not_negative_zero & ((bytes[0] & 1).ct_eq(&bytes[0])),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||
Point::from_bytes(bytes)
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Self::Repr {
|
||||
let Some(z) = Option::<FieldElement>::from(self.z.invert()) else {
|
||||
return *GenericArray::from_slice(&[0; 33]);
|
||||
};
|
||||
let x = self.x * z;
|
||||
let y = self.y * z;
|
||||
|
||||
let mut res = *GenericArray::from_slice(&[0; 33]);
|
||||
res[1 ..].as_mut().copy_from_slice(&x.to_repr());
|
||||
|
||||
// The following conditional select normalizes the sign to 0 when x is 0
|
||||
let y_sign = u8::conditional_select(&y.is_odd().unwrap_u8(), &0, x.ct_eq(&FieldElement::ZERO));
|
||||
res[0] |= y_sign;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeGroup for Point {}
|
||||
|
||||
impl ec_divisors::DivisorCurve for Point {
|
||||
type FieldElement = FieldElement;
|
||||
|
||||
fn a() -> Self::FieldElement {
|
||||
FieldElement::from(0u64)
|
||||
}
|
||||
fn b() -> Self::FieldElement {
|
||||
FieldElement::from(7u64)
|
||||
}
|
||||
|
||||
fn to_xy(point: Self) -> Option<(Self::FieldElement, Self::FieldElement)> {
|
||||
let z: Self::FieldElement = Option::from(point.z.invert())?;
|
||||
Some((point.x * z, point.y * z))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_curve() {
|
||||
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generator() {
|
||||
assert_eq!(
|
||||
Point::generator(),
|
||||
Point::from_bytes(GenericArray::from_slice(&hex_literal::hex!(
|
||||
"000000000000000000000000000000000000000000000000000000000000000001"
|
||||
)))
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_x_is_invalid() {
|
||||
assert!(Option::<FieldElement>::from(recover_y(FieldElement::ZERO)).is_none());
|
||||
}
|
||||
|
||||
// Checks random won't infinitely loop
|
||||
#[test]
|
||||
fn random() {
|
||||
Point::random(&mut rand_core::OsRng);
|
||||
}
|
||||
51
crypto/evrf/secq256k1/src/scalar.rs
Normal file
51
crypto/evrf/secq256k1/src/scalar.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use zeroize::{DefaultIsZeroes, Zeroize};
|
||||
|
||||
use crypto_bigint::{
|
||||
U256, U512,
|
||||
modular::constant_mod::{ResidueParams, Residue},
|
||||
};
|
||||
|
||||
const MODULUS_STR: &str = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F";
|
||||
|
||||
impl_modulus!(HelioseleneQ, U256, MODULUS_STR);
|
||||
type ResidueType = Residue<HelioseleneQ, { HelioseleneQ::LIMBS }>;
|
||||
|
||||
/// The Scalar field of secq256k1.
|
||||
///
|
||||
/// This is equivalent to the field secp256k1 is defined over.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Scalar(pub(crate) ResidueType);
|
||||
|
||||
impl DefaultIsZeroes for Scalar {}
|
||||
|
||||
pub(crate) const MODULUS: U256 = U256::from_be_hex(MODULUS_STR);
|
||||
|
||||
const WIDE_MODULUS: U512 = U512::from_be_hex(concat!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
|
||||
));
|
||||
|
||||
field!(
|
||||
Scalar,
|
||||
ResidueType,
|
||||
MODULUS_STR,
|
||||
MODULUS,
|
||||
WIDE_MODULUS,
|
||||
3,
|
||||
1,
|
||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
"0000000000000000000000000000000000000000000000000000000000000009",
|
||||
);
|
||||
|
||||
impl Scalar {
|
||||
/// Perform a wide reduction, presumably to obtain a non-biased Scalar field element.
|
||||
pub fn wide_reduce(bytes: [u8; 64]) -> Scalar {
|
||||
Scalar(Residue::new(&reduce(U512::from_le_slice(bytes.as_ref()))))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scalar_field() {
|
||||
ff_group_tests::prime_field::test_prime_field_bits::<_, Scalar>(&mut rand_core::OsRng);
|
||||
}
|
||||
Reference in New Issue
Block a user