#![cfg_attr(not(feature = "std"), no_std)]
#![allow(non_snake_case)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc = document_features::document_features!()]
extern crate alloc;
use alloc::collections::BTreeMap;
use frost_rerandomized::RandomizedCiphersuite;
use p256::{
elliptic_curve::{
hash2curve::{hash_to_field, ExpandMsgXmd},
sec1::{FromEncodedPoint, ToEncodedPoint},
Field as FFField, PrimeField,
},
AffinePoint, ProjectivePoint, Scalar,
};
use rand_core::{CryptoRng, RngCore};
use sha2::{Digest, Sha256};
use frost_core as frost;
#[cfg(test)]
mod tests;
#[cfg(feature = "serde")]
pub use frost_core::serde;
pub use frost_core::{Ciphersuite, Field, FieldError, Group, GroupError};
pub use rand_core;
pub type Error = frost_core::Error<P256Sha256>;
#[derive(Clone, Copy)]
pub struct P256ScalarField;
impl Field for P256ScalarField {
type Scalar = Scalar;
type Serialization = [u8; 32];
fn zero() -> Self::Scalar {
Scalar::ZERO
}
fn one() -> Self::Scalar {
Scalar::ONE
}
fn invert(scalar: &Self::Scalar) -> Result<Self::Scalar, FieldError> {
if *scalar == <Self as Field>::zero() {
Err(FieldError::InvalidZeroScalar)
} else {
Ok(scalar.invert().unwrap())
}
}
fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
Scalar::random(rng)
}
fn serialize(scalar: &Self::Scalar) -> Self::Serialization {
scalar.to_bytes().into()
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Scalar, FieldError> {
let field_bytes: &p256::FieldBytes = buf.into();
match Scalar::from_repr(*field_bytes).into() {
Some(s) => Ok(s),
None => Err(FieldError::MalformedScalar),
}
}
fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization {
let mut array = Self::serialize(scalar);
array.reverse();
array
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct P256Group;
impl Group for P256Group {
type Field = P256ScalarField;
type Element = ProjectivePoint;
type Serialization = [u8; 33];
fn cofactor() -> <Self::Field as Field>::Scalar {
Scalar::ONE
}
fn identity() -> Self::Element {
ProjectivePoint::IDENTITY
}
fn generator() -> Self::Element {
ProjectivePoint::GENERATOR
}
fn serialize(element: &Self::Element) -> Result<Self::Serialization, GroupError> {
if *element == Self::identity() {
return Err(GroupError::InvalidIdentityElement);
}
let mut fixed_serialized = [0; 33];
let serialized_point = element.to_encoded_point(true);
let serialized = serialized_point.as_bytes();
fixed_serialized.copy_from_slice(serialized);
Ok(fixed_serialized)
}
fn deserialize(buf: &Self::Serialization) -> Result<Self::Element, GroupError> {
let encoded_point =
p256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?;
match Option::<AffinePoint>::from(AffinePoint::from_encoded_point(&encoded_point)) {
Some(point) => {
if point.is_identity().into() {
Err(GroupError::InvalidIdentityElement)
} else {
Ok(ProjectivePoint::from(point))
}
}
None => Err(GroupError::MalformedElement),
}
}
}
fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] {
let mut h = Sha256::new();
for i in inputs {
h.update(i);
}
let mut output = [0u8; 32];
output.copy_from_slice(h.finalize().as_slice());
output
}
fn hash_to_scalar(domain: &[&[u8]], msg: &[u8]) -> Scalar {
let mut u = [P256ScalarField::zero()];
hash_to_field::<ExpandMsgXmd<Sha256>, Scalar>(&[msg], domain, &mut u)
.expect("should never return error according to error cases described in ExpandMsgXmd");
u[0]
}
const CONTEXT_STRING: &str = "FROST-P256-SHA256-v1";
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct P256Sha256;
impl Ciphersuite for P256Sha256 {
const ID: &'static str = CONTEXT_STRING;
type Group = P256Group;
type HashOutput = [u8; 32];
type SignatureSerialization = [u8; 65];
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))
}
}
impl RandomizedCiphersuite for P256Sha256 {
fn hash_randomizer(m: &[u8]) -> Option<<<Self::Group as Group>::Field as Field>::Scalar> {
Some(hash_to_scalar(
&[CONTEXT_STRING.as_bytes(), b"randomizer"],
m,
))
}
}
type P = P256Sha256;
pub type Identifier = frost::Identifier<P>;
pub mod keys {
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>(
secret: &SigningKey,
max_signers: u16,
min_signers: u16,
identifiers: IdentifierList,
rng: &mut R,
) -> Result<(BTreeMap<Identifier, SecretShare>, PublicKeyPackage), Error> {
frost::keys::split(secret, max_signers, min_signers, identifiers, rng)
}
pub fn reconstruct(secret_shares: &[KeyPackage]) -> Result<SigningKey, Error> {
frost::keys::reconstruct(secret_shares)
}
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 mod dkg;
pub mod refresh;
pub mod repairable;
}
pub mod round1 {
use crate::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, 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_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 SigningKey = frost_core::SigningKey<P>;
pub type VerifyingKey = frost_core::VerifyingKey<P>;