#![doc = include_str!("redpallas/README.md")]
#![allow(non_snake_case)]
#![deny(missing_docs)]
use alloc::collections::BTreeMap;
use frost_rerandomized::RandomizedCiphersuite;
use group::GroupEncoding;
#[cfg(feature = "alloc")]
use group::{ff::Field as FFField, ff::PrimeField, Group as FFGroup};
use pasta_curves::pallas;
pub mod rerandomized;
#[cfg(feature = "serde")]
pub use frost_rerandomized::frost_core::serde;
pub use frost_rerandomized::frost_core::{
self as frost, Ciphersuite, Field, FieldError, Group, GroupError,
};
pub use rand_core;
use rand_core::{CryptoRng, RngCore};
use crate::{frost::redpallas::keys::EvenY, hash::HStar, orchard, private::Sealed};
pub type Error = frost_rerandomized::frost_core::Error<PallasBlake2b512>;
#[derive(Clone, Copy)]
pub struct PallasScalarField;
impl Field for PallasScalarField {
type Scalar = pallas::Scalar;
type Serialization = [u8; 32];
fn zero() -> Self::Scalar {
Self::Scalar::zero()
}
fn one() -> Self::Scalar {
Self::Scalar::one()
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
if *scalar == <Self as Field>::zero() {
Err(FieldError::InvalidZeroScalar)
} else {
Ok(Self::Scalar::invert(scalar).unwrap())
}
}
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Self::Scalar::random(rng)
}
fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
scalar.to_repr()
}
fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
Self::serialize(scalar)
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
match pallas::Scalar::from_repr(*buf).into() {
Some(s) => Ok(s),
None => Err(FieldError::MalformedScalar),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PallasGroup;
impl Group for PallasGroup {
type Field = PallasScalarField;
type Element = pallas::Point;
type Serialization = [u8; 32];
fn cofactor() -> <Self::Field as Field>::Scalar {
Self::Field::one()
}
fn identity() -> Self::Element {
Self::Element::identity()
}
fn generator() -> Self::Element {
orchard::SpendAuth::basepoint()
}
fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
if *element == Self::identity() {
return Err(GroupError::InvalidIdentityElement);
}
Ok(element.to_bytes())
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
let point = Self::Element::from_bytes(buf);
match Option::<Self::Element>::from(point) {
Some(point) => {
if point == Self::identity() {
Err(GroupError::InvalidIdentityElement)
} else {
Ok(point)
}
}
None => Err(GroupError::MalformedElement),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "self::serde"))]
pub struct PallasBlake2b512;
impl Ciphersuite for PallasBlake2b512 {
const ID: &'static str = "FROST(Pallas, BLAKE2b-512)";
type Group = PallasGroup;
type HashOutput = [u8; 64];
type SignatureSerialization = [u8; 64];
fn H1(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
HStar::<orchard::SpendAuth>::new(b"FROST_RedPallasR")
.update(m)
.finalize()
}
fn H2(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
HStar::<orchard::SpendAuth>::default().update(m).finalize()
}
fn H3(m: &[u8]) -> <<Self::Group as Group>::Field as Field>::Scalar {
HStar::<orchard::SpendAuth>::new(b"FROST_RedPallasN")
.update(m)
.finalize()
}
fn H4(m: &[u8]) -> Self::HashOutput {
let mut state = blake2b_simd::Params::new()
.hash_length(64)
.personal(b"FROST_RedPallasM")
.to_state();
*state.update(m).finalize().as_array()
}
fn H5(m: &[u8]) -> Self::HashOutput {
let mut state = blake2b_simd::Params::new()
.hash_length(64)
.personal(b"FROST_RedPallasC")
.to_state();
*state.update(m).finalize().as_array()
}
fn HDKG(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
Some(
HStar::<orchard::SpendAuth>::new(b"FROST_RedPallasD")
.update(m)
.finalize(),
)
}
fn HID(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
Some(
HStar::<orchard::SpendAuth>::new(b"FROST_RedPallasI")
.update(m)
.finalize(),
)
}
fn post_generate(
secret_shares: BTreeMap<frost::Identifier<Self>, frost::keys::SecretShare<Self>>,
public_key_package: frost::keys::PublicKeyPackage<Self>,
) -> Result<
(
BTreeMap<frost::Identifier<Self>, frost::keys::SecretShare<Self>>,
frost::keys::PublicKeyPackage<Self>,
),
frost::Error<Self>,
> {
Ok(keys::into_even_y((secret_shares, public_key_package)))
}
fn post_dkg(
key_package: frost::keys::KeyPackage<Self>,
public_key_package: frost::keys::PublicKeyPackage<Self>,
) -> Result<
(
frost::keys::KeyPackage<Self>,
frost::keys::PublicKeyPackage<Self>,
),
frost::Error<Self>,
> {
let is_even = public_key_package.has_even_y();
Ok((
key_package.into_even_y(Some(is_even)),
public_key_package.into_even_y(Some(is_even)),
))
}
}
impl RandomizedCiphersuite for PallasBlake2b512 {
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
Some(
HStar::<orchard::SpendAuth>::new(b"FROST_RedPallasA")
.update(m)
.finalize(),
)
}
}
type P = PallasBlake2b512;
pub type Identifier = frost::Identifier<P>;
pub mod keys {
use alloc::{collections::BTreeMap, vec::Vec};
use super::*;
pub type IdentifierList<'a> = frost::keys::IdentifierList<'a, P>;
pub fn generate_with_dealer<RNG: RngCore + CryptoRng>(
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
mut rng: RNG,
) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::generate_with_dealer(max_signers, min_signers, identifiers, &mut rng)
}
pub fn split<R: RngCore + CryptoRng>(
key: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(key, max_signers, min_signers, identifiers, rng)
}
pub type SecretShare = frost::keys::SecretShare<P>;
pub type SigningShare = frost::keys::SigningShare<P>;
pub type VerifyingShare = frost::keys::VerifyingShare<P>;
pub type KeyPackage = frost::keys::KeyPackage<P>;
pub type PublicKeyPackage = frost::keys::PublicKeyPackage<P>;
pub type VerifiableSecretSharingCommitment = frost::keys::VerifiableSecretSharingCommitment<P>;
pub trait EvenY {
fn has_even_y(&self) -> bool;
fn into_even_y(self, is_even: Option<bool>) -> Self;
}
impl EvenY for PublicKeyPackage {
fn has_even_y(&self) -> bool {
let verifying_key = self.verifying_key();
match verifying_key.serialize() {
Ok(verifying_key_serialized) => verifying_key_serialized[31] & 0x80 == 0,
Err(_) => true,
}
}
fn into_even_y(self, is_even: Option<bool>) -> Self {
let is_even = is_even.unwrap_or_else(|| self.has_even_y());
if !is_even {
let verifying_key = VerifyingKey::new(-self.verifying_key().to_element());
let verifying_shares: BTreeMap<_, _> = self
.verifying_shares()
.iter()
.map(|(i, vs)| {
let vs = VerifyingShare::new(-vs.to_element());
(*i, vs)
})
.collect();
PublicKeyPackage::new(verifying_shares, verifying_key, self.min_signers())
} else {
self
}
}
}
impl EvenY for SecretShare {
fn has_even_y(&self) -> bool {
let key_package: KeyPackage = self
.clone()
.try_into()
.expect("Should work; expected to be called in freshly generated SecretShares");
key_package.has_even_y()
}
fn into_even_y(self, is_even: Option<bool>) -> Self {
let is_even = is_even.unwrap_or_else(|| self.has_even_y());
if !is_even {
let signing_share = SigningShare::new(-self.signing_share().to_scalar());
let coefficients: Vec<_> = self
.commitment()
.coefficients()
.iter()
.map(|e| {
<PallasBlake2b512 as Ciphersuite>::Group::serialize(&-e.value())
.expect("none of the coefficient commitments are the identity")
})
.collect();
let commitments = VerifiableSecretSharingCommitment::deserialize(coefficients)
.expect("Should work since they were just serialized");
SecretShare::new(*self.identifier(), signing_share, commitments)
} else {
self
}
}
}
impl EvenY for KeyPackage {
fn has_even_y(&self) -> bool {
let pubkey = self.verifying_key();
match pubkey.serialize() {
Ok(pubkey_serialized) => pubkey_serialized[31] & 0x80 == 0,
Err(_) => true,
}
}
fn into_even_y(self, is_even: Option<bool>) -> Self {
let is_even = is_even.unwrap_or_else(|| self.has_even_y());
if !is_even {
let verifying_key = VerifyingKey::new(-self.verifying_key().to_element());
let signing_share = SigningShare::new(-self.signing_share().to_scalar());
let verifying_share = VerifyingShare::new(-self.verifying_share().to_element());
KeyPackage::new(
*self.identifier(),
signing_share,
verifying_share,
verifying_key,
*self.min_signers(),
)
} else {
self
}
}
}
pub(crate) fn into_even_y(
(secret_shares, public_key_package): (BTreeMap<Identifier, SecretShare>, PublicKeyPackage),
) -> (BTreeMap<Identifier, SecretShare>, PublicKeyPackage) {
let is_even = public_key_package.has_even_y();
let public_key_package = public_key_package.into_even_y(Some(is_even));
let secret_shares = secret_shares
.iter()
.map(|(i, s)| (*i, s.clone().into_even_y(Some(is_even))))
.collect();
(secret_shares, public_key_package)
}
pub mod dkg;
pub mod repairable;
}
pub mod round1 {
use frost_rerandomized::frost_core::keys::SigningShare;
use super::*;
pub type SigningNonces = frost::round1::SigningNonces<P>;
pub type SigningCommitments = frost::round1::SigningCommitments<P>;
pub type NonceCommitment = frost::round1::NonceCommitment<P>;
pub fn commit<RNG>(
secret: &SigningShare<P>,
rng: &mut RNG,
) -> (SigningNonces, SigningCommitments)
where
RNG: CryptoRng + RngCore,
{
frost::round1::commit::<P, RNG>(secret, rng)
}
}
pub type SigningPackage = frost::SigningPackage<P>;
pub mod round2 {
use super::*;
pub type SignatureShare = frost::round2::SignatureShare<P>;
pub fn sign(
signing_package: &SigningPackage,
signer_nonces: &round1::SigningNonces,
key_package: &keys::KeyPackage,
) -> Result<SignatureShare, Error> {
frost::round2::sign(signing_package, signer_nonces, key_package)
}
}
pub type Signature = frost_rerandomized::frost_core::Signature<P>;
pub fn aggregate(
signing_package: &SigningPackage,
signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
) -> Result<Signature, Error> {
frost::aggregate(signing_package, signature_shares, pubkeys)
}
pub type CheaterDetection = frost::CheaterDetection;
pub fn aggregate_custom(
signing_package: &SigningPackage,
signature_shares: &BTreeMap<Identifier, round2::SignatureShare>,
pubkeys: &keys::PublicKeyPackage,
cheater_detection: CheaterDetection,
) -> Result<Signature, Error> {
frost::aggregate_custom(
signing_package,
signature_shares,
pubkeys,
cheater_detection,
)
}
pub type SigningKey = frost_rerandomized::frost_core::SigningKey<P>;
pub type VerifyingKey = frost_rerandomized::frost_core::VerifyingKey<P>;