Something went wrong. Try again.
Typelevel cryptographic algo tracking crates.io/crates/evidence
typelevel cryptography util
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597//! Signed payloads with type-level tracking.//!//! [`Signed<T, S, C>`] represents a payload that has been signed but not yet verified.//! The payload is _intentionally inaccessible_ — you must call [`Signed::try_verify`]//! to obtain a [`Verified<T, S, C>`](crate::verified::Verified) witness that proves//! verification succeeded.//!//! # Type Parameters//!//! - `T`: The payload type that was signed//! - `S`: The signature primitive (e.g., [`Ed25519`](crate::signature::ed25519::Ed25519))//! - `C`: The codec used to serialize the payload before signing//!//! # Example//!//! ```//! # #[cfg(feature = "ed25519")]//! # {//! use evidence::{codec::Identity, signature::{ed25519::Ed25519, Signer}, signed::Signed, verified::Verified};//!//! // Create a signing key//! let signing_key = ed25519_dalek::SigningKey::from_bytes(&[1u8; 32]);//!//! // Sign some data//! let data = b"hello world";//! let signed: Signed<[u8; 11], Ed25519, Identity> = Signed::seal(&signing_key, data);//!//! // Payload is NOT accessible here — must verify first//! // signed.payload() // <- This method doesn't exist!//!//! // Verify to get access//! let verified: Verified<[u8; 11], Ed25519, Identity> = signed.try_verify().unwrap();//! assert_eq!(verified.payload(), data);//! # }//! ```
use alloc::vec::Vec;use core::marker::PhantomData;
use future_form::FutureForm;
use crate::{ codec::{Decode, Encode}, signature::{AsyncSigner, SignaturePrimitive, Signer}, verified::Verified,};
/// A signed payload that has not yet been verified.////// This type deliberately does _not_ provide access to the payload./// You must call [`try_verify`](Self::try_verify) to obtain a/// [`Verified<T, S, C>`] witness that proves verification succeeded.////// # Construction////// Use [`seal`](Self::seal) or [`seal_async`](Self::seal_async) to create/// a signed payload from a signer. Use [`seal_verified`](Self::seal_verified)/// or [`seal_verified_async`](Self::seal_verified_async) to sign and obtain/// a [`Verified`] witness directly without redundant re-verification.////// For deserialization from untrusted sources, import the/// [`SignedUnchecked`] extension trait.pub struct Signed<T, S: SignaturePrimitive, C> { issuer: S::VerifyingKey, signature: S::Signature, encoded_payload: Vec<u8>, _marker: PhantomData<fn() -> (T, C)>,}
impl<T, S: SignaturePrimitive, C> Clone for Signed<T, S, C>where S::VerifyingKey: Clone, S::Signature: Clone,{ fn clone(&self) -> Self { Self { issuer: self.issuer.clone(), signature: self.signature.clone(), encoded_payload: self.encoded_payload.clone(), _marker: PhantomData, } }}
impl<T, S: SignaturePrimitive, C> PartialEq for Signed<T, S, C>where S::VerifyingKey: PartialEq, S::Signature: PartialEq,{ fn eq(&self, other: &Self) -> bool { self.issuer == other.issuer && self.signature == other.signature && self.encoded_payload == other.encoded_payload }}
impl<T, S: SignaturePrimitive, C> Eq for Signed<T, S, C>where S::VerifyingKey: Eq, S::Signature: Eq,{}
impl<T, S: SignaturePrimitive, C> core::hash::Hash for Signed<T, S, C>where S::VerifyingKey: core::hash::Hash, S::Signature: core::hash::Hash,{ fn hash<H: core::hash::Hasher>(&self, state: &mut H) { self.issuer.hash(state); self.signature.hash(state); self.encoded_payload.hash(state); }}
impl<T, S: SignaturePrimitive, C> core::fmt::Debug for Signed<T, S, C>where S::VerifyingKey: core::fmt::Debug, S::Signature: core::fmt::Debug,{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Signed") .field("issuer", &self.issuer) .field("signature", &self.signature) .field("encoded_payload", &self.encoded_payload) .finish() }}
impl<T, S: SignaturePrimitive, C> Signed<T, S, C> { /// Create a signed payload from its components. /// /// This is `pub(crate)` — external users should use [`seal`](Self::seal) /// or the [`SignedUnchecked`] extension trait. #[must_use] pub(crate) fn new( issuer: S::VerifyingKey, signature: S::Signature, encoded_payload: Vec<u8>, ) -> Self { Self { issuer, signature, encoded_payload, _marker: PhantomData, } }
/// Sign a payload using a synchronous signer. /// /// The payload is encoded using codec `C`, then signed with signer `K`. /// /// # Panics /// /// Panics if the codec fails to encode the payload. #[must_use] #[allow(clippy::expect_used)] // documented panic on encode failure pub fn seal<K: Signer<S>>(signer: &K, payload: &T) -> Self where C: Encode<T>, { let encoded = C::encode(payload).expect("encoding failed"); let signature = signer.sign(&encoded); Self::new(signer.verifying_key(), signature, encoded) }
/// Sign a payload using an asynchronous signer. /// /// The payload is encoded using codec `C`, then signed with signer `K`. /// Use this for hardware security modules or remote signing services. /// /// The `F` parameter selects the [`FutureForm`] — use /// [`Sendable`](future_form::Sendable) for multi-threaded runtimes or /// [`Local`](future_form::Local) for Wasm / single-threaded executors. /// /// # Panics /// /// Panics if the codec fails to encode the payload. #[allow(clippy::expect_used)] // documented panic on encode failure pub async fn seal_async<F: FutureForm, K: AsyncSigner<S, F>>(signer: &K, payload: &T) -> Self where C: Encode<T>, { let encoded = C::encode(payload).expect("encoding failed"); let signature = signer.sign(&encoded).await; Self::new(signer.verifying_key(), signature, encoded) }
/// Sign a payload and return a [`Verified`] witness directly. /// /// Unlike [`seal`](Self::seal) followed by [`try_verify`](Self::try_verify), /// this method skips the redundant verification and decode steps — the /// caller just signed the data, so verification is tautological, and the /// original `T` is kept without round-tripping through the codec. /// /// This is the primary constructor for locally-authored data that will /// be used immediately in verified form. /// /// # Panics /// /// Panics if the codec fails to encode the payload. #[allow(clippy::expect_used)] // documented panic on encode failure pub fn seal_verified<K: Signer<S>>(signer: &K, payload: T) -> Verified<T, S, C> where C: Encode<T>, { let encoded = C::encode(&payload).expect("encoding failed"); let signature = signer.sign(&encoded); let envelope = Self::new(signer.verifying_key(), signature, encoded); Verified::new(envelope, payload) }
/// Sign a payload asynchronously and return a [`Verified`] witness directly. /// /// Async variant of [`seal_verified`](Self::seal_verified). Use this for /// hardware security modules or remote signing services. /// /// The `F` parameter selects the [`FutureForm`] — use /// [`Sendable`](future_form::Sendable) for multi-threaded runtimes or /// [`Local`](future_form::Local) for Wasm / single-threaded executors. /// /// # Panics /// /// Panics if the codec fails to encode the payload. #[allow(clippy::expect_used)] // documented panic on encode failure pub async fn seal_verified_async<F: FutureForm, K: AsyncSigner<S, F>>( signer: &K, payload: T, ) -> Verified<T, S, C> where C: Encode<T>, { let encoded = C::encode(&payload).expect("encoding failed"); let signature = signer.sign(&encoded).await; let envelope = Self::new(signer.verifying_key(), signature, encoded); Verified::new(envelope, payload) }
/// Verify the signature and decode the payload. /// /// On success, returns a [`Verified<T, S, C>`] witness that proves /// verification succeeded. The verified payload is only accessible /// through this witness type. /// /// The original `Signed` envelope is cloned into the `Verified` witness. /// Use [`into_verified`](Self::into_verified) to avoid the clone. /// /// # Errors /// /// Returns [`VerificationError::InvalidSignature`] if the signature /// does not verify against the issuer's public key. /// /// Returns [`VerificationError::DecodeError`] if the payload cannot /// be decoded using codec `C`. pub fn try_verify(&self) -> Result<Verified<T, S, C>, VerificationError> where C: Decode<T>, S::VerifyingKey: Clone, S::Signature: Clone, { S::verify(&self.issuer, &self.encoded_payload, &self.signature) .map_err(|_| VerificationError::InvalidSignature)?;
let payload = C::decode(&self.encoded_payload).map_err(|_| VerificationError::DecodeError)?;
Ok(Verified::new(self.clone(), payload)) }
/// Verify the signature and decode the payload, consuming the `Signed` envelope. /// /// Like [`try_verify`](Self::try_verify), but moves `self` into the /// [`Verified`] witness instead of cloning. /// /// # Errors /// /// Returns [`VerificationError::InvalidSignature`] if the signature /// does not verify against the issuer's public key. /// /// Returns [`VerificationError::DecodeError`] if the payload cannot /// be decoded using codec `C`. pub fn into_verified(self) -> Result<Verified<T, S, C>, VerificationError> where C: Decode<T>, { S::verify(&self.issuer, &self.encoded_payload, &self.signature) .map_err(|_| VerificationError::InvalidSignature)?;
let payload = C::decode(&self.encoded_payload).map_err(|_| VerificationError::DecodeError)?;
Ok(Verified::new(self, payload)) }
/// Get the issuer's public key. #[must_use] pub const fn issuer(&self) -> &S::VerifyingKey { &self.issuer }
/// Get the signature. #[must_use] pub const fn signature(&self) -> &S::Signature { &self.signature }
/// Get the encoded payload bytes. /// /// Note: This returns the raw encoded bytes, _not_ the decoded payload. /// The decoded payload is only accessible after verification via /// [`Verified::payload`](crate::verified::Verified::payload). #[must_use] pub fn encoded_payload(&self) -> &[u8] { &self.encoded_payload }}
/// Error returned when signature verification fails.////// Details of _why_ verification failed are intentionally hidden/// to avoid leaking information that could aid timing attacks.#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum VerificationError { /// Signature did not verify against the issuer's public key. InvalidSignature,
/// Payload could not be decoded. DecodeError,}
impl core::fmt::Display for VerificationError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::InvalidSignature => write!(f, "invalid signature"), Self::DecodeError => write!(f, "payload decode error"), } }}
/// Extension trait for constructing [`Signed`] from raw components.////// This trait is _intentionally_ not in the prelude. Importing it is an explicit/// acknowledgment that you are bypassing the normal signing flow.////// # When to use////// - Deserializing a signed payload from storage or network/// - Interoperating with external systems/// - Testing////// # Example////// ```/// # #[cfg(feature = "ed25519")]/// # {/// use evidence::{codec::Identity, signature::ed25519::Ed25519, signed::{Signed, SignedUnchecked}};////// // Reconstruct from deserialized components/// let issuer = ed25519_dalek::VerifyingKey::from_bytes(&[0u8; 32]).unwrap();/// let signature = ed25519_dalek::Signature::from_bytes(&[0u8; 64]);/// let encoded = vec![1, 2, 3, 4];////// let signed: Signed<Vec<u8>, Ed25519, Identity> =/// Signed::from_unchecked_parts(issuer, signature, encoded);/// # }/// ```pub trait SignedUnchecked<T, S: SignaturePrimitive, C> { /// Create a signed payload from raw components. /// /// # Safety (logical) /// /// This does not perform any verification. The caller must ensure /// the components represent a valid signed payload. fn from_unchecked_parts( issuer: S::VerifyingKey, signature: S::Signature, encoded_payload: Vec<u8>, ) -> Self;}
impl<T, S: SignaturePrimitive, C> SignedUnchecked<T, S, C> for Signed<T, S, C> { fn from_unchecked_parts( issuer: S::VerifyingKey, signature: S::Signature, encoded_payload: Vec<u8>, ) -> Self { Self::new(issuer, signature, encoded_payload) }}
#[cfg(feature = "serde")]impl<T, S: SignaturePrimitive, C> serde::Serialize for Signed<T, S, C>where S::VerifyingKey: serde::Serialize, S::Signature: serde::Serialize,{ fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> { use serde::ser::SerializeStruct; let mut state = serializer.serialize_struct("Signed", 3)?; state.serialize_field("issuer", &self.issuer)?; state.serialize_field("signature", &self.signature)?; state.serialize_field("encoded_payload", &self.encoded_payload)?; state.end() }}
#[cfg(feature = "serde")]impl<'de, T, S: SignaturePrimitive, C> serde::Deserialize<'de> for Signed<T, S, C>where S::VerifyingKey: serde::Deserialize<'de>, S::Signature: serde::Deserialize<'de>,{ fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { use serde::de::{MapAccess, Visitor};
struct SignedVisitor<T, S: SignaturePrimitive, C>(PhantomData<(T, S, C)>);
impl<'de, T, S: SignaturePrimitive, C> Visitor<'de> for SignedVisitor<T, S, C> where S::VerifyingKey: serde::Deserialize<'de>, S::Signature: serde::Deserialize<'de>, { type Value = Signed<T, S, C>;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("struct Signed") }
fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Signed<T, S, C>, V::Error> { let mut issuer = None; let mut signature = None; let mut encoded_payload = None;
while let Some(key) = map.next_key::<&str>()? { match key { "issuer" => issuer = Some(map.next_value()?), "signature" => signature = Some(map.next_value()?), "encoded_payload" => encoded_payload = Some(map.next_value()?), _ => { let _: serde::de::IgnoredAny = map.next_value()?; } } }
let issuer = issuer.ok_or_else(|| serde::de::Error::missing_field("issuer"))?; let signature = signature.ok_or_else(|| serde::de::Error::missing_field("signature"))?; let encoded_payload = encoded_payload .ok_or_else(|| serde::de::Error::missing_field("encoded_payload"))?;
Ok(Signed::new(issuer, signature, encoded_payload)) } }
const FIELDS: &[&str] = &["issuer", "signature", "encoded_payload"]; deserializer.deserialize_struct("Signed", FIELDS, SignedVisitor(PhantomData)) }}
#[cfg(feature = "arbitrary")]impl<'a, T, S: SignaturePrimitive, C> arbitrary::Arbitrary<'a> for Signed<T, S, C>where S::VerifyingKey: arbitrary::Arbitrary<'a>, S::Signature: arbitrary::Arbitrary<'a>,{ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { let issuer = S::VerifyingKey::arbitrary(u)?; let signature = S::Signature::arbitrary(u)?; let encoded_payload = Vec::arbitrary(u)?; Ok(Self::new(issuer, signature, encoded_payload)) }}
#[cfg(feature = "bolero")]impl<T: 'static, S: SignaturePrimitive + 'static, C: 'static> bolero_generator::TypeGenerator for Signed<T, S, C>where S::VerifyingKey: bolero_generator::TypeGenerator, S::Signature: bolero_generator::TypeGenerator,{ fn generate<D: bolero_generator::Driver>(driver: &mut D) -> Option<Self> { let issuer = S::VerifyingKey::generate(driver)?; let signature = S::Signature::generate(driver)?; let encoded_payload = Vec::generate(driver)?; Some(Self::new(issuer, signature, encoded_payload)) }}
#[cfg(feature = "proptest")]impl<T: 'static, S: SignaturePrimitive + 'static, C: 'static> proptest::arbitrary::Arbitrary for Signed<T, S, C>where S::VerifyingKey: proptest::arbitrary::Arbitrary + 'static, S::Signature: proptest::arbitrary::Arbitrary + 'static,{ type Parameters = (); type Strategy = proptest::strategy::BoxedStrategy<Self>;
fn arbitrary_with((): Self::Parameters) -> Self::Strategy { use proptest::prelude::*; ( any::<S::VerifyingKey>(), any::<S::Signature>(), proptest::collection::vec(any::<u8>(), 0..256), ) .prop_map(|(issuer, signature, encoded_payload)| { Self::new(issuer, signature, encoded_payload) }) .boxed() }}
#[cfg(feature = "rkyv")]/// Zero-copy [`rkyv`] serialization support for [`Signed`].pub mod archive { use super::{SignaturePrimitive, Signed}; use alloc::vec::Vec; use rkyv::{Archive, Archived, Deserialize, Serialize, rancor::Fallible};
impl<T, S: SignaturePrimitive, C> Archive for Signed<T, S, C> where S::VerifyingKey: Archive, S::Signature: Archive, { type Archived = ArchivedSigned<S::VerifyingKey, S::Signature>; type Resolver = SignedResolver<S::VerifyingKey, S::Signature>;
fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) { let helper = SignedHelper { issuer: self.issuer.clone(), signature: self.signature.clone(), encoded_payload: self.encoded_payload.clone(), }; helper.resolve(resolver, out); } }
impl<T, S: SignaturePrimitive, C, Ser> Serialize<Ser> for Signed<T, S, C> where S::VerifyingKey: Serialize<Ser>, S::Signature: Serialize<Ser>, Ser: Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized, { fn serialize(&self, serializer: &mut Ser) -> Result<Self::Resolver, Ser::Error> { let helper = SignedHelper { issuer: self.issuer.clone(), signature: self.signature.clone(), encoded_payload: self.encoded_payload.clone(), }; helper.serialize(serializer) } }
impl<T, S: SignaturePrimitive, C, D> Deserialize<Signed<T, S, C>, D> for ArchivedSigned<S::VerifyingKey, S::Signature> where S::VerifyingKey: Archive, S::Signature: Archive, Archived<S::VerifyingKey>: Deserialize<S::VerifyingKey, D>, Archived<S::Signature>: Deserialize<S::Signature, D>, D: Fallible + ?Sized, D::Error: rkyv::rancor::Source, { fn deserialize(&self, deserializer: &mut D) -> Result<Signed<T, S, C>, D::Error> { let helper: SignedHelper<S::VerifyingKey, S::Signature> = <ArchivedSigned<S::VerifyingKey, S::Signature> as Deserialize< SignedHelper<S::VerifyingKey, S::Signature>, D, >>::deserialize(self, deserializer)?; Ok(Signed::new( helper.issuer, helper.signature, helper.encoded_payload, )) } }
/// Helper struct for rkyv serialization. /// /// The phantom type parameters from [`Signed`] are erased in the archived /// form since they only matter at compile time. #[derive(Debug, Archive, Serialize, Deserialize)] pub struct SignedHelper<VerifyingKey, Signature> { issuer: VerifyingKey, signature: Signature, encoded_payload: Vec<u8>, }
/// Type alias for the archived form of [`Signed`]. pub type ArchivedSigned<VerifyingKey, Signature> = ArchivedSignedHelper<VerifyingKey, Signature>;
/// Type alias for the resolver of [`Signed`]. pub type SignedResolver<VerifyingKey, Signature> = SignedHelperResolver<VerifyingKey, Signature>;}