use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use crate::error::CompositionError;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum EvidenceScheme {
Stark,
BbsPlus,
SdJwt,
Mdoc,
TlsNotary,
TeeTdx,
TeeSevSnp,
Ed25519,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Evidence {
pub scheme: EvidenceScheme,
#[serde(with = "serde_bytes")]
pub proof: Vec<u8>,
#[serde(rename = "public-inputs", default, skip_serializing_if = "Option::is_none")]
pub public_inputs: Option<Vec<ByteString>>,
#[serde(rename = "verifier-key", default, skip_serializing_if = "Option::is_none")]
pub verifier_key: Option<ByteString>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub envelope: Option<EvidenceEnvelope>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ByteString(pub Vec<u8>);
impl Serialize for ByteString {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_bytes(&self.0)
}
}
impl<'de> Deserialize<'de> for ByteString {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let bytes = serde_bytes::ByteBuf::deserialize(d)?;
Ok(Self(bytes.into_vec()))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum EvidenceEnvelope {
Stark(StarkProofEnvelope),
BbsPlus(BbsPlusEnvelope),
SdJwt(SdJwtEnvelope),
Mdoc(MdocEnvelope),
TlsNotary(TlsNotaryEnvelope),
Tee(TeeAttestationEnvelope),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StarkProofEnvelope {
pub scheme: String,
pub version: u64,
#[serde(with = "serde_bytes")]
pub proof: Vec<u8>,
#[serde(rename = "public-inputs")]
pub public_inputs: Vec<ByteString>,
#[serde(rename = "verifier-params", default, skip_serializing_if = "Option::is_none")]
pub verifier_params: Option<ByteString>,
#[serde(rename = "transcript-tag", default, skip_serializing_if = "Option::is_none")]
pub transcript_tag: Option<ByteString>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BbsPlusEnvelope {
pub scheme: String,
#[serde(with = "serde_bytes")]
pub signature: Vec<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disclosed: Option<Vec<u64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub nonce: Option<ByteString>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SdJwtEnvelope {
pub scheme: String,
pub jwt: String,
pub disclosures: Vec<String>,
#[serde(rename = "key-binding", default, skip_serializing_if = "Option::is_none")]
pub key_binding: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MdocEnvelope {
pub scheme: String,
#[serde(rename = "issuer-signed", with = "serde_bytes")]
pub issuer_signed: Vec<u8>,
#[serde(rename = "device-signed", default, skip_serializing_if = "Option::is_none")]
pub device_signed: Option<ByteString>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TlsNotaryEnvelope {
pub scheme: String,
#[serde(rename = "session-header", with = "serde_bytes")]
pub session_header: Vec<u8>,
#[serde(rename = "substring-proof", with = "serde_bytes")]
pub substring_proof: Vec<u8>,
#[serde(rename = "notary-pubkey", default, skip_serializing_if = "Option::is_none")]
pub notary_pubkey: Option<ByteString>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TeeAttestationEnvelope {
pub scheme: String,
#[serde(with = "serde_bytes")]
pub quote: Vec<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub collateral: Option<ByteString>,
}
impl Evidence {
pub fn scheme_tag(&self) -> &str {
match self.scheme {
EvidenceScheme::Stark => "stark",
EvidenceScheme::BbsPlus => "bbs-plus",
EvidenceScheme::SdJwt => "sd-jwt",
EvidenceScheme::Mdoc => "mdoc",
EvidenceScheme::TlsNotary => "tls-notary",
EvidenceScheme::TeeTdx => "tee-tdx",
EvidenceScheme::TeeSevSnp => "tee-sev-snp",
EvidenceScheme::Ed25519 => "ed25519",
}
}
pub fn proof(&self) -> &[u8] { &self.proof }
pub fn public_inputs(&self) -> Option<&[ByteString]> { self.public_inputs.as_deref() }
pub fn verifier_key(&self) -> Option<&ByteString> { self.verifier_key.as_ref() }
pub fn new(
scheme: EvidenceScheme,
proof: Vec<u8>,
envelope: Option<EvidenceEnvelope>,
) -> Result<Self, CompositionError> {
let e = Self {
scheme,
proof,
public_inputs: None,
verifier_key: None,
envelope,
};
e.validate_shape()?;
Ok(e)
}
pub fn validate_shape(&self) -> Result<(), CompositionError> {
if self.proof.is_empty() {
return Err(CompositionError::Invariant(
"evidence.proof must be non-empty",
));
}
match (&self.scheme, &self.envelope) {
(EvidenceScheme::Stark, Some(EvidenceEnvelope::Stark(_)))
| (EvidenceScheme::BbsPlus, Some(EvidenceEnvelope::BbsPlus(_)))
| (EvidenceScheme::SdJwt, Some(EvidenceEnvelope::SdJwt(_)))
| (EvidenceScheme::Mdoc, Some(EvidenceEnvelope::Mdoc(_)))
| (EvidenceScheme::TlsNotary, Some(EvidenceEnvelope::TlsNotary(_)))
| (EvidenceScheme::TeeTdx | EvidenceScheme::TeeSevSnp, Some(EvidenceEnvelope::Tee(_)))
| (_, None) => Ok(()),
_ => Err(CompositionError::Invariant(
"evidence.envelope variant must match scheme",
)),
}
}
}