Use GroupEncoding instead of Curve's from_slice/to_bytes

Increases usage of standardization while expanding dalek_ff_group.

Closes https://github.com/serai-dex/serai/issues/26 by moving 
dfg::EdwardsPoint to only be for the prime subgroup.
This commit is contained in:
Luke Parker
2022-06-28 01:25:26 -04:00
parent ac17645fc8
commit 3de7a76051
14 changed files with 141 additions and 178 deletions

View File

@@ -1,34 +1,27 @@
use core::convert::TryInto;
use rand_core::{RngCore, CryptoRng};
use sha2::{Digest, Sha512};
use group::{ff::PrimeField, Group};
use dalek_ff_group::Scalar;
use crate::{curve::{CurveError, Curve}, algorithm::Hram};
use crate::{curve::Curve, algorithm::Hram};
macro_rules! dalek_curve {
(
$Curve: ident,
$Hram: ident,
$Point: ident,
$Compressed: ident,
$Table: ident,
$POINT: ident,
$TABLE: ident,
$torsioned: expr,
$ID: literal,
$CONTEXT: literal,
$chal: literal,
$digest: literal,
) => {
use dalek_ff_group::{$Point, $Compressed, $Table, $POINT, $TABLE};
use dalek_ff_group::{$Point, $Table, $POINT, $TABLE};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct $Curve;
@@ -75,43 +68,6 @@ macro_rules! dalek_curve {
fn G_len() -> usize {
32
}
fn F_from_slice(slice: &[u8]) -> Result<Self::F, CurveError> {
let scalar = Self::F::from_repr(
slice.try_into().map_err(|_| CurveError::InvalidLength(32, slice.len()))?
);
if !bool::from(scalar.is_some()) {
Err(CurveError::InvalidScalar)?;
}
Ok(scalar.unwrap())
}
fn G_from_slice(slice: &[u8]) -> Result<Self::G, CurveError> {
let bytes = slice.try_into().map_err(|_| CurveError::InvalidLength(32, slice.len()))?;
let point = $Compressed::new(bytes).decompress().ok_or(CurveError::InvalidPoint)?;
// Ban identity
if point.is_identity().into() {
Err(CurveError::InvalidPoint)?;
}
// Ban torsioned points to meet the prime order group requirement
if $torsioned(point) {
Err(CurveError::InvalidPoint)?;
}
// Ban points which weren't canonically encoded
if point.compress().to_bytes() != bytes {
Err(CurveError::InvalidPoint)?;
}
Ok(point)
}
fn F_to_bytes(f: &Self::F) -> Vec<u8> {
f.to_repr().to_vec()
}
fn G_to_bytes(g: &Self::G) -> Vec<u8> {
g.compress().to_bytes().to_vec()
}
}
#[derive(Copy, Clone)]
@@ -130,11 +86,9 @@ dalek_curve!(
Ristretto,
IetfRistrettoHram,
RistrettoPoint,
CompressedRistretto,
RistrettoBasepointTable,
RISTRETTO_BASEPOINT_POINT,
RISTRETTO_BASEPOINT_TABLE,
|_| false,
b"ristretto",
b"FROST-RISTRETTO255-SHA512-v5",
b"chal",
@@ -146,11 +100,9 @@ dalek_curve!(
Ed25519,
IetfEd25519Hram,
EdwardsPoint,
CompressedEdwardsY,
EdwardsBasepointTable,
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_TABLE,
|point: EdwardsPoint| !bool::from(point.is_torsion_free()),
b"edwards25519",
b"",
b"",

View File

@@ -1,14 +1,12 @@
use core::convert::TryInto;
use rand_core::{RngCore, CryptoRng};
use sha2::{digest::Update, Digest, Sha256};
use group::{ff::{Field, PrimeField}, Group, GroupEncoding};
use group::{ff::Field, GroupEncoding};
use elliptic_curve::{bigint::{Encoding, U384}, hash2curve::{Expander, ExpandMsg, ExpandMsgXmd}};
use crate::{curve::{CurveError, Curve}, algorithm::Hram};
use crate::{curve::{Curve, F_from_slice}, algorithm::Hram};
macro_rules! kp_curve {
(
@@ -65,7 +63,7 @@ macro_rules! kp_curve {
let mut modulus = vec![0; 16];
modulus.extend((Self::F::zero() - Self::F::one()).to_bytes());
let modulus = U384::from_be_slice(&modulus).wrapping_add(&U384::ONE);
Self::F_from_slice(
F_from_slice::<Self::F>(
&U384::from_be_slice(&{
let mut bytes = [0; 48];
ExpandMsgXmd::<Sha256>::expand_message(
@@ -85,38 +83,6 @@ macro_rules! kp_curve {
fn G_len() -> usize {
33
}
fn F_from_slice(slice: &[u8]) -> Result<Self::F, CurveError> {
let bytes: [u8; 32] = slice.try_into()
.map_err(|_| CurveError::InvalidLength(32, slice.len()))?;
let scalar = Self::F::from_repr(bytes.into());
if scalar.is_none().into() {
Err(CurveError::InvalidScalar)?;
}
Ok(scalar.unwrap())
}
fn G_from_slice(slice: &[u8]) -> Result<Self::G, CurveError> {
let bytes: [u8; 33] = slice.try_into()
.map_err(|_| CurveError::InvalidLength(33, slice.len()))?;
let point = Self::G::from_bytes(&bytes.into());
if point.is_none().into() || point.unwrap().is_identity().into() {
Err(CurveError::InvalidPoint)?;
}
Ok(point.unwrap())
}
fn F_to_bytes(f: &Self::F) -> Vec<u8> {
f.to_bytes().to_vec()
}
fn G_to_bytes(g: &Self::G) -> Vec<u8> {
g.to_bytes().to_vec()
}
}
#[derive(Clone)]
@@ -126,7 +92,7 @@ macro_rules! kp_curve {
fn hram(R: &$lib::ProjectivePoint, A: &$lib::ProjectivePoint, m: &[u8]) -> $lib::Scalar {
$Curve::hash_to_F(
&[$CONTEXT as &[u8], b"chal"].concat(),
&[&$Curve::G_to_bytes(R), &$Curve::G_to_bytes(A), m].concat()
&[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat()
)
}
}

View File

@@ -4,7 +4,7 @@ use thiserror::Error;
use rand_core::{RngCore, CryptoRng};
use group::{ff::PrimeField, Group, GroupOps};
use group::{ff::PrimeField, Group, GroupOps, prime::PrimeGroup};
#[cfg(any(test, feature = "dalek"))]
mod dalek;
@@ -42,7 +42,7 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
// This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses
type F: PrimeField;
/// Group element type
type G: Group<Scalar = Self::F> + GroupOps;
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup;
/// Precomputed table type
type T: Mul<Self::F, Output = Self::G>;
@@ -99,23 +99,31 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug {
// that is on them
#[allow(non_snake_case)]
fn G_len() -> usize;
/// Field element from slice. Preferred to be canonical yet does not have to be
// Required due to the lack of standardized encoding functions provided by ff/group
// While they do technically exist, their usage of Self::Repr breaks all potential library usage
// without helper functions like this
#[allow(non_snake_case)]
fn F_from_slice(slice: &[u8]) -> Result<Self::F, CurveError>;
/// Group element from slice. Must require canonicity or risks differing binding factors
#[allow(non_snake_case)]
fn G_from_slice(slice: &[u8]) -> Result<Self::G, CurveError>;
/// Obtain a vector of the byte encoding of F
#[allow(non_snake_case)]
fn F_to_bytes(f: &Self::F) -> Vec<u8>;
/// Obtain a vector of the byte encoding of G
#[allow(non_snake_case)]
fn G_to_bytes(g: &Self::G) -> Vec<u8>;
}
/// Field element from slice
#[allow(non_snake_case)]
pub(crate) fn F_from_slice<F: PrimeField>(slice: &[u8]) -> Result<F, CurveError> {
let mut encoding = F::Repr::default();
encoding.as_mut().copy_from_slice(slice);
let point = Option::<F>::from(F::from_repr(encoding)).ok_or(CurveError::InvalidScalar)?;
if point.to_repr().as_ref() != slice {
Err(CurveError::InvalidScalar)?;
}
Ok(point)
}
/// Group element from slice
#[allow(non_snake_case)]
pub(crate) fn G_from_slice<G: PrimeGroup>(slice: &[u8]) -> Result<G, CurveError> {
let mut encoding = G::Repr::default();
encoding.as_mut().copy_from_slice(slice);
let point = Option::<G>::from(G::from_bytes(&encoding)).ok_or(CurveError::InvalidPoint)?;
// Ban the identity, per the FROST spec, and non-canonical points
if (point.is_identity().into()) || (point.to_bytes().as_ref() != slice) {
Err(CurveError::InvalidPoint)?;
}
Ok(point)
}