mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add prime-field crate
prime-field introduces a macro to generate a prime field, in its entitrety, de-duplicating code across minimal-ed448, embedwards25519, and secq256k1.
This commit is contained in:
3
.github/workflows/crypto-tests.yml
vendored
3
.github/workflows/crypto-tests.yml
vendored
@@ -35,6 +35,9 @@ jobs:
|
|||||||
-p ciphersuite-kp256 \
|
-p ciphersuite-kp256 \
|
||||||
-p multiexp \
|
-p multiexp \
|
||||||
-p schnorr-signatures \
|
-p schnorr-signatures \
|
||||||
|
-p prime-field \
|
||||||
|
-p secq256k1 \
|
||||||
|
-p embedwards25519 \
|
||||||
-p dkg \
|
-p dkg \
|
||||||
-p dkg-recovery \
|
-p dkg-recovery \
|
||||||
-p dkg-dealer \
|
-p dkg-dealer \
|
||||||
|
|||||||
75
Cargo.lock
generated
75
Cargo.lock
generated
@@ -2298,7 +2298,19 @@ checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"serdect",
|
"serdect 0.2.0",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"serdect 0.3.0",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -2419,7 +2431,7 @@ name = "dalek-ff-group"
|
|||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
"crypto-bigint",
|
"crypto-bigint 0.5.5",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"ff",
|
"ff",
|
||||||
@@ -2505,7 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
|
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"syn 1.0.109",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2884,7 +2896,7 @@ dependencies = [
|
|||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
"rfc6979",
|
"rfc6979",
|
||||||
"serdect",
|
"serdect 0.2.0",
|
||||||
"signature",
|
"signature",
|
||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
@@ -2956,7 +2968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct",
|
"base16ct",
|
||||||
"crypto-bigint",
|
"crypto-bigint 0.5.5",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"ff",
|
"ff",
|
||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
@@ -2964,7 +2976,7 @@ dependencies = [
|
|||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"sec1",
|
"sec1",
|
||||||
"serdect",
|
"serdect 0.2.0",
|
||||||
"subtle",
|
"subtle",
|
||||||
"tap",
|
"tap",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -2976,7 +2988,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
"crypto-bigint",
|
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"ec-divisors",
|
"ec-divisors",
|
||||||
"ff-group-tests",
|
"ff-group-tests",
|
||||||
@@ -2984,10 +2995,9 @@ dependencies = [
|
|||||||
"generic-array 1.2.0",
|
"generic-array 1.2.0",
|
||||||
"hex",
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
"prime-field",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"rustversion",
|
|
||||||
"std-shims",
|
"std-shims",
|
||||||
"subtle",
|
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4181,7 +4191,7 @@ dependencies = [
|
|||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2 0.4.10",
|
"socket2 0.5.8",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -4672,7 +4682,7 @@ dependencies = [
|
|||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"serdect",
|
"serdect 0.2.0",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4752,7 +4762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.53.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5934,16 +5944,11 @@ name = "minimal-ed448"
|
|||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
"crypto-bigint",
|
|
||||||
"ff",
|
|
||||||
"ff-group-tests",
|
"ff-group-tests",
|
||||||
"generic-array 1.2.0",
|
|
||||||
"group",
|
|
||||||
"hex",
|
"hex",
|
||||||
|
"prime-field",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"rustversion",
|
|
||||||
"sha3",
|
"sha3",
|
||||||
"subtle",
|
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6099,7 +6104,7 @@ name = "monero-generators"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff"
|
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-bigint",
|
"crypto-bigint 0.5.5",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"group",
|
"group",
|
||||||
@@ -6610,7 +6615,7 @@ version = "0.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate 1.3.1",
|
"proc-macro-crate 3.2.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
@@ -7215,6 +7220,19 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prime-field"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-bigint 0.6.1",
|
||||||
|
"ff",
|
||||||
|
"ff-group-tests",
|
||||||
|
"paste",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "primeorder"
|
name = "primeorder"
|
||||||
version = "0.13.6"
|
version = "0.13.6"
|
||||||
@@ -9450,7 +9468,7 @@ dependencies = [
|
|||||||
"der",
|
"der",
|
||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"serdect",
|
"serdect 0.2.0",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -9520,8 +9538,8 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"k256",
|
"k256",
|
||||||
|
"prime-field",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"rustversion",
|
|
||||||
"std-shims",
|
"std-shims",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -10159,6 +10177,7 @@ dependencies = [
|
|||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
"minimal-ed448",
|
"minimal-ed448",
|
||||||
"multiexp",
|
"multiexp",
|
||||||
|
"prime-field",
|
||||||
"schnorr-signatures",
|
"schnorr-signatures",
|
||||||
"secq256k1",
|
"secq256k1",
|
||||||
]
|
]
|
||||||
@@ -10799,6 +10818,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serdect"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@@ -13283,7 +13312,7 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ members = [
|
|||||||
"crypto/multiexp",
|
"crypto/multiexp",
|
||||||
"crypto/schnorr",
|
"crypto/schnorr",
|
||||||
|
|
||||||
|
"crypto/prime-field",
|
||||||
"crypto/evrf/secq256k1",
|
"crypto/evrf/secq256k1",
|
||||||
"crypto/evrf/embedwards25519",
|
"crypto/evrf/embedwards25519",
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ed448"
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["ed448", "ff", "group"]
|
keywords = ["ed448", "ff", "group"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.83"
|
rust-version = "1.86"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -17,22 +17,13 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustversion = "1"
|
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }
|
||||||
|
|
||||||
rand_core = { version = "0.6", default-features = false }
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
|
||||||
subtle = { version = "^2.4", default-features = false }
|
|
||||||
|
|
||||||
sha3 = { version = "0.10", default-features = false }
|
sha3 = { version = "0.10", default-features = false }
|
||||||
|
|
||||||
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
prime-field = { path = "../prime-field", default-features = false }
|
||||||
group = { version = "0.13", default-features = false }
|
|
||||||
ciphersuite = { path = "../ciphersuite", default-features = false }
|
ciphersuite = { path = "../ciphersuite", default-features = false }
|
||||||
|
|
||||||
generic-array = { version = "1", default-features = false }
|
|
||||||
crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
@@ -41,6 +32,6 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
|||||||
ff-group-tests = { path = "../ff-group-tests" }
|
ff-group-tests = { path = "../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["zeroize/alloc", "ciphersuite/alloc"]
|
alloc = ["zeroize/alloc", "prime-field/alloc", "ciphersuite/alloc"]
|
||||||
std = ["alloc", "rand_core/std", "zeroize/std", "subtle/std", "sha3/std", "ff/std", "ciphersuite/std"]
|
std = ["alloc", "zeroize/std", "sha3/std", "prime-field/std", "ciphersuite/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,311 +0,0 @@
|
|||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
// Use black_box when possible
|
|
||||||
#[rustversion::since(1.66)]
|
|
||||||
mod black_box {
|
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
|
||||||
#[allow(clippy::incompatible_msrv)]
|
|
||||||
core::hint::black_box(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[rustversion::before(1.66)]
|
|
||||||
mod black_box {
|
|
||||||
pub(crate) fn black_box<T>(val: T) -> T {
|
|
||||||
val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
use black_box::black_box;
|
|
||||||
|
|
||||||
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);
|
|
||||||
#[allow(clippy::cast_lossless)]
|
|
||||||
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 {
|
|
||||||
$Value($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 {
|
|
||||||
$Value($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 {
|
|
||||||
$wrapper(Residue::new(&$inner::from(a)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! field {
|
|
||||||
(
|
|
||||||
$FieldName: ident,
|
|
||||||
$ResidueType: ident,
|
|
||||||
|
|
||||||
$MODULUS_STR: ident,
|
|
||||||
$MODULUS: ident,
|
|
||||||
$WIDE_MODULUS: ident,
|
|
||||||
|
|
||||||
$NUM_BITS: literal,
|
|
||||||
|
|
||||||
$MULTIPLICATIVE_GENERATOR: literal,
|
|
||||||
$DELTA: expr,
|
|
||||||
) => {
|
|
||||||
use core::{
|
|
||||||
ops::{Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
|
||||||
iter::{Sum, Product},
|
|
||||||
};
|
|
||||||
|
|
||||||
use subtle::{Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallySelectable};
|
|
||||||
use rand_core::RngCore;
|
|
||||||
|
|
||||||
use generic_array::{typenum::U57, GenericArray};
|
|
||||||
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: U896) -> U448 {
|
|
||||||
U448::from_le_slice(&x.rem(&NonZero::new($WIDE_MODULUS).unwrap()).to_le_bytes()[.. 56])
|
|
||||||
}
|
|
||||||
|
|
||||||
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, U448, u8);
|
|
||||||
from_wrapper!($FieldName, U448, u16);
|
|
||||||
from_wrapper!($FieldName, U448, u32);
|
|
||||||
from_wrapper!($FieldName, U448, u64);
|
|
||||||
from_wrapper!($FieldName, U448, u128);
|
|
||||||
|
|
||||||
impl Neg for $FieldName {
|
|
||||||
type Output = $FieldName;
|
|
||||||
fn neg(self) -> $FieldName {
|
|
||||||
$FieldName(self.0.neg())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Neg for &'a $FieldName {
|
|
||||||
type Output = $FieldName;
|
|
||||||
fn neg(self) -> Self::Output {
|
|
||||||
(*self).neg()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $FieldName {
|
|
||||||
/// Perform an exponentiation.
|
|
||||||
pub fn pow(&self, other: $FieldName) -> $FieldName {
|
|
||||||
let mut table = [$FieldName(Residue::ONE); 16];
|
|
||||||
table[1] = *self;
|
|
||||||
for i in 2 .. 16 {
|
|
||||||
table[i] = table[i - 1] * self;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut res = $FieldName(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(&mut bit);
|
|
||||||
bits |= bit;
|
|
||||||
bit.zeroize();
|
|
||||||
|
|
||||||
if ((i + 1) % 4) == 0 {
|
|
||||||
if i != 3 {
|
|
||||||
for _ in 0 .. 4 {
|
|
||||||
res *= res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut scale_by = $FieldName(Residue::ONE);
|
|
||||||
#[allow(clippy::needless_range_loop)]
|
|
||||||
for i in 0 .. 16 {
|
|
||||||
#[allow(clippy::cast_possible_truncation)] // Safe since 0 .. 16
|
|
||||||
{
|
|
||||||
scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res *= scale_by;
|
|
||||||
bits = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Field for $FieldName {
|
|
||||||
const ZERO: Self = $FieldName(Residue::ZERO);
|
|
||||||
const ONE: Self = $FieldName(Residue::ONE);
|
|
||||||
|
|
||||||
fn random(mut rng: impl RngCore) -> Self {
|
|
||||||
let mut bytes = [0; 112];
|
|
||||||
rng.fill_bytes(&mut bytes);
|
|
||||||
$FieldName(Residue::new(&reduce(U896::from_le_slice(bytes.as_ref()))))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn square(&self) -> Self {
|
|
||||||
*self * self
|
|
||||||
}
|
|
||||||
fn double(&self) -> Self {
|
|
||||||
*self + self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn invert(&self) -> CtOption<Self> {
|
|
||||||
const NEG_2: $FieldName =
|
|
||||||
$FieldName($ResidueType::sub(&$ResidueType::ZERO, &$ResidueType::new(&U448::from_u8(2))));
|
|
||||||
CtOption::new(self.pow(NEG_2), !self.is_zero())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sqrt(&self) -> CtOption<Self> {
|
|
||||||
const MOD_1_4: $FieldName = $FieldName($ResidueType::new(
|
|
||||||
&$MODULUS.saturating_add(&U448::ONE).wrapping_div(&U448::from_u8(4)),
|
|
||||||
));
|
|
||||||
|
|
||||||
let res = self.pow(MOD_1_4);
|
|
||||||
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 = GenericArray<u8, U57>;
|
|
||||||
|
|
||||||
const MODULUS: &'static str = $MODULUS_STR;
|
|
||||||
|
|
||||||
const NUM_BITS: u32 = $NUM_BITS;
|
|
||||||
const CAPACITY: u32 = $NUM_BITS - 1;
|
|
||||||
|
|
||||||
const TWO_INV: Self = $FieldName($ResidueType::new(&U448::from_u8(2)).invert().0);
|
|
||||||
|
|
||||||
const MULTIPLICATIVE_GENERATOR: Self =
|
|
||||||
$FieldName(Residue::new(&U448::from_u8($MULTIPLICATIVE_GENERATOR)));
|
|
||||||
// True for both the Ed448 Scalar field and FieldElement field
|
|
||||||
const S: u32 = 1;
|
|
||||||
|
|
||||||
// Both fields have their root of unity as -1
|
|
||||||
const ROOT_OF_UNITY: Self =
|
|
||||||
$FieldName($ResidueType::sub(&$ResidueType::ZERO, &$ResidueType::new(&U448::ONE)));
|
|
||||||
const ROOT_OF_UNITY_INV: Self = $FieldName(Self::ROOT_OF_UNITY.0.invert().0);
|
|
||||||
|
|
||||||
const DELTA: Self = $FieldName(Residue::new(&U448::from_le_hex($DELTA)));
|
|
||||||
|
|
||||||
fn from_repr(bytes: Self::Repr) -> CtOption<Self> {
|
|
||||||
let res = U448::from_le_slice(&bytes[.. 56]);
|
|
||||||
CtOption::new($FieldName(Residue::new(&res)), res.ct_lt(&$MODULUS) & bytes[56].ct_eq(&0))
|
|
||||||
}
|
|
||||||
fn to_repr(&self) -> Self::Repr {
|
|
||||||
let mut repr = GenericArray::<u8, U57>::default();
|
|
||||||
repr[.. 56].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; 56];
|
|
||||||
|
|
||||||
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
|
||||||
let mut repr = [0; 56];
|
|
||||||
repr.copy_from_slice(&self.to_repr()[.. 56]);
|
|
||||||
repr.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
|
||||||
let mut repr = [0; 56];
|
|
||||||
repr.copy_from_slice(&MODULUS.to_le_bytes()[.. 56]);
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -8,10 +8,12 @@ use sha3::{
|
|||||||
Shake256,
|
Shake256,
|
||||||
};
|
};
|
||||||
|
|
||||||
use group::Group;
|
use ciphersuite::{
|
||||||
use crate::{Scalar, Point};
|
group::{ff::FromUniformBytes, Group},
|
||||||
|
Ciphersuite,
|
||||||
|
};
|
||||||
|
|
||||||
use ciphersuite::Ciphersuite;
|
use crate::{Scalar, Point};
|
||||||
|
|
||||||
/// Shake256, fixed to a 114-byte output, as used by Ed448.
|
/// Shake256, fixed to a 114-byte output, as used by Ed448.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
@@ -69,13 +71,16 @@ impl Ciphersuite for Ed448 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
||||||
Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_ref().try_into().unwrap())
|
let digest = Self::H::digest([dst, data].concat());
|
||||||
|
let mut wide_scalar = [0; 114];
|
||||||
|
wide_scalar.copy_from_slice(digest.as_ref());
|
||||||
|
Scalar::from_uniform_bytes(&wide_scalar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ed448() {
|
fn test_ed448() {
|
||||||
use ff::PrimeField;
|
use ciphersuite::group::ff::PrimeField;
|
||||||
|
|
||||||
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
|
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
|
||||||
|
|
||||||
@@ -94,7 +99,7 @@ fn test_ed448() {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
)
|
||||||
.to_repr()
|
.to_repr()
|
||||||
.to_vec(),
|
.as_ref(),
|
||||||
hex::decode(
|
hex::decode(
|
||||||
"\
|
"\
|
||||||
67a6f023e77361707c6e894c625e809e80f33fdb310810053ae29e28\
|
67a6f023e77361707c6e894c625e809e80f33fdb310810053ae29e28\
|
||||||
@@ -102,5 +107,6 @@ e7011f3193b9020e73c183a98cc3a519160ed759376dd92c94831622\
|
|||||||
00"
|
00"
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.as_slice()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
use zeroize::{DefaultIsZeroes, Zeroize};
|
|
||||||
|
|
||||||
use crypto_bigint::{
|
|
||||||
U448, U896,
|
|
||||||
modular::constant_mod::{ResidueParams, Residue},
|
|
||||||
};
|
|
||||||
|
|
||||||
const MODULUS_STR: &str = concat!(
|
|
||||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
);
|
|
||||||
|
|
||||||
impl_modulus!(FieldModulus, U448, MODULUS_STR);
|
|
||||||
pub(crate) type ResidueType = Residue<FieldModulus, { FieldModulus::LIMBS }>;
|
|
||||||
|
|
||||||
/// Ed448 field element.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
|
||||||
pub struct FieldElement(pub(crate) ResidueType);
|
|
||||||
|
|
||||||
impl DefaultIsZeroes for FieldElement {}
|
|
||||||
|
|
||||||
// 2**448 - 2**224 - 1
|
|
||||||
pub(crate) const MODULUS: U448 = U448::from_be_hex(MODULUS_STR);
|
|
||||||
|
|
||||||
const WIDE_MODULUS: U896 = U896::from_be_hex(concat!(
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
||||||
));
|
|
||||||
|
|
||||||
pub(crate) const Q_4: FieldElement = FieldElement(ResidueType::new(
|
|
||||||
&MODULUS.saturating_add(&U448::ONE).wrapping_div(&U448::from_u8(4)),
|
|
||||||
));
|
|
||||||
|
|
||||||
field!(
|
|
||||||
FieldElement,
|
|
||||||
ResidueType,
|
|
||||||
MODULUS_STR,
|
|
||||||
MODULUS,
|
|
||||||
WIDE_MODULUS,
|
|
||||||
448,
|
|
||||||
7,
|
|
||||||
concat!(
|
|
||||||
"31000000000000000000000000000000000000000000000000000000",
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_field() {
|
|
||||||
ff_group_tests::prime_field::test_prime_field_bits::<_, FieldElement>(&mut rand_core::OsRng);
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,47 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(clippy::redundant_closure_call)]
|
|
||||||
|
|
||||||
#[macro_use]
|
prime_field::odd_prime_field!(
|
||||||
mod backend;
|
FieldElement,
|
||||||
|
// 2**448 - 2**224 - 1
|
||||||
|
concat!(
|
||||||
|
"00",
|
||||||
|
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
),
|
||||||
|
"07",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
mod scalar;
|
prime_field::odd_prime_field!(
|
||||||
pub use scalar::Scalar;
|
Scalar,
|
||||||
|
concat!(
|
||||||
mod field;
|
"00",
|
||||||
pub use field::FieldElement;
|
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
|
||||||
|
),
|
||||||
|
"02",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
mod point;
|
mod point;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
|
|
||||||
mod ciphersuite;
|
mod ciphersuite;
|
||||||
pub use crate::ciphersuite::Ed448;
|
pub use crate::ciphersuite::Ed448;
|
||||||
|
|
||||||
|
pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||||
|
use core::hint::black_box;
|
||||||
|
use prime_field::zeroize::Zeroize;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,49 +3,46 @@ use core::{
|
|||||||
iter::Sum,
|
iter::Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use prime_field::{
|
||||||
|
subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable},
|
||||||
|
zeroize::Zeroize,
|
||||||
|
rand_core::RngCore,
|
||||||
|
crypto_bigint::U512,
|
||||||
|
};
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use ciphersuite::group::{
|
||||||
use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable};
|
|
||||||
|
|
||||||
use crypto_bigint::{U448, modular::constant_mod::Residue};
|
|
||||||
|
|
||||||
use group::{
|
|
||||||
ff::{Field, PrimeField, PrimeFieldBits},
|
ff::{Field, PrimeField, PrimeFieldBits},
|
||||||
Group, GroupEncoding,
|
Group, GroupEncoding,
|
||||||
prime::PrimeGroup,
|
prime::PrimeGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{u8_from_bool, Scalar, FieldElement};
|
||||||
backend::u8_from_bool,
|
|
||||||
scalar::Scalar,
|
|
||||||
field::{ResidueType, FieldElement, Q_4},
|
|
||||||
};
|
|
||||||
|
|
||||||
const D: FieldElement =
|
const G_Y: FieldElement = FieldElement::from(&U512::from_be_hex(concat!(
|
||||||
FieldElement(ResidueType::sub(&ResidueType::ZERO, &Residue::new(&U448::from_u16(39081))));
|
"0000000000000000",
|
||||||
|
|
||||||
const G_Y: FieldElement = FieldElement(Residue::new(&U448::from_be_hex(concat!(
|
|
||||||
"693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e",
|
"693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e",
|
||||||
"05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14",
|
"05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14",
|
||||||
))));
|
)));
|
||||||
|
|
||||||
const G_X: FieldElement = FieldElement(Residue::new(&U448::from_be_hex(concat!(
|
const G_X: FieldElement = FieldElement::from(&U512::from_be_hex(concat!(
|
||||||
|
"0000000000000000",
|
||||||
"4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324",
|
"4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324",
|
||||||
"a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e",
|
"a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e",
|
||||||
))));
|
)));
|
||||||
|
|
||||||
fn recover_x(y: FieldElement) -> CtOption<FieldElement> {
|
fn recover_x(y: FieldElement) -> CtOption<FieldElement> {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let D = -<FieldElement as From<u16>>::from(39081u16);
|
||||||
|
|
||||||
let ysq = y.square();
|
let ysq = y.square();
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let D_ysq = D * ysq;
|
let D_ysq = D * ysq;
|
||||||
(D_ysq - FieldElement::ONE).invert().and_then(|inverted| {
|
(D_ysq - FieldElement::ONE).invert().and_then(|inverted| {
|
||||||
let temp = (ysq - FieldElement::ONE) * inverted;
|
let xsq = (ysq - FieldElement::ONE) * inverted;
|
||||||
let mut x = temp.pow(Q_4);
|
xsq.sqrt().and_then(|mut x| {
|
||||||
x.conditional_negate(x.is_odd());
|
x.conditional_negate(x.is_odd());
|
||||||
|
CtOption::new(x, (xsq + ysq).ct_eq(&(FieldElement::ONE + (xsq * D_ysq))))
|
||||||
let xsq = x.square();
|
})
|
||||||
CtOption::new(x, (xsq + ysq).ct_eq(&(FieldElement::ONE + (xsq * D_ysq))))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +101,9 @@ impl ConditionallySelectable for Point {
|
|||||||
impl Add for Point {
|
impl Add for Point {
|
||||||
type Output = Point;
|
type Output = Point;
|
||||||
fn add(self, other: Self) -> Self {
|
fn add(self, other: Self) -> Self {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let D = -<FieldElement as From<u16>>::from(39081u16);
|
||||||
|
|
||||||
// 12 muls, 7 additions, 4 negations
|
// 12 muls, 7 additions, 4 negations
|
||||||
let xcp = self.x * other.x;
|
let xcp = self.x * other.x;
|
||||||
let ycp = self.y * other.y;
|
let ycp = self.y * other.y;
|
||||||
@@ -302,7 +302,7 @@ impl GroupEncoding for Point {
|
|||||||
|
|
||||||
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
|
||||||
// Extract and clear the sign bit
|
// Extract and clear the sign bit
|
||||||
let sign = Choice::from(bytes[56] >> 7);
|
let sign = Choice::from(bytes.as_ref()[56] >> 7);
|
||||||
let mut bytes = *bytes;
|
let mut bytes = *bytes;
|
||||||
let mut_ref: &mut [u8] = bytes.as_mut();
|
let mut_ref: &mut [u8] = bytes.as_mut();
|
||||||
mut_ref[56] &= !(1 << 7);
|
mut_ref[56] &= !(1 << 7);
|
||||||
@@ -350,63 +350,48 @@ fn generator() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn torsion() {
|
fn torsion() {
|
||||||
use generic_array::GenericArray;
|
|
||||||
|
|
||||||
// Uses the originally suggested generator which had torsion
|
// Uses the originally suggested generator which had torsion
|
||||||
let old_y = FieldElement::from_repr(*GenericArray::from_slice(
|
const TORSIONED_Y: &str = "\
|
||||||
&hex::decode(
|
|
||||||
"\
|
|
||||||
12796c1532041525945f322e414d434467cfd5c57c9a9af2473b2775\
|
12796c1532041525945f322e414d434467cfd5c57c9a9af2473b2775\
|
||||||
8c921c4828b277ca5f2891fc4f3d79afdf29a64c72fb28b59c16fa51\
|
8c921c4828b277ca5f2891fc4f3d79afdf29a64c72fb28b59c16fa51\
|
||||||
00",
|
00";
|
||||||
)
|
let mut repr = <FieldElement as PrimeField>::Repr::default();
|
||||||
.unwrap(),
|
repr.as_mut().copy_from_slice(&hex::decode(TORSIONED_Y).unwrap());
|
||||||
))
|
|
||||||
.unwrap();
|
let old_y = FieldElement::from_repr(repr).unwrap();
|
||||||
let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::ONE };
|
let old = Point { x: -recover_x(old_y).unwrap(), y: old_y, z: FieldElement::ONE };
|
||||||
assert!(bool::from(!old.is_torsion_free()));
|
assert!(bool::from(!old.is_torsion_free()));
|
||||||
|
|
||||||
|
assert!(bool::from(Point::from_bytes(&old.to_bytes()).is_none()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vector() {
|
fn vector() {
|
||||||
use generic_array::GenericArray;
|
const TWO_G: &str = "\
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Point::generator().double(),
|
|
||||||
Point::from_bytes(GenericArray::from_slice(
|
|
||||||
&hex::decode(
|
|
||||||
"\
|
|
||||||
ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a\
|
ed8693eacdfbeada6ba0cdd1beb2bcbb98302a3a8365650db8c4d88a\
|
||||||
726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae\
|
726de3b7d74d8835a0d76e03b0c2865020d659b38d04d74a63e905ae\
|
||||||
80"
|
80";
|
||||||
)
|
let mut two_g = <Point as GroupEncoding>::Repr::default();
|
||||||
.unwrap()
|
two_g.as_mut().copy_from_slice(&hex::decode(TWO_G).unwrap());
|
||||||
))
|
assert_eq!(Point::generator().double(), Point::from_bytes(&two_g).unwrap());
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
const SCALAR: &str = "\
|
||||||
Point::generator() *
|
|
||||||
Scalar::from_repr(*GenericArray::from_slice(
|
|
||||||
&hex::decode(
|
|
||||||
"\
|
|
||||||
6298e1eef3c379392caaed061ed8a31033c9e9e3420726f23b404158\
|
6298e1eef3c379392caaed061ed8a31033c9e9e3420726f23b404158\
|
||||||
a401cd9df24632adfe6b418dc942d8a091817dd8bd70e1c72ba52f3c\
|
a401cd9df24632adfe6b418dc942d8a091817dd8bd70e1c72ba52f3c\
|
||||||
00"
|
00";
|
||||||
)
|
let mut scalar = <Scalar as PrimeField>::Repr::default();
|
||||||
.unwrap()
|
scalar.as_mut().copy_from_slice(&hex::decode(SCALAR).unwrap());
|
||||||
))
|
|
||||||
.unwrap(),
|
const SCALED_G: &str = "\
|
||||||
Point::from_bytes(GenericArray::from_slice(
|
|
||||||
&hex::decode(
|
|
||||||
"\
|
|
||||||
3832f82fda00ff5365b0376df705675b63d2a93c24c6e81d40801ba2\
|
3832f82fda00ff5365b0376df705675b63d2a93c24c6e81d40801ba2\
|
||||||
65632be10f443f95968fadb70d10786827f30dc001c8d0f9b7c1d1b0\
|
65632be10f443f95968fadb70d10786827f30dc001c8d0f9b7c1d1b0\
|
||||||
00"
|
00";
|
||||||
)
|
let mut scaled_g = <Point as GroupEncoding>::Repr::default();
|
||||||
.unwrap()
|
scaled_g.as_mut().copy_from_slice(&hex::decode(SCALED_G).unwrap());
|
||||||
))
|
|
||||||
.unwrap()
|
assert_eq!(
|
||||||
|
Point::generator() * Scalar::from_repr(scalar).unwrap(),
|
||||||
|
Point::from_bytes(&scaled_g).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
use zeroize::{DefaultIsZeroes, Zeroize};
|
|
||||||
|
|
||||||
use crypto_bigint::{
|
|
||||||
U448, U896, U1024,
|
|
||||||
modular::constant_mod::{ResidueParams, Residue},
|
|
||||||
};
|
|
||||||
|
|
||||||
const MODULUS_STR: &str = concat!(
|
|
||||||
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
"7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
|
|
||||||
);
|
|
||||||
|
|
||||||
impl_modulus!(ScalarModulus, U448, MODULUS_STR);
|
|
||||||
type ResidueType = Residue<ScalarModulus, { ScalarModulus::LIMBS }>;
|
|
||||||
|
|
||||||
/// Ed448 Scalar field element.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
|
||||||
pub struct Scalar(pub(crate) ResidueType);
|
|
||||||
|
|
||||||
impl DefaultIsZeroes for Scalar {}
|
|
||||||
|
|
||||||
// 2**446 - 13818066809895115352007386748515426880336692474882178609894547503885
|
|
||||||
pub(crate) const MODULUS: U448 = U448::from_be_hex(MODULUS_STR);
|
|
||||||
|
|
||||||
const WIDE_MODULUS: U896 = U896::from_be_hex(concat!(
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
"7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
|
|
||||||
));
|
|
||||||
|
|
||||||
const WIDE_REDUCTION_MODULUS: NonZero<U1024> = NonZero::from_uint(U1024::from_be_hex(concat!(
|
|
||||||
"00000000000000000000000000000000",
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
"3fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
"7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
|
|
||||||
)));
|
|
||||||
|
|
||||||
field!(
|
|
||||||
Scalar,
|
|
||||||
ResidueType,
|
|
||||||
MODULUS_STR,
|
|
||||||
MODULUS,
|
|
||||||
WIDE_MODULUS,
|
|
||||||
446,
|
|
||||||
2,
|
|
||||||
concat!(
|
|
||||||
"04000000000000000000000000000000000000000000000000000000",
|
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Scalar {
|
|
||||||
/// Perform a wide reduction to obtain a non-biased Scalar.
|
|
||||||
pub fn wide_reduce(bytes: [u8; 114]) -> Scalar {
|
|
||||||
let mut bytes_128 = [0; 128];
|
|
||||||
bytes_128[.. 114].copy_from_slice(&bytes);
|
|
||||||
let wide = U1024::from_le_slice(&bytes_128);
|
|
||||||
Scalar(Residue::new(&U448::from_le_slice(
|
|
||||||
&wide.rem(&WIDE_REDUCTION_MODULUS).to_le_bytes()[.. 56],
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_scalar() {
|
|
||||||
ff_group_tests::prime_field::test_prime_field_bits::<_, Scalar>(&mut rand_core::OsRng);
|
|
||||||
}
|
|
||||||
@@ -7,26 +7,22 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/evrf/embedw
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["curve25519", "ed25519", "ristretto255", "group"]
|
keywords = ["curve25519", "ed25519", "ristretto255", "group"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.83"
|
rust-version = "1.86"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustversion = "1"
|
|
||||||
hex-literal = { version = "0.4", default-features = false }
|
hex-literal = { version = "0.4", default-features = false }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false, optional = true }
|
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false, optional = true }
|
||||||
|
|
||||||
rand_core = { version = "0.6", default-features = false }
|
|
||||||
|
|
||||||
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||||
subtle = { version = "^2.4", default-features = false }
|
|
||||||
|
|
||||||
generic-array = { version = "1", default-features = false }
|
generic-array = { version = "1", default-features = false }
|
||||||
crypto-bigint = { version = "0.5", default-features = false, features = ["zeroize"] }
|
|
||||||
|
|
||||||
|
prime-field = { path = "../../prime-field", default-features = false }
|
||||||
dalek-ff-group = { path = "../../dalek-ff-group", version = "0.4", default-features = false }
|
dalek-ff-group = { path = "../../dalek-ff-group", version = "0.4", default-features = false }
|
||||||
|
|
||||||
blake2 = { version = "0.10", default-features = false }
|
blake2 = { version = "0.10", default-features = false }
|
||||||
@@ -42,6 +38,6 @@ rand_core = { version = "0.6", features = ["std"] }
|
|||||||
ff-group-tests = { path = "../../ff-group-tests" }
|
ff-group-tests = { path = "../../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["std-shims", "zeroize/alloc", "ciphersuite/alloc"]
|
alloc = ["std-shims", "zeroize/alloc", "prime-field/alloc", "ciphersuite/alloc"]
|
||||||
std = ["std-shims/std", "rand_core/std", "zeroize/std", "subtle/std", "blake2/std", "ciphersuite/std", "ec-divisors/std", "generalized-bulletproofs-ec-gadgets/std"]
|
std = ["std-shims/std", "zeroize/std", "prime-field/std", "blake2/std", "ciphersuite/std", "ec-divisors/std", "generalized-bulletproofs-ec-gadgets/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
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,
|
|
||||||
|
|
||||||
$NUM_BITS: literal,
|
|
||||||
$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 ciphersuite::group::ff::{
|
|
||||||
Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes, 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 = $NUM_BITS;
|
|
||||||
const CAPACITY: u32 = $NUM_BITS - 1;
|
|
||||||
|
|
||||||
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 FromUniformBytes<64> for $FieldName {
|
|
||||||
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
|
|
||||||
$FieldName(Residue::new(&reduce(U512::from_le_slice(bytes))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -8,19 +8,38 @@ use std_shims::prelude::*;
|
|||||||
use std_shims::io::{self, Read};
|
use std_shims::io::{self, Read};
|
||||||
|
|
||||||
use generic_array::typenum::{Sum, Diff, Quot, U, U1, U2};
|
use generic_array::typenum::{Sum, Diff, Quot, U, U1, U2};
|
||||||
use ciphersuite::group::{ff::PrimeField, Group};
|
use ciphersuite::group::{
|
||||||
|
ff::{PrimeField, FromUniformBytes},
|
||||||
|
Group,
|
||||||
|
};
|
||||||
|
|
||||||
#[macro_use]
|
prime_field::odd_prime_field!(
|
||||||
mod backend;
|
Scalar,
|
||||||
|
"0fffffffffffffffffffffffffffffffe53f4debb78ff96877063f0306eef96b",
|
||||||
mod scalar;
|
"0a",
|
||||||
pub use scalar::Scalar;
|
false
|
||||||
|
);
|
||||||
|
|
||||||
pub use dalek_ff_group::Scalar as FieldElement;
|
pub use dalek_ff_group::Scalar as FieldElement;
|
||||||
|
|
||||||
mod point;
|
mod point;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
|
|
||||||
|
pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||||
|
use core::hint::black_box;
|
||||||
|
use prime_field::zeroize::Zeroize;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/// Ciphersuite for Embedwards25519.
|
/// Ciphersuite for Embedwards25519.
|
||||||
///
|
///
|
||||||
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
||||||
@@ -41,7 +60,9 @@ impl ciphersuite::Ciphersuite for Embedwards25519 {
|
|||||||
|
|
||||||
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
||||||
use blake2::Digest;
|
use blake2::Digest;
|
||||||
Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_slice().try_into().unwrap())
|
<Scalar as FromUniformBytes<64>>::from_uniform_bytes(
|
||||||
|
&Self::H::digest([dst, data].concat()).into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We override the provided impl, which compares against the reserialization, because
|
// We override the provided impl, which compares against the reserialization, because
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ use core::{
|
|||||||
iter::Sum,
|
iter::Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::RngCore;
|
use prime_field::{
|
||||||
|
subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable},
|
||||||
use zeroize::Zeroize;
|
zeroize::Zeroize,
|
||||||
use subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable};
|
rand_core::RngCore,
|
||||||
|
};
|
||||||
|
|
||||||
use ciphersuite::group::{
|
use ciphersuite::group::{
|
||||||
ff::{Field, PrimeField, PrimeFieldBits},
|
ff::{Field, PrimeField, PrimeFieldBits},
|
||||||
@@ -14,7 +15,7 @@ use ciphersuite::group::{
|
|||||||
prime::PrimeGroup,
|
prime::PrimeGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{backend::u8_from_bool, Scalar, FieldElement};
|
use crate::{u8_from_bool, Scalar, FieldElement};
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn B() -> FieldElement {
|
fn B() -> FieldElement {
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
use zeroize::{DefaultIsZeroes, Zeroize};
|
|
||||||
|
|
||||||
use crypto_bigint::{
|
|
||||||
U256, U512,
|
|
||||||
modular::constant_mod::{ResidueParams, Residue},
|
|
||||||
};
|
|
||||||
|
|
||||||
const MODULUS_STR: &str = "0fffffffffffffffffffffffffffffffe53f4debb78ff96877063f0306eef96b";
|
|
||||||
|
|
||||||
impl_modulus!(EmbedwardsQ, U256, MODULUS_STR);
|
|
||||||
type ResidueType = Residue<EmbedwardsQ, { EmbedwardsQ::LIMBS }>;
|
|
||||||
|
|
||||||
/// The Scalar field of Embedwards25519.
|
|
||||||
///
|
|
||||||
/// 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",
|
|
||||||
"0fffffffffffffffffffffffffffffffe53f4debb78ff96877063f0306eef96b",
|
|
||||||
));
|
|
||||||
|
|
||||||
field!(
|
|
||||||
Scalar,
|
|
||||||
ResidueType,
|
|
||||||
MODULUS_STR,
|
|
||||||
MODULUS,
|
|
||||||
WIDE_MODULUS,
|
|
||||||
252,
|
|
||||||
10,
|
|
||||||
1,
|
|
||||||
"0fffffffffffffffffffffffffffffffe53f4debb78ff96877063f0306eef96a",
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000064",
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -7,19 +7,19 @@ repository = "https://github.com/serai-dex/serai/tree/develop/crypto/evrf/secq25
|
|||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
keywords = ["secp256k1", "secq256k1", "group"]
|
keywords = ["secp256k1", "secq256k1", "group"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85"
|
rust-version = "1.86"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustversion = "1"
|
|
||||||
hex-literal = { version = "0.4", default-features = false }
|
hex-literal = { version = "0.4", default-features = false }
|
||||||
|
|
||||||
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false, optional = true }
|
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false, optional = true }
|
||||||
|
|
||||||
k256 = { version = "0.13", default-features = false, features = ["arithmetic"] }
|
k256 = { version = "0.13", default-features = false, features = ["arithmetic"] }
|
||||||
|
prime-field = { path = "../../prime-field", default-features = false }
|
||||||
|
|
||||||
blake2 = { version = "0.10", default-features = false }
|
blake2 = { version = "0.10", default-features = false }
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false }
|
ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false }
|
||||||
@@ -34,6 +34,6 @@ rand_core = { version = "0.6", features = ["std"] }
|
|||||||
ff-group-tests = { path = "../../ff-group-tests" }
|
ff-group-tests = { path = "../../ff-group-tests" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = ["std-shims", "k256/alloc", "ciphersuite/alloc"]
|
alloc = ["std-shims", "k256/alloc", "prime-field/alloc", "ciphersuite/alloc"]
|
||||||
std = ["std-shims/std", "k256/std", "blake2/std", "ciphersuite/std", "ec-divisors/std", "generalized-bulletproofs-ec-gadgets/std"]
|
std = ["std-shims/std", "k256/std", "prime-field/std", "blake2/std", "ciphersuite/std", "ec-divisors/std", "generalized-bulletproofs-ec-gadgets/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,301 +0,0 @@
|
|||||||
use k256::elliptic_curve::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,
|
|
||||||
|
|
||||||
$NUM_BITS: literal,
|
|
||||||
$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 k256::elliptic_curve::{
|
|
||||||
subtle::{Choice, CtOption, ConstantTimeEq, ConstantTimeLess, ConditionallySelectable},
|
|
||||||
rand_core::RngCore,
|
|
||||||
bigint::{Integer, NonZero, Encoding, impl_modulus},
|
|
||||||
group::ff::{
|
|
||||||
Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes, 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_be_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 = $NUM_BITS;
|
|
||||||
const CAPACITY: u32 = $NUM_BITS - 1;
|
|
||||||
|
|
||||||
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_be_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_be_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> {
|
|
||||||
let mut repr = [0; 32];
|
|
||||||
repr.copy_from_slice(&self.0.retrieve().to_le_bytes());
|
|
||||||
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 FromUniformBytes<64> for $FieldName {
|
|
||||||
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
|
|
||||||
$FieldName(Residue::new(&reduce(U512::from_le_slice(bytes))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,20 +10,39 @@ use std_shims::io::{self, Read};
|
|||||||
use k256::elliptic_curve::{
|
use k256::elliptic_curve::{
|
||||||
zeroize::Zeroize,
|
zeroize::Zeroize,
|
||||||
generic_array::typenum::{Sum, Diff, Quot, U, U1, U2},
|
generic_array::typenum::{Sum, Diff, Quot, U, U1, U2},
|
||||||
group::{ff::PrimeField, Group},
|
group::{
|
||||||
|
ff::{PrimeField, FromUniformBytes},
|
||||||
|
Group,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[macro_use]
|
prime_field::odd_prime_field!(
|
||||||
mod backend;
|
Scalar,
|
||||||
|
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
|
||||||
mod scalar;
|
"03",
|
||||||
pub use scalar::Scalar;
|
true
|
||||||
|
);
|
||||||
|
|
||||||
pub use k256::Scalar as FieldElement;
|
pub use k256::Scalar as FieldElement;
|
||||||
|
|
||||||
mod point;
|
mod point;
|
||||||
pub use point::Point;
|
pub use point::Point;
|
||||||
|
|
||||||
|
pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
||||||
|
use core::hint::black_box;
|
||||||
|
use prime_field::zeroize::Zeroize;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/// Ciphersuite for Secq256k1.
|
/// Ciphersuite for Secq256k1.
|
||||||
///
|
///
|
||||||
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
/// hash_to_F is implemented with a naive concatenation of the dst and data, allowing transposition
|
||||||
@@ -47,7 +66,9 @@ impl ciphersuite::Ciphersuite for Secq256k1 {
|
|||||||
|
|
||||||
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
|
||||||
use blake2::Digest;
|
use blake2::Digest;
|
||||||
Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_slice().try_into().unwrap())
|
<Scalar as FromUniformBytes<64>>::from_uniform_bytes(
|
||||||
|
&Self::H::digest([dst, data].concat()).into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We override the provided impl, which compares against the reserialization, because
|
// We override the provided impl, which compares against the reserialization, because
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use k256::elliptic_curve::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{backend::u8_from_bool, Scalar, FieldElement};
|
use crate::{u8_from_bool, Scalar, FieldElement};
|
||||||
|
|
||||||
fn recover_y(x: FieldElement) -> CtOption<FieldElement> {
|
fn recover_y(x: FieldElement) -> CtOption<FieldElement> {
|
||||||
// x**3 + B since a = 0
|
// x**3 + B since a = 0
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
use k256::elliptic_curve::{
|
|
||||||
zeroize::{DefaultIsZeroes, Zeroize},
|
|
||||||
bigint::{
|
|
||||||
U256, U512,
|
|
||||||
modular::constant_mod::{ResidueParams, Residue},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const MODULUS_STR: &str = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F";
|
|
||||||
|
|
||||||
impl_modulus!(SecQ, U256, MODULUS_STR);
|
|
||||||
type ResidueType = Residue<SecQ, { SecQ::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,
|
|
||||||
256,
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,10 @@ use digest::Digest;
|
|||||||
|
|
||||||
use minimal_ed448::{Scalar, Point};
|
use minimal_ed448::{Scalar, Point};
|
||||||
pub use minimal_ed448::Ed448;
|
pub use minimal_ed448::Ed448;
|
||||||
pub use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
pub use ciphersuite::{
|
||||||
|
group::{ff::FromUniformBytes, GroupEncoding},
|
||||||
|
Ciphersuite,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{curve::Curve, algorithm::Hram};
|
use crate::{curve::Curve, algorithm::Hram};
|
||||||
|
|
||||||
@@ -18,17 +21,18 @@ pub(crate) struct Ietf8032Ed448Hram;
|
|||||||
impl Ietf8032Ed448Hram {
|
impl Ietf8032Ed448Hram {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub(crate) fn hram(context: &[u8], R: &Point, A: &Point, m: &[u8]) -> Scalar {
|
pub(crate) fn hram(context: &[u8], R: &Point, A: &Point, m: &[u8]) -> Scalar {
|
||||||
Scalar::wide_reduce(
|
Scalar::from_uniform_bytes(
|
||||||
<Ed448 as Ciphersuite>::H::digest(
|
&<[u8; 114]>::try_from(
|
||||||
[
|
<Ed448 as Ciphersuite>::H::digest(
|
||||||
&[b"SigEd448".as_ref(), &[0, u8::try_from(context.len()).unwrap()]].concat(),
|
[
|
||||||
context,
|
&[b"SigEd448".as_ref(), &[0, u8::try_from(context.len()).unwrap()]].concat(),
|
||||||
&[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat(),
|
context,
|
||||||
]
|
&[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat(),
|
||||||
.concat(),
|
]
|
||||||
|
.concat(),
|
||||||
|
)
|
||||||
|
.as_slice(),
|
||||||
)
|
)
|
||||||
.as_ref()
|
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
31
crypto/prime-field/Cargo.toml
Normal file
31
crypto/prime-field/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
name = "prime-field"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A library to declare an ff::PrimeField via crypto-bigint"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/prime-field"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = ["ff", "field", "prime", "crypto-bigint"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.86"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
paste = { version = "1", default-features = false }
|
||||||
|
zeroize = { version = "^1.5", default-features = false }
|
||||||
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
|
crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] }
|
||||||
|
|
||||||
|
ff = { version = "0.13", default-features = false, features = ["bits"] }
|
||||||
|
|
||||||
|
ff-group-tests = { version = "0.13", path = "../ff-group-tests", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = ["zeroize/alloc", "crypto-bigint/alloc", "ff/alloc"]
|
||||||
|
std = ["zeroize/std", "subtle/std", "rand_core/std", "ff/std", "ff-group-tests"]
|
||||||
|
default = ["std"]
|
||||||
21
crypto/prime-field/LICENSE
Normal file
21
crypto/prime-field/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/prime-field/README.md
Normal file
3
crypto/prime-field/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Prime Field
|
||||||
|
|
||||||
|
A comprehensive macro to declare a prime field.
|
||||||
521
crypto/prime-field/src/lib.rs
Normal file
521
crypto/prime-field/src/lib.rs
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
pub use subtle;
|
||||||
|
pub use zeroize;
|
||||||
|
pub use rand_core;
|
||||||
|
pub use crypto_bigint;
|
||||||
|
pub use ff;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod __prime_field_private {
|
||||||
|
pub use paste;
|
||||||
|
pub use ff_group_tests;
|
||||||
|
|
||||||
|
use crypto_bigint::{Word, Uint, modular::ConstMontyParams};
|
||||||
|
|
||||||
|
/// Remove the "0x"-prefix from a hex string.
|
||||||
|
///
|
||||||
|
/// May panic if the string isn't valid hex.
|
||||||
|
pub const fn hex_str_without_prefix(hex: &str) -> &str {
|
||||||
|
if hex.len() < 2 {
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if hex.as_bytes()[1] == b'x' {
|
||||||
|
assert!(hex.as_bytes()[0] == b'0', "invalid hex string for modulus");
|
||||||
|
hex.split_at(2).1
|
||||||
|
} else {
|
||||||
|
hex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn uint_to_u64_words<const LIMBS: usize, const WORDS: usize>(
|
||||||
|
value: Uint<LIMBS>,
|
||||||
|
) -> [u64; WORDS] {
|
||||||
|
let mut res = [0u64; WORDS];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < Uint::<LIMBS>::LIMBS {
|
||||||
|
let word: Word = value.as_limbs()[i].0;
|
||||||
|
let bits = i * (Word::BITS as usize);
|
||||||
|
let j = bits / (u64::BITS as usize);
|
||||||
|
res[j] |= word << (bits % (u64::BITS as usize));
|
||||||
|
if (j + 1) < WORDS {
|
||||||
|
if let Some(remaining_bits) =
|
||||||
|
((bits % (u64::BITS as usize)) + (Word::BITS as usize)).checked_sub(u64::BITS as usize)
|
||||||
|
{
|
||||||
|
if remaining_bits != 0 {
|
||||||
|
res[j + 1] |= word >> ((Word::BITS as usize) - remaining_bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn u64_words_to_uint<const LIMBS: usize, const WORDS: usize>(
|
||||||
|
words: [u64; WORDS],
|
||||||
|
) -> Uint<LIMBS> {
|
||||||
|
let mut reconstruction = Uint::<LIMBS>::ZERO;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < WORDS {
|
||||||
|
reconstruction = reconstruction
|
||||||
|
.bitor(&Uint::<LIMBS>::from_u64(words[i]).shl_vartime((i * (u64::BITS as usize)) as u32));
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
reconstruction
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub const fn calculate_S<const LIMBS: usize, P: ConstMontyParams<LIMBS>>() -> u32 {
|
||||||
|
let mut i = 0;
|
||||||
|
loop {
|
||||||
|
let bit = P::MODULUS.as_ref().wrapping_sub(&Uint::<LIMBS>::ONE).bit_vartime(i);
|
||||||
|
if !bit {
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! odd_prime_field {
|
||||||
|
(
|
||||||
|
$name: ident,
|
||||||
|
$modulus_as_be_hex: expr,
|
||||||
|
$multiplicative_generator_as_be_hex: expr,
|
||||||
|
$big_endian: literal
|
||||||
|
) => {
|
||||||
|
prime_field::__prime_field_private::paste::paste! {
|
||||||
|
mod [<$name __prime_field_private>] {
|
||||||
|
use core::{
|
||||||
|
ops::*,
|
||||||
|
iter::{Sum, Product},
|
||||||
|
};
|
||||||
|
use prime_field::{
|
||||||
|
subtle::{
|
||||||
|
Choice, CtOption, ConstantTimeEq, ConditionallySelectable, ConditionallyNegatable,
|
||||||
|
},
|
||||||
|
zeroize::Zeroize,
|
||||||
|
rand_core::RngCore,
|
||||||
|
crypto_bigint::{
|
||||||
|
Limb, Encoding, Integer, Uint,
|
||||||
|
modular::{ConstMontyParams, ConstMontyForm},
|
||||||
|
impl_modulus,
|
||||||
|
},
|
||||||
|
ff::*,
|
||||||
|
__prime_field_private::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MODULUS_WITHOUT_PREFIX: &str = hex_str_without_prefix($modulus_as_be_hex);
|
||||||
|
const MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX: &str =
|
||||||
|
hex_str_without_prefix($multiplicative_generator_as_be_hex);
|
||||||
|
|
||||||
|
const MODULUS_BYTES: usize = MODULUS_WITHOUT_PREFIX.len() / 2;
|
||||||
|
type UnderlyingUint = Uint<{ MODULUS_BYTES.div_ceil(Limb::BYTES) }>;
|
||||||
|
|
||||||
|
const PADDED_MODULUS_WITHOUT_PREFIX_BYTES: [u8; 2 * UnderlyingUint::BYTES] = {
|
||||||
|
let mut res = [b'0'; 2 * UnderlyingUint::BYTES];
|
||||||
|
let start = (2 * UnderlyingUint::BYTES) - MODULUS_WITHOUT_PREFIX.len();
|
||||||
|
let mut i = start;
|
||||||
|
while i < (2 * UnderlyingUint::BYTES) {
|
||||||
|
res[i] = MODULUS_WITHOUT_PREFIX.as_bytes()[i - start];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
};
|
||||||
|
const PADDED_MODULUS_WITHOUT_PREFIX: &str = {
|
||||||
|
match core::str::from_utf8(&PADDED_MODULUS_WITHOUT_PREFIX_BYTES) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_) => panic!("couldn't successfully pad modulus"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const PADDED_MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX_BYTES:
|
||||||
|
[u8; 2 * UnderlyingUint::BYTES] = {
|
||||||
|
let mut res = [b'0'; 2 * UnderlyingUint::BYTES];
|
||||||
|
let start = (2 * UnderlyingUint::BYTES) - MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX.len();
|
||||||
|
let mut i = start;
|
||||||
|
while i < (2 * UnderlyingUint::BYTES) {
|
||||||
|
res[i] = MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX.as_bytes()[i - start];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
};
|
||||||
|
const PADDED_MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX: &str = {
|
||||||
|
match core::str::from_utf8(&PADDED_MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX_BYTES) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_) => panic!("couldn't successfully pad multiplicative generator"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
impl_modulus!(Params, UnderlyingUint, PADDED_MODULUS_WITHOUT_PREFIX);
|
||||||
|
type Underlying = ConstMontyForm<Params, { UnderlyingUint::LIMBS }>;
|
||||||
|
|
||||||
|
const MODULUS: &UnderlyingUint = Params::MODULUS.as_ref();
|
||||||
|
const MODULUS_MINUS_ONE: UnderlyingUint = MODULUS.wrapping_sub(&UnderlyingUint::ONE);
|
||||||
|
const MODULUS_MINUS_TWO: UnderlyingUint = MODULUS.wrapping_sub(&UnderlyingUint::from_u8(2));
|
||||||
|
const T: UnderlyingUint = MODULUS_MINUS_ONE.shr_vartime($name::S);
|
||||||
|
|
||||||
|
/// A field automatically generated with `short-weierstrass`.
|
||||||
|
#[derive(Clone, Copy, Eq, Debug)]
|
||||||
|
pub struct $name(Underlying);
|
||||||
|
|
||||||
|
impl Default for $name {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
/// Create a `$name` from the `Uint` type underlying it.
|
||||||
|
pub const fn from(value: &UnderlyingUint) -> Self {
|
||||||
|
$name(Underlying::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u8> for $name {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
Self::from(&UnderlyingUint::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u16> for $name {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
Self::from(&UnderlyingUint::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u32> for $name {
|
||||||
|
fn from(value: u32) -> Self {
|
||||||
|
Self::from(&UnderlyingUint::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<u64> for $name {
|
||||||
|
fn from(value: u64) -> Self {
|
||||||
|
Self::from(&UnderlyingUint::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstantTimeEq for $name {
|
||||||
|
fn ct_eq(&self, other: &Self) -> Choice {
|
||||||
|
self.0.ct_eq(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialEq for $name {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
bool::from(self.ct_eq(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConditionallySelectable for $name {
|
||||||
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||||
|
Self(<_>::conditional_select(&a.0, &b.0, choice))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ConditionallyNegatable for $name {
|
||||||
|
fn conditional_negate(&mut self, negate: Choice) {
|
||||||
|
self.0.conditional_negate(negate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zeroize for $name {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.0.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn neg(self) -> Self {
|
||||||
|
Self(-self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sub for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self(self.0 - other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Mul for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, other: Self) -> Self {
|
||||||
|
Self(self.0 * other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AddAssign for $name {
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
self.0 += other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SubAssign for $name {
|
||||||
|
fn sub_assign(&mut self, other: Self) {
|
||||||
|
self.0 -= other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl MulAssign for $name {
|
||||||
|
fn mul_assign(&mut self, other: Self) {
|
||||||
|
self.0 *= other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Sum for $name {
|
||||||
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
|
let mut res = Self::ZERO;
|
||||||
|
for item in iter {
|
||||||
|
res += item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Product for $name {
|
||||||
|
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
|
let mut res = Self::ONE;
|
||||||
|
for item in iter {
|
||||||
|
res *= item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Add<&'a Self> for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, other: &'a Self) -> Self {
|
||||||
|
Self(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Sub<&'a Self> for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(self, other: &'a Self) -> Self {
|
||||||
|
Self(self.0 - other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Mul<&'a Self> for $name {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, other: &'a Self) -> Self {
|
||||||
|
Self(self.0 * other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Sum<&'a Self> for $name {
|
||||||
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
|
let mut res = Self::ZERO;
|
||||||
|
for item in iter {
|
||||||
|
res += item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Product<&'a Self> for $name {
|
||||||
|
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
|
let mut res = Self::ONE;
|
||||||
|
for item in iter {
|
||||||
|
res *= item;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> AddAssign<&'a Self> for $name {
|
||||||
|
fn add_assign(&mut self, other: &'a Self) {
|
||||||
|
self.0 += other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> SubAssign<&'a Self> for $name {
|
||||||
|
fn sub_assign(&mut self, other: &'a Self) {
|
||||||
|
self.0 -= other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> MulAssign<&'a Self> for $name {
|
||||||
|
fn mul_assign(&mut self, other: &'a Self) {
|
||||||
|
self.0 *= other.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Field for $name {
|
||||||
|
const ZERO: Self = Self(Underlying::ZERO);
|
||||||
|
const ONE: Self = Self(Underlying::ONE);
|
||||||
|
fn random(mut rng: impl RngCore) -> Self {
|
||||||
|
let mut bytes = [0; 2 * MODULUS_BYTES];
|
||||||
|
rng.fill_bytes(&mut bytes);
|
||||||
|
Self::from_uniform_bytes(&bytes)
|
||||||
|
}
|
||||||
|
fn square(&self) -> Self {
|
||||||
|
Self(self.0.square())
|
||||||
|
}
|
||||||
|
fn double(&self) -> Self {
|
||||||
|
Self(self.0.double())
|
||||||
|
}
|
||||||
|
fn invert(&self) -> CtOption<Self> {
|
||||||
|
CtOption::from(self.0.inv()).map(Self)
|
||||||
|
}
|
||||||
|
fn sqrt(&self) -> CtOption<Self> {
|
||||||
|
const THREE_MOD_FOUR: bool = (MODULUS.as_words()[0] % 4) == 3;
|
||||||
|
const ONE_MOD_EIGHT: bool = (MODULUS.as_words()[0] % 8) == 1;
|
||||||
|
const FIVE_MOD_EIGHT: bool = (MODULUS.as_words()[0] % 8) == 5;
|
||||||
|
|
||||||
|
let sqrt = if THREE_MOD_FOUR {
|
||||||
|
const SQRT_EXP: UnderlyingUint =
|
||||||
|
MODULUS.shr_vartime(2).wrapping_add(&UnderlyingUint::ONE);
|
||||||
|
Self(self.0.pow(&SQRT_EXP))
|
||||||
|
} else if ONE_MOD_EIGHT {
|
||||||
|
const TM1D2: UnderlyingUint = (T.wrapping_sub(&UnderlyingUint::ONE)).shr_vartime(1);
|
||||||
|
const TM1D2_WORDS_LEN: usize = UnderlyingUint::BITS.div_ceil(u64::BITS) as usize;
|
||||||
|
const TM1D2_WORDS: [u64; TM1D2_WORDS_LEN] = uint_to_u64_words(TM1D2);
|
||||||
|
|
||||||
|
const TM1D2_RECONSTRUCTION: UnderlyingUint = u64_words_to_uint(TM1D2_WORDS);
|
||||||
|
const RECONSTRUCTION_EQUALS_VALUE: bool = {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut res = true;
|
||||||
|
while i < TM1D2_WORDS_LEN {
|
||||||
|
res &= TM1D2_RECONSTRUCTION.as_words()[i] == TM1D2.as_words()[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
};
|
||||||
|
const _ASSERT_RECONSTRUCTION_EQUALS_VALUE:
|
||||||
|
[(); 0 - ((!RECONSTRUCTION_EQUALS_VALUE) as usize)] = [(); _];
|
||||||
|
|
||||||
|
helpers::sqrt_tonelli_shanks::<Self, _>(self, TM1D2_WORDS).unwrap_or(Self::ZERO)
|
||||||
|
} else {
|
||||||
|
const SQRT_EXP: UnderlyingUint = MODULUS.shr_vartime(3);
|
||||||
|
let upsilon = self.double().0.pow(&SQRT_EXP);
|
||||||
|
let i = (upsilon.square() * &self.0).double();
|
||||||
|
Self(upsilon * self.0 * (i - Self::ONE.0))
|
||||||
|
};
|
||||||
|
|
||||||
|
let sqrt = <_>::conditional_select(&sqrt, &-sqrt, sqrt.0.retrieve().is_odd());
|
||||||
|
CtOption::new(sqrt, sqrt.square().ct_eq(self))
|
||||||
|
}
|
||||||
|
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
|
||||||
|
helpers::sqrt_ratio_generic(num, div)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Repr([u8; MODULUS_BYTES]);
|
||||||
|
impl Default for Repr {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self([0; _])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsRef<[u8]> for Repr {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsMut<[u8]> for Repr {
|
||||||
|
fn as_mut(&mut self) -> &mut [u8] {
|
||||||
|
self.0.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeField for $name {
|
||||||
|
type Repr = Repr;
|
||||||
|
const MODULUS: &str = $modulus_as_be_hex;
|
||||||
|
const NUM_BITS: u32 = MODULUS.bits();
|
||||||
|
const CAPACITY: u32 = Self::NUM_BITS - 1;
|
||||||
|
const TWO_INV: Self =
|
||||||
|
Self(Underlying::new(&UnderlyingUint::from_u8(2)).pow(&MODULUS_MINUS_TWO));
|
||||||
|
const MULTIPLICATIVE_GENERATOR: Self = Self(
|
||||||
|
Underlying::new(
|
||||||
|
&UnderlyingUint::from_be_hex(PADDED_MULTIPLICATIVE_GENERATOR_WITHOUT_PREFIX)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const S: u32 = calculate_S::<_, Params>();
|
||||||
|
const ROOT_OF_UNITY: Self = Self(Self::MULTIPLICATIVE_GENERATOR.0.pow(&T));
|
||||||
|
const ROOT_OF_UNITY_INV: Self = Self(Self::ROOT_OF_UNITY.0.pow(&MODULUS_MINUS_TWO));
|
||||||
|
const DELTA: Self = {
|
||||||
|
let two_to_the_s = UnderlyingUint::ONE.shl_vartime(Self::S);
|
||||||
|
Self(Self::MULTIPLICATIVE_GENERATOR.0.pow(&two_to_the_s))
|
||||||
|
};
|
||||||
|
|
||||||
|
fn to_repr(&self) -> Self::Repr {
|
||||||
|
let mut res = Repr([0; _]);
|
||||||
|
if $big_endian {
|
||||||
|
res.0.copy_from_slice(
|
||||||
|
&self.0.retrieve().to_be_bytes()[(UnderlyingUint::BYTES - MODULUS_BYTES) ..]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res.0.copy_from_slice(&self.0.retrieve().to_le_bytes()[.. MODULUS_BYTES]);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||||
|
let mut expanded_repr = [0; UnderlyingUint::BYTES];
|
||||||
|
let result = Self(if $big_endian {
|
||||||
|
expanded_repr[(UnderlyingUint::BYTES - MODULUS_BYTES) .. ].copy_from_slice(&repr.0);
|
||||||
|
Underlying::new(&UnderlyingUint::from_be_bytes(expanded_repr))
|
||||||
|
} else {
|
||||||
|
expanded_repr[.. MODULUS_BYTES].copy_from_slice(&repr.0);
|
||||||
|
Underlying::new(&UnderlyingUint::from_le_bytes(expanded_repr))
|
||||||
|
});
|
||||||
|
CtOption::new(result, result.to_repr().0.ct_eq(&repr.0))
|
||||||
|
}
|
||||||
|
fn is_odd(&self) -> Choice {
|
||||||
|
self.0.retrieve().is_odd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimeFieldBits for $name {
|
||||||
|
type ReprBits = [u8; UnderlyingUint::BYTES];
|
||||||
|
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
|
||||||
|
self.0.retrieve().to_le_bytes().into()
|
||||||
|
}
|
||||||
|
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||||
|
MODULUS.to_le_bytes().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromUniformBytes<{ 2 * MODULUS_BYTES }> for $name {
|
||||||
|
fn from_uniform_bytes(bytes: &[u8; 2 * MODULUS_BYTES]) -> Self {
|
||||||
|
let mut expanded_wide_repr = [0; 2 * UnderlyingUint::BYTES];
|
||||||
|
expanded_wide_repr[.. (2 * MODULUS_BYTES)].copy_from_slice(bytes);
|
||||||
|
let bytes = expanded_wide_repr;
|
||||||
|
|
||||||
|
let lo =
|
||||||
|
Underlying::new(&UnderlyingUint::from_le_slice(&bytes[.. UnderlyingUint::BYTES]));
|
||||||
|
let hi =
|
||||||
|
Underlying::new(&UnderlyingUint::from_le_slice(&bytes[UnderlyingUint::BYTES ..]));
|
||||||
|
const HI: Underlying = {
|
||||||
|
let mut res = Underlying::new(&UnderlyingUint::ONE);
|
||||||
|
let mut i = 0;
|
||||||
|
while i < UnderlyingUint::BITS {
|
||||||
|
res = res.double();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
};
|
||||||
|
Self(lo + (hi * HI))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BITS_PLUS_SECURITY_LEVEL: usize =
|
||||||
|
(MODULUS.bits() + MODULUS.bits().div_ceil(2)) as usize;
|
||||||
|
const BITS_PLUS_SECURITY_LEVEL_BYTES: usize = BITS_PLUS_SECURITY_LEVEL.div_ceil(8);
|
||||||
|
impl FromUniformBytes<{ BITS_PLUS_SECURITY_LEVEL_BYTES }> for $name {
|
||||||
|
fn from_uniform_bytes(bytes: &[u8; BITS_PLUS_SECURITY_LEVEL_BYTES]) -> Self {
|
||||||
|
let mut larger = [0; 2 * MODULUS_BYTES];
|
||||||
|
larger[.. BITS_PLUS_SECURITY_LEVEL_BYTES].copy_from_slice(bytes);
|
||||||
|
Self::from_uniform_bytes(&larger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use prime_field::__prime_field_private::ff_group_tests;
|
||||||
|
use ff_group_tests::prime_field::test_prime_field_bits;
|
||||||
|
test_prime_field_bits::<_, $name>(&mut prime_field::rand_core::OsRng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use [<$name __prime_field_private>]::$name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
6
crypto/prime-field/tests/5mod8.rs
Normal file
6
crypto/prime-field/tests/5mod8.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
prime_field::odd_prime_field!(
|
||||||
|
Scalar,
|
||||||
|
"0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
|
||||||
|
"02",
|
||||||
|
false
|
||||||
|
);
|
||||||
@@ -309,12 +309,12 @@ fn embedded_curve_keys(network: Network) -> EmbeddedCurveKeys {
|
|||||||
embedwards25519: {
|
embedwards25519: {
|
||||||
let key = Zeroizing::new(<Embedwards25519 as Ciphersuite>::F::random(&mut rng));
|
let key = Zeroizing::new(<Embedwards25519 as Ciphersuite>::F::random(&mut rng));
|
||||||
let pub_key = Embedwards25519::generator() * key.deref();
|
let pub_key = Embedwards25519::generator() * key.deref();
|
||||||
(Zeroizing::new(key.to_repr().as_slice().to_vec()), pub_key.to_bytes().to_vec())
|
(Zeroizing::new(key.to_repr().as_ref().to_vec()), pub_key.to_bytes().to_vec())
|
||||||
},
|
},
|
||||||
secq256k1: {
|
secq256k1: {
|
||||||
let key = Zeroizing::new(<Secq256k1 as Ciphersuite>::F::random(&mut rng));
|
let key = Zeroizing::new(<Secq256k1 as Ciphersuite>::F::random(&mut rng));
|
||||||
let pub_key = Secq256k1::generator() * key.deref();
|
let pub_key = Secq256k1::generator() * key.deref();
|
||||||
(Zeroizing::new(key.to_repr().as_slice().to_vec()), pub_key.to_bytes().to_vec())
|
(Zeroizing::new(key.to_repr().as_ref().to_vec()), pub_key.to_bytes().to_vec())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,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"] }
|
||||||
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 }
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub use multiexp;
|
|||||||
|
|
||||||
pub use schnorr_signatures;
|
pub use schnorr_signatures;
|
||||||
|
|
||||||
|
pub use prime_field;
|
||||||
pub use secq256k1;
|
pub use secq256k1;
|
||||||
pub use embedwards25519;
|
pub use embedwards25519;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user