use serde::{de::DeserializeOwned, Deserialize, Serialize};
use super::state::NodeId;
use super::FoldError;
pub const SIGNATURE_LEN: usize = 64;
#[derive(Debug, Clone, Copy, Default)]
pub struct EnvelopeMeta {
pub announced_at: u64,
pub ttl_secs: Option<u32>,
pub flags: u8,
}
pub fn placeholder_signature() -> Vec<u8> {
vec![0u8; SIGNATURE_LEN]
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedAnnouncement<P> {
pub kind: u16,
pub class: u64,
pub node_id: NodeId,
pub generation: u64,
pub announced_at: u64,
pub ttl_secs: Option<u32>,
pub flags: u8,
pub payload: P,
pub signature: Vec<u8>,
}
impl<P> SignedAnnouncement<P> {
pub fn placeholder(
kind: u16,
class: u64,
node_id: NodeId,
generation: u64,
meta: EnvelopeMeta,
payload: P,
) -> Self {
Self {
kind,
class,
node_id,
generation,
announced_at: meta.announced_at,
ttl_secs: meta.ttl_secs,
flags: meta.flags,
payload,
signature: placeholder_signature(),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum WireError {
#[error("wire decode failed: {0}")]
Decode(#[from] postcard::Error),
#[error("signature length {0} != expected {expected}", expected = SIGNATURE_LEN)]
BadSignatureLength(usize),
#[error("placeholder (all-zero) signature reached the dispatch path")]
PlaceholderSignature,
#[error("signature verification failed")]
InvalidSignature,
#[error("publisher public key bytes are not a valid Ed25519 point")]
InvalidPublicKey,
#[error("envelope kind {got:#06x} does not match expected {expected:#06x}")]
KindMismatch {
got: u16,
expected: u16,
},
#[error("apply rejected: {0}")]
Apply(#[from] FoldError),
}
pub(super) fn signing_bytes<P: Serialize>(
kind: u16,
class: u64,
node_id: NodeId,
generation: u64,
meta: &EnvelopeMeta,
payload: &P,
) -> Result<Vec<u8>, postcard::Error> {
#[derive(Serialize)]
struct ToSign<'a, P: Serialize> {
kind: u16,
class: u64,
node_id: NodeId,
generation: u64,
announced_at: u64,
ttl_secs: Option<u32>,
flags: u8,
payload: &'a P,
}
postcard::to_allocvec(&ToSign {
kind,
class,
node_id,
generation,
announced_at: meta.announced_at,
ttl_secs: meta.ttl_secs,
flags: meta.flags,
payload,
})
}
impl<P: Serialize + DeserializeOwned> SignedAnnouncement<P> {
pub fn sign(
keypair: &crate::adapter::net::identity::EntityKeypair,
kind: u16,
class: u64,
node_id: NodeId,
generation: u64,
meta: EnvelopeMeta,
payload: P,
) -> Result<Self, WireError> {
let bytes = signing_bytes(kind, class, node_id, generation, &meta, &payload)?;
let sig = keypair.sign(&bytes);
Ok(Self {
kind,
class,
node_id,
generation,
announced_at: meta.announced_at,
ttl_secs: meta.ttl_secs,
flags: meta.flags,
payload,
signature: sig.to_bytes().to_vec(),
})
}
pub fn verify(
&self,
publisher: &crate::adapter::net::identity::EntityId,
) -> Result<(), WireError> {
if self.signature.len() != SIGNATURE_LEN {
return Err(WireError::BadSignatureLength(self.signature.len()));
}
if self.signature == placeholder_signature() {
return Err(WireError::PlaceholderSignature);
}
let mut sig_bytes = [0u8; SIGNATURE_LEN];
sig_bytes.copy_from_slice(&self.signature);
let sig = ed25519_dalek::Signature::from_bytes(&sig_bytes);
let meta = EnvelopeMeta {
announced_at: self.announced_at,
ttl_secs: self.ttl_secs,
flags: self.flags,
};
let bytes = signing_bytes(
self.kind,
self.class,
self.node_id,
self.generation,
&meta,
&self.payload,
)?;
publisher.verify(&bytes, &sig).map_err(|e| match e {
crate::adapter::net::identity::EntityError::InvalidPublicKey => {
WireError::InvalidPublicKey
}
_ => WireError::InvalidSignature,
})
}
pub fn encode(&self) -> Result<Vec<u8>, WireError> {
postcard::to_allocvec(self).map_err(WireError::Decode)
}
pub fn decode(bytes: &[u8]) -> Result<Self, WireError> {
postcard::from_bytes(bytes).map_err(WireError::Decode)
}
pub fn decode_and_verify(
bytes: &[u8],
publisher: &crate::adapter::net::identity::EntityId,
) -> Result<Self, WireError> {
let ann = Self::decode(bytes)?;
ann.verify(publisher)?;
Ok(ann)
}
}