mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add crate for working with short Weierstrass elliptic curves
This commit is contained in:
1
.github/workflows/crypto-tests.yml
vendored
1
.github/workflows/crypto-tests.yml
vendored
@@ -36,6 +36,7 @@ jobs:
|
|||||||
-p multiexp \
|
-p multiexp \
|
||||||
-p schnorr-signatures \
|
-p schnorr-signatures \
|
||||||
-p prime-field \
|
-p prime-field \
|
||||||
|
-p short-weierstrass \
|
||||||
-p secq256k1 \
|
-p secq256k1 \
|
||||||
-p embedwards25519 \
|
-p embedwards25519 \
|
||||||
-p dkg \
|
-p dkg \
|
||||||
|
|||||||
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -474,7 +474,7 @@ dependencies = [
|
|||||||
"alloy-rlp",
|
"alloy-rlp",
|
||||||
"alloy-serde",
|
"alloy-serde",
|
||||||
"alloy-sol-types",
|
"alloy-sol-types",
|
||||||
"itertools 0.13.0",
|
"itertools 0.14.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
@@ -2517,7 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
|
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"syn 2.0.106",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2988,6 +2988,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"ec-divisors",
|
"ec-divisors",
|
||||||
"ff-group-tests",
|
"ff-group-tests",
|
||||||
@@ -2997,6 +2998,7 @@ dependencies = [
|
|||||||
"hex-literal",
|
"hex-literal",
|
||||||
"prime-field",
|
"prime-field",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
"short-weierstrass",
|
||||||
"std-shims",
|
"std-shims",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -4559,6 +4561,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@@ -10180,6 +10191,7 @@ dependencies = [
|
|||||||
"prime-field",
|
"prime-field",
|
||||||
"schnorr-signatures",
|
"schnorr-signatures",
|
||||||
"secq256k1",
|
"secq256k1",
|
||||||
|
"short-weierstrass",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -10911,6 +10923,17 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "short-weierstrass"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"group",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.6"
|
version = "1.4.6"
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ members = [
|
|||||||
"crypto/schnorr",
|
"crypto/schnorr",
|
||||||
|
|
||||||
"crypto/prime-field",
|
"crypto/prime-field",
|
||||||
|
"crypto/short-weierstrass",
|
||||||
"crypto/evrf/secq256k1",
|
"crypto/evrf/secq256k1",
|
||||||
"crypto/evrf/embedwards25519",
|
"crypto/evrf/embedwards25519",
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,15 @@ pub mod __prime_field_private {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a odd-prime field.
|
||||||
|
///
|
||||||
|
/// The length of the `modulus_as_be_hex` string will effect the size of the underlying
|
||||||
|
/// representation and the encoding. It MAY have a "0x" prefix, if preferred.
|
||||||
|
///
|
||||||
|
/// `multiplicative_generator_as_be_hex` MAY have a "0x" prefix. It MAY be short and of a length
|
||||||
|
/// less than `modulus_as_be_hex`.
|
||||||
|
///
|
||||||
|
/// `big_endian` controls if the encoded representation will be big-endian or not.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! odd_prime_field {
|
macro_rules! odd_prime_field {
|
||||||
(
|
(
|
||||||
|
|||||||
27
crypto/short-weierstrass/Cargo.toml
Normal file
27
crypto/short-weierstrass/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "short-weierstrass"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A library for working with curves in a short Weierstrass form"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/short-weierstrass"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = ["ff", "group", "elliptic-curve", "weierstrass"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.79"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
zeroize = { version = "^1.5", default-features = false }
|
||||||
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
|
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||||
|
group = { version = "0.13", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc"]
|
||||||
|
std = ["zeroize/std", "subtle/std", "rand_core/std", "ff/std"]
|
||||||
|
default = ["std"]
|
||||||
21
crypto/short-weierstrass/LICENSE
Normal file
21
crypto/short-weierstrass/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022-2025 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/short-weierstrass/README.md
Normal file
3
crypto/short-weierstrass/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Short Weierstrass
|
||||||
|
|
||||||
|
A library for working with elliptic curves represented in short Weierstrass form.
|
||||||
96
crypto/short-weierstrass/src/affine.rs
Normal file
96
crypto/short-weierstrass/src/affine.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use core::ops::*;
|
||||||
|
|
||||||
|
use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable};
|
||||||
|
use zeroize::DefaultIsZeroes;
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use group::ff::Field;
|
||||||
|
|
||||||
|
use crate::{ShortWeierstrass, Projective};
|
||||||
|
|
||||||
|
/// A point represented with affine coordinates.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Affine<C: ShortWeierstrass> {
|
||||||
|
pub(crate) x: C::FieldElement,
|
||||||
|
pub(crate) y: C::FieldElement,
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Clone for Affine<C> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Copy for Affine<C> {}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Affine<C> {
|
||||||
|
pub fn try_from(projective: Projective<C>) -> CtOption<Self> {
|
||||||
|
projective.z.invert().map(|z_inv| Self { x: projective.x * z_inv, y: projective.y * z_inv })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Default for Affine<C> {
|
||||||
|
fn default() -> Self {
|
||||||
|
C::GENERATOR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> DefaultIsZeroes for Affine<C> {}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> ConstantTimeEq for Affine<C> {
|
||||||
|
fn ct_eq(&self, other: &Self) -> Choice {
|
||||||
|
self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> PartialEq for Affine<C> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.ct_eq(other).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Eq for Affine<C> {}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> ConditionallySelectable for Affine<C> {
|
||||||
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||||
|
let x = C::FieldElement::conditional_select(&a.x, &b.x, choice);
|
||||||
|
let y = C::FieldElement::conditional_select(&a.y, &b.y, choice);
|
||||||
|
Affine { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Neg for Affine<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn neg(mut self) -> Self::Output {
|
||||||
|
self.y = -self.y;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> ConditionallyNegatable for Affine<C> {
|
||||||
|
fn conditional_negate(&mut self, negate: Choice) {
|
||||||
|
self.y = <_>::conditional_select(&self.y, &-self.y, negate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Affine<C> {
|
||||||
|
/// Create an affine point from `x, y` coordinates, without performing any checks.
|
||||||
|
///
|
||||||
|
/// This should NOT be used. It is solely intended for trusted data at compile-time. It MUST NOT
|
||||||
|
/// be used with any untrusted/unvalidated data.
|
||||||
|
pub const fn from_xy_unchecked(x: C::FieldElement, y: C::FieldElement) -> Self { Self { x, y } }
|
||||||
|
|
||||||
|
/// The `x, y` coordinates of this point.
|
||||||
|
pub fn coordinates(self) -> (C::FieldElement, C::FieldElement) {
|
||||||
|
(self.x, self.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sample a random on-curve point with an unknown discrete logarithm w.r.t. any other points.
|
||||||
|
pub fn random(mut rng: impl RngCore) -> Self {
|
||||||
|
loop {
|
||||||
|
let x = C::FieldElement::random(&mut rng);
|
||||||
|
let y_square = ((x.square() + C::A) * x) + C::B;
|
||||||
|
let Some(mut y) = Option::<C::FieldElement>::from(y_square.sqrt()) else { continue };
|
||||||
|
if (rng.next_u64() % 2) == 1 {
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
return Self { x, y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
crypto/short-weierstrass/src/lib.rs
Normal file
30
crypto/short-weierstrass/src/lib.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![no_std]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
use group::ff::PrimeField;
|
||||||
|
|
||||||
|
mod affine;
|
||||||
|
pub use affine::Affine;
|
||||||
|
mod projective;
|
||||||
|
pub use projective::Projective;
|
||||||
|
|
||||||
|
/// An elliptic curve represented in short Weierstrass form, with equation `y^2 = x^3 + A x + B`.
|
||||||
|
pub trait ShortWeierstrass: 'static + Sized + Debug {
|
||||||
|
/// The field the elliptic curve is defined over.
|
||||||
|
type FieldElement: Zeroize + PrimeField;
|
||||||
|
/// The constant `A` from the curve equation.
|
||||||
|
const A: Self::FieldElement;
|
||||||
|
/// The constant `B` from the curve equation.
|
||||||
|
const B: Self::FieldElement;
|
||||||
|
/// A generator of this elliptic curve.
|
||||||
|
const GENERATOR: Affine<Self>;
|
||||||
|
/// The scalar type.
|
||||||
|
///
|
||||||
|
/// This may be omitted by specifying `()`.
|
||||||
|
type Scalar;
|
||||||
|
}
|
||||||
313
crypto/short-weierstrass/src/projective.rs
Normal file
313
crypto/short-weierstrass/src/projective.rs
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
use core::{hint::black_box, borrow::Borrow, ops::*, iter::Sum};
|
||||||
|
|
||||||
|
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable};
|
||||||
|
use zeroize::{Zeroize, DefaultIsZeroes};
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use group::{
|
||||||
|
ff::{Field, PrimeFieldBits},
|
||||||
|
Group,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{ShortWeierstrass, Affine};
|
||||||
|
|
||||||
|
/// A point represented with projective coordinates.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Projective<C: ShortWeierstrass> {
|
||||||
|
pub(crate) x: C::FieldElement,
|
||||||
|
pub(crate) y: C::FieldElement,
|
||||||
|
pub(crate) z: C::FieldElement,
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Clone for Projective<C> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Copy for Projective<C> {}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> From<Affine<C>> for Projective<C> {
|
||||||
|
fn from(affine: Affine<C>) -> Self {
|
||||||
|
Self { x: affine.x, y: affine.y, z: C::FieldElement::ONE }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Default for Projective<C> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::IDENTITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> DefaultIsZeroes for Projective<C> {}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> ConstantTimeEq for Projective<C> {
|
||||||
|
fn ct_eq(&self, other: &Self) -> Choice {
|
||||||
|
let c1 = (self.x * other.z).ct_eq(&(other.x * self.z));
|
||||||
|
let c2 = (self.y * other.z).ct_eq(&(other.y * self.z));
|
||||||
|
c1 & c2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> PartialEq for Projective<C> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.ct_eq(other).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Eq for Projective<C> {}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> ConditionallySelectable for Projective<C> {
|
||||||
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||||
|
let x = C::FieldElement::conditional_select(&a.x, &b.x, choice);
|
||||||
|
let y = C::FieldElement::conditional_select(&a.y, &b.y, choice);
|
||||||
|
let z = C::FieldElement::conditional_select(&a.z, &b.z, choice);
|
||||||
|
Projective { x, y, z }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Neg for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn neg(mut self) -> Self::Output {
|
||||||
|
self.y = -self.y;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> ConditionallyNegatable for Projective<C> {
|
||||||
|
fn conditional_negate(&mut self, negate: Choice) {
|
||||||
|
self.y = <_>::conditional_select(&self.y, &-self.y, negate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Add for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
// add-1998-cmo-2
|
||||||
|
/*
|
||||||
|
We don't use the complete formulas from 2015 as they require the curve is prime order, when we
|
||||||
|
do not.
|
||||||
|
*/
|
||||||
|
fn add(self, p2: Self) -> Self {
|
||||||
|
let Self { x: X1, y: Y1, z: Z1 } = self;
|
||||||
|
let Self { x: X2, y: Y2, z: Z2 } = p2;
|
||||||
|
|
||||||
|
let Y1Z2 = Y1 * Z2;
|
||||||
|
let X1Z2 = X1 * Z2;
|
||||||
|
let Z1Z2 = Z1 * Z2;
|
||||||
|
let u = (Y2 * Z1) - Y1Z2;
|
||||||
|
let uu = u.square();
|
||||||
|
let v = (X2 * Z1) - X1Z2;
|
||||||
|
let vv = v.square();
|
||||||
|
let vvv = v * vv;
|
||||||
|
let R = vv * X1Z2;
|
||||||
|
let A = (uu * Z1Z2) - vvv - R.double();
|
||||||
|
let X3 = v * A;
|
||||||
|
let Y3 = (u * (R - A)) - (vvv * Y1Z2);
|
||||||
|
let Z3 = vvv * Z1Z2;
|
||||||
|
|
||||||
|
let res = Self { x: X3, y: Y3, z: Z3 };
|
||||||
|
|
||||||
|
let same_x_coordinate = (self.x * p2.z).ct_eq(&(p2.x * self.z));
|
||||||
|
let same_y_coordinate = (self.y * p2.z).ct_eq(&(p2.y * self.z));
|
||||||
|
let res = <_>::conditional_select(
|
||||||
|
&res,
|
||||||
|
&Self::IDENTITY,
|
||||||
|
(self.is_identity_internal() & p2.is_identity_internal()) |
|
||||||
|
(same_x_coordinate & (!same_y_coordinate)),
|
||||||
|
);
|
||||||
|
let res =
|
||||||
|
<_>::conditional_select(&res, &self.double_internal(), same_x_coordinate & same_y_coordinate);
|
||||||
|
let res = <_>::conditional_select(&res, &p2, self.is_identity_internal());
|
||||||
|
<_>::conditional_select(&res, &self, p2.is_identity_internal())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Sub for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(self, p2: Self) -> Self {
|
||||||
|
self + -p2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> AddAssign for Projective<C> {
|
||||||
|
fn add_assign(&mut self, p2: Self) {
|
||||||
|
*self = *self + p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> SubAssign for Projective<C> {
|
||||||
|
fn sub_assign(&mut self, p2: Self) {
|
||||||
|
*self = *self - p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Add<&Self> for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, p2: &Self) -> Self {
|
||||||
|
self + *p2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> Sub<&Self> for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(self, p2: &Self) -> Self {
|
||||||
|
self - *p2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> AddAssign<&Self> for Projective<C> {
|
||||||
|
fn add_assign(&mut self, p2: &Self) {
|
||||||
|
*self = *self + p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass> SubAssign<&Self> for Projective<C> {
|
||||||
|
fn sub_assign(&mut self, p2: &Self) {
|
||||||
|
*self = *self - p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Projective<C> {
|
||||||
|
/// The additive identity, or point at infinity.
|
||||||
|
pub const IDENTITY: Self =
|
||||||
|
Projective { x: C::FieldElement::ZERO, y: C::FieldElement::ONE, z: C::FieldElement::ZERO };
|
||||||
|
/// A generator of this elliptic curve.
|
||||||
|
pub const GENERATOR: Self =
|
||||||
|
Projective { x: C::GENERATOR.x, y: C::GENERATOR.y, z: C::FieldElement::ONE };
|
||||||
|
|
||||||
|
fn is_identity_internal(&self) -> Choice {
|
||||||
|
self.z.ct_eq(&C::FieldElement::ZERO)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbl-1998-cmo-2
|
||||||
|
fn double_internal(&self) -> Self {
|
||||||
|
let Self { x: X1, y: Y1, z: Z1 } = *self;
|
||||||
|
|
||||||
|
let X1X1 = X1.square();
|
||||||
|
let w = (C::A * Z1.square()) + X1X1.double() + X1X1;
|
||||||
|
let s = Y1 * Z1;
|
||||||
|
let ss = s.square();
|
||||||
|
let sss = s * ss;
|
||||||
|
let R = Y1 * s;
|
||||||
|
let B = X1 * R;
|
||||||
|
let B4 = B.double().double();
|
||||||
|
let h = w.square() - B4.double();
|
||||||
|
let X3 = (h * s).double();
|
||||||
|
let Y3 = w * (B4 - h) - R.square().double().double().double();
|
||||||
|
let Z3 = sss.double().double().double();
|
||||||
|
|
||||||
|
let res = Self { x: X3, y: Y3, z: Z3 };
|
||||||
|
<_>::conditional_select(&res, &Self::IDENTITY, self.is_identity_internal())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass> Sum for Projective<C> {
|
||||||
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
|
let mut res = Self::IDENTITY;
|
||||||
|
for item in iter {
|
||||||
|
res += item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C: ShortWeierstrass> Sum<&'a Self> for Projective<C> {
|
||||||
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
|
let mut res = Self::IDENTITY;
|
||||||
|
for item in iter {
|
||||||
|
res += item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass<Scalar = ()>> Projective<C> {
|
||||||
|
/// Sample a random on-curve point with an unknown discrete logarithm w.r.t. any other points.
|
||||||
|
pub fn random(rng: impl RngCore) -> Self {
|
||||||
|
Self::from(Affine::random(rng))
|
||||||
|
}
|
||||||
|
/// If this point is the additive identity.
|
||||||
|
pub fn is_identity(&self) -> Choice {
|
||||||
|
self.is_identity_internal()
|
||||||
|
}
|
||||||
|
/// The sum of this point with itself.
|
||||||
|
pub fn double(&self) -> Self {
|
||||||
|
self.double_internal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>, S: Borrow<C::Scalar>> Mul<S> for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, scalar: S) -> Self {
|
||||||
|
let mut table = [Self::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 scalar.borrow().to_le_bits().iter_mut().rev().enumerate() {
|
||||||
|
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(u8::from(bit));
|
||||||
|
bit.zeroize();
|
||||||
|
debug_assert!((res | 1) == 1);
|
||||||
|
|
||||||
|
bit_ref.zeroize();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>, S: Borrow<C::Scalar>> MulAssign<S>
|
||||||
|
for Projective<C>
|
||||||
|
{
|
||||||
|
fn mul_assign(&mut self, scalar: S) {
|
||||||
|
*self = *self * scalar.borrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> Mul<&C::Scalar> for Projective<C> {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, scalar: &C::Scalar) -> Self {
|
||||||
|
self * *scalar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> MulAssign<&C::Scalar> for Projective<C> {
|
||||||
|
fn mul_assign(&mut self, scalar: &C::Scalar) {
|
||||||
|
*self *= *scalar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> Group for Projective<C> {
|
||||||
|
type Scalar = C::Scalar;
|
||||||
|
fn random(rng: impl RngCore) -> Self {
|
||||||
|
Self::from(Affine::<C>::random(rng))
|
||||||
|
}
|
||||||
|
fn identity() -> Self {
|
||||||
|
Self::IDENTITY
|
||||||
|
}
|
||||||
|
fn generator() -> Self {
|
||||||
|
C::GENERATOR.into()
|
||||||
|
}
|
||||||
|
fn is_identity(&self) -> Choice {
|
||||||
|
self.is_identity_internal()
|
||||||
|
}
|
||||||
|
fn double(&self) -> Self {
|
||||||
|
self.double_internal()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ multiexp = { path = "../../crypto/multiexp", default-features = false, features
|
|||||||
schnorr-signatures = { path = "../../crypto/schnorr", default-features = false }
|
schnorr-signatures = { path = "../../crypto/schnorr", default-features = false }
|
||||||
|
|
||||||
prime-field = { path = "../../crypto/prime-field", default-features = false, features = ["alloc"] }
|
prime-field = { path = "../../crypto/prime-field", default-features = false, features = ["alloc"] }
|
||||||
|
short-weierstrass = { path = "../../crypto/short-weierstrass", default-features = false, features = ["alloc"] }
|
||||||
secq256k1 = { path = "../../crypto/evrf/secq256k1", default-features = false }
|
secq256k1 = { path = "../../crypto/evrf/secq256k1", default-features = false }
|
||||||
embedwards25519 = { path = "../../crypto/evrf/embedwards25519", default-features = false }
|
embedwards25519 = { path = "../../crypto/evrf/embedwards25519", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub use multiexp;
|
|||||||
pub use schnorr_signatures;
|
pub use schnorr_signatures;
|
||||||
|
|
||||||
pub use prime_field;
|
pub use prime_field;
|
||||||
|
pub use short_weierstrass;
|
||||||
pub use secq256k1;
|
pub use secq256k1;
|
||||||
pub use embedwards25519;
|
pub use embedwards25519;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user