use crate::checkpoint::{
build_checkpoint_inclusion_proof_for_artifact, canonical_checkpoint_manifest_payload_bytes,
checkpoint_leaf_hash, compute_checkpoint_binding_hash, validate_checkpoint_manifest,
verify_checkpoint_inclusion, CheckpointInclusionProof, CheckpointManifest,
};
#[cfg(test)]
use crate::checkpoint::{build_checkpoint_manifest, CheckpointArtifact};
use crate::content::{
compute_content_descriptor_hash, validate_content_descriptor, verify_full_root_proof_package,
ContentDescriptor, RangeProofPackage, VerifiedRange, CONTENT_PROOF_TYPE_FULL_ROOT_V1,
};
#[cfg(feature = "bao-range-proofs")]
use crate::content::{verify_bao_range_proof_package, CONTENT_PROOF_TYPE_BAO_OUTBOARD_V1};
use crate::envelope::{
verify_attestation, Attestation, CaptureEnvelope, PublishEnvelope, TransformEnvelope,
};
use crate::error::{TrazaeoError, TrazaeoResult};
use crate::utils::Hash;
use crate::verification::VerificationState;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeliveryProofPackage {
pub proof_version: String,
pub artifact_id: String,
pub content_descriptor: ContentDescriptor,
pub content_proof_type: String,
pub content_proof_bytes: Vec<u8>,
pub verified_ranges: Vec<VerifiedRange>,
pub lineage_envelopes: Vec<String>,
pub checkpoint_manifest: CheckpointManifest,
pub checkpoint_inclusion_proof: CheckpointInclusionProof,
pub signature_bundle: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeliveryProofVerificationReport {
pub package_state: VerificationState,
pub signature_state: VerificationState,
pub trust_state: VerificationState,
pub binding_state: VerificationState,
pub reproducibility_state: VerificationState,
pub lineage_state: VerificationState,
pub content_state: VerificationState,
pub checkpoint_state: VerificationState,
pub storage_state: VerificationState,
pub reasons: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeliveryProofVerificationResult {
pub report: DeliveryProofVerificationReport,
pub verified_bytes: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
enum ParsedLineageEnvelope {
Capture(CaptureEnvelope),
Transform(TransformEnvelope),
Publish(PublishEnvelope),
}
impl ParsedLineageEnvelope {
fn envelope_type(&self) -> &'static str {
match self {
Self::Capture(_) => "capture",
Self::Transform(_) => "transform",
Self::Publish(_) => "publish",
}
}
fn subject_id(&self) -> &str {
match self {
Self::Capture(envelope) => &envelope.subject_id,
Self::Transform(envelope) => &envelope.subject_id,
Self::Publish(envelope) => &envelope.subject_id,
}
}
fn lineage_ref(&self) -> String {
format!("{}://{}", self.envelope_type(), self.subject_id())
}
fn attestations(&self) -> &[Attestation] {
match self {
Self::Capture(envelope) => &envelope.attestations,
Self::Transform(envelope) => &envelope.attestations,
Self::Publish(envelope) => &envelope.attestations,
}
}
fn canonical_attestation_payload_bytes(&self) -> Vec<u8> {
match self {
Self::Capture(envelope) => envelope.canonical_attestation_payload_bytes(),
Self::Transform(envelope) => envelope.canonical_attestation_payload_bytes(),
Self::Publish(envelope) => envelope.canonical_attestation_payload_bytes(),
}
}
fn validate(&self) -> Result<(), Vec<String>> {
match self {
Self::Capture(envelope) => envelope.validate(),
Self::Transform(envelope) => envelope.validate(),
Self::Publish(envelope) => envelope.validate(),
}
}
}
pub fn build_delivery_proof_package(
range_proof_package: &RangeProofPackage,
lineage_envelopes: Vec<String>,
checkpoint_manifest: CheckpointManifest,
signature_bundle: Vec<String>,
) -> TrazaeoResult<DeliveryProofPackage> {
validate_content_descriptor(&range_proof_package.content_descriptor)?;
validate_checkpoint_manifest(&checkpoint_manifest)?;
let checkpoint_inclusion_proof = build_checkpoint_inclusion_proof_for_artifact(
&checkpoint_manifest,
&range_proof_package.artifact_id,
)?;
Ok(DeliveryProofPackage {
proof_version: "1.0.0".to_string(),
artifact_id: range_proof_package.artifact_id.clone(),
content_descriptor: range_proof_package.content_descriptor.clone(),
content_proof_type: range_proof_package.content_proof_type.clone(),
content_proof_bytes: range_proof_package.content_proof_bytes.clone(),
verified_ranges: range_proof_package.verified_ranges.clone(),
lineage_envelopes,
checkpoint_manifest,
checkpoint_inclusion_proof,
signature_bundle,
})
}
fn validate_ref_hash_pair_local(
reasons: &mut Vec<String>,
ref_name: &str,
hash_name: &str,
ref_value: &Option<String>,
hash_value: &Option<String>,
) {
let has_ref = ref_value
.as_ref()
.is_some_and(|value| !value.trim().is_empty());
let has_hash = hash_value
.as_ref()
.is_some_and(|value| !value.trim().is_empty());
if has_ref ^ has_hash {
reasons.push(format!("{ref_name} and {hash_name} must be set together"));
}
}
fn parse_attestation_json(raw: &str, context: &'static str) -> TrazaeoResult<Attestation> {
serde_json::from_str(raw)
.map_err(|_| TrazaeoError::serialization(context, "malformed serialized attestation entry"))
}
fn parse_lineage_envelope(raw: &str) -> TrazaeoResult<ParsedLineageEnvelope> {
let value: serde_json::Value = serde_json::from_str(raw).map_err(|_| {
TrazaeoError::serialization(
"parse lineage envelope",
"lineage_envelopes entries must be serialized envelope JSON",
)
})?;
let envelope_type = value
.get("envelope_type")
.and_then(serde_json::Value::as_str)
.ok_or_else(|| {
TrazaeoError::invalid_input(
"parse lineage envelope",
"lineage envelope missing envelope_type",
)
})?;
match envelope_type {
"capture" => Ok(ParsedLineageEnvelope::Capture(
serde_json::from_value(value).map_err(|_| {
TrazaeoError::serialization(
"parse lineage envelope",
"invalid capture envelope payload",
)
})?,
)),
"transform" => Ok(ParsedLineageEnvelope::Transform(
serde_json::from_value(value).map_err(|_| {
TrazaeoError::serialization(
"parse lineage envelope",
"invalid transform envelope payload",
)
})?,
)),
"publish" => Ok(ParsedLineageEnvelope::Publish(
serde_json::from_value(value).map_err(|_| {
TrazaeoError::serialization(
"parse lineage envelope",
"invalid publish envelope payload",
)
})?,
)),
_ => Err(TrazaeoError::invalid_input(
"parse lineage envelope",
"unsupported lineage envelope type",
)),
}
}
fn parse_lineage_envelopes(
lineage_envelopes: &[String],
reasons: &mut Vec<String>,
) -> Option<Vec<ParsedLineageEnvelope>> {
if lineage_envelopes.is_empty() {
reasons.push("lineage_envelopes must not be empty".to_string());
return None;
}
let mut parsed = Vec::with_capacity(lineage_envelopes.len());
for envelope_json in lineage_envelopes {
match parse_lineage_envelope(envelope_json) {
Ok(envelope) => match envelope.validate() {
Ok(()) => parsed.push(envelope),
Err(errors) => {
reasons.extend(errors);
return None;
}
},
Err(err) => {
reasons.push(err.to_string());
return None;
}
}
}
Some(parsed)
}
fn verify_lineage_signatures(
parsed_lineage: Option<&[ParsedLineageEnvelope]>,
signature_bundle: &[String],
reasons: &mut Vec<String>,
) -> VerificationState {
let Some(parsed_lineage) = parsed_lineage else {
reasons.push(
"unable to verify lineage signatures without valid lineage envelopes".to_string(),
);
return VerificationState::Failed;
};
if signature_bundle.is_empty() {
reasons.push("signature_bundle must not be empty".to_string());
return VerificationState::Failed;
}
let mut provided_bundle = Vec::with_capacity(signature_bundle.len());
for entry in signature_bundle {
match parse_attestation_json(entry, "verify delivery proof package") {
Ok(attestation) => provided_bundle.push(attestation),
Err(err) => {
reasons.push(err.to_string());
return VerificationState::Failed;
}
}
}
let expected_bundle = parsed_lineage
.iter()
.flat_map(|envelope| envelope.attestations().iter().cloned())
.collect::<Vec<_>>();
if provided_bundle != expected_bundle {
reasons.push(
"signature_bundle must exactly match the attestations embedded in lineage_envelopes"
.to_string(),
);
return VerificationState::Failed;
}
for envelope in parsed_lineage {
if envelope.attestations().is_empty() {
reasons.push("lineage envelopes must contain attestations".to_string());
return VerificationState::Failed;
}
let payload = envelope.canonical_attestation_payload_bytes();
if !envelope
.attestations()
.iter()
.all(|attestation| verify_attestation(attestation, &payload))
{
reasons.push("invalid lineage envelope attestation signature".to_string());
return VerificationState::Failed;
}
}
VerificationState::Passed
}
fn verify_checkpoint_signatures(
checkpoint_manifest: &CheckpointManifest,
reasons: &mut Vec<String>,
) -> VerificationState {
if checkpoint_manifest.checkpoint_signature_bundle.is_empty() {
reasons.push("checkpoint_signature_bundle must not be empty".to_string());
return VerificationState::Failed;
}
let payload = canonical_checkpoint_manifest_payload_bytes(checkpoint_manifest);
for entry in &checkpoint_manifest.checkpoint_signature_bundle {
let attestation = match parse_attestation_json(entry, "verify delivery proof package") {
Ok(attestation) => attestation,
Err(err) => {
reasons.push(err.to_string());
return VerificationState::Failed;
}
};
if !verify_attestation(&attestation, &payload) {
reasons.push("invalid checkpoint manifest signature".to_string());
return VerificationState::Failed;
}
}
VerificationState::Passed
}
fn verify_binding_state(
package: &DeliveryProofPackage,
reasons: &mut Vec<String>,
) -> VerificationState {
let mut ok = true;
if package.artifact_id != package.content_descriptor.artifact_id {
reasons.push("artifact_id must match content_descriptor.artifact_id".to_string());
ok = false;
}
let mut pair_errors = Vec::new();
validate_ref_hash_pair_local(
&mut pair_errors,
"outboard_ref",
"outboard_hash",
&package.content_descriptor.outboard_ref,
&package.content_descriptor.outboard_hash,
);
if !pair_errors.is_empty() {
reasons.extend(pair_errors);
ok = false;
}
let matching_artifacts = package
.checkpoint_manifest
.published_artifacts
.iter()
.filter(|artifact| artifact.artifact_id == package.artifact_id)
.collect::<Vec<_>>();
if matching_artifacts.len() != 1 {
reasons.push("checkpoint_manifest must contain exactly one published_artifacts entry for artifact_id".to_string());
ok = false;
}
if let Some(artifact) = matching_artifacts.first() {
if artifact.content_root_hash != package.content_descriptor.content_root_hash {
reasons.push("published_artifact content_root_hash must match content_descriptor.content_root_hash".to_string());
ok = false;
}
if artifact.media_type != package.content_descriptor.media_type {
reasons.push(
"published_artifact media_type must match content_descriptor.media_type"
.to_string(),
);
ok = false;
}
let mut artifact_pair_errors = Vec::new();
validate_ref_hash_pair_local(
&mut artifact_pair_errors,
"content_descriptor_ref",
"content_descriptor_hash",
&artifact.content_descriptor_ref,
&artifact.content_descriptor_hash,
);
if !artifact_pair_errors.is_empty() {
reasons.extend(artifact_pair_errors);
ok = false;
}
if artifact.content_descriptor_ref.is_some() || artifact.content_descriptor_hash.is_some() {
let expected_descriptor_hash =
hex::encode(compute_content_descriptor_hash(&package.content_descriptor).0);
if artifact.content_descriptor_hash.as_deref()
!= Some(expected_descriptor_hash.as_str())
{
reasons.push(
"content_descriptor_hash must match the canonical hash of content_descriptor"
.to_string(),
);
ok = false;
}
}
}
let expected_manifest_hash =
hex::encode(compute_checkpoint_binding_hash(&package.checkpoint_manifest).0);
let expected_leaf_hash =
hex::encode(checkpoint_leaf_hash(&package.artifact_id, &expected_manifest_hash).0);
if package.checkpoint_inclusion_proof.leaf_hash != expected_leaf_hash {
reasons.push(
"checkpoint_inclusion_proof.leaf_hash must match artifact_id bound to checkpoint manifest"
.to_string(),
);
ok = false;
}
if ok {
VerificationState::Passed
} else {
VerificationState::Failed
}
}
fn verify_lineage_state(
checkpoint_manifest: &CheckpointManifest,
parsed_lineage: Option<&[ParsedLineageEnvelope]>,
reasons: &mut Vec<String>,
) -> VerificationState {
let Some(parsed_lineage) = parsed_lineage else {
reasons.push(
"lineage completeness could not be evaluated because lineage_envelopes are invalid"
.to_string(),
);
return VerificationState::Failed;
};
let derived_refs = parsed_lineage
.iter()
.map(ParsedLineageEnvelope::lineage_ref)
.collect::<Vec<_>>();
if derived_refs != checkpoint_manifest.lineage_refs {
reasons.push(
"lineage_envelopes must exactly cover checkpoint_manifest.lineage_refs in order"
.to_string(),
);
VerificationState::Failed
} else {
VerificationState::Passed
}
}
fn verify_content_state(
package: &DeliveryProofPackage,
artifact_bytes: Option<&[u8]>,
reasons: &mut Vec<String>,
) -> (VerificationState, Option<Vec<u8>>) {
let range_package = RangeProofPackage {
proof_version: package.proof_version.clone(),
artifact_id: package.artifact_id.clone(),
content_descriptor: package.content_descriptor.clone(),
content_proof_type: package.content_proof_type.clone(),
content_proof_bytes: package.content_proof_bytes.clone(),
verified_ranges: package.verified_ranges.clone(),
};
let verified = match package.content_proof_type.as_str() {
CONTENT_PROOF_TYPE_FULL_ROOT_V1 => {
let Some(artifact_bytes) = artifact_bytes else {
reasons
.push("artifact bytes are required for full_root_v1 verification".to_string());
return (VerificationState::Failed, None);
};
verify_full_root_proof_package(&range_package, artifact_bytes)
}
#[cfg(feature = "bao-range-proofs")]
CONTENT_PROOF_TYPE_BAO_OUTBOARD_V1 => verify_bao_range_proof_package(&range_package),
#[cfg(not(feature = "bao-range-proofs"))]
"bao_outboard_v1" => Err(TrazaeoError::invalid_input(
"verify delivery proof package",
"bao_outboard_v1 verification requires the bao-range-proofs feature",
)),
_ => Err(TrazaeoError::invalid_input(
"verify delivery proof package",
"unsupported content_proof_type",
)),
};
match verified {
Ok(bytes) => (VerificationState::Passed, Some(bytes)),
Err(err) => {
reasons.push(err.to_string());
(VerificationState::Failed, None)
}
}
}
fn verify_checkpoint_state(
package: &DeliveryProofPackage,
trusted_checkpoint_log_root: Option<&Hash>,
reasons: &mut Vec<String>,
) -> VerificationState {
let checkpoint_log_root =
match hex::decode(&package.checkpoint_manifest.checkpoint_log_root_hash) {
Ok(bytes) => bytes,
Err(err) => {
reasons.push(format!(
"invalid checkpoint_log_root_hash hex in checkpoint_manifest: {err}"
));
return VerificationState::Failed;
}
};
let checkpoint_log_root: [u8; 32] = match checkpoint_log_root.as_slice().try_into() {
Ok(root) => root,
Err(_) => {
reasons.push(
"checkpoint_manifest.checkpoint_log_root_hash must be exactly 32 bytes".to_string(),
);
return VerificationState::Failed;
}
};
let manifest_root = Hash(checkpoint_log_root);
if !verify_checkpoint_inclusion(&manifest_root, &package.checkpoint_inclusion_proof) {
reasons.push("checkpoint inclusion proof does not match checkpoint_manifest.checkpoint_log_root_hash".to_string());
return VerificationState::Failed;
}
if let Some(trusted_checkpoint_log_root) = trusted_checkpoint_log_root {
if !verify_checkpoint_inclusion(
trusted_checkpoint_log_root,
&package.checkpoint_inclusion_proof,
) {
reasons.push(
"checkpoint inclusion proof does not match the trusted checkpoint-log root"
.to_string(),
);
return VerificationState::Failed;
}
VerificationState::Passed
} else {
reasons.push(
"checkpoint inclusion was only verified against the self-supplied manifest root"
.to_string(),
);
VerificationState::NotEvaluated
}
}
pub fn is_delivery_proof_report_success(report: &DeliveryProofVerificationReport) -> bool {
let trust_ok = report.trust_state == VerificationState::Passed
|| report.trust_state == VerificationState::NotEvaluated;
let repro_ok = report.reproducibility_state == VerificationState::Passed
|| report.reproducibility_state == VerificationState::NotEvaluated;
let checkpoint_ok = report.checkpoint_state == VerificationState::Passed
|| report.checkpoint_state == VerificationState::NotEvaluated;
let storage_ok = report.storage_state == VerificationState::Passed
|| report.storage_state == VerificationState::NotEvaluated;
report.package_state == VerificationState::Passed
&& report.signature_state == VerificationState::Passed
&& trust_ok
&& report.binding_state == VerificationState::Passed
&& repro_ok
&& report.lineage_state == VerificationState::Passed
&& report.content_state == VerificationState::Passed
&& checkpoint_ok
&& storage_ok
}
pub fn verify_delivery_proof_package_with_report(
package: &DeliveryProofPackage,
artifact_bytes: Option<&[u8]>,
trusted_checkpoint_log_root: Option<&Hash>,
) -> DeliveryProofVerificationResult {
let mut report = DeliveryProofVerificationReport {
package_state: VerificationState::Passed,
signature_state: VerificationState::NotEvaluated,
trust_state: VerificationState::NotEvaluated,
binding_state: VerificationState::NotEvaluated,
reproducibility_state: VerificationState::NotEvaluated,
lineage_state: VerificationState::NotEvaluated,
content_state: VerificationState::NotEvaluated,
checkpoint_state: VerificationState::NotEvaluated,
storage_state: VerificationState::NotEvaluated,
reasons: Vec::new(),
};
if package.proof_version != "1.0.0" {
report
.reasons
.push("proof_version must be 1.0.0".to_string());
report.package_state = VerificationState::Failed;
}
if package.artifact_id.trim().is_empty() {
report
.reasons
.push("artifact_id must not be empty".to_string());
report.package_state = VerificationState::Failed;
}
if package.content_proof_type.trim().is_empty() {
report
.reasons
.push("content_proof_type must not be empty".to_string());
report.package_state = VerificationState::Failed;
}
if package.verified_ranges.is_empty() {
report
.reasons
.push("verified_ranges must not be empty".to_string());
report.package_state = VerificationState::Failed;
}
if let Err(err) = validate_content_descriptor(&package.content_descriptor) {
report.reasons.push(err.to_string());
report.package_state = VerificationState::Failed;
}
if let Err(err) = validate_checkpoint_manifest(&package.checkpoint_manifest) {
report.reasons.push(err.to_string());
report.package_state = VerificationState::Failed;
}
let parsed_lineage = parse_lineage_envelopes(&package.lineage_envelopes, &mut report.reasons);
report.binding_state = verify_binding_state(package, &mut report.reasons);
report.lineage_state = verify_lineage_state(
&package.checkpoint_manifest,
parsed_lineage.as_deref(),
&mut report.reasons,
);
let lineage_signature_state = verify_lineage_signatures(
parsed_lineage.as_deref(),
&package.signature_bundle,
&mut report.reasons,
);
let checkpoint_signature_state =
verify_checkpoint_signatures(&package.checkpoint_manifest, &mut report.reasons);
report.signature_state = if lineage_signature_state == VerificationState::Passed
&& checkpoint_signature_state == VerificationState::Passed
{
VerificationState::Passed
} else {
VerificationState::Failed
};
let (content_state, verified_bytes) =
verify_content_state(package, artifact_bytes, &mut report.reasons);
report.content_state = content_state;
report.checkpoint_state =
verify_checkpoint_state(package, trusted_checkpoint_log_root, &mut report.reasons);
DeliveryProofVerificationResult {
report,
verified_bytes,
}
}
pub fn verify_delivery_proof_package(
package: &DeliveryProofPackage,
artifact_bytes: Option<&[u8]>,
) -> TrazaeoResult<Vec<u8>> {
verify_delivery_proof_package_against_root(package, artifact_bytes, None)
}
pub fn verify_delivery_proof_package_against_root(
package: &DeliveryProofPackage,
artifact_bytes: Option<&[u8]>,
trusted_checkpoint_log_root: Option<&Hash>,
) -> TrazaeoResult<Vec<u8>> {
let result = verify_delivery_proof_package_with_report(
package,
artifact_bytes,
trusted_checkpoint_log_root,
);
if !is_delivery_proof_report_success(&result.report) {
let message = if result.report.reasons.is_empty() {
"delivery proof verification failed".to_string()
} else {
result.report.reasons.join("; ")
};
return Err(TrazaeoError::invalid_input(
"verify delivery proof package",
message,
));
}
result.verified_bytes.ok_or_else(|| {
TrazaeoError::external(
"verify delivery proof package",
"content verification succeeded without returning verified bytes",
)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "bao-range-proofs")]
use crate::content::{
attach_bao_outboard, build_bao_range_proof_package, generate_bao_outboard,
};
use crate::content::{
build_content_descriptor, build_full_root_proof_package, compute_content_descriptor_hash,
ContentDescriptorInput,
};
use crate::envelope::{make_attestation, ArtifactRecord};
use crate::transform_pipeline::{build_transform_envelope, TransformStageInput};
const TEST_SIGNING_KEY_HEX: &str =
"4f3edf983ac636a65a842ce7c78d9aa706d3b113bce036f9a4f5762b76f70f18";
fn signed_transform_envelope(content_root_hash: &str) -> (String, String, String) {
let seed = make_attestation(
"transformer",
TEST_SIGNING_KEY_HEX,
"2026-01-01T00:00:00Z",
b"",
)
.expect("seed attestation");
let stage = TransformStageInput {
schema_version: "1.0.0".to_string(),
issued_at: "2026-01-01T00:00:00Z".to_string(),
subject_id: "transform-1".to_string(),
transform_job_id: "job-1".to_string(),
transform_stage: "raw_to_release".to_string(),
input_refs: vec!["obj://raw/1".to_string()],
output_refs: vec!["obj://release/1".to_string()],
input_artifacts: vec![ArtifactRecord {
artifact_id: "input-1".to_string(),
artifact_ref: "obj://raw/1".to_string(),
content_root_hash: "input-root".to_string(),
}],
output_artifacts: vec![ArtifactRecord {
artifact_id: "artifact-1".to_string(),
artifact_ref: "obj://release/1".to_string(),
content_root_hash: content_root_hash.to_string(),
}],
toolchain: "rust".to_string(),
parameters_ref: "cfg://1".to_string(),
parameters_hash: "cfg-hash".to_string(),
determinism_profile: "det-v1".to_string(),
runtime_env_ref: Some("oci://img".to_string()),
runtime_env_hash: Some("img-hash".to_string()),
provenance_start_mode: "transport_capture".to_string(),
source_manifest_ref: None,
source_manifest_hash: None,
source_root_hash: None,
transform_spec_ref: None,
transform_spec_hash: None,
chunking_profile_ref: None,
chunking_profile_hash: None,
execution_manifest_ref: None,
execution_manifest_hash: None,
runtime_manifest_ref: None,
runtime_manifest_hash: None,
key_id: seed.key_id.clone(),
};
let mut envelope = build_transform_envelope(&stage, seed.clone());
let attestation = make_attestation(
"transformer",
TEST_SIGNING_KEY_HEX,
"2026-01-01T00:00:00Z",
&envelope.canonical_attestation_payload_bytes(),
)
.expect("transform attestation");
envelope.attestations = vec![attestation.clone()];
(
serde_json::to_string(&envelope).expect("serialize transform envelope"),
serde_json::to_string(&attestation).expect("serialize transform attestation"),
format!("transform://{}", envelope.subject_id),
)
}
fn signed_checkpoint_manifest(
descriptor: &crate::content::ContentDescriptor,
lineage_ref: &str,
) -> CheckpointManifest {
let mut manifest = build_checkpoint_manifest(
"checkpoint-1",
"2026-01-01T00:00:00Z/2026-01-01T01:00:00Z",
None,
Vec::new(),
vec![CheckpointArtifact {
artifact_id: descriptor.artifact_id.clone(),
content_root_hash: descriptor.content_root_hash.clone(),
content_descriptor_ref: Some("descriptor://artifact-1".to_string()),
content_descriptor_hash: Some(hex::encode(
compute_content_descriptor_hash(descriptor).0,
)),
media_type: descriptor.media_type.clone(),
}],
vec![lineage_ref.to_string()],
);
let attestation = make_attestation(
"checkpoint-signer",
TEST_SIGNING_KEY_HEX,
"2026-01-01T00:00:00Z",
&canonical_checkpoint_manifest_payload_bytes(&manifest),
)
.expect("checkpoint attestation");
manifest.checkpoint_signature_bundle =
vec![serde_json::to_string(&attestation).expect("serialize checkpoint attestation")];
manifest
}
#[test]
fn full_root_delivery_proof_package_roundtrip() {
let data = b"abcdefghijklmno";
let descriptor = build_content_descriptor(ContentDescriptorInput {
artifact_id: "artifact-1",
root: Hash(*blake3::hash(data).as_bytes()),
chunk_size: 1024,
leaf_count: 1,
byte_length: data.len() as u64,
media_type: "application/octet-stream",
created_at: "2026-01-01T00:00:00Z",
});
let range = build_full_root_proof_package(&descriptor).expect("range");
let (lineage_envelope_json, signature_json, lineage_ref) =
signed_transform_envelope(&descriptor.content_root_hash);
let checkpoint_manifest = signed_checkpoint_manifest(&descriptor, &lineage_ref);
let package = build_delivery_proof_package(
&range,
vec![lineage_envelope_json],
checkpoint_manifest,
vec![signature_json],
)
.expect("delivery package");
let verified =
verify_delivery_proof_package(&package, Some(data)).expect("verify delivery package");
assert_eq!(verified, data);
}
#[test]
fn delivery_proof_report_emits_explicit_states() {
let data = b"abcdefghijklmno";
let descriptor = build_content_descriptor(ContentDescriptorInput {
artifact_id: "artifact-1",
root: Hash(*blake3::hash(data).as_bytes()),
chunk_size: 1024,
leaf_count: 1,
byte_length: data.len() as u64,
media_type: "application/octet-stream",
created_at: "2026-01-01T00:00:00Z",
});
let range = build_full_root_proof_package(&descriptor).expect("range");
let (lineage_envelope_json, signature_json, lineage_ref) =
signed_transform_envelope(&descriptor.content_root_hash);
let checkpoint_manifest = signed_checkpoint_manifest(&descriptor, &lineage_ref);
let trusted_root = Hash(
hex::decode(&checkpoint_manifest.checkpoint_log_root_hash)
.expect("trusted root hex")
.as_slice()
.try_into()
.expect("trusted root bytes"),
);
let package = build_delivery_proof_package(
&range,
vec![lineage_envelope_json],
checkpoint_manifest,
vec![signature_json],
)
.expect("delivery package");
let result =
verify_delivery_proof_package_with_report(&package, Some(data), Some(&trusted_root));
assert_eq!(result.report.package_state, VerificationState::Passed);
assert_eq!(result.report.signature_state, VerificationState::Passed);
assert_eq!(result.report.trust_state, VerificationState::NotEvaluated);
assert_eq!(result.report.binding_state, VerificationState::Passed);
assert_eq!(
result.report.reproducibility_state,
VerificationState::NotEvaluated
);
assert_eq!(result.report.lineage_state, VerificationState::Passed);
assert_eq!(result.report.content_state, VerificationState::Passed);
assert_eq!(result.report.checkpoint_state, VerificationState::Passed);
assert_eq!(result.report.storage_state, VerificationState::NotEvaluated);
assert_eq!(result.verified_bytes.as_deref(), Some(data.as_slice()));
}
#[test]
fn delivery_proof_report_fails_for_incomplete_lineage() {
let data = b"abcdefghijklmno";
let descriptor = build_content_descriptor(ContentDescriptorInput {
artifact_id: "artifact-1",
root: Hash(*blake3::hash(data).as_bytes()),
chunk_size: 1024,
leaf_count: 1,
byte_length: data.len() as u64,
media_type: "application/octet-stream",
created_at: "2026-01-01T00:00:00Z",
});
let range = build_full_root_proof_package(&descriptor).expect("range");
let (lineage_envelope_json, signature_json, lineage_ref) =
signed_transform_envelope(&descriptor.content_root_hash);
let mut checkpoint_manifest = signed_checkpoint_manifest(&descriptor, &lineage_ref);
checkpoint_manifest
.lineage_refs
.push("capture://capture-1".to_string());
let package = build_delivery_proof_package(
&range,
vec![lineage_envelope_json],
checkpoint_manifest,
vec![signature_json],
)
.expect("delivery package");
let result = verify_delivery_proof_package_with_report(&package, Some(data), None);
assert_eq!(result.report.lineage_state, VerificationState::Failed);
assert!(!is_delivery_proof_report_success(&result.report));
assert!(verify_delivery_proof_package(&package, Some(data)).is_err());
}
#[cfg(feature = "bao-range-proofs")]
#[test]
fn delivery_proof_package_roundtrip() {
let data = b"abcdefghijklmno";
let descriptor = build_content_descriptor(ContentDescriptorInput {
artifact_id: "artifact-1",
root: Hash(*blake3::hash(data).as_bytes()),
chunk_size: 1024,
leaf_count: 1,
byte_length: data.len() as u64,
media_type: "application/octet-stream",
created_at: "2026-01-01T00:00:00Z",
});
let outboard = generate_bao_outboard(data);
let descriptor = attach_bao_outboard(&descriptor, Some("outboard://1"), &outboard);
let (lineage_envelope_json, signature_json, lineage_ref) =
signed_transform_envelope(&descriptor.content_root_hash);
let range =
build_bao_range_proof_package(&descriptor, data, &outboard.outboard_bytes, 0, 4)
.expect("range");
let checkpoint_manifest = signed_checkpoint_manifest(&descriptor, &lineage_ref);
let package = build_delivery_proof_package(
&range,
vec![lineage_envelope_json],
checkpoint_manifest,
vec![signature_json],
)
.expect("delivery package");
let decoded =
verify_delivery_proof_package(&package, None).expect("verify delivery package");
assert_eq!(decoded, b"abcd");
}
}