#[cfg(feature = "decaf377")]
mod decaf377_suite {
use core::ops::{Add, Mul, Sub};
use decaf377::{Element, Encoding, Fr};
use frost_core::{Ciphersuite, Field, FieldError, Group, GroupError};
use rand_core::{CryptoRng, RngCore};
use sha2::{Digest, Sha512};
#[derive(Clone, Copy)]
pub struct Decaf377ScalarField;
impl Field for Decaf377ScalarField {
type Scalar = Fr;
type Serialization = [u8; 32];
fn zero() -> Self::Scalar {
Fr::ZERO
}
fn one() -> Self::Scalar {
Fr::ONE
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
scalar.inverse().ok_or(FieldError::InvalidZeroScalar)
}
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
let mut bytes = [0u8; 64];
rng.fill_bytes(&mut bytes);
Fr::from_le_bytes_mod_order(&bytes)
}
fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
Fr::to_bytes(scalar)
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
Fr::from_bytes_checked(buf).map_err(|_| FieldError::MalformedScalar)
}
fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
Self::serialize(scalar)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Decaf377Element(pub Element);
impl Eq for Decaf377Element {}
impl Decaf377Element {
#[inline]
pub fn into_inner(self) -> Element {
self.0
}
}
impl From<Element> for Decaf377Element {
#[inline]
fn from(e: Element) -> Self {
Self(e)
}
}
impl Add for Decaf377Element {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0)
}
}
impl Sub for Decaf377Element {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0)
}
}
impl Mul<Fr> for Decaf377Element {
type Output = Self;
#[inline]
fn mul(self, rhs: Fr) -> Self {
Self(self.0 * rhs)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Decaf377Group;
impl Group for Decaf377Group {
type Field = Decaf377ScalarField;
type Element = Decaf377Element;
type Serialization = [u8; 32];
fn cofactor() -> <Self::Field as Field>::Scalar {
Fr::ONE
}
fn identity() -> Self::Element {
Decaf377Element(Element::IDENTITY)
}
fn generator() -> Self::Element {
Decaf377Element(Element::GENERATOR)
}
fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
if *element == Self::identity() {
return Err(GroupError::InvalidIdentityElement);
}
Ok(element.0.vartime_compress().0)
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
let element = Decaf377Element(
Encoding(*buf)
.vartime_decompress()
.map_err(|_| GroupError::MalformedElement)?,
);
if element == Self::identity() {
Err(GroupError::InvalidIdentityElement)
} else {
Ok(element)
}
}
}
fn hash_to_array(inputs: &[&[u8]]) -> [u8; 64] {
let mut h = Sha512::new();
for i in inputs {
h.update(i);
}
let mut output = [0u8; 64];
output.copy_from_slice(h.finalize().as_ref());
output
}
fn hash_to_scalar(inputs: &[&[u8]]) -> Fr {
Fr::from_le_bytes_mod_order(&hash_to_array(inputs))
}
const CONTEXT_STRING: &str = "FROST-decaf377-SHA512-v1";
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Decaf377Sha512;
impl Ciphersuite for Decaf377Sha512 {
const ID: &'static str = CONTEXT_STRING;
type Group = Decaf377Group;
type HashOutput = [u8; 64];
type SignatureSerialization = [u8; 64];
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho", m])
}
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"chal", m])
}
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce", m])
}
fn H4(m: &[u8]) -> Self::HashOutput {
hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m])
}
fn H5(m: &[u8]) -> Self::HashOutput {
hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m])
}
fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg", m]))
}
fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
Some(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id", m]))
}
}
}
#[cfg(feature = "decaf377")]
pub use decaf377_suite::{
Decaf377Element, Decaf377Group, Decaf377ScalarField, Decaf377Sha512,
};
use alloc::vec::Vec;
use crate::curve::{CurvePoint, CurveScalar};
use crate::error::Error as FrostitoError;
use crate::lagrange::compute_lagrange_coefficients;
use crate::nested::InnerSigningParamsV2;
use frost_core::{
challenge, Field, Group, compute_binding_factor_list, compute_group_commitment, Ciphersuite as Cs,
Element as ZfElement, Identifier, Scalar as CScalar, SigningPackage, VerifyingKey,
};
pub fn identifier_to_index<C: Cs>(id: &Identifier<C>) -> Result<u32, FrostitoError> {
let b = id.serialize();
if b.len() < 4 {
return Err(FrostitoError::InvalidIndex);
}
let little = b[4..].iter().all(|x| *x == 0);
let big = b[..b.len() - 4].iter().all(|x| *x == 0);
let v = match (little, big) {
(true, _) => u32::from_le_bytes([b[0], b[1], b[2], b[3]]),
(false, true) => {
let t = &b[b.len() - 4..];
u32::from_be_bytes([t[0], t[1], t[2], t[3]])
}
(false, false) => return Err(FrostitoError::InvalidIndex),
};
if v == 0 {
return Err(FrostitoError::InvalidIndex);
}
Ok(v)
}
pub fn inner_params_from_zf<C>(
package: &SigningPackage<C>,
verifying_key: &VerifyingKey<C>,
nested_index: u32,
) -> Result<InnerSigningParamsV2<CScalar<C>>, FrostitoError>
where
C: Cs,
ZfElement<C>: CurvePoint<Scalar = CScalar<C>>,
CScalar<C>: CurveScalar,
{
let mut indices = Vec::with_capacity(package.signing_commitments().len());
for id in package.signing_commitments().keys() {
indices.push(identifier_to_index::<C>(id)?);
}
indices.sort_unstable();
let pos = indices
.iter()
.position(|&i| i == nested_index)
.ok_or(FrostitoError::InvalidIndex)?;
let lagrange = compute_lagrange_coefficients::<CScalar<C>>(&indices)?;
let nested_id: Identifier<C> = u16::try_from(nested_index)
.map_err(|_| FrostitoError::InvalidIndex)?
.try_into()
.map_err(|_| FrostitoError::InvalidIndex)?;
let factors = compute_binding_factor_list::<C>(package, verifying_key, &[])
.map_err(|_| FrostitoError::InvalidCommitment)?;
let rho_bytes = factors
.get(&nested_id)
.ok_or(FrostitoError::InvalidIndex)?
.serialize();
let rho_ser = <<<C as Cs>::Group as Group>::Field as Field>::Serialization::try_from(
&rho_bytes[..],
)
.map_err(|_| FrostitoError::InvalidResponse)?;
let rho = <<<C as Cs>::Group as Group>::Field as Field>::deserialize(&rho_ser)
.map_err(|_| FrostitoError::InvalidResponse)?;
let r = compute_group_commitment::<C>(package, &factors)
.map_err(|_| FrostitoError::InvalidCommitment)?;
let c = challenge::<C>(&r.to_element(), verifying_key, package.message())
.map_err(|_| FrostitoError::InvalidCommitment)?;
Ok(InnerSigningParamsV2::from_parts(
rho,
c.to_scalar(),
lagrange[pos],
))
}