use super::{VerifiedAggKeygenInput, encpedpop};
use crate::{Schnorr, frost::*};
use alloc::collections::{BTreeMap, BTreeSet};
use secp256kfun::{hash::*, prelude::*};
pub trait CertificationScheme: Clone {
type Signature: Clone + core::fmt::Debug + PartialEq;
fn certify(
&self,
keypair: &KeyPair,
verified_input: &VerifiedAggKeygenInput,
) -> Self::Signature;
fn verify_cert(
&self,
cert_key: Point,
verified_input: &VerifiedAggKeygenInput,
signature: &Self::Signature,
) -> bool;
}
impl<H: Hash32, NG: NonceGen + Clone> CertificationScheme for Schnorr<H, NG> {
type Signature = crate::Signature;
fn certify(
&self,
keypair: &KeyPair,
verified_input: &VerifiedAggKeygenInput,
) -> Self::Signature {
let cert_bytes = verified_input.cert_bytes();
let message = crate::Message::new("BIP DKG/cert", cert_bytes.as_ref());
let keypair_even_y = (*keypair).into();
self.sign(&keypair_even_y, message)
}
fn verify_cert(
&self,
cert_key: Point,
verified_input: &VerifiedAggKeygenInput,
signature: &Self::Signature,
) -> bool {
let cert_bytes = verified_input.cert_bytes();
let message = crate::Message::new("BIP DKG/cert", cert_bytes.as_ref());
let cert_key_even_y = cert_key.into_point_with_even_y().0;
self.verify(&cert_key_even_y, message, signature)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CertifiedKeygen<Sig> {
verified_input: VerifiedAggKeygenInput,
certificate: BTreeMap<Point, Sig>,
}
impl<Sig> CertifiedKeygen<Sig> {
fn from_verified(
verified_input: VerifiedAggKeygenInput,
certificate: BTreeMap<Point, Sig>,
) -> Self {
Self {
verified_input,
certificate,
}
}
pub fn new<S>(
cert_scheme: &S,
verified_input: VerifiedAggKeygenInput,
certificates: BTreeMap<Point, Sig>,
) -> Result<Self, CertificateError>
where
S: CertificationScheme<Signature = Sig>,
{
let mut certifier = Certifier::new(cert_scheme.clone(), verified_input);
for (key, sig) in certificates {
certifier
.receive_certificate(key, sig)
.map_err(|err| match err {
ReceiveCertError::UnknownParty => CertificateError::Unexpected { key },
ReceiveCertError::InvalidSignature => CertificateError::InvalidCert { key },
})?;
}
if let Some(key) = certifier.first_missing() {
return Err(CertificateError::Missing { key });
}
Ok(certifier.finish().expect("just verified completeness"))
}
pub fn recover_share<H: Hash32, S: CertificationScheme<Signature = Sig>>(
&self,
cert_scheme: &S,
share_index: ShareIndex,
keypair: KeyPair,
) -> Result<PairedSecretShare, RecoverShareError> {
let cert_key = keypair.public_key();
let my_cert = self
.certificate
.get(&cert_key)
.ok_or(RecoverShareError::NotCertifiedByKey)?;
if !cert_scheme.verify_cert(cert_key, self.verified_agg_input(), my_cert) {
return Err(RecoverShareError::InvalidCertification);
}
Ok(self
.verified_agg_input()
.recover_share::<H>(share_index, &keypair)?)
}
pub fn verified_agg_input(&self) -> &VerifiedAggKeygenInput {
&self.verified_input
}
pub fn certificate(&self) -> &BTreeMap<Point, Sig> {
&self.certificate
}
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum CertificateError {
InvalidCert {
key: Point,
},
Missing {
key: Point,
},
Unexpected {
key: Point,
},
}
impl core::fmt::Display for CertificateError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CertificateError::InvalidCert { key } => {
write!(f, "certificate for key {key} was invalid")
}
CertificateError::Missing { key } => {
write!(f, "certificate for key {key} was missing")
}
CertificateError::Unexpected { key } => {
write!(
f,
"certificate map contained unexpected entry for key {key}"
)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for CertificateError {}
#[cfg(feature = "vrf_cert_keygen")]
pub mod vrf_cert {
use super::*;
use secp256kfun::digest::typenum::U32;
use secp256kfun::{digest::core_api::BlockSizeUser, hash::HashAdd};
use vrf_fun::{SimpleVrf, VrfProof};
pub type CertVrfProof = VrfProof<U32>;
#[derive(Clone, Debug)]
pub struct VrfCertScheme<H> {
name: &'static str,
_hash: core::marker::PhantomData<H>,
}
impl<H> PartialEq for VrfCertScheme<H> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl<H> VrfCertScheme<H> {
pub fn new(name: &'static str) -> Self {
Self {
name,
_hash: core::marker::PhantomData,
}
}
}
impl<H> CertificationScheme for VrfCertScheme<H>
where
H: Hash32, H: BlockSizeUser<BlockSize = secp256kfun::digest::typenum::U64>,
{
type Signature = CertVrfProof;
fn certify(
&self,
keypair: &KeyPair,
verified_input: &VerifiedAggKeygenInput,
) -> Self::Signature {
let cert_bytes = verified_input.cert_bytes();
let h =
Point::hash_to_curve(H::default().ds(self.name).add(&cert_bytes[..])).normalize();
let vrf = SimpleVrf::<H>::default().with_name(self.name);
vrf.prove(keypair, h)
}
fn verify_cert(
&self,
cert_key: Point,
verified_input: &VerifiedAggKeygenInput,
signature: &Self::Signature,
) -> bool {
let cert_bytes = verified_input.cert_bytes();
let h =
Point::hash_to_curve(H::default().ds(self.name).add(&cert_bytes[..])).normalize();
let vrf = SimpleVrf::<H>::default().with_name(self.name);
vrf.verify(cert_key, h, signature).is_some()
}
}
impl super::CertifiedKeygen<CertVrfProof> {
pub fn vrf_security_check(&self, mut hasher: impl Hash32) -> [u8; 32] {
for vrf_proof in self.certificate.values() {
let gamma = vrf_proof.dangerously_access_gamma_without_verifying();
hasher.update(gamma.to_bytes().as_ref());
}
hasher.finalize_fixed().into()
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Certifier<S: CertificationScheme> {
cert_scheme: S,
verified_agg_input: VerifiedAggKeygenInput,
certificates: BTreeMap<Point, S::Signature>,
}
impl<S: CertificationScheme> Certifier<S> {
pub fn new(cert_scheme: S, verified_agg_input: VerifiedAggKeygenInput) -> Self {
Self {
cert_scheme,
verified_agg_input,
certificates: BTreeMap::new(),
}
}
pub fn receive_certificate(
&mut self,
from: Point,
signature: S::Signature,
) -> Result<(), ReceiveCertError> {
if !self.verified_agg_input.required_keys().contains(&from) {
return Err(ReceiveCertError::UnknownParty);
}
if !self
.cert_scheme
.verify_cert(from, &self.verified_agg_input, &signature)
{
return Err(ReceiveCertError::InvalidSignature);
}
self.certificates.entry(from).or_insert(signature);
Ok(())
}
pub fn cert_scheme(&self) -> &S {
&self.cert_scheme
}
pub fn required_keys(&self) -> BTreeSet<Point> {
self.verified_agg_input.required_keys()
}
pub fn is_finished(&self) -> bool {
self.certificates.len() == self.verified_agg_input.required_keys().len()
}
pub fn first_missing(&self) -> Option<Point> {
self.verified_agg_input
.required_keys()
.iter()
.find(|k| !self.certificates.contains_key(k))
.copied()
}
pub fn missing_count(&self) -> usize {
self.verified_agg_input
.required_keys()
.len()
.saturating_sub(self.certificates.len())
}
pub fn required_count(&self) -> usize {
self.verified_agg_input.required_keys().len()
}
pub fn finish(self) -> Result<CertifiedKeygen<S::Signature>, IncompleteCertificates> {
if !self.is_finished() {
return Err(IncompleteCertificates);
}
Ok(CertifiedKeygen::from_verified(
self.verified_agg_input,
self.certificates,
))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReceiveCertError {
UnknownParty,
InvalidSignature,
}
impl core::fmt::Display for ReceiveCertError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ReceiveCertError::UnknownParty => write!(f, "Certificate from unknown party"),
ReceiveCertError::InvalidSignature => write!(f, "Invalid certificate signature"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ReceiveCertError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IncompleteCertificates;
impl core::fmt::Display for IncompleteCertificates {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Not all certificates received")
}
}
#[cfg(feature = "std")]
impl std::error::Error for IncompleteCertificates {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RecoverShareError {
NotCertifiedByKey,
InvalidCertification,
Recovery(encpedpop::RecoverShareError),
}
impl From<encpedpop::RecoverShareError> for RecoverShareError {
fn from(err: encpedpop::RecoverShareError) -> Self {
RecoverShareError::Recovery(err)
}
}
impl core::fmt::Display for RecoverShareError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RecoverShareError::NotCertifiedByKey => {
write!(f, "this keypair has not certified the keygen")
}
RecoverShareError::InvalidCertification => {
write!(f, "our stored certificate did not verify")
}
RecoverShareError::Recovery(err) => write!(f, "{err}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for RecoverShareError {}
#[cfg(test)]
mod test {
#[test]
#[cfg(feature = "vrf_cert_keygen")]
fn test_certifier_with_vrf_cert_scheme_is_partial_eq() {
use super::*;
use sha2::Sha256;
fn assert_partial_eq<T: PartialEq>() {}
assert_partial_eq::<Certifier<vrf_cert::VrfCertScheme<Sha256>>>();
}
}