qualia_core_db/render/
barrier.rs1use ed25519_dalek::{Signature, VerifyingKey};
10
11use crate::container_10d::header::Container10dHeader;
12use crate::container_10d::provenance_section::{decode_provenance_section, validate_provenance};
13use crate::container_10d::section::{parse_section_table, SectionType};
14use crate::container_10d::{decode_mesh_section, verify_whole_file_crc32c};
15use crate::crypto::verifiable_credential::{decode_credential, verify_grounded, VcError};
16use crate::indexing::QuinIndex;
17use crate::modalities::logic::geometry_asset_shacl::{
18 validate_geometry_manifest, GeometryAssetConfiguration, GeometryConstraintViolation,
19 GeometryManifestFacts,
20};
21use crate::render::assets::Mesh;
22
23#[derive(Debug, PartialEq, Eq)]
25pub enum BarrierError {
26 ContainerIntegrity(String),
28 ManifestViolation(Vec<GeometryConstraintViolation>),
30 ProvenanceFailed(String),
32 CredentialInvalid(VcError),
34 CredentialMalformed,
36 NoMesh,
38}
39
40impl std::fmt::Display for BarrierError {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 Self::ContainerIntegrity(e) => write!(f, "Barrier: Container integrity: {e}"),
44 Self::ManifestViolation(vs) => {
45 write!(f, "Barrier: SHACL manifest violations: {vs:?}")
46 }
47 Self::ProvenanceFailed(e) => write!(f, "Barrier: Provenance gate failed: {e}"),
48 Self::CredentialInvalid(e) => write!(f, "Barrier: VC invalid: {e}"),
49 Self::CredentialMalformed => write!(f, "Barrier: VC payload malformed"),
50 Self::NoMesh => write!(f, "Barrier: No QuantizedMesh section found"),
51 }
52 }
53}
54impl std::error::Error for BarrierError {}
55
56pub fn validate_before_render(
64 container_bytes: &[u8],
65 manifest: &GeometryManifestFacts,
66 config: &GeometryAssetConfiguration,
67 index: &QuinIndex,
68 now: u32,
69 key_resolver: impl Fn(u64) -> Option<VerifyingKey>,
70) -> Result<Mesh, BarrierError> {
71 let violations = validate_geometry_manifest(manifest, config);
73 if !violations.is_empty() {
74 return Err(BarrierError::ManifestViolation(violations));
75 }
76
77 let mut bytes_mut = container_bytes.to_vec();
79 verify_whole_file_crc32c(&mut bytes_mut)
80 .map_err(|e| BarrierError::ContainerIntegrity(e.to_string()))?;
81
82 let header = Container10dHeader::parse(&bytes_mut)
83 .map_err(|e| BarrierError::ContainerIntegrity(e.to_string()))?;
84
85 let descs = parse_section_table(&bytes_mut, &header)
86 .map_err(|e| BarrierError::ContainerIntegrity(format!("{e:?}")))?;
87
88 let mut mesh = None;
89 let mut provenance = None;
90
91 for desc in descs.iter() {
92 let st = SectionType::from_u8(desc.section_type);
93 if let Some(st) = st {
94 let off = desc.byte_offset as usize;
95 let len = desc.byte_length as usize;
96 let payload = &bytes_mut[off..off + len];
97
98 if st == SectionType::QuantizedMesh {
99 mesh = Some(payload);
100 } else if st == SectionType::ProvenanceSidecar {
101 provenance = Some(payload);
102 }
103 }
104 }
105
106 let prov_payload = provenance
108 .ok_or_else(|| BarrierError::ProvenanceFailed("No provenance sidecar found".to_string()))?;
109
110 let view = decode_provenance_section(prov_payload)
111 .map_err(|e| BarrierError::ProvenanceFailed(e.to_string()))?;
112
113 validate_provenance(&view).map_err(|e| BarrierError::ProvenanceFailed(e.to_string()))?;
114
115 if let Some(vc_bytes) = view.vc() {
117 if vc_bytes.len() < 64 {
118 return Err(BarrierError::CredentialMalformed);
119 }
120 let (sig_bytes, cred_bytes) = vc_bytes.split_at(64);
121 let signature = Signature::from_bytes(sig_bytes.try_into().unwrap());
122 let credential =
123 decode_credential(cred_bytes).map_err(|_| BarrierError::CredentialMalformed)?;
124
125 let issuer_key = key_resolver(credential.issuer).ok_or_else(|| {
126 BarrierError::CredentialInvalid(VcError::InvalidSignature) })?;
128
129 verify_grounded(&credential, &issuer_key, &signature, now, index)
130 .map_err(BarrierError::CredentialInvalid)?;
131 }
132
133 let mesh_payload = mesh.ok_or(BarrierError::NoMesh)?;
135 decode_mesh_section(mesh_payload).map_err(|e| BarrierError::ContainerIntegrity(e.to_string()))
136}