Add support for Ristretto

Replaces P-256 as the curve used for testing FROST.
This commit is contained in:
Luke Parker 2022-06-06 04:22:49 -04:00
parent e0ce6e5c12
commit 301634dd8e
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
12 changed files with 477 additions and 412 deletions

View file

@ -11,7 +11,7 @@ use curve25519_dalek::{
use transcript::{Transcript as TranscriptTrait, DigestTranscript}; use transcript::{Transcript as TranscriptTrait, DigestTranscript};
use frost::Curve; use frost::Curve;
pub use frost::curves::ed25519::Ed25519; pub use frost::curves::dalek::Ed25519;
use dalek_ff_group as dfg; use dalek_ff_group as dfg;
use crate::random_scalar; use crate::random_scalar;

View file

@ -16,88 +16,123 @@ use dalek::{
traits::Identity, traits::Identity,
scalar::Scalar as DScalar, scalar::Scalar as DScalar,
edwards::{ edwards::{
EdwardsPoint as DPoint, EdwardsPoint as DEdwardsPoint,
EdwardsBasepointTable as DTable, EdwardsBasepointTable as DEdwardsBasepointTable,
CompressedEdwardsY as DCompressed CompressedEdwardsY as DCompressedEdwards
},
ristretto::{
RistrettoPoint as DRistrettoPoint,
RistrettoBasepointTable as DRistrettoBasepointTable,
CompressedRistretto as DCompressedRistretto
} }
}; };
use group::{ff::{Field, PrimeField}, Group}; use group::{ff::{Field, PrimeField}, Group};
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] macro_rules! deref_borrow {
pub struct Scalar(pub DScalar); ($Source: ident, $Target: ident) => {
impl Deref for $Source {
impl Deref for Scalar { type Target = $Target;
type Target = DScalar;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl Borrow<DScalar> for Scalar { impl Borrow<$Target> for $Source {
fn borrow(&self) -> &DScalar { fn borrow(&self) -> &$Target {
&self.0 &self.0
} }
} }
impl Borrow<DScalar> for &Scalar { impl Borrow<$Target> for &$Source {
fn borrow(&self) -> &DScalar { fn borrow(&self) -> &$Target {
&self.0 &self.0
} }
} }
impl Add<Scalar> for Scalar {
type Output = Self;
fn add(self, other: Scalar) -> Scalar { Self(self.0 + other.0) }
} }
impl AddAssign for Scalar {
fn add_assign(&mut self, other: Scalar) { self.0 += other.0 }
} }
impl<'a> Add<&'a Scalar> for Scalar { macro_rules! math {
($Value: ident, $Factor: ident, $Product: ident) => {
impl Add<$Value> for $Value {
type Output = Self; type Output = Self;
fn add(self, other: &'a Scalar) -> Scalar { Self(self.0 + other.0) } fn add(self, other: $Value) -> Self::Output { Self(self.0 + other.0) }
} }
impl<'a> AddAssign<&'a Scalar> for Scalar { impl AddAssign for $Value {
fn add_assign(&mut self, other: &'a Scalar) { self.0 += other.0 } fn add_assign(&mut self, other: $Value) { self.0 += other.0 }
} }
impl Sub<Scalar> for Scalar { impl<'a> Add<&'a $Value> for $Value {
type Output = Self; type Output = Self;
fn sub(self, other: Scalar) -> Scalar { Self(self.0 - other.0) } fn add(self, other: &'a $Value) -> Self::Output { Self(self.0 + other.0) }
} }
impl SubAssign for Scalar { impl<'a> AddAssign<&'a $Value> for $Value {
fn sub_assign(&mut self, other: Scalar) { self.0 -= other.0 } fn add_assign(&mut self, other: &'a $Value) { self.0 += other.0 }
} }
impl<'a> Sub<&'a Scalar> for Scalar { impl Sub<$Value> for $Value {
type Output = Self; type Output = Self;
fn sub(self, other: &'a Scalar) -> Scalar { Self(self.0 - other.0) } fn sub(self, other: $Value) -> Self::Output { Self(self.0 - other.0) }
} }
impl<'a> SubAssign<&'a Scalar> for Scalar { impl SubAssign for $Value {
fn sub_assign(&mut self, other: &'a Scalar) { self.0 -= other.0 } fn sub_assign(&mut self, other: $Value) { self.0 -= other.0 }
} }
impl Neg for Scalar { impl<'a> Sub<&'a $Value> for $Value {
type Output = Self; type Output = Self;
fn neg(self) -> Scalar { Self(-self.0) } fn sub(self, other: &'a $Value) -> Self::Output { Self(self.0 - other.0) }
}
impl<'a> SubAssign<&'a $Value> for $Value {
fn sub_assign(&mut self, other: &'a $Value) { self.0 -= other.0 }
} }
impl Mul<Scalar> for Scalar { impl Neg for $Value {
type Output = Self; type Output = Self;
fn mul(self, other: Scalar) -> Scalar { Self(self.0 * other.0) } fn neg(self) -> Self::Output { Self(-self.0) }
}
impl MulAssign for Scalar {
fn mul_assign(&mut self, other: Scalar) { self.0 *= other.0 }
} }
impl<'a> Mul<&'a Scalar> for Scalar { impl Mul<$Factor> for $Value {
type Output = Self; type Output = $Product;
fn mul(self, other: &'a Scalar) -> Scalar { Self(self.0 * other.0) } fn mul(self, other: $Factor) -> Self::Output { Self(self.0 * other.0) }
}
impl MulAssign<$Factor> for $Value {
fn mul_assign(&mut self, other: $Factor) { self.0 *= other.0 }
}
impl<'a> Mul<&'a $Factor> for $Value {
type Output = Self;
fn mul(self, b: &'a $Factor) -> $Product { Self(b.0 * self.0) }
}
impl<'a> MulAssign<&'a $Factor> for $Value {
fn mul_assign(&mut self, other: &'a $Factor) { self.0 *= other.0 }
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Scalar(pub DScalar);
deref_borrow!(Scalar, DScalar);
math!(Scalar, Scalar, Scalar);
impl Scalar {
pub fn from_canonical_bytes(bytes: [u8; 32]) -> Option<Scalar> {
DScalar::from_canonical_bytes(bytes).map(|x| Self(x))
}
pub fn from_bytes_mod_order(bytes: [u8; 32]) -> Scalar {
Self(DScalar::from_bytes_mod_order(bytes))
}
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
Self(DScalar::from_bytes_mod_order_wide(bytes))
}
pub fn from_hash<D: Digest<OutputSize = U64>>(hash: D) -> Scalar {
let mut output = [0u8; 64];
output.copy_from_slice(&hash.finalize());
Scalar(DScalar::from_bytes_mod_order_wide(&output))
} }
impl<'a> MulAssign<&'a Scalar> for Scalar {
fn mul_assign(&mut self, other: &'a Scalar) { self.0 *= other.0 }
} }
impl ConstantTimeEq for Scalar { impl ConstantTimeEq for Scalar {
@ -153,126 +188,52 @@ impl PrimeField for Scalar {
fn root_of_unity() -> Self { unimplemented!() } fn root_of_unity() -> Self { unimplemented!() }
} }
impl Scalar { macro_rules! dalek_group {
pub fn from_hash<D: Digest<OutputSize = U64>>(hash: D) -> Scalar { (
let mut output = [0u8; 64]; $Point: ident,
output.copy_from_slice(&hash.finalize()); $DPoint: ident,
Scalar(DScalar::from_bytes_mod_order_wide(&output))
}
}
$Table: ident,
$DTable: ident,
$Compressed: ident,
$DCompressed: ident,
$BASEPOINT_POINT: ident,
$BASEPOINT_TABLE: ident
) => {
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct EdwardsPoint(pub DPoint); pub struct $Point(pub $DPoint);
pub const ED25519_BASEPOINT_POINT: EdwardsPoint = EdwardsPoint(constants::ED25519_BASEPOINT_POINT); deref_borrow!($Point, $DPoint);
math!($Point, Scalar, $Point);
impl Deref for EdwardsPoint { pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT);
type Target = DPoint;
fn deref(&self) -> &Self::Target { impl Sum<$Point> for $Point {
&self.0 fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point { Self($DPoint::sum(iter)) }
} }
impl<'a> Sum<&'a $Point> for $Point {
fn sum<I: Iterator<Item = &'a $Point>>(iter: I) -> $Point { Self($DPoint::sum(iter)) }
} }
impl Borrow<DPoint> for EdwardsPoint { impl Group for $Point {
fn borrow(&self) -> &DPoint {
&self.0
}
}
impl Borrow<DPoint> for &EdwardsPoint {
fn borrow(&self) -> &DPoint {
&self.0
}
}
impl Add<EdwardsPoint> for EdwardsPoint {
type Output = Self;
fn add(self, b: EdwardsPoint) -> EdwardsPoint { Self(self.0 + b.0) }
}
impl AddAssign<EdwardsPoint> for EdwardsPoint {
fn add_assign(&mut self, other: EdwardsPoint) { self.0 += other.0 }
}
impl Sum<EdwardsPoint> for EdwardsPoint {
fn sum<I: Iterator<Item = EdwardsPoint>>(iter: I) -> EdwardsPoint { Self(DPoint::sum(iter)) }
}
impl<'a> Add<&'a EdwardsPoint> for EdwardsPoint {
type Output = Self;
fn add(self, b: &'a EdwardsPoint) -> EdwardsPoint { Self(self.0 + b.0) }
}
impl<'a> AddAssign<&'a EdwardsPoint> for EdwardsPoint {
fn add_assign(&mut self, other: &'a EdwardsPoint) { self.0 += other.0 }
}
impl<'a> Sum<&'a EdwardsPoint> for EdwardsPoint {
fn sum<I: Iterator<Item = &'a EdwardsPoint>>(iter: I) -> EdwardsPoint { Self(DPoint::sum(iter)) }
}
impl Sub<EdwardsPoint> for EdwardsPoint {
type Output = Self;
fn sub(self, b: EdwardsPoint) -> EdwardsPoint { Self(self.0 - b.0) }
}
impl SubAssign<EdwardsPoint> for EdwardsPoint {
fn sub_assign(&mut self, other: EdwardsPoint) { self.0 -= other.0 }
}
impl<'a> Sub<&'a EdwardsPoint> for EdwardsPoint {
type Output = Self;
fn sub(self, b: &'a EdwardsPoint) -> EdwardsPoint { Self(self.0 - b.0) }
}
impl<'a> SubAssign<&'a EdwardsPoint> for EdwardsPoint {
fn sub_assign(&mut self, other: &'a EdwardsPoint) { self.0 -= other.0 }
}
impl Neg for EdwardsPoint {
type Output = Self;
fn neg(self) -> EdwardsPoint { Self(-self.0) }
}
impl Mul<Scalar> for EdwardsPoint {
type Output = Self;
fn mul(self, b: Scalar) -> EdwardsPoint { Self(b.0 * self.0) }
}
impl MulAssign<Scalar> for EdwardsPoint {
fn mul_assign(&mut self, other: Scalar) { self.0 *= other.0 }
}
impl<'a> Mul<&'a Scalar> for EdwardsPoint {
type Output = Self;
fn mul(self, b: &'a Scalar) -> EdwardsPoint { Self(b.0 * self.0) }
}
impl<'a> MulAssign<&'a Scalar> for EdwardsPoint {
fn mul_assign(&mut self, other: &'a Scalar) { self.0 *= other.0 }
}
impl Group for EdwardsPoint {
type Scalar = Scalar; type Scalar = Scalar;
fn random(rng: impl RngCore) -> Self { &ED25519_BASEPOINT_TABLE * Scalar::random(rng) } fn random(rng: impl RngCore) -> Self { &$BASEPOINT_TABLE * Scalar::random(rng) }
fn identity() -> Self { Self(DPoint::identity()) } fn identity() -> Self { Self($DPoint::identity()) }
fn generator() -> Self { ED25519_BASEPOINT_POINT } fn generator() -> Self { $BASEPOINT_POINT }
fn is_identity(&self) -> Choice { self.0.ct_eq(&DPoint::identity()) } fn is_identity(&self) -> Choice { self.0.ct_eq(&$DPoint::identity()) }
fn double(&self) -> Self { *self + self } fn double(&self) -> Self { *self + self }
} }
impl Scalar { pub struct $Compressed(pub $DCompressed);
pub fn from_canonical_bytes(bytes: [u8; 32]) -> Option<Scalar> { deref_borrow!($Compressed, $DCompressed);
DScalar::from_canonical_bytes(bytes).map(|x| Self(x)) impl $Compressed {
} pub fn new(y: [u8; 32]) -> $Compressed {
pub fn from_bytes_mod_order(bytes: [u8; 32]) -> Scalar { Self($DCompressed(y))
Self(DScalar::from_bytes_mod_order(bytes))
}
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
Self(DScalar::from_bytes_mod_order_wide(bytes))
}
} }
pub struct CompressedEdwardsY(pub DCompressed); pub fn decompress(&self) -> Option<$Point> {
impl CompressedEdwardsY { self.0.decompress().map(|x| $Point(x))
pub fn new(y: [u8; 32]) -> CompressedEdwardsY {
Self(DCompressed(y))
}
pub fn decompress(&self) -> Option<EdwardsPoint> {
self.0.decompress().map(|x| EdwardsPoint(x))
} }
pub fn to_bytes(&self) -> [u8; 32] { pub fn to_bytes(&self) -> [u8; 32] {
@ -280,36 +241,53 @@ impl CompressedEdwardsY {
} }
} }
impl $Point {
pub fn compress(&self) -> $Compressed {
$Compressed(self.0.compress())
}
}
pub struct $Table(pub $DTable);
deref_borrow!($Table, $DTable);
pub const $BASEPOINT_TABLE: $Table = $Table(constants::$BASEPOINT_TABLE);
impl Mul<Scalar> for &$Table {
type Output = $Point;
fn mul(self, b: Scalar) -> $Point { $Point(&b.0 * &self.0) }
}
};
}
dalek_group!(
EdwardsPoint,
DEdwardsPoint,
EdwardsBasepointTable,
DEdwardsBasepointTable,
CompressedEdwardsY,
DCompressedEdwards,
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_TABLE
);
impl EdwardsPoint { impl EdwardsPoint {
pub fn is_torsion_free(&self) -> bool { pub fn is_torsion_free(&self) -> bool {
self.0.is_torsion_free() self.0.is_torsion_free()
} }
pub fn compress(&self) -> CompressedEdwardsY {
CompressedEdwardsY(self.0.compress())
}
} }
pub struct EdwardsBasepointTable(pub DTable); dalek_group!(
pub const ED25519_BASEPOINT_TABLE: EdwardsBasepointTable = EdwardsBasepointTable( RistrettoPoint,
constants::ED25519_BASEPOINT_TABLE DRistrettoPoint,
RistrettoBasepointTable,
DRistrettoBasepointTable,
CompressedRistretto,
DCompressedRistretto,
RISTRETTO_BASEPOINT_POINT,
RISTRETTO_BASEPOINT_TABLE
); );
impl Deref for EdwardsBasepointTable {
type Target = DTable;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<DTable> for &EdwardsBasepointTable {
fn borrow(&self) -> &DTable {
&self.0
}
}
impl Mul<Scalar> for &EdwardsBasepointTable {
type Output = EdwardsPoint;
fn mul(self, b: Scalar) -> EdwardsPoint { EdwardsPoint(&b.0 * &self.0) }
}

View file

@ -30,12 +30,13 @@ multiexp = { path = "../multiexp", features = ["batch"] }
rand = "0.8" rand = "0.8"
sha2 = "0.10" sha2 = "0.10"
elliptic-curve = { version = "0.12", features = ["hash2curve"] } dalek-ff-group = { path = "../dalek-ff-group" }
p256 = { version = "0.11", features = ["arithmetic", "hash2curve"] }
[features] [features]
curves = [] curves = ["sha2"] # All officially denoted curves use the SHA2 family of hashes
kp256 = ["elliptic-curve"] kp256 = ["elliptic-curve", "curves"]
p256 = ["curves", "kp256", "sha2", "dep:p256"] p256 = ["dep:p256", "kp256"]
k256 = ["curves", "kp256", "sha2", "dep:k256"] k256 = ["dep:k256", "kp256"]
ed25519 = ["curves", "sha2", "dalek-ff-group"] dalek = ["curves", "dalek-ff-group"]
ed25519 = ["dalek"]
ristretto = ["dalek"]

View file

@ -0,0 +1,163 @@
use core::convert::TryInto;
use rand_core::{RngCore, CryptoRng};
use sha2::{Digest, Sha512};
use ff::PrimeField;
use group::Group;
use dalek_ff_group::Scalar;
use crate::{CurveError, 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};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct $Curve;
impl Curve for $Curve {
type F = Scalar;
type G = $Point;
type T = &'static $Table;
const ID: &'static [u8] = $ID;
const GENERATOR: Self::G = $POINT;
const GENERATOR_TABLE: Self::T = &$TABLE;
const LITTLE_ENDIAN: bool = true;
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed);
seed.extend(&secret.to_bytes());
Self::hash_to_F(b"nonce", &seed)
}
fn hash_msg(msg: &[u8]) -> Vec<u8> {
Sha512::new()
.chain_update($CONTEXT)
.chain_update($digest)
.chain_update(msg)
.finalize()
.to_vec()
}
fn hash_binding_factor(binding: &[u8]) -> Self::F {
Self::hash_to_F(b"rho", binding)
}
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
Scalar::from_hash(Sha512::new().chain_update($CONTEXT).chain_update(dst).chain_update(msg))
}
fn F_len() -> usize {
32
}
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 scalar.is_some().unwrap_u8() == 0 {
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();
if let Some(point) = point {
// 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)
} else {
Err(CurveError::InvalidPoint)
}
}
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)]
pub struct $Hram;
impl Hram<$Curve> for $Hram {
#[allow(non_snake_case)]
fn hram(R: &$Point, A: &$Point, m: &[u8]) -> Scalar {
$Curve::hash_to_F($chal, &[&R.compress().to_bytes(), &A.compress().to_bytes(), m].concat())
}
}
}
}
#[cfg(feature = "ed25519")]
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"",
b"",
);
#[cfg(any(test, feature = "ristretto"))]
dalek_curve!(
Ristretto,
IetfRistrettoHram,
RistrettoPoint,
CompressedRistretto,
RistrettoBasepointTable,
RISTRETTO_BASEPOINT_POINT,
RISTRETTO_BASEPOINT_TABLE,
|_| false,
b"ristretto",
b"FROST-RISTRETTO255-SHA512-v5",
b"chal",
b"digest",
);

View file

@ -1,104 +0,0 @@
use core::convert::TryInto;
use rand_core::{RngCore, CryptoRng};
use sha2::{Digest, Sha512};
use ff::PrimeField;
use group::Group;
use dalek_ff_group::{
EdwardsBasepointTable,
ED25519_BASEPOINT_POINT, ED25519_BASEPOINT_TABLE,
Scalar, EdwardsPoint, CompressedEdwardsY
};
use crate::{CurveError, Curve, algorithm::Hram};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Ed25519;
impl Curve for Ed25519 {
type F = Scalar;
type G = EdwardsPoint;
type T = &'static EdwardsBasepointTable;
const ID: &'static [u8] = b"edwards25519";
const GENERATOR: Self::G = ED25519_BASEPOINT_POINT;
const GENERATOR_TABLE: Self::T = &ED25519_BASEPOINT_TABLE;
const LITTLE_ENDIAN: bool = true;
fn random_nonce<R: RngCore + CryptoRng>(secret: Self::F, rng: &mut R) -> Self::F {
let mut seed = vec![0; 32];
rng.fill_bytes(&mut seed);
seed.extend(&secret.to_bytes());
Self::hash_to_F(b"nonce", &seed)
}
fn hash_msg(msg: &[u8]) -> Vec<u8> {
Sha512::digest(msg).to_vec()
}
fn hash_binding_factor(binding: &[u8]) -> Self::F {
Self::hash_to_F(b"rho", binding)
}
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
Scalar::from_hash(Sha512::new().chain_update(dst).chain_update(msg))
}
fn F_len() -> usize {
32
}
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 scalar.is_some().unwrap_u8() == 0 {
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 = CompressedEdwardsY::new(bytes).decompress();
if let Some(point) = point {
// Ban identity and torsioned points
if point.is_identity().into() || (!bool::from(point.is_torsion_free())) {
Err(CurveError::InvalidPoint)?;
}
// Ban points which weren't canonically encoded
if point.compress().to_bytes() != bytes {
Err(CurveError::InvalidPoint)?;
}
Ok(point)
} else {
Err(CurveError::InvalidPoint)
}
}
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)]
pub struct IetfEd25519Hram;
impl Hram<Ed25519> for IetfEd25519Hram {
#[allow(non_snake_case)]
fn hram(R: &EdwardsPoint, A: &EdwardsPoint, m: &[u8]) -> Scalar {
Ed25519::hash_to_F(b"", &[&R.compress().to_bytes(), &A.compress().to_bytes(), m].concat())
}
}

View file

@ -10,7 +10,7 @@ use group::{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::{CurveError, Curve};
#[cfg(any(test, feature = "p256"))] #[cfg(feature = "p256")]
use crate::algorithm::Hram; use crate::algorithm::Hram;
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -25,9 +25,9 @@ pub(crate) trait KP256Instance<G> {
const GENERATOR: G; const GENERATOR: G;
} }
#[cfg(any(test, feature = "p256"))] #[cfg(feature = "p256")]
pub type P256 = KP256<p256::ProjectivePoint>; pub type P256 = KP256<p256::ProjectivePoint>;
#[cfg(any(test, feature = "p256"))] #[cfg(feature = "p256")]
impl KP256Instance<p256::ProjectivePoint> for P256 { impl KP256Instance<p256::ProjectivePoint> for P256 {
const CONTEXT: &'static [u8] = b"FROST-P256-SHA256-v5"; const CONTEXT: &'static [u8] = b"FROST-P256-SHA256-v5";
const ID: &'static [u8] = b"P-256"; const ID: &'static [u8] = b"P-256";
@ -139,10 +139,10 @@ impl<G: Group + GroupEncoding> Curve for KP256<G> where
} }
} }
#[cfg(any(test, feature = "p256"))] #[cfg(feature = "p256")]
#[derive(Clone)] #[derive(Clone)]
pub struct IetfP256Hram; pub struct IetfP256Hram;
#[cfg(any(test, feature = "p256"))] #[cfg(feature = "p256")]
impl Hram<P256> for IetfP256Hram { impl Hram<P256> for IetfP256Hram {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hram(R: &p256::ProjectivePoint, A: &p256::ProjectivePoint, m: &[u8]) -> p256::Scalar { fn hram(R: &p256::ProjectivePoint, A: &p256::ProjectivePoint, m: &[u8]) -> p256::Scalar {

View file

@ -1,5 +1,5 @@
#[cfg(any(test, feature = "kp256"))] #[cfg(any(test, feature = "dalek"))]
pub mod kp256; pub mod dalek;
#[cfg(feature = "ed25519")] #[cfg(feature = "kp256")]
pub mod ed25519; pub mod kp256;

View file

@ -0,0 +1,77 @@
use rand::rngs::OsRng;
use crate::{curves::dalek, tests::vectors::{Vectors, test_with_vectors}};
#[cfg(any(test, feature = "ristretto"))]
#[test]
fn ristretto_vectors() {
test_with_vectors::<_, dalek::Ristretto, dalek::IetfRistrettoHram>(
&mut OsRng,
Vectors {
threshold: 2,
shares: &[
"5c3430d391552f6e60ecdc093ff9f6f4488756aa6cebdbad75a768010b8f830e",
"b06fc5eac20b4f6e1b271d9df2343d843e1e1fb03c4cbb673f2872d459ce6f01",
"f17e505f0e2581c6acfe54d3846a622834b5e7b50cad9a2109a97ba7a80d5c04"
],
group_secret: "1b25a55e463cfd15cf14a5d3acc3d15053f08da49c8afcf3ab265f2ebc4f970b",
group_key: "e2a62f39eede11269e3bd5a7d97554f5ca384f9f6d3dd9c3c0d05083c7254f57",
msg: "74657374",
included: &[1, 3],
nonces: &[
[
"b358743151e33d84bf00c12f71808f4103957c3e2cabab7b895c436b5e70f90c",
"7bd112153b9ae1ab9b31f5e78f61f5c4ca9ee67b7ea6d1181799c409d14c350c"
],
[
"22acad88478e0d0373a991092a322ebd1b9a2dad90451a976d0db3215426af0e",
"9155e3d7bcf7cd468b980c7e20b2c77cbdfbe33a1dcae031fd8bc6b1403f4b04"
]
],
sig_shares: &[
"ff801b4e0839faa67f16dee4127b9f7fbcf5fd007900257b0e2bbc02cbe5e709",
"afdf5481023c855bf3411a5c8a5fafa92357296a078c3b80dc168f294cb4f504"
],
sig: "deae61af10e8ee48ba492573592fba547f5debeff6bd6e2024e8673584746f5e".to_owned() +
"ae6070cf0a757f027358f8409dda4e29e04c276b808c60fbea414b2c179add0e"
}
);
}
#[cfg(feature = "ed25519")]
#[test]
fn ed25519_vectors() {
test_with_vectors::<_, dalek::Ed25519, dalek::IetfEd25519Hram>(
&mut OsRng,
Vectors {
threshold: 2,
shares: &[
"929dcc590407aae7d388761cddb0c0db6f5627aea8e217f4a033f2ec83d93509",
"a91e66e012e4364ac9aaa405fcafd370402d9859f7b6685c07eed76bf409e80d",
"d3cb090a075eb154e82fdb4b3cb507f110040905468bb9c46da8bdea643a9a02"
],
group_secret: "7b1c33d3f5291d85de664833beb1ad469f7fb6025a0ec78b3a790c6e13a98304",
group_key: "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673",
msg: "74657374",
included: &[1, 3],
nonces: &[
[
"8c76af04340e83bb5fc427c117d38347fc8ef86d5397feea9aa6412d96c05b0a",
"14a37ddbeae8d9e9687369e5eb3c6d54f03dc19d76bb54fb5425131bc37a600b"
],
[
"5ca39ebab6874f5e7b5089f3521819a2aa1e2cf738bae6974ee80555de2ef70e",
"0afe3650c4815ff37becd3c6948066e906e929ea9b8f546c74e10002dbcc150c"
]
],
sig_shares: &[
"4369474a398aa10357b60d683da91ea6a767dcf53fd541a8ed6b4d780827ea0a",
"32fcc690d926075e45d2dfb746bab71447943cddbefe80d122c39174aa2e1004"
],
sig: "2b8d9c6995333c5990e3a3dd6568785539d3322f7f0376452487ea35cfda587b".to_owned() +
"75650edb12b1a8619c88ed1f8463d6baeefb18d3fed3c279102fdfecb255fa0e"
}
);
}

View file

@ -1,51 +0,0 @@
use rand::rngs::OsRng;
use crate::{
curves::ed25519::{Ed25519, IetfEd25519Hram},
tests::{curve::test_curve, schnorr::test_schnorr, vectors::{Vectors, vectors}}
};
#[test]
fn ed25519_curve() {
test_curve::<_, Ed25519>(&mut OsRng);
}
#[test]
fn ed25519_schnorr() {
test_schnorr::<_, Ed25519>(&mut OsRng);
}
#[test]
fn ed25519_vectors() {
vectors::<Ed25519, IetfEd25519Hram>(
Vectors {
threshold: 2,
shares: &[
"929dcc590407aae7d388761cddb0c0db6f5627aea8e217f4a033f2ec83d93509",
"a91e66e012e4364ac9aaa405fcafd370402d9859f7b6685c07eed76bf409e80d",
"d3cb090a075eb154e82fdb4b3cb507f110040905468bb9c46da8bdea643a9a02"
],
group_secret: "7b1c33d3f5291d85de664833beb1ad469f7fb6025a0ec78b3a790c6e13a98304",
group_key: "15d21ccd7ee42959562fc8aa63224c8851fb3ec85a3faf66040d380fb9738673",
msg: "74657374",
included: &[1, 3],
nonces: &[
[
"8c76af04340e83bb5fc427c117d38347fc8ef86d5397feea9aa6412d96c05b0a",
"14a37ddbeae8d9e9687369e5eb3c6d54f03dc19d76bb54fb5425131bc37a600b"
],
[
"5ca39ebab6874f5e7b5089f3521819a2aa1e2cf738bae6974ee80555de2ef70e",
"0afe3650c4815ff37becd3c6948066e906e929ea9b8f546c74e10002dbcc150c"
]
],
sig_shares: &[
"4369474a398aa10357b60d683da91ea6a767dcf53fd541a8ed6b4d780827ea0a",
"32fcc690d926075e45d2dfb746bab71447943cddbefe80d122c39174aa2e1004"
],
sig: "2b8d9c6995333c5990e3a3dd6568785539d3322f7f0376452487ea35cfda587b".to_owned() +
"75650edb12b1a8619c88ed1f8463d6baeefb18d3fed3c279102fdfecb255fa0e"
}
);
}

View file

@ -1,26 +1,27 @@
use rand::rngs::OsRng; use rand::rngs::OsRng;
use crate::{ #[cfg(feature = "k256")]
curves::kp256::{P256, IetfP256Hram}, use crate::tests::{curve::test_curve, schnorr::test_schnorr};
tests::{curve::test_curve, schnorr::test_schnorr, vectors::{Vectors, vectors}}
};
#[cfg(feature = "k256")] #[cfg(feature = "k256")]
use crate::curves::kp256::K256; use crate::curves::kp256::K256;
#[cfg(feature = "p256")]
use crate::tests::vectors::{Vectors, test_with_vectors};
#[cfg(feature = "p256")]
use crate::curves::kp256::{P256, IetfP256Hram};
#[cfg(feature = "k256")]
#[test] #[test]
fn p256_curve() { fn k256_not_ietf() {
test_curve::<_, P256>(&mut OsRng); test_curve::<_, K256>(&mut OsRng);
} test_schnorr::<_, K256>(&mut OsRng);
#[test]
fn p256_schnorr() {
test_schnorr::<_, P256>(&mut OsRng);
} }
#[cfg(feature = "p256")]
#[test] #[test]
fn p256_vectors() { fn p256_vectors() {
vectors::<P256, IetfP256Hram>( test_with_vectors::<_, P256, IetfP256Hram>(
&mut OsRng,
Vectors { Vectors {
threshold: 2, threshold: 2,
shares: &[ shares: &[
@ -52,15 +53,3 @@ fn p256_vectors() {
} }
); );
} }
#[cfg(feature = "k256")]
#[test]
fn k256_curve() {
test_curve::<_, K256>(&mut OsRng);
}
#[cfg(feature = "k256")]
#[test]
fn k256_schnorr() {
test_schnorr::<_, K256>(&mut OsRng);
}

View file

@ -1,3 +1,4 @@
#[cfg(any(test, feature = "dalek"))]
mod dalek;
#[cfg(feature = "kp256")]
mod kp256; mod kp256;
#[cfg(feature = "ed25519")]
mod ed25519;

View file

@ -1,10 +1,12 @@
use std::{sync::Arc, collections::HashMap}; use std::{sync::Arc, collections::HashMap};
use rand_core::{RngCore, CryptoRng};
use crate::{ use crate::{
Curve, MultisigKeys, Curve, MultisigKeys,
algorithm::{Schnorr, Hram}, algorithm::{Schnorr, Hram},
sign::{PreprocessPackage, StateMachine, AlgorithmMachine}, sign::{PreprocessPackage, StateMachine, AlgorithmMachine},
tests::recover tests::{curve::test_curve, schnorr::test_schnorr, recover}
}; };
pub struct Vectors { pub struct Vectors {
@ -55,7 +57,16 @@ fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<u16, Multisi
keys keys
} }
pub fn vectors<C: Curve, H: Hram<C>>(vectors: Vectors) { pub fn test_with_vectors<
R: RngCore + CryptoRng,
C: Curve,
H: Hram<C>
>(rng: &mut R, vectors: Vectors) {
// Do basic tests before trying the vectors
test_curve::<_, C>(&mut *rng);
test_schnorr::<_, C>(rng);
// Test against the vectors
let keys = vectors_to_multisig_keys::<C>(&vectors); let keys = vectors_to_multisig_keys::<C>(&vectors);
let group_key = C::G_from_slice(&hex::decode(vectors.group_key).unwrap()).unwrap(); let group_key = C::G_from_slice(&hex::decode(vectors.group_key).unwrap()).unwrap();
assert_eq!( assert_eq!(