mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-13 22:49:25 +00:00
Move secq256k1 to short-weierstrass
This commit is contained in:
@@ -4,12 +4,17 @@
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use std_shims::prelude::*;
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[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,
|
||||
generic_array::typenum::U,
|
||||
group::{
|
||||
ff::{PrimeField, FromUniformBytes},
|
||||
Group,
|
||||
@@ -25,34 +30,85 @@ prime_field::odd_prime_field!(
|
||||
|
||||
pub use k256::Scalar as FieldElement;
|
||||
|
||||
mod point;
|
||||
pub use point::Point;
|
||||
use short_weierstrass::{ShortWeierstrass, Affine, Projective};
|
||||
|
||||
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.
|
||||
///
|
||||
/// 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 dsts be substrings of each other.
|
||||
#[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 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 an all-zero encoding for the identity as `0` isn't the `x` coordinate of an on-curve point
|
||||
// This uses `unsafe` to construct a `GenericArray` at compile-time as ``
|
||||
const IDENTITY: Self::Repr = GenericArray::from_array([0; 33]);
|
||||
fn compress(x: Self::FieldElement, odd_y: Choice) -> Self::Repr {
|
||||
// If `y` is odd, followed by the big-endian `x` coordinate
|
||||
let mut res = GenericArray::default();
|
||||
res[0] = odd_y.unwrap_u8();
|
||||
{
|
||||
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 = Choice::from(bytes[0] & 1);
|
||||
// Check if the extra byte was malleated
|
||||
let invalid = !bytes[0].ct_eq(&odd_y.unwrap_u8());
|
||||
|
||||
// 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;
|
||||
@@ -64,6 +120,10 @@ impl ciphersuite::Ciphersuite for Secq256k1 {
|
||||
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(
|
||||
@@ -73,7 +133,7 @@ impl ciphersuite::Ciphersuite for Secq256k1 {
|
||||
|
||||
// We override the provided impl, which compares against the reserialization, because
|
||||
// we already require canonicity
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
#[cfg(feature = "alloc")]
|
||||
#[allow(non_snake_case)]
|
||||
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
|
||||
use ciphersuite::group::GroupEncoding;
|
||||
@@ -87,6 +147,35 @@ impl ciphersuite::Ciphersuite for Secq256k1 {
|
||||
}
|
||||
}
|
||||
|
||||
#[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!(
|
||||
"000000000000000000000000000000000000000000000000000000000000000001"
|
||||
)))
|
||||
.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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user