Use a macro to generate the Secp256k1/P-256 curves

This commit is contained in:
Luke Parker
2022-06-24 08:44:12 -04:00
parent 03e759b1fd
commit 7ee9581d67
3 changed files with 133 additions and 134 deletions

View File

@@ -36,8 +36,8 @@ dalek-ff-group = { path = "../dalek-ff-group" }
[features] [features]
curves = ["sha2"] # All officially denoted curves use the SHA2 family of hashes curves = ["sha2"] # All officially denoted curves use the SHA2 family of hashes
kp256 = ["elliptic-curve", "curves"] kp256 = ["elliptic-curve", "curves"]
p256 = ["dep:p256", "kp256"] p256 = ["kp256", "dep:p256"]
k256 = ["dep:k256", "kp256"] secp256k1 = ["kp256", "k256"]
dalek = ["curves", "dalek-ff-group"] dalek = ["curves", "dalek-ff-group"]
ed25519 = ["dalek"] ed25519 = ["dalek"]
ristretto = ["dalek"] ristretto = ["dalek"]

View File

@@ -1,4 +1,4 @@
use core::{marker::PhantomData, convert::TryInto}; use core::convert::TryInto;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
@@ -8,66 +8,41 @@ use group::{ff::{Field, PrimeField}, Group, GroupEncoding};
use elliptic_curve::{bigint::{Encoding, U384}, hash2curve::{Expander, ExpandMsg, ExpandMsgXmd}}; use elliptic_curve::{bigint::{Encoding, U384}, hash2curve::{Expander, ExpandMsg, ExpandMsgXmd}};
use crate::{CurveError, Curve}; use crate::{curves::{CurveError, Curve}, algorithm::Hram};
#[cfg(feature = "p256")]
use crate::algorithm::Hram;
#[allow(non_snake_case)] macro_rules! kp_curve {
(
$lib: ident,
$Curve: ident,
$Hram: ident,
$ID: literal,
$CONTEXT: literal
) => {
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct KP256<G: Group> { pub struct $Curve;
_G: PhantomData<G> impl Curve for $Curve {
} type F = $lib::Scalar;
type G = $lib::ProjectivePoint;
type T = $lib::ProjectivePoint;
pub(crate) trait KP256Instance<G> { const ID: &'static [u8] = $ID;
const CONTEXT: &'static [u8];
const ID: &'static [u8];
const GENERATOR: G;
}
#[cfg(feature = "p256")] const GENERATOR: Self::G = $lib::ProjectivePoint::GENERATOR;
pub type P256 = KP256<p256::ProjectivePoint>; const GENERATOR_TABLE: Self::G = $lib::ProjectivePoint::GENERATOR;
#[cfg(feature = "p256")]
impl KP256Instance<p256::ProjectivePoint> for P256 {
const CONTEXT: &'static [u8] = b"FROST-P256-SHA256-v5";
const ID: &'static [u8] = b"P-256";
const GENERATOR: p256::ProjectivePoint = p256::ProjectivePoint::GENERATOR;
}
#[cfg(feature = "k256")]
pub type K256 = KP256<k256::ProjectivePoint>;
#[cfg(feature = "k256")]
impl KP256Instance<k256::ProjectivePoint> for K256 {
const CONTEXT: &'static [u8] = b"FROST-secp256k1-SHA256-v5";
const ID: &'static [u8] = b"secp256k1";
const GENERATOR: k256::ProjectivePoint = k256::ProjectivePoint::GENERATOR;
}
impl<G: Group + GroupEncoding> Curve for KP256<G> where
KP256<G>: KP256Instance<G>,
G::Scalar: PrimeField,
<G::Scalar as PrimeField>::Repr: From<[u8; 32]> + AsRef<[u8]>,
G::Repr: From<[u8; 33]> + AsRef<[u8]> {
type F = G::Scalar;
type G = G;
type T = G;
const ID: &'static [u8] = <Self as KP256Instance<G>>::ID;
const GENERATOR: Self::G = <Self as KP256Instance<G>>::GENERATOR;
const GENERATOR_TABLE: Self::G = <Self as KP256Instance<G>>::GENERATOR;
const LITTLE_ENDIAN: bool = false; const LITTLE_ENDIAN: bool = false;
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F { fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32]; let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed); rng.fill_bytes(&mut seed);
seed.extend(secret.to_repr().as_ref()); seed.extend(secret.to_bytes());
Self::hash_to_F(&[Self::CONTEXT, b"nonce"].concat(), &seed) Self::hash_to_F(&[$CONTEXT as &[u8], b"nonce"].concat(), &seed)
} }
fn hash_msg(msg: &[u8]) -> Vec<u8> { fn hash_msg(msg: &[u8]) -> Vec<u8> {
(&Sha256::new() (&Sha256::new()
.chain(Self::CONTEXT) .chain($CONTEXT)
.chain(b"digest") .chain(b"digest")
.chain(msg) .chain(msg)
.finalize() .finalize()
@@ -75,7 +50,7 @@ impl<G: Group + GroupEncoding> Curve for KP256<G> where
} }
fn hash_binding_factor(binding: &[u8]) -> Self::F { fn hash_binding_factor(binding: &[u8]) -> Self::F {
Self::hash_to_F(&[Self::CONTEXT, b"rho"].concat(), binding) Self::hash_to_F(&[$CONTEXT as &[u8], b"rho"].concat(), binding)
} }
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F { fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
@@ -85,13 +60,19 @@ impl<G: Group + GroupEncoding> Curve for KP256<G> where
dst = &oversize; dst = &oversize;
} }
// While one of these two libraries does support directly hashing to the Scalar field, the
// other doesn't. While that's probably an oversight, this is a universally working method
let mut modulus = vec![0; 16]; let mut modulus = vec![0; 16];
modulus.extend((Self::F::zero() - Self::F::one()).to_repr().as_ref()); modulus.extend((Self::F::zero() - Self::F::one()).to_bytes());
let modulus = U384::from_be_slice(&modulus).wrapping_add(&U384::ONE); let modulus = U384::from_be_slice(&modulus).wrapping_add(&U384::ONE);
Self::F_from_slice( Self::F_from_slice(
&U384::from_be_slice(&{ &U384::from_be_slice(&{
let mut bytes = [0; 48]; let mut bytes = [0; 48];
ExpandMsgXmd::<Sha256>::expand_message(&[msg], dst, 48).unwrap().fill_bytes(&mut bytes); ExpandMsgXmd::<Sha256>::expand_message(
&[msg],
dst,
48
).unwrap().fill_bytes(&mut bytes);
bytes bytes
}).reduce(&modulus).unwrap().to_be_bytes()[16 ..] }).reduce(&modulus).unwrap().to_be_bytes()[16 ..]
).unwrap() ).unwrap()
@@ -130,24 +111,42 @@ impl<G: Group + GroupEncoding> Curve for KP256<G> where
} }
fn F_to_bytes(f: &Self::F) -> Vec<u8> { fn F_to_bytes(f: &Self::F) -> Vec<u8> {
f.to_repr().as_ref().to_vec() f.to_bytes().to_vec()
} }
fn G_to_bytes(g: &Self::G) -> Vec<u8> { fn G_to_bytes(g: &Self::G) -> Vec<u8> {
g.to_bytes().as_ref().to_vec() g.to_bytes().to_vec()
}
}
#[derive(Clone)]
pub struct $Hram;
impl Hram<$Curve> for $Hram {
#[allow(non_snake_case)]
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()
)
}
}
} }
} }
#[cfg(feature = "p256")] #[cfg(feature = "p256")]
#[derive(Clone)] kp_curve!(
pub struct IetfP256Hram; p256,
#[cfg(feature = "p256")] P256,
impl Hram<P256> for IetfP256Hram { IetfP256Hram,
#[allow(non_snake_case)] b"P-256",
fn hram(R: &p256::ProjectivePoint, A: &p256::ProjectivePoint, m: &[u8]) -> p256::Scalar { b"FROST-P256-SHA256-v5"
P256::hash_to_F( );
&[P256::CONTEXT, b"chal"].concat(),
&[&P256::G_to_bytes(R), &P256::G_to_bytes(A), m].concat() #[cfg(feature = "secp256k1")]
) kp_curve!(
} k256,
} Secp256k1,
NonIetfSecp256k1Hram,
b"secp256k1",
b"FROST-secp256k1-SHA256-v5"
);

