Skip to main content

qualia_core_db/modalities/
carrier.rs

1//! Multi-modal semantic binding **logic** (§29, legal_logic.md) — the content-addressed
2//! carrier binding + extraction.
3//!
4//! SCOPE (honest): this is the tamper-evident *binding* between a media blob and its semantic
5//! graph, plus extraction — the part that matters for provenance and evidence. The actual
6//! binary *container codecs* (PDF/A-3, XMP, PNG, Open Badges v3 byte layout) are task #9; this
7//! module does NOT write those container formats. It content-addresses the blob (real BLAKE3)
8//! and verifies that the carried graph is bound to *that exact* media (any edit breaks it).
9
10use crate::NQuin;
11
12/// Content-address a media blob → a 64-bit media tag (the low 8 bytes of its BLAKE3 hash, into
13/// the one identifier space). `Hash(Blob) → Tag_Media`. Real cryptographic hash, not a toy.
14pub fn media_tag(blob: &[u8]) -> u64 {
15    let h = blake3::hash(blob);
16    let b = h.as_bytes();
17    u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
18}
19
20/// Verify a carrier's binding: re-hash `blob` and confirm it matches the `bound_media_tag` the
21/// carrier recorded. Tamper-evident — any change to the media breaks the binding to its graph.
22#[inline]
23pub fn verify_binding(blob: &[u8], bound_media_tag: u64) -> bool {
24    media_tag(blob) == bound_media_tag
25}
26
27/// Extract the payload quins carried alongside a medium into `out` (`Extract(C_VC) → Σ(Quins)`).
28/// Returns the count written. Zero-heap (caller-supplied `out`).
29pub fn extract_payload(payload: &[NQuin], out: &mut [NQuin]) -> usize {
30    let n = payload.len().min(out.len());
31    out[..n].copy_from_slice(&payload[..n]);
32    n
33}
34
35// ─── Merkle-DAG content addressing (IPLD-equivalent) ──────────────────────────────
36
37/// Hash an internal Merkle-DAG node from its ordered `children` tags: BLAKE3 over the
38/// little-endian concatenation. Order-sensitive (a node commits to its ordered children).
39/// Zero-heap (fixed-size BLAKE3 state on the stack).
40pub fn merkle_node(children: &[u64]) -> u64 {
41    let mut hasher = blake3::Hasher::new();
42    for &c in children {
43        hasher.update(&c.to_le_bytes());
44    }
45    u64::from_le_bytes(hasher.finalize().as_bytes()[..8].try_into().unwrap())
46}
47
48/// Verify a Merkle-DAG node tag against its children (recompute + compare). Tamper-evident.
49#[inline]
50pub fn verify_merkle_node(node_tag: u64, children: &[u64]) -> bool {
51    merkle_node(children) == node_tag
52}
53
54// ─── Streaming hash (massive blobs, bypassing RAM) ────────────────────────────────
55
56/// A streaming content-hash accumulator — feed chunks of a massive blob (e.g. via DirectStorage)
57/// without ever holding it whole in RAM, then finalize to the 64-bit media tag. Zero-heap (the
58/// BLAKE3 state is fixed-size on the stack; chunks are borrowed, not retained).
59#[derive(Default)]
60pub struct StreamHasher {
61    inner: blake3::Hasher,
62}
63
64impl StreamHasher {
65    pub fn new() -> Self {
66        Self {
67            inner: blake3::Hasher::new(),
68        }
69    }
70    /// Feed the next chunk.
71    pub fn update(&mut self, chunk: &[u8]) {
72        self.inner.update(chunk);
73    }
74    /// Finalize to the media tag. Equals [`media_tag`] over the concatenated chunks.
75    pub fn finalize(&self) -> u64 {
76        u64::from_le_bytes(self.inner.finalize().as_bytes()[..8].try_into().unwrap())
77    }
78}
79
80// ─── Cryptographic multi-signature over the payload ───────────────────────────────
81
82/// A **k-of-n multi-signature** over the payload is satisfied iff at least `k` distinct valid
83/// signer attestations are present. The individual signatures are verified by the crypto layer
84/// (Ed25519 / post-quantum ML-DSA via `fiduciary_crypto`); this is the threshold gate.
85/// `valid_signers` = the count of distinct verified signers.
86#[inline]
87pub fn multisig_satisfied(valid_signers: usize, k: usize) -> bool {
88    k > 0 && valid_signers >= k
89}
90
91// ─── Verifiable redaction ─────────────────────────────────────────────────────────
92
93/// **Verifiable redaction**: a redacted blob hides content while preserving the signature/binding.
94/// Each leaf is committed by its hash in a Merkle root; redacting a leaf replaces its *content*
95/// with its *hash tag* — the leaf tags (redacted or not) still recompute the original `root`.
96/// Returns true iff the (possibly-redacted) `leaf_tags` still hash to `original_root`.
97pub fn redaction_preserves_root(leaf_tags: &[u64], original_root: u64) -> bool {
98    merkle_node(leaf_tags) == original_root
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    fn quin(s: u64, o: u64) -> NQuin {
106        let mut q = NQuin {
107            subject: s,
108            predicate: 7,
109            object: o,
110            context: 0,
111            metadata: 0,
112            parity: 0,
113        };
114        q.parity = q.subject ^ q.predicate ^ q.object ^ q.context;
115        q
116    }
117
118    #[test]
119    fn media_tag_is_deterministic_and_content_addressed() {
120        let blob = b"a signed evidentiary photograph's bytes";
121        let tag = media_tag(blob);
122        assert_eq!(tag, media_tag(blob), "deterministic");
123        assert_ne!(tag, media_tag(b"different bytes"), "content-addressed");
124    }
125
126    #[test]
127    fn binding_is_tamper_evident() {
128        let blob = b"original media";
129        let tag = media_tag(blob);
130        assert!(verify_binding(blob, tag), "intact media verifies");
131        assert!(
132            !verify_binding(b"tampered media", tag),
133            "any edit breaks the binding"
134        );
135    }
136
137    #[test]
138    fn payload_extracts_round_trip() {
139        let payload = [quin(1, 2), quin(3, 4)];
140        let mut out = [NQuin::default(); 4];
141        let n = extract_payload(&payload, &mut out);
142        assert_eq!(n, 2);
143        assert_eq!(out[0].subject, 1);
144        assert_eq!(out[1].object, 4);
145    }
146
147    #[test]
148    fn merkle_dag_node_is_order_sensitive_and_tamper_evident() {
149        let a = media_tag(b"leaf-a");
150        let b = media_tag(b"leaf-b");
151        let root = merkle_node(&[a, b]);
152        assert!(verify_merkle_node(root, &[a, b]));
153        assert_ne!(
154            merkle_node(&[a, b]),
155            merkle_node(&[b, a]),
156            "order matters in a DAG"
157        );
158        assert!(
159            !verify_merkle_node(root, &[a, media_tag(b"tampered")]),
160            "any child change breaks it"
161        );
162    }
163
164    #[test]
165    fn streaming_hash_equals_one_shot() {
166        let blob = b"a very large evidentiary recording streamed in chunks";
167        let mut sh = StreamHasher::new();
168        sh.update(&blob[..10]);
169        sh.update(&blob[10..30]);
170        sh.update(&blob[30..]);
171        assert_eq!(
172            sh.finalize(),
173            media_tag(blob),
174            "streaming == one-shot media_tag"
175        );
176    }
177
178    #[test]
179    fn multisig_threshold_and_verifiable_redaction() {
180        // 2-of-3 multisig.
181        assert!(multisig_satisfied(2, 2));
182        assert!(multisig_satisfied(3, 2));
183        assert!(!multisig_satisfied(1, 2));
184        assert!(!multisig_satisfied(3, 0), "a zero threshold is invalid");
185
186        // Redaction: replacing a leaf's content with its hash tag preserves the root.
187        let l0 = media_tag(b"public clause");
188        let l1 = media_tag(b"private medical detail");
189        let root = merkle_node(&[l0, l1]);
190        // The "redacted" view carries l1's TAG (not its content) → same tags → same root verifies.
191        assert!(redaction_preserves_root(&[l0, l1], root));
192        // Substituting a different tag (forging the redaction) fails.
193        assert!(!redaction_preserves_root(&[l0, media_tag(b"forged")], root));
194    }
195}