use super::{
system::{KeyedSig, SigShare},
Error, Result,
};
use crate::{
messaging::signature_aggregator::{Error as AggregatorError, SignatureAggregator},
routing::SectionKeyShare,
types::{PublicKey, Signature},
};
use bls::PublicKey as BlsPublicKey;
use ed25519_dalek::{
Keypair as EdKeypair, PublicKey as EdPublicKey, Signature as EdSignature, Signer as _,
Verifier as _,
};
use serde::{Deserialize, Serialize};
use xor_name::XorName;
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct ServiceAuth {
pub public_key: PublicKey,
pub signature: Signature,
}
#[derive(Clone, Eq, PartialEq, custom_debug::Debug, serde::Deserialize, serde::Serialize)]
pub struct NodeAuth {
pub section_pk: BlsPublicKey,
#[debug(with = "PublicKey::fmt_ed25519")]
pub public_key: EdPublicKey,
#[debug(with = "Signature::fmt_ed25519")]
#[serde(with = "serde_bytes")]
pub signature: EdSignature,
}
impl NodeAuth {
pub(crate) fn authorize(
section_pk: BlsPublicKey,
keypair: &EdKeypair,
payload: impl AsRef<[u8]>,
) -> AuthorityProof<Self> {
AuthorityProof(NodeAuth {
section_pk,
public_key: keypair.public,
signature: keypair.sign(payload.as_ref()),
})
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct BlsShareAuth {
pub section_pk: BlsPublicKey,
pub src_name: XorName,
pub sig_share: SigShare,
}
impl BlsShareAuth {
pub(crate) fn authorize(
section_pk: BlsPublicKey,
src_name: XorName,
key_share: &SectionKeyShare,
payload: impl AsRef<[u8]>,
) -> AuthorityProof<Self> {
AuthorityProof(BlsShareAuth {
section_pk,
src_name,
sig_share: SigShare {
public_key_set: key_share.public_key_set.clone(),
index: key_share.index,
signature_share: key_share.secret_key_share.sign(payload),
},
})
}
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct SectionAuth {
pub src_name: XorName,
pub sig: KeyedSig,
}
impl SectionAuth {
pub(crate) async fn try_authorize(
aggregator: SignatureAggregator,
share: BlsShareAuth,
payload: impl AsRef<[u8]>,
) -> Result<AuthorityProof<Self>, AggregatorError> {
let sig = aggregator
.add(payload.as_ref(), share.sig_share.clone())
.await?;
if share.sig_share.public_key_set.public_key() != sig.public_key {
return Err(AggregatorError::InvalidShare);
}
if sig.public_key != share.section_pk {
return Err(AggregatorError::InvalidShare);
}
Ok(AuthorityProof(SectionAuth {
src_name: share.src_name,
sig,
}))
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AuthorityProof<T>(T);
impl<T: VerifyAuthority> AuthorityProof<T> {
pub fn verify(inner: T, payload: impl AsRef<[u8]>) -> Result<Self> {
inner.verify_authority(payload).map(Self)
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> core::ops::Deref for AuthorityProof<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait VerifyAuthority: Sized + sealed::Sealed {
fn verify_authority(self, payload: impl AsRef<[u8]>) -> Result<Self>;
}
impl VerifyAuthority for ServiceAuth {
fn verify_authority(self, payload: impl AsRef<[u8]>) -> Result<Self> {
self.public_key
.verify(&self.signature, payload)
.map_err(|_| Error::InvalidSignature)?;
Ok(self)
}
}
impl sealed::Sealed for ServiceAuth {}
impl VerifyAuthority for NodeAuth {
fn verify_authority(self, payload: impl AsRef<[u8]>) -> Result<Self> {
self.public_key
.verify(payload.as_ref(), &self.signature)
.map_err(|_| Error::InvalidSignature)?;
Ok(self)
}
}
impl sealed::Sealed for NodeAuth {}
impl VerifyAuthority for BlsShareAuth {
fn verify_authority(self, payload: impl AsRef<[u8]>) -> Result<Self> {
if self.sig_share.public_key_set.public_key() != self.section_pk {
return Err(Error::InvalidSignature);
}
if !self.sig_share.verify(payload.as_ref()) {
return Err(Error::InvalidSignature);
}
Ok(self)
}
}
impl sealed::Sealed for BlsShareAuth {}
impl VerifyAuthority for SectionAuth {
fn verify_authority(self, payload: impl AsRef<[u8]>) -> Result<Self> {
if !self.sig.public_key.verify(&self.sig.signature, payload) {
return Err(Error::InvalidSignature);
}
Ok(self)
}
}
impl sealed::Sealed for SectionAuth {}
mod sealed {
#[allow(missing_docs, unreachable_pub)]
pub trait Sealed {}
}