View File

@@ -1,20 +1,20 @@
use rand::rngs::OsRng; use rand::rngs::OsRng;
#[cfg(feature = "k256")] #[cfg(feature = "secp256k1")]
use crate::tests::{curve::test_curve, schnorr::test_schnorr}; use crate::tests::{curve::test_curve, schnorr::test_schnorr};
#[cfg(feature = "k256")] #[cfg(feature = "secp256k1")]
use crate::curves::kp256::K256; use crate::curves::kp256::Secp256k1;
#[cfg(feature = "p256")] #[cfg(feature = "p256")]
use crate::tests::vectors::{Vectors, test_with_vectors}; use crate::tests::vectors::{Vectors, test_with_vectors};
#[cfg(feature = "p256")] #[cfg(feature = "p256")]
use crate::curves::kp256::{P256, IetfP256Hram}; use crate::curves::kp256::{P256, IetfP256Hram};
#[cfg(feature = "k256")] #[cfg(feature = "secp256k1")]
#[test] #[test]
fn k256_not_ietf() { fn secp256k1_non_ietf() {
test_curve::<_, K256>(&mut OsRng); test_curve::<_, Secp256k1>(&mut OsRng);
test_schnorr::<_, K256>(&mut OsRng); test_schnorr::<_, Secp256k1>(&mut OsRng);
} }
#[cfg(feature = "p256")] #[cfg(feature = "p256")]