Move the contents of the evrf/ folder to the crypto/ folder

It was justified when it had several libraries, which it no longer does thanks
to the upstreaming with monero-oxide.
This commit is contained in:
Luke Parker
2025-08-28 23:55:05 -04:00
parent 5526b8d439
commit ffe1b60a11
14 changed files with 26 additions and 26 deletions

View File

@@ -0,0 +1,40 @@
[package]
name = "secq256k1"
version = "0.1.0"
description = "An implementation of the curve secp256k1 cycles with"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/secq256k1"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = ["secp256k1", "secq256k1", "group"]
edition = "2021"
rust-version = "1.86"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
hex-literal = { version = "0.4", default-features = false }
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true }
generic-array = { version = "1", default-features = false }
k256 = { version = "0.13", default-features = false, features = ["arithmetic"] }
prime-field = { path = "../prime-field", default-features = false }
short-weierstrass = { path = "../short-weierstrass", default-features = false }
blake2 = { version = "0.10", default-features = false }
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false, optional = true }
[dev-dependencies]
hex = "0.4"
rand_core = { version = "0.6", features = ["std"] }
ff-group-tests = { path = "../ff-group-tests" }
[features]
alloc = ["std-shims", "generic-array/alloc", "k256/alloc", "prime-field/alloc", "short-weierstrass/alloc", "ciphersuite/alloc", "generalized-bulletproofs-ec-gadgets"]
std = ["alloc", "std-shims/std", "k256/std", "prime-field/std", "blake2/std", "ciphersuite/std", "generalized-bulletproofs-ec-gadgets/std"]
default = ["std"]

21
crypto/secq256k1/LICENSE Normal file
View 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.

View File

@@ -0,0 +1,5 @@
# secq256k1
An implementation of the curve secp256k1 cycles with.
Scalars and field elements are encoded in their big-endian formats.

184
crypto/secq256k1/src/lib.rs Normal file
View File

@@ -0,0 +1,184 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
#[allow(unused_imports)]
use std_shims::prelude::*;
#[cfg(feature = "alloc")]
use std_shims::io::{self, Read};
// Doesn't use the `generic-array 0.14` exported by `k256::elliptic_curve` as we need `1.0`
use generic_array::{
typenum::{U, U33},
GenericArray,
};
use k256::elliptic_curve::{
subtle::{Choice, ConstantTimeEq, ConditionallySelectable},
zeroize::Zeroize,
group::{
ff::{PrimeField, FromUniformBytes},
Group,
},
sec1::Tag,
};
prime_field::odd_prime_field!(
Scalar,
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
"03",
true
);
pub use k256::Scalar as FieldElement;
use short_weierstrass::{ShortWeierstrass, Affine, Projective};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Secq256k1;
impl Zeroize for Secq256k1 {
fn zeroize(&mut self) {}
}
impl ShortWeierstrass for Secq256k1 {
type FieldElement = FieldElement;
const A: FieldElement = FieldElement::ZERO;
const B: FieldElement = {
let two = FieldElement::ONE.add(&FieldElement::ONE);
let four = two.add(&two);
let six = four.add(&two);
six.add(&FieldElement::ONE)
};
const PRIME_ORDER: bool = true;
const GENERATOR: Affine<Self> = Affine::from_xy_unchecked(FieldElement::ONE, {
let y_be =
hex_literal::hex!("0c7c97045a2074634909abdf82c9bd0248916189041f2af0c1b800d1ffc278c0");
let mut res = FieldElement::ZERO;
let mut i = 0;
while i < 32 {
let mut j = 0;
while j < 8 {
// Shift over the existing result
res = res.add(&res);
// Add this bit, if set
if ((y_be[i] >> (8 - 1 - j)) & 1) == 1 {
res = res.add(&FieldElement::ONE);
}
j += 1;
}
i += 1;
}
res
});
type Scalar = Scalar;
type Repr = GenericArray<u8, U33>;
/// Use the SEC1-encoded identity point, which happens to be all zeroes
const IDENTITY: Self::Repr = GenericArray::from_array([0; 33]);
fn encode_compressed(x: Self::FieldElement, odd_y: Choice) -> Self::Repr {
let mut res = GenericArray::default();
res[0] =
<_>::conditional_select(&(Tag::CompressedEvenY as u8), &(Tag::CompressedOddY as u8), odd_y);
{
let res: &mut [u8] = res.as_mut();
res[1 ..].copy_from_slice(x.to_repr().as_ref());
}
res
}
fn decode_compressed(bytes: &Self::Repr) -> (<Self::FieldElement as PrimeField>::Repr, Choice) {
// Parse out if `y` is odd
let odd_y = bytes[0].ct_eq(&(Tag::CompressedOddY as u8));
// Check if the tag was malleated
let expected_tag =
<_>::conditional_select(&(Tag::CompressedEvenY as u8), &(Tag::CompressedOddY as u8), odd_y);
let invalid = !bytes[0].ct_eq(&expected_tag);
// Copy the alleged `x` coordinate, overwriting with `0xffffff...` if the sign byte was
// malleated (causing the `x` coordinate to be invalid)
let mut x = <Self::FieldElement as PrimeField>::Repr::default();
{
let x: &mut [u8] = x.as_mut();
for i in 0 .. 32 {
x[i] = <_>::conditional_select(&bytes[1 + i], &u8::MAX, invalid);
}
}
(x, odd_y)
}
// No points have a torsion element as this a prime-order curve
fn has_torsion_element(_point: Projective<Self>) -> Choice {
0.into()
}
}
pub type Point = Projective<Secq256k1>;
impl ciphersuite::Ciphersuite for Secq256k1 {
type F = Scalar;
type G = Point;
type H = blake2::Blake2b512;
const ID: &'static [u8] = b"secq256k1";
fn generator() -> Self::G {
Point::generator()
}
/// `hash_to_F` is implemented with a naive concatenation of the `dst` and `data`, allowing
/// transposition between the two. This means `dst: b"abc", data: b"def"`, will produce the same
/// scalar as `dst: "abcdef", data: b""`. Please use carefully, not letting `dst` valuess be
/// substrings of each other.
fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
use blake2::Digest;
<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 already require canonicity
#[cfg(feature = "alloc")]
#[allow(non_snake_case)]
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
use ciphersuite::group::GroupEncoding;
let mut encoding = <Self::G as GroupEncoding>::Repr::default();
reader.read_exact(encoding.as_mut())?;
let point = Option::<Self::G>::from(Self::G::from_bytes(&encoding))
.ok_or_else(|| io::Error::other("invalid point"))?;
Ok(point)
}
}
#[cfg(feature = "alloc")]
impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Secq256k1 {
type ScalarBits = U<{ Scalar::NUM_BITS as usize }>;
}
#[test]
fn test_curve() {
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
}
#[test]
fn generator() {
use ciphersuite::group::GroupEncoding;
assert_eq!(
Point::generator(),
Point::from_bytes(GenericArray::from_slice(&hex_literal::hex!(
"020000000000000000000000000000000000000000000000000000000000000001"
)))
.unwrap()
);
}
#[test]
fn zero_x_is_off_curve() {
assert!(bool::from(Affine::<Secq256k1>::decompress(FieldElement::ZERO, 1.into()).is_none()));
}
// Checks random won't infinitely loop
#[test]
fn random() {
Point::random(&mut rand_core::OsRng);
}