1	//! Integration tests for hierarchical consent delegation.
2	//! Mirrors Verus proofs from cubie_consent_hierarchy_spec.rs.
3
4	use cubie_bare_metal::consent::*;
5
6	// ---------------------------------------------------------------------------
7	// Test fixtures
8	// ---------------------------------------------------------------------------
9
10	fn root_node() -> ConsentNode {
11	    ConsentNode {
12	        node_id: 1, parent_id: 0, depth: 0,
13	        consent_grant: true, sub_policy: 100, capacity: 500, active: true,
14	    }
15	}
16
17	fn child_node() -> ConsentNode {
18	    ConsentNode {
19	        node_id: 2, parent_id: 1, depth: 1,
20	        consent_grant: true, sub_policy: 50, capacity: 100, active: true,
21	    }
22	}
23
24	fn valid_stabilizer() -> StabilizerCode {
25	    StabilizerCode { code_distance: 3, num_checks: 10, syndrome_valid: true }
26	}
27
28	fn valid_channel() -> ConsentChannel {
29	    ConsentChannel { src_id: 1, dst_id: 2, bandwidth: 1000, ch_open: true }
30	}
31
32	// ---------------------------------------------------------------------------
33	// CUB-0741: Conditional QEC realization
34	// ---------------------------------------------------------------------------
35
36	#[test]
37	fn full_consent_implies_stabilizer_ok() {
38	    let p = root_node();
39	    let c = child_node();
40	    let sc = valid_stabilizer();
41	    assert!(full_consent_ok(&p, &c, &sc));
42	    assert!(stabilizer_ok(&sc));
43	}
44
45	// ---------------------------------------------------------------------------
46	// CUB-0742: Stabilizer validity
47	// ---------------------------------------------------------------------------
48
49	#[test]
50	fn zero_distance_stabilizer_invalid() {
51	    let sc = StabilizerCode { code_distance: 0, num_checks: 10, syndrome_valid: true };
52	    assert!(!stabilizer_ok(&sc));
53	}
54
55	#[test]
56	fn positive_distance_valid_syndrome_ok() {
57	    let sc = valid_stabilizer();
58	    assert!(stabilizer_ok(&sc));
59	}
60
61	// ---------------------------------------------------------------------------
62	// CUB-0743: Holevo-bounded extraction
63	// ---------------------------------------------------------------------------
64
65	#[test]
66	fn sub_policy_within_capacity() {
67	    let p = root_node();
68	    let c = child_node(); // sub_policy=50, parent capacity=500
69	    assert!(sub_policy_compliant(&p, &c));
70	    assert!(c.sub_policy <= p.capacity);
71	}
72
73	#[test]
74	fn sub_policy_exceeds_capacity() {
75	    let p = root_node();
76	    let mut c = child_node();
77	    c.sub_policy = 999; // exceeds parent capacity of 500
78	    assert!(!sub_policy_compliant(&p, &c));
79	}
80
81	// ---------------------------------------------------------------------------
82	// CUB-0744: Measurement-collapse consent (cascade revoke)
83	// ---------------------------------------------------------------------------
84
85	#[test]
86	fn parent_inactive_cascades_deny() {
87	    let mut p = root_node();
88	    p.active = false;
89	    let c = child_node();
90	    assert!(!cascading_consent(&p, &c));
91	}
92
93	#[test]
94	fn parent_inactive_full_consent_fails() {
95	    let mut p = root_node();
96	    p.active = false;
97	    let c = child_node();
98	    let sc = valid_stabilizer();
99	    assert!(!full_consent_ok(&p, &c, &sc));
100	}
101
102	// ---------------------------------------------------------------------------
103	// CUB-0745: Entanglement grant/revoke correlation
104	// ---------------------------------------------------------------------------
105
106	#[test]
107	fn all_active_granted_passes() {
108	    let p = root_node();
109	    let c = child_node();
110	    assert!(cascading_consent(&p, &c));
111	}
112
113	#[test]
114	fn parent_consent_revoked_fails() {
115	    let mut p = root_node();
116	    p.consent_grant = false;
117	    let c = child_node();
118	    assert!(!cascading_consent(&p, &c));
119	}
120
121	#[test]
122	fn child_consent_revoked_fails() {
123	    let p = root_node();
124	    let mut c = child_node();
125	    c.consent_grant = false;
126	    assert!(!cascading_consent(&p, &c));
127	}
128
129	// ---------------------------------------------------------------------------
130	// CUB-0746: Context-dependent access by depth
131	// ---------------------------------------------------------------------------
132
133	#[test]
134	fn shallower_requestor_allowed() {
135	    let requestor = root_node(); // depth 0
136	    let target = child_node();   // depth 1
137	    assert!(can_modify_preferences(&requestor, &target));
138	}
139
140	#[test]
141	fn deeper_requestor_denied() {
142	    let mut requestor = child_node();
143	    requestor.depth = 5;
144	    let mut target = root_node();
145	    target.depth = 2;
146	    assert!(!can_modify_preferences(&requestor, &target));
147	}
148
149	// ---------------------------------------------------------------------------
150	// CUB-0748: Consent channel requires active endpoints
151	// ---------------------------------------------------------------------------
152
153	#[test]
154	fn channel_with_active_endpoints() {
155	    let ch = valid_channel();
156	    let src = root_node();
157	    let dst = child_node();
158	    assert!(channel_valid(&ch, &src, &dst));
159	}
160
161	#[test]
162	fn channel_with_inactive_source_fails() {
163	    let ch = valid_channel();
164	    let mut src = root_node();
165	    src.active = false;
166	    let dst = child_node();
167	    assert!(!channel_valid(&ch, &src, &dst));
168	}
169
170	// ---------------------------------------------------------------------------
171	// CUB-0749: Child access requires channel + consent
172	// ---------------------------------------------------------------------------
173
174	#[test]
175	fn child_access_parent_both_required() {
176	    let ch = valid_channel();
177	    let p = root_node();
178	    let c = child_node();
179	    assert!(child_can_access_parent(&ch, &p, &c));
180	}
181
182	#[test]
183	fn child_access_fails_without_consent() {
184	    let ch = valid_channel();
185	    let mut p = root_node();
186	    p.consent_grant = false;
187	    let c = child_node();
188	    assert!(!child_can_access_parent(&ch, &p, &c));
189	}
190
191	// ---------------------------------------------------------------------------
192	// CUB-0750: Percolation threshold
193	// ---------------------------------------------------------------------------
194
195	#[test]
196	fn above_threshold_flows() {
197	    let ch = valid_channel(); // bandwidth=1000
198	    assert!(percolates(&ch, 500));
199	}
200
201	#[test]
202	fn below_threshold_blocked() {
203	    let mut ch = valid_channel();
204	    ch.bandwidth = 100;
205	    assert!(!percolates(&ch, 500));
206	}
207
208	#[test]
209	fn closed_channel_blocked() {
210	    let mut ch = valid_channel();
211	    ch.ch_open = false;
212	    assert!(!percolates(&ch, 500));
213	}
214
215	// ---------------------------------------------------------------------------
216	// CUB-0751: Consent capacity
217	// ---------------------------------------------------------------------------
218
219	#[test]
220	fn capacity_available_when_under() {
221	    let node = root_node(); // capacity=500
222	    assert!(capacity_available(&node, 499));
223	}
224
225	#[test]
226	fn capacity_exhausted_when_at_limit() {
227	    let node = root_node(); // capacity=500
228	    assert!(!capacity_available(&node, 500));
229	}
230
231	#[test]
232	fn capacity_exhausted_when_over() {
233	    let node = root_node();
234	    assert!(!capacity_available(&node, 999));
235	}
236
237	// ---------------------------------------------------------------------------
238	// CUB-0752: Inactive node no modification
239	// ---------------------------------------------------------------------------
240
241	#[test]
242	fn inactive_node_denies_modification() {
243	    let requestor = root_node();
244	    let mut target = child_node();
245	    target.active = false;
246	    assert!(!can_modify_preferences(&requestor, &target));
247	}
248
249	// ---------------------------------------------------------------------------
250	// CUB-0753: Effective sub-policy bounded
251	// ---------------------------------------------------------------------------
252
253	#[test]
254	fn effective_policy_bounded_by_parent() {
255	    let p = root_node();   // capacity=500
256	    let c = child_node();  // sub_policy=50
257	    assert!(effective_sub_policy(&p, &c) <= p.capacity);
258	}
259
260	#[test]
261	fn effective_policy_bounded_by_child() {
262	    let p = root_node();
263	    let c = child_node();
264	    assert!(effective_sub_policy(&p, &c) <= c.sub_policy);
265	}
266
267	#[test]
268	fn effective_policy_takes_min() {
269	    let p = root_node();   // capacity=500
270	    let c = child_node();  // sub_policy=50
271	    assert_eq!(effective_sub_policy(&p, &c), 50); // min(50, 500) = 50
272
273	    let mut c2 = child_node();
274	    c2.sub_policy = 999;
275	    assert_eq!(effective_sub_policy(&p, &c2), 500); // min(999, 500) = 500
276	}
277

//! Integration tests for hierarchical consent delegation.
//! Mirrors Verus proofs from cubie_consent_hierarchy_spec.rs.

use cubie_bare_metal::consent::*;

// ---------------------------------------------------------------------------
// Test fixtures
// ---------------------------------------------------------------------------

fn root_node() -> ConsentNode {
    ConsentNode {
        node_id: 1, parent_id: 0, depth: 0,
        consent_grant: true, sub_policy: 100, capacity: 500, active: true,
    }
}

fn child_node() -> ConsentNode {
    ConsentNode {
        node_id: 2, parent_id: 1, depth: 1,
        consent_grant: true, sub_policy: 50, capacity: 100, active: true,
    }
}

fn valid_stabilizer() -> StabilizerCode {
    StabilizerCode { code_distance: 3, num_checks: 10, syndrome_valid: true }
}

fn valid_channel() -> ConsentChannel {
    ConsentChannel { src_id: 1, dst_id: 2, bandwidth: 1000, ch_open: true }
}

// ---------------------------------------------------------------------------
// CUB-0741: Conditional QEC realization
// ---------------------------------------------------------------------------

#[test]
fn full_consent_implies_stabilizer_ok() {
    let p = root_node();
    let c = child_node();
    let sc = valid_stabilizer();
    assert!(full_consent_ok(&p, &c, &sc));
    assert!(stabilizer_ok(&sc));
}

// ---------------------------------------------------------------------------
// CUB-0742: Stabilizer validity
// ---------------------------------------------------------------------------

#[test]
fn zero_distance_stabilizer_invalid() {
    let sc = StabilizerCode { code_distance: 0, num_checks: 10, syndrome_valid: true };
    assert!(!stabilizer_ok(&sc));
}

#[test]
fn positive_distance_valid_syndrome_ok() {
    let sc = valid_stabilizer();
    assert!(stabilizer_ok(&sc));
}

// ---------------------------------------------------------------------------
// CUB-0743: Holevo-bounded extraction
// ---------------------------------------------------------------------------

#[test]
fn sub_policy_within_capacity() {
    let p = root_node();
    let c = child_node(); // sub_policy=50, parent capacity=500
    assert!(sub_policy_compliant(&p, &c));
    assert!(c.sub_policy <= p.capacity);
}

#[test]
fn sub_policy_exceeds_capacity() {
    let p = root_node();
    let mut c = child_node();
    c.sub_policy = 999; // exceeds parent capacity of 500
    assert!(!sub_policy_compliant(&p, &c));
}

// ---------------------------------------------------------------------------
// CUB-0744: Measurement-collapse consent (cascade revoke)
// ---------------------------------------------------------------------------

#[test]
fn parent_inactive_cascades_deny() {
    let mut p = root_node();
    p.active = false;
    let c = child_node();
    assert!(!cascading_consent(&p, &c));
}

#[test]
fn parent_inactive_full_consent_fails() {
    let mut p = root_node();
    p.active = false;
    let c = child_node();
    let sc = valid_stabilizer();
    assert!(!full_consent_ok(&p, &c, &sc));
}

// ---------------------------------------------------------------------------
// CUB-0745: Entanglement grant/revoke correlation
// ---------------------------------------------------------------------------

#[test]
fn all_active_granted_passes() {
    let p = root_node();
    let c = child_node();
    assert!(cascading_consent(&p, &c));
}

#[test]
fn parent_consent_revoked_fails() {
    let mut p = root_node();
    p.consent_grant = false;
    let c = child_node();
    assert!(!cascading_consent(&p, &c));
}

#[test]
fn child_consent_revoked_fails() {
    let p = root_node();
    let mut c = child_node();
    c.consent_grant = false;
    assert!(!cascading_consent(&p, &c));
}

// ---------------------------------------------------------------------------
// CUB-0746: Context-dependent access by depth
// ---------------------------------------------------------------------------

#[test]
fn shallower_requestor_allowed() {
    let requestor = root_node(); // depth 0
    let target = child_node();   // depth 1
    assert!(can_modify_preferences(&requestor, &target));
}

#[test]
fn deeper_requestor_denied() {
    let mut requestor = child_node();
    requestor.depth = 5;
    let mut target = root_node();
    target.depth = 2;
    assert!(!can_modify_preferences(&requestor, &target));
}

// ---------------------------------------------------------------------------
// CUB-0748: Consent channel requires active endpoints
// ---------------------------------------------------------------------------

#[test]
fn channel_with_active_endpoints() {
    let ch = valid_channel();
    let src = root_node();
    let dst = child_node();
    assert!(channel_valid(&ch, &src, &dst));
}

#[test]
fn channel_with_inactive_source_fails() {
    let ch = valid_channel();
    let mut src = root_node();
    src.active = false;
    let dst = child_node();
    assert!(!channel_valid(&ch, &src, &dst));
}

// ---------------------------------------------------------------------------
// CUB-0749: Child access requires channel + consent
// ---------------------------------------------------------------------------

#[test]
fn child_access_parent_both_required() {
    let ch = valid_channel();
    let p = root_node();
    let c = child_node();
    assert!(child_can_access_parent(&ch, &p, &c));
}

#[test]
fn child_access_fails_without_consent() {
    let ch = valid_channel();
    let mut p = root_node();
    p.consent_grant = false;
    let c = child_node();
    assert!(!child_can_access_parent(&ch, &p, &c));
}

// ---------------------------------------------------------------------------
// CUB-0750: Percolation threshold
// ---------------------------------------------------------------------------

#[test]
fn above_threshold_flows() {
    let ch = valid_channel(); // bandwidth=1000
    assert!(percolates(&ch, 500));
}

#[test]
fn below_threshold_blocked() {
    let mut ch = valid_channel();
    ch.bandwidth = 100;
    assert!(!percolates(&ch, 500));
}

#[test]
fn closed_channel_blocked() {
    let mut ch = valid_channel();
    ch.ch_open = false;
    assert!(!percolates(&ch, 500));
}

// ---------------------------------------------------------------------------
// CUB-0751: Consent capacity
// ---------------------------------------------------------------------------

#[test]
fn capacity_available_when_under() {
    let node = root_node(); // capacity=500
    assert!(capacity_available(&node, 499));
}

#[test]
fn capacity_exhausted_when_at_limit() {
    let node = root_node(); // capacity=500
    assert!(!capacity_available(&node, 500));
}

#[test]
fn capacity_exhausted_when_over() {
    let node = root_node();
    assert!(!capacity_available(&node, 999));
}

// ---------------------------------------------------------------------------
// CUB-0752: Inactive node no modification
// ---------------------------------------------------------------------------

#[test]
fn inactive_node_denies_modification() {
    let requestor = root_node();
    let mut target = child_node();
    target.active = false;
    assert!(!can_modify_preferences(&requestor, &target));
}

// ---------------------------------------------------------------------------
// CUB-0753: Effective sub-policy bounded
// ---------------------------------------------------------------------------

#[test]
fn effective_policy_bounded_by_parent() {
    let p = root_node();   // capacity=500
    let c = child_node();  // sub_policy=50
    assert!(effective_sub_policy(&p, &c) <= p.capacity);
}

#[test]
fn effective_policy_bounded_by_child() {
    let p = root_node();
    let c = child_node();
    assert!(effective_sub_policy(&p, &c) <= c.sub_policy);
}

#[test]
fn effective_policy_takes_min() {
    let p = root_node();   // capacity=500
    let c = child_node();  // sub_policy=50
    assert_eq!(effective_sub_policy(&p, &c), 50); // min(50, 500) = 50

    let mut c2 = child_node();
    c2.sub_policy = 999;
    assert_eq!(effective_sub_policy(&p, &c2), 500); // min(999, 500) = 500
}


1	//! Integration tests for cubie_admit — runs on host with mock MMIO.
2	#![allow(unused_unsafe)] // cubie_admit is unsafe on real hardware; mock is safe
3	//!
4	//! Test matrix:
5	//!   ✓ Full allow path (all conditions pass)
6	//!   ✗ Null pointer
7	//!   ✗ Each GPU corner flag false (who/what/how)
8	//!   ✗ Each deployment corner flag false (where/when/why)
9	//!   ✗ has_valid_lease = false
10	//!   ✗ epoch_fresh = false
11	//!   ✗ HMAC mismatch (wrong capability_hmac)
12	//!   ✗ HMAC wrong key slot
13	//!   ✗ NVLink waiter detected (SM=0 + VRAM>0)
14	//!   ✗ PCIe congested (BW below threshold)
15	//!   ✓ PCIe adapter cache hit (fast path)
16	//!   ✓ NVLink safe when SM>0 (active peer)
17	//!   ✓ NVLink safe when VRAM=0 (no memory held)
18
19	use cubie_bare_metal::{
20	    cubie_admit, hmac_sha256, AdmitResult, CubieRequest,
21	    mmio, pcie,
22	};
23	use std::ptr;
24
25	// ---------------------------------------------------------------------------
26	// Test fixture helpers
27	// ---------------------------------------------------------------------------
28
29	const TEST_KEY: [u8; 32] = [0x42u8; 32];
30	const TEST_REQUEST_HASH: u64 = 0xDEAD_BEEF_CAFE_0001;
31	const TEST_ADAPTER_ID: u64 = 0x1234_5678_9ABC_DEF0;
32
33	fn compute_hmac(request_hash: u64) -> u64 {
34	    let mac = hmac_sha256(&TEST_KEY, &request_hash.to_be_bytes());
35	    u64::from_be_bytes(mac[0..8].try_into().unwrap())
36	}
37
38	/// Reset all mock state and return a fully-passing CubieRequest.
39	fn valid_request() -> CubieRequest {
40	    // Clear PCIe cache so each test starts clean
41	    pcie::clear_adapter_cache();
42
43	    // Set eFuse key for slot 0
44	    mmio::mock_set_efuse_key(0, &TEST_KEY);
45
46	    // NVLink peer 0: SM active (non-zero), no VRAM held
47	    mmio::mock_set_nvlink_sm(0, 100);
48	    mmio::mock_set_nvlink_vram(0, 0);
49
50	    // PCIe: high bandwidth
51	    mmio::mock_set_pcie_bw(8192);
52
53	    CubieRequest {
54	        who_pass: true, what_pass: true, how_pass: true,
55	        where_pass: true, when_pass: true, why_pass: true,
56	        has_valid_lease: true,
57	        epoch_fresh: true,
58	        request_hash:    TEST_REQUEST_HASH,
59	        capability_hmac: compute_hmac(TEST_REQUEST_HASH),
60	        key_slot:  0,
61	        peer_id:   0,
62	        adapter_id: TEST_ADAPTER_ID,
63	    }
64	}
65
66	fn is_allow(r: AdmitResult) -> bool {
67	    matches!(r, AdmitResult::Allow)
68	}
69
70	fn is_deny(r: AdmitResult) -> bool {
71	    matches!(r, AdmitResult::Deny)
72	}
73
74	// ---------------------------------------------------------------------------
75	// Happy path
76	// ---------------------------------------------------------------------------
77
78	#[test]
79	fn allow_all_conditions_pass() {
80	    let req = valid_request();
81	    assert!(is_allow(unsafe { cubie_admit(&req) }));
82	}
83
84	// ---------------------------------------------------------------------------
85	// Null pointer
86	// ---------------------------------------------------------------------------
87
88	#[test]
89	fn deny_null_pointer() {
90	    assert!(is_deny(unsafe { cubie_admit(ptr::null()) }));
91	}
92
93	// ---------------------------------------------------------------------------
94	// GPU corner: one flag false at a time
95	// ---------------------------------------------------------------------------
96
97	#[test]
98	fn deny_who_false() {
99	    let mut req = valid_request();
100	    req.who_pass = false;
101	    assert!(is_deny(unsafe { cubie_admit(&req) }));
102	}
103
104	#[test]
105	fn deny_what_false() {
106	    let mut req = valid_request();
107	    req.what_pass = false;
108	    assert!(is_deny(unsafe { cubie_admit(&req) }));
109	}
110
111	#[test]
112	fn deny_how_false() {
113	    let mut req = valid_request();
114	    req.how_pass = false;
115	    assert!(is_deny(unsafe { cubie_admit(&req) }));
116	}
117
118	// ---------------------------------------------------------------------------
119	// Deployment corner: one flag false at a time
120	// ---------------------------------------------------------------------------
121
122	#[test]
123	fn deny_where_false() {
124	    let mut req = valid_request();
125	    req.where_pass = false;
126	    assert!(is_deny(unsafe { cubie_admit(&req) }));
127	}
128
129	#[test]
130	fn deny_when_false() {
131	    let mut req = valid_request();
132	    req.when_pass = false;
133	    assert!(is_deny(unsafe { cubie_admit(&req) }));
134	}
135
136	#[test]
137	fn deny_why_false() {
138	    let mut req = valid_request();
139	    req.why_pass = false;
140	    assert!(is_deny(unsafe { cubie_admit(&req) }));
141	}
142
143	// ---------------------------------------------------------------------------
144	// Lease and epoch
145	// ---------------------------------------------------------------------------
146
147	#[test]
148	fn deny_lease_invalid() {
149	    let mut req = valid_request();
150	    req.has_valid_lease = false;
151	    assert!(is_deny(unsafe { cubie_admit(&req) }));
152	}
153
154	#[test]
155	fn deny_epoch_stale() {
156	    let mut req = valid_request();
157	    req.epoch_fresh = false;
158	    assert!(is_deny(unsafe { cubie_admit(&req) }));
159	}
160
161	// ---------------------------------------------------------------------------
162	// HMAC checks
163	// ---------------------------------------------------------------------------
164
165	#[test]
166	fn deny_hmac_mismatch() {
167	    let mut req = valid_request();
168	    req.capability_hmac = req.capability_hmac.wrapping_add(1); // corrupt
169	    assert!(is_deny(unsafe { cubie_admit(&req) }));
170	}
171
172	#[test]
173	fn deny_hmac_wrong_key_slot() {
174	    // Slot 1 key is all-zeros (never set) — HMAC will not match slot 0 MAC
175	    let mut req = valid_request();
176	    req.key_slot = 1;
177	    assert!(is_deny(unsafe { cubie_admit(&req) }));
178	}
179
180	#[test]
181	fn deny_hmac_wrong_request_hash() {
182	    let mut req = valid_request();
183	    // capability_hmac was computed for TEST_REQUEST_HASH, now hash is different
184	    req.request_hash = TEST_REQUEST_HASH.wrapping_add(1);
185	    assert!(is_deny(unsafe { cubie_admit(&req) }));
186	}
187
188	#[test]
189	fn allow_hmac_exact_match() {
190	    // Recompute with a different hash to confirm the crypto path works end-to-end
191	    let hash = 0xFEED_FACE_DEAD_BEEF_u64;
192	    let mut req = valid_request();
193	    req.request_hash    = hash;
194	    req.capability_hmac = compute_hmac(hash);
195	    assert!(is_allow(unsafe { cubie_admit(&req) }));
196	}
197
198	// ---------------------------------------------------------------------------
199	// NVLink waiter detection
200	// ---------------------------------------------------------------------------
201
202	#[test]
203	fn deny_nvlink_waiter_sm_zero_vram_held() {
204	    let mut req = valid_request();
205	    mmio::mock_set_nvlink_sm(0, 0);   // SM idle
206	    mmio::mock_set_nvlink_vram(0, 512); // 512 MiB held — waiter signature
207	    assert!(is_deny(unsafe { cubie_admit(&req) }));
208	}
209
210	#[test]
211	fn allow_nvlink_sm_active_vram_held() {
212	    // SM active + VRAM held = legitimate compute, not a waiter
213	    let req = valid_request();
214	    mmio::mock_set_nvlink_sm(0, 50);
215	    mmio::mock_set_nvlink_vram(0, 512);
216	    assert!(is_allow(unsafe { cubie_admit(&req) }));
217	}
218
219	#[test]
220	fn allow_nvlink_sm_zero_no_vram() {
221	    // SM idle + no VRAM = peer is genuinely idle, not holding anything
222	    let req = valid_request();
223	    mmio::mock_set_nvlink_sm(0, 0);
224	    mmio::mock_set_nvlink_vram(0, 0);
225	    assert!(is_allow(unsafe { cubie_admit(&req) }));
226	}
227
228	// ---------------------------------------------------------------------------
229	// PCIe adapter cache and bandwidth
230	// ---------------------------------------------------------------------------
231
232	#[test]
233	fn deny_pcie_congested_cache_miss() {
234	    pcie::clear_adapter_cache();
235	    let req = valid_request();
236	    mmio::mock_set_pcie_bw(1024); // 1 GB/s — below 4 GB/s threshold
237	    assert!(is_deny(unsafe { cubie_admit(&req) }));
238	}
239
240	#[test]
241	fn allow_pcie_good_bandwidth() {
242	    pcie::clear_adapter_cache();
243	    let req = valid_request();
244	    mmio::mock_set_pcie_bw(4096); // exactly at threshold — allowed
245	    assert!(is_allow(unsafe { cubie_admit(&req) }));
246	}
247
248	#[test]
249	fn allow_pcie_cache_hit_bypasses_bw_check() {
250	    pcie::clear_adapter_cache();
251	    let req = valid_request();
252	    // First call: BW good → adapter gets cached
253	    mmio::mock_set_pcie_bw(8192);
254	    assert!(is_allow(unsafe { cubie_admit(&req) }));
255	    // Second call: BW now bad, but adapter is cached → still allowed
256	    mmio::mock_set_pcie_bw(100);
257	    assert!(is_allow(unsafe { cubie_admit(&req) }));
258	}
259
260	#[test]
261	fn deny_zero_adapter_id_sentinel() {
262	    let mut req = valid_request();
263	    req.adapter_id = 0; // sentinel EMPTY value
264	    assert!(is_deny(unsafe { cubie_admit(&req) }));
265	}
266

//! Integration tests for cubie_admit — runs on host with mock MMIO.
#![allow(unused_unsafe)] // cubie_admit is unsafe on real hardware; mock is safe
//!
//! Test matrix:
//!   ✓ Full allow path (all conditions pass)
//!   ✗ Null pointer
//!   ✗ Each GPU corner flag false (who/what/how)
//!   ✗ Each deployment corner flag false (where/when/why)
//!   ✗ has_valid_lease = false
//!   ✗ epoch_fresh = false
//!   ✗ HMAC mismatch (wrong capability_hmac)
//!   ✗ HMAC wrong key slot
//!   ✗ NVLink waiter detected (SM=0 + VRAM>0)
//!   ✗ PCIe congested (BW below threshold)
//!   ✓ PCIe adapter cache hit (fast path)
//!   ✓ NVLink safe when SM>0 (active peer)
//!   ✓ NVLink safe when VRAM=0 (no memory held)

use cubie_bare_metal::{
    cubie_admit, hmac_sha256, AdmitResult, CubieRequest,
    mmio, pcie,
};
use std::ptr;

// ---------------------------------------------------------------------------
// Test fixture helpers
// ---------------------------------------------------------------------------

const TEST_KEY: [u8; 32] = [0x42u8; 32];
const TEST_REQUEST_HASH: u64 = 0xDEAD_BEEF_CAFE_0001;
const TEST_ADAPTER_ID: u64 = 0x1234_5678_9ABC_DEF0;

fn compute_hmac(request_hash: u64) -> u64 {
    let mac = hmac_sha256(&TEST_KEY, &request_hash.to_be_bytes());
    u64::from_be_bytes(mac[0..8].try_into().unwrap())
}

/// Reset all mock state and return a fully-passing CubieRequest.
fn valid_request() -> CubieRequest {
    // Clear PCIe cache so each test starts clean
    pcie::clear_adapter_cache();

    // Set eFuse key for slot 0
    mmio::mock_set_efuse_key(0, &TEST_KEY);

    // NVLink peer 0: SM active (non-zero), no VRAM held
    mmio::mock_set_nvlink_sm(0, 100);
    mmio::mock_set_nvlink_vram(0, 0);

    // PCIe: high bandwidth
    mmio::mock_set_pcie_bw(8192);

    CubieRequest {
        who_pass: true, what_pass: true, how_pass: true,
        where_pass: true, when_pass: true, why_pass: true,
        has_valid_lease: true,
        epoch_fresh: true,
        request_hash:    TEST_REQUEST_HASH,
        capability_hmac: compute_hmac(TEST_REQUEST_HASH),
        key_slot:  0,
        peer_id:   0,
        adapter_id: TEST_ADAPTER_ID,
    }
}

fn is_allow(r: AdmitResult) -> bool {
    matches!(r, AdmitResult::Allow)
}

fn is_deny(r: AdmitResult) -> bool {
    matches!(r, AdmitResult::Deny)
}

// ---------------------------------------------------------------------------
// Happy path
// ---------------------------------------------------------------------------

#[test]
fn allow_all_conditions_pass() {
    let req = valid_request();
    assert!(is_allow(unsafe { cubie_admit(&req) }));
}

// ---------------------------------------------------------------------------
// Null pointer
// ---------------------------------------------------------------------------

#[test]
fn deny_null_pointer() {
    assert!(is_deny(unsafe { cubie_admit(ptr::null()) }));
}

// ---------------------------------------------------------------------------
// GPU corner: one flag false at a time
// ---------------------------------------------------------------------------

#[test]
fn deny_who_false() {
    let mut req = valid_request();
    req.who_pass = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_what_false() {
    let mut req = valid_request();
    req.what_pass = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_how_false() {
    let mut req = valid_request();
    req.how_pass = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

// ---------------------------------------------------------------------------
// Deployment corner: one flag false at a time
// ---------------------------------------------------------------------------

#[test]
fn deny_where_false() {
    let mut req = valid_request();
    req.where_pass = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_when_false() {
    let mut req = valid_request();
    req.when_pass = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_why_false() {
    let mut req = valid_request();
    req.why_pass = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

// ---------------------------------------------------------------------------
// Lease and epoch
// ---------------------------------------------------------------------------

#[test]
fn deny_lease_invalid() {
    let mut req = valid_request();
    req.has_valid_lease = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_epoch_stale() {
    let mut req = valid_request();
    req.epoch_fresh = false;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

// ---------------------------------------------------------------------------
// HMAC checks
// ---------------------------------------------------------------------------

#[test]
fn deny_hmac_mismatch() {
    let mut req = valid_request();
    req.capability_hmac = req.capability_hmac.wrapping_add(1); // corrupt
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_hmac_wrong_key_slot() {
    // Slot 1 key is all-zeros (never set) — HMAC will not match slot 0 MAC
    let mut req = valid_request();
    req.key_slot = 1;
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_hmac_wrong_request_hash() {
    let mut req = valid_request();
    // capability_hmac was computed for TEST_REQUEST_HASH, now hash is different
    req.request_hash = TEST_REQUEST_HASH.wrapping_add(1);
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn allow_hmac_exact_match() {
    // Recompute with a different hash to confirm the crypto path works end-to-end
    let hash = 0xFEED_FACE_DEAD_BEEF_u64;
    let mut req = valid_request();
    req.request_hash    = hash;
    req.capability_hmac = compute_hmac(hash);
    assert!(is_allow(unsafe { cubie_admit(&req) }));
}

// ---------------------------------------------------------------------------
// NVLink waiter detection
// ---------------------------------------------------------------------------

#[test]
fn deny_nvlink_waiter_sm_zero_vram_held() {
    let mut req = valid_request();
    mmio::mock_set_nvlink_sm(0, 0);   // SM idle
    mmio::mock_set_nvlink_vram(0, 512); // 512 MiB held — waiter signature
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn allow_nvlink_sm_active_vram_held() {
    // SM active + VRAM held = legitimate compute, not a waiter
    let req = valid_request();
    mmio::mock_set_nvlink_sm(0, 50);
    mmio::mock_set_nvlink_vram(0, 512);
    assert!(is_allow(unsafe { cubie_admit(&req) }));
}

#[test]
fn allow_nvlink_sm_zero_no_vram() {
    // SM idle + no VRAM = peer is genuinely idle, not holding anything
    let req = valid_request();
    mmio::mock_set_nvlink_sm(0, 0);
    mmio::mock_set_nvlink_vram(0, 0);
    assert!(is_allow(unsafe { cubie_admit(&req) }));
}

// ---------------------------------------------------------------------------
// PCIe adapter cache and bandwidth
// ---------------------------------------------------------------------------

#[test]
fn deny_pcie_congested_cache_miss() {
    pcie::clear_adapter_cache();
    let req = valid_request();
    mmio::mock_set_pcie_bw(1024); // 1 GB/s — below 4 GB/s threshold
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}

#[test]
fn allow_pcie_good_bandwidth() {
    pcie::clear_adapter_cache();
    let req = valid_request();
    mmio::mock_set_pcie_bw(4096); // exactly at threshold — allowed
    assert!(is_allow(unsafe { cubie_admit(&req) }));
}

#[test]
fn allow_pcie_cache_hit_bypasses_bw_check() {
    pcie::clear_adapter_cache();
    let req = valid_request();
    // First call: BW good → adapter gets cached
    mmio::mock_set_pcie_bw(8192);
    assert!(is_allow(unsafe { cubie_admit(&req) }));
    // Second call: BW now bad, but adapter is cached → still allowed
    mmio::mock_set_pcie_bw(100);
    assert!(is_allow(unsafe { cubie_admit(&req) }));
}

#[test]
fn deny_zero_adapter_id_sentinel() {
    let mut req = valid_request();
    req.adapter_id = 0; // sentinel EMPTY value
    assert!(is_deny(unsafe { cubie_admit(&req) }));
}


I've read every source file. Now launching the adversarial drill -- red team (3 agents in parallel) attacking different surfaces, then blue team responds.


ToolSearch

Question: select:TaskCreate,TaskUpdate


Question: select:TaskCreate,TaskUpdate


TaskCreate

Red Team: Attack math proofs and verification chain

Launch red team agents to adversarially audit the Coq/Lean proofs, Verus specs, and bare-metal Rust code for smoke, pseudo-code, trivial tautologies, and gaps in the verification chain


Task #10 created successfully: Red Team: Attack math proofs and verification chain


Blue Team: Counter red team findings with defenses

Respond to each red team finding with either a fix or a justified defense


Task #11 created successfully: Blue Team: Counter red team findings with defenses


Consensus: Synthesize red/blue into hardening plan

Merge red team attacks and blue team defenses into a consensus hardening plan with concrete fixes


Task #12 created successfully: Consensus: Synthesize red/blue into hardening plan


Implement hardening: Crux-MIR, Aeneas, Creusot, cargo-miri

Set up and run the verification tools (Crux-MIR, Aeneas, Creusot, cargo-miri) plus implement consensus fixes


Task #13 created successfully: Implement hardening: Crux-MIR, Aeneas, Creusot, cargo-miri


Updated task #10 status


Launching 3 red team agents in parallel -- each attacking a different surface of the verification stack.


Agent

Red Team: Attack formal proofs

Prompt:
You are RED TEAM — an adversarial auditor of formal verification claims. Your job is to find every weakness, tautology, smoke-and-mirrors trick, and gap in the formal proof stack. Be ruthless. Assume the developer was lazy and cutting corners.

CONTEXT: A "Cubie Trust Compiler" claims 754 theorems across Coq 8.18.0, Lean 4, and Verus. I need you to audit the 21 NEW theorems (CUB-0734 through CUB-0754) specifically.

Here are the actual Coq proofs you must attack:

== CubieDualLedger.v (7 theorems, CUB-0734..0740) ==

CUB-0734 (dual_ledger_non_disclosure): Proves that if dual_ledger_ok=true, then public_hides_identity=true.
PROOF: Simply destructures the conjunction (andb_true_iff) and returns the middle conjunct.

CUB-0735 (anon_linkage_preserved): Proves dual_ledger_ok=true implies anon_id_linked=true.
PROOF: Same pattern — destructure conjunction, return first conjunct.

CUB-0736 (no_consent_no_access): Proves priv_consent_grant=false implies dual_ledger_ok=false.
PROOF: Unfolds, rewrites with false, uses andb_false_r.

CUB-0737 (no_capability_no_access): Proves pub_capability=false implies dual_ledger_ok=false.
PROOF: Similar rewrite + case analysis on all other bools.

CUB-0738 (policy_filter_independent_of_consent): Proves that if policy_filter=true and dual_ledger_ok=false, then policy_filter is STILL true.
PROOF: Just returns the hypothesis. Literally `exact Hf`.

CUB-0739 (identity_collision_detection): Proves if two dual-ledgers share anon_hash but differ in real_id_hash, and both are valid, then their anon_hashes are equal.
PROOF: Just returns the hypothesis `exact Hsame`.

CUB-0740 (epoch_mismatch_breaks_linkage): Proves epoch mismatch implies anon_id_linked=false.
PROOF: Uses Nat.eqb_neq, case analysis.

== CubieQuantumConsentAdvanced.v (14 theorems, CUB-0741..0754) ==

These prove properties of a consent hierarchy: cascading consent, sub-policy bounds, stabilizer validity, channel validity, capacity limits, percolation thresholds, effective sub-policy bounds, and graph capacity.

Key proof patterns: Most use unfold + rewrite + reflexivity, or apply Nat.leb_le/Nat.ltb_lt, or simple case analysis with destruct.

YOUR ATTACK VECTORS:

1. **TAUTOLOGY DETECTION**: Which theorems are trivially true by definition? (i.e., just extracting a conjunct from a conjunction, or returning a hypothesis unchanged). Rate each CUB theorem 1-10 on "non-triviality" where 1 = pure tautology and 10 = deep mathematical insight.

2. **PROOF VACUITY**: Are any proofs vacuously true? Do they prove things that are true regardless of the hypothesis? Is CUB-0738 just "if P is true then P is true" in disguise?

3. **MODEL ADEQUACY**: The Coq model uses `nat` (Peano naturals) for hashes and IDs. The Rust code uses `u64`. Does the Coq model actually capture overflow behavior? Could an adversary exploit the gap between infinite-precision Coq nat and finite u64?

4. **VERIFICATION GAP**: The Coq proofs operate on Coq Record types. The Rust runtime uses #[repr(C)] structs. There is NO formal link between them. The proofs don't verify the Rust code — they verify a Coq model. How wide is this gap?

5. **QUANTUM THEATER**: The consent hierarchy theorems use quantum physics terminology (QEC, Holevo bound, entanglement, measurement collapse). Is this legitimate mathematical structure or is it dressing up simple boolean algebra with impressive-sounding names?

6. **HMAC TRUNCATION**: The capability_hmac_verify function truncates HMAC-SHA256 to 64 bits. What is the collision probability? Is this cryptographically adequate?

7. **LEAN MIRROR WEAKNESS**: Are the Lean 4 proofs truly independent verification, or just syntactic translations of the Coq? If they use the same predicate definitions, what additional assurance do they provide?

For EACH finding, provide:
- SEVERITY: CRITICAL / HIGH / MEDIUM / LOW
- FINDING: What you found
- EVIDENCE: Why it's a problem
- SUGGESTED ENHANCEMENT: What would fix it

Report in under 800 words. Be brutally honest.


Red Team: Attack Rust runtime code

Prompt:
You are RED TEAM — an adversarial security auditor targeting the bare-metal Rust runtime. Your job is to find every bug, undefined behavior, security vulnerability, and lazy shortcut in the implementation.

CONTEXT: A bare-metal Rust library (no_std, no alloc, #[repr(C)]) implements an AI workload admission gate. It claims ~730ns/call and formal verification. I need you to attack the IMPLEMENTATION, not the proofs.

Here is the actual source code:

== sha256.rs ==
- FIPS 180-4 SHA-256 in ~150 lines
- K constants and H0 initial values present
- compress() function with message schedule + 64 rounds
- sha256_short() for messages ≤55 bytes (single block)
- sha256() for messages up to ~119 bytes (two blocks)
- Uses wrapping_add for modular arithmetic
- try_into().unwrap() on line 123 for block slice conversion

== hmac.rs ==
- HMAC-SHA256 per FIPS 198-1
- Fixed inner buffer of 128 bytes (64 ipad + 64 max message)
- Fixed outer buffer of 96 bytes (64 opad + 32 inner hash)
- ct_eq_32: constant-time 32-byte equality using OR accumulator

== hmac_stub.rs (capability_hmac_verify) ==
- Reads 32-byte key from eFuse via MMIO
- Computes HMAC-SHA256(key, request_hash.to_be_bytes()) — 8 byte message
- TRUNCATES to first 8 bytes for comparison
- Builds two 32-byte arrays: mac_truncated[0..8] = mac[0..8], rest zeros; expected[0..8] = capability_hmac bytes, rest zeros
- Calls ct_eq_32 to compare

== pcie.rs ==
- Static mutable ADAPTER_CACHE: [u64; 16] — accessed via raw pointers
- cache_contains() scans with read_volatile through raw pointer arithmetic
- cache_insert() writes with write_volatile
- FIFO eviction (overwrites slot 0 when full)
- adapter_swap_ok() checks cache, then reads MMIO PCIe bandwidth
- BW_THRESHOLD_MBS = 4096 (4 GB/s)
- adapter_id == 0 (EMPTY sentinel) always denied

== nvlink.rs ==
- Reads SM activity + VRAM allocation from MMIO
- Waiter signature: sm==0 && vram>0 → deny
- Otherwise allow

== mmio.rs ==
- Production: read_volatile from fixed addresses (EFUSE_BASE=0x1000_0000, NVLINK_BASE=0xFF00_0000, PCIE_BASE=0xFF10_0000)
- Mock: AtomicU32 arrays with Relaxed ordering

== consent.rs + dual_ledger.rs ==
- Pure boolean functions, no unsafe, no MMIO
- All #[inline], all take references

YOUR ATTACK VECTORS:

1. **UNDEFINED BEHAVIOR**: Find any UB in the unsafe code. Are the raw pointer operations in pcie.rs sound? Is the `static mut ADAPTER_CACHE` safe for single-threaded bare-metal? What if it's not actually single-threaded?

2. **SHA-256 CORRECTNESS**: Are the K constants correct? Are the sigma/gamma functions correct? Has the implementation been tested against NIST test vectors? The sha256() function has a try_into().unwrap() — can this panic on bare-metal (where panic = infinite loop)?

3. **HMAC TRUNCATION SECURITY**: Truncating HMAC-SHA256 to 64 bits means 2^64 collision space. Is this adequate? What's the birthday attack threshold? For a workload admission gate processing ~1M requests/sec, when does collision become likely?

4. **TIMING SIDE CHANNELS**: ct_eq_32 uses OR accumulator — is this truly constant-time on all targets (RISC-V, x86, AArch64)? Does the compiler optimize away the constant-time property? Are there other timing leaks (e.g., short-circuit && in admit_inner)?

5. **ADAPTER CACHE POISONING**: The PCIe adapter cache uses static mut with raw pointers. Can an attacker inject a malicious adapter_id into the cache? Once cached, it bypasses all bandwidth checks forever. What's the eviction policy weakness?

6. **MOCK vs PRODUCTION DIVERGENCE**: The mock feature uses AtomicU32 with Relaxed ordering. Production uses read_volatile. Are these semantically equivalent? Could tests pass but production fail?

7. **INPUT VALIDATION**: cubie_admit takes a raw pointer. It checks null but not alignment. What happens with misaligned pointers on RISC-V (which may not support unaligned access)?

8. **PANIC PATHS**: Find every possible panic in the no_std code. try_into().unwrap() in sha256.rs — can it fail? Any index bounds that could panic?

For EACH finding, provide:
- SEVERITY: CRITICAL / HIGH / MEDIUM / LOW
- FINDING: What you found
- EVIDENCE: Specific code location
- SUGGESTED FIX: Concrete code change

Report in under 800 words.


Red Team: Attack test coverage gaps

Prompt:
You are RED TEAM — an adversarial test coverage auditor. Your job is to find every gap in the test suite, every missing edge case, and every scenario where the tests give false confidence.

CONTEXT: A bare-metal Rust admission gate claims "62/62 tests pass" with 21 admit tests, 26 consent hierarchy tests, and 15 dual-ledger tests. I need you to find what ISN'T tested.

Here are the actual test files:

== admit.rs (21 tests) ==
Tests: allow_all_pass, deny_null, deny_who/what/how/where/when/why_false, deny_lease_invalid, deny_epoch_stale, deny_hmac_mismatch, deny_hmac_wrong_key_slot, deny_hmac_wrong_request_hash, allow_hmac_exact_match, deny_nvlink_waiter, allow_nvlink_sm_active_vram_held, allow_nvlink_sm_zero_no_vram, deny_pcie_congested, allow_pcie_good_bw, allow_pcie_cache_hit, deny_zero_adapter_id

== consent_hierarchy.rs (26 tests) ==
Tests cover: CUB-0741 through CUB-0753 with positive and negative cases for cascading_consent, sub_policy_compliant, stabilizer_ok, full_consent_ok, channel_valid, child_can_access_parent, percolates, capacity_available, can_modify_preferences, effective_sub_policy.

== dual_ledger.rs (15 tests) ==
Tests cover: CUB-0734 through CUB-0740 with positive and negative cases for dual_ledger_ok, anon_id_linked, public_hides_identity, consent_valid, policy_filter_active.

YOUR ATTACK VECTORS:

1. **SHA-256 NIST VECTORS**: Are there ANY tests that verify sha256() against known NIST test vectors (FIPS 180-4 examples)? If not, the entire crypto foundation is unverified. The HMAC tests only prove internal consistency (HMAC matches itself), not correctness.

2. **HMAC NIST VECTORS**: Same question — are there tests against RFC 4231 test vectors? Or does the test just compute HMAC and check it matches its own output?

3. **BOUNDARY CONDITIONS**:
   - What happens when sha256() receives exactly 55 bytes? Exactly 56 bytes? Exactly 64 bytes? 119 bytes? 120 bytes?
   - What happens when hmac inner buffer overflows (msg > 64 bytes)?
   - What happens with key_slot = 255? peer_id = 255?

4. **CONCURRENCY**: The tests run sequentially but use shared mutable state (ADAPTER_CACHE, MOCK_EFUSE, etc.). Is there any test that verifies thread safety? The `clear_adapter_cache()` is called per-test but what about test parallelism in cargo test?

5. **PROPERTY-BASED TESTING**: Are there any property-based tests (quickcheck, proptest)? For a system claiming formal verification, the tests should exercise random inputs, not just hand-picked constants.

6. **NEGATIVE SPACE**: What COMBINATIONS of failures are tested? Each test flips ONE flag. What about:
   - All flags false simultaneously?
   - HMAC correct but all bools false?
   - HMAC wrong AND NVLink waiter detected?
   - Multiple simultaneous hardware failures?

7. **CONSENT HIERARCHY GAPS**:
   - MAX_CONSENT_DEPTH is 8 — is it ever tested?
   - What happens with depth=255? node_id=0? capacity=0?
   - What about cycles in the hierarchy (child.parent_id = child.node_id)?
   - self-referential nodes?

8. **DUAL LEDGER GAPS**:
   - anon_hash = 0 for both sides (trivial match)?
   - pub_policy_hash = 0 AND priv_real_id_hash = 0 (identity leak with zeros)?
   - What about u64::MAX values?

9. **PERFORMANCE REGRESSION**: Is there any benchmark that would catch performance regression from ~730ns? The speedtest binary exists but is it run as part of CI?

10. **CROSS-COMPILATION TESTING**: Tests only run on the host (x86_64 with std+mock). The 3 cross-compilation targets (RISC-V, x86_64-none, AArch64) only verify compilation, not execution. No test runs on actual bare-metal.

For EACH finding provide:
- SEVERITY: CRITICAL / HIGH / MEDIUM / LOW
- GAP: What's missing
- RISK: What could go wrong because of this gap
- SUGGESTED TEST: Concrete test code or approach

Report in under 800 words.


I now have everything I need. Here is my adversarial audit.

---

## RED TEAM AUDIT: CUB-0734 through CUB-0754 (21 Theorems)

### FINDING 1: Pervasive Tautologies -- Most Theorems Are Definitional Extraction

**SEVERITY: HIGH**

The majority of these theorems prove nothing beyond "if a conjunction is true, then one of its conjuncts is true" or "if a hypothesis is assumed, then it holds." Non-triviality ratings (1=pure tautology, 10=deep insight):

| CUB | Score | Verdict |
|-----|-------|---------|
| 0734 | 1 | Extracts conjunct 2 from a 3-way AND. |
| 0735 | 1 | Extracts conjunct 1 from the same AND. |
| 0736 | 2 | `false && x = false`. One rewrite step. |
| 0737 | 2 | Same pattern as 0736. |
| 0738 | **1** | `P -> Q -> P`. This is literally the K combinator. The second hypothesis is never used. The proof is `exact Hf`. |
| 0739 | **1** | Conclusion = hypothesis `Hsame`. The "collision detection" and "Sybil prevention" claims in the docstring are completely unsupported by the proof. It proves: "if X=Y then X=Y." The two unused hypotheses (different real_id_hash, both valid) are theater. |
| 0740 | 3 | Genuine work: epoch inequality implies eqb returns false. Still shallow. |
| 0741 | 1 | Extracts conjunct from a conjunction (same as 0734). |
| 0742 | 2 | `0 < 0` is false. |
| 0743 | 2 | `leb` implies `<=`. Definitional. |
| 0744 | 2 | `false && x = false`. |
| 0745 | 2 | Substitute all `true` values, get `true`. |
| 0746 | 2 | Same Nat.leb_le conversion as 0743. |
| 0747 | 3 | Shows two `if` chains agree when both branches are `true`. Marginally interesting. |
| 0748 | 2 | `false && x = false` again. CUB-0748b (`consent_channel_bounded`) proves `x <= x`. Via `lia`. |
| 0749 | 1 | Two more conjunct extractions. |
| 0750 | 2 | Nat.leb_gt / Nat.leb_le again. |
| 0751 | 2 | Nat.ltb_ge / Nat.ltb_lt. |
| 0752 | 2 | Destruct on active, then leb. |
| 0753 | 3 | Uses Nat.le_min_r / Nat.le_min_l. Standard library fact. |
| 0754 | 3 | Uses list fold + lia. Highest actual proof effort in the set. |

**Evidence**: Zero theorems exceed Coq difficulty level "homework exercise 1." No theorem requires induction, no theorem reasons about multi-step computation, no theorem involves quantifier alternation. The entire set could be replaced by `decide` or `simp` in Lean.

**Enhancement**: Prove properties that are NOT definitional: e.g., that the admission predicate is monotone (weakening one field can only go from allow to deny), or that the dual-ledger construction is information-theoretically hiding (formalize what "non-disclosure" actually means against a computational adversary, not just "two fields are not equal").

---

### FINDING 2: CUB-0738 and CUB-0739 Are Vacuous/Fraudulent

**SEVERITY: CRITICAL**

**CUB-0738** (`policy_filter_independent_of_consent`): The theorem statement is `P -> Q -> P`. The second hypothesis (`dual_ledger_ok dl = false`) is completely unused. The proof is literally `exact Hf`. This proves nothing about independence -- it is the tautology "if policy_filter_active is true, then policy_filter_active is true." The claim of "orthogonal axis" in the docstring is unsupported.

**CUB-0739** (`identity_collision_detection`): The conclusion is identical to hypothesis `Hsame`. The three other hypotheses (different real_id_hash, both dual_ledger_ok) are never touched. The claim that this "detects collisions" or "prevents Sybil attacks" is false. The theorem says: "if two anon_hashes are equal, they are equal." It does not establish that collisions are detectable, that different real identities are distinguishable, or that any invariant is violated on collision.

**Evidence**: Both Lean mirrors confirm the same pattern -- `hf` returned directly (0738), `hsame` returned directly (0739). File: `C:\Users\NickV\cubie-verification\lean\CubieDualLedger.lean`, lines 120 and 131.

**Enhancement**: CUB-0738 should prove that `policy_filter_active` and `consent_valid` are logically independent (there exist models where each combination of true/false is satisfiable). CUB-0739 should prove that a collision (same anon_hash, different real_id_hash) violates an injectivity invariant, not just repeat a hypothesis.

---

### FINDING 3: HMAC Truncation to 64 Bits

**SEVERITY: CRITICAL**

File `C:\Users\NickV\cubie-verification\bare-metal\src\hmac_stub.rs`, lines 51-60: The `capability_hmac_verify` function truncates HMAC-SHA256 to its first 8 bytes (64 bits). Collision probability via birthday bound is approximately `2^(-32)` for `2^32` attempts -- about 4 billion requests to find a collision with 50% probability. For a hardware gate that may process millions of requests, this is dangerously low. NIST SP 800-107 recommends a minimum MAC tag length of 64 bits only for low-security applications, and 128 bits for general use.

Furthermore, the Coq model (`public_hides_identity`) uses `nat` equality to model hash comparison but does not model truncation at all. The formal proof says nothing about collision resistance of the truncated MAC.

**Enhancement**: Use at minimum 128 bits (16 bytes) of the HMAC. Model truncation in the Coq formalization and prove that the security reduction from full HMAC to truncated HMAC is bounded.

---

### FINDING 4: Coq-to-Rust Verification Gap is Total

**SEVERITY: CRITICAL**

The Coq proofs operate on Coq `Record` types with `nat` (unbounded Peano naturals). The Rust runtime uses `#[repr(C)]` structs with `u64` (64-bit unsigned integers) and `u8`. There is NO extraction, NO Verus spec for the dual_ledger module (confirmed: `verus/*dual*` and `verus/*consent*` return no files), and NO formal link between the two. Specifically:

- **Overflow**: Coq `nat` is infinite-precision. Rust `u64` wraps at `2^64 - 1`. The Coq proofs assume `anon_hash` comparison is on exact mathematical values. In Rust, two different infinite-precision hashes could collide at `u64` width. No overflow proof exists.
- **Depth field**: Coq uses `nat` for `depth`. Rust uses `u8` (max 255). The Coq proof of `access_by_relationship` assumes unbounded depth, but the Rust `MAX_CONSENT_DEPTH` is 8 and `depth` is `u8`. This constraint is unmodeled.
- **No extraction**: The Rust code was hand-written to "mirror" the Coq definitions. There is no Coq extraction to OCaml/Haskell, no verified compilation, and no Verus proof linking the Rust implementation to the Coq spec.

The proofs verify a mathematical model. The model is not formally connected to the code that runs.

**Enhancement**: Write Verus specs for `dual_ledger.rs` and `consent.rs` (as was done for the admit gate via `cubie_runtime_spec.rs`). Alternatively, use Coq extraction to generate the runtime code. At minimum, add overflow assertions and model `u64` width in the Coq formalization using `Z` (integers) with explicit bounds.

---

### FINDING 5: Quantum Terminology Is Pure Theater

**SEVERITY: MEDIUM**

Every quantum physics term in CubieQuantumConsentAdvanced.v maps to trivial boolean/arithmetic operations:

| Quantum Term | Actual Operation |
|---|---|
| "QEC realization" (CUB-0741) | Extracting a conjunct from AND |
| "Holevo bound" (CUB-0743) | `child.sub_policy <= parent.capacity` |
| "Measurement collapse" (CUB-0744) | `false && x = false` |
| "Entanglement correlation" (CUB-0745) | `true && true && true && true = true` |
| "Non-commutative algebra" (CUB-0747) | Two `if-then-else` chains agree when both branches pass |
| "Stabilizer code" | A struct with a bool field `syndrome_valid` |
| "Wedge reconstruction" (CUB-0749) | Extracting a conjunct from AND |
| "Information percolation" (CUB-0750) | `threshold <= bandwidth` |

None of these involve Hilbert spaces, tensor products, unitary evolution, error-correction codes, quantum channels, or any mathematical structure from quantum information theory. The `StabilizerCode` record has no generators, no check matrix, no syndrome computation -- it is a boolean flag named `syndrome_valid`. Calling `Nat.leb` "the Holevo bound" is misleading to any reviewer.

**Enhancement**: Either implement actual quantum error correction formalism (stabilizer groups, Pauli matrices, syndrome extraction) or rename these theorems to reflect what they actually prove: boolean access control properties.

---

### FINDING 6: Lean Mirrors Provide Zero Additional Assurance

**SEVERITY: MEDIUM**

The Lean 4 files (`C:\Users\NickV\cubie-verification\lean\CubieDualLedger.lean`, `C:\Users\NickV\cubie-verification\lean\CubieQuantumConsentAdvanced.lean`) are line-for-line syntactic translations of the Coq files. They use identical predicate definitions, identical structure layouts, and prove identical theorem statements. The Lean proofs use `simp` where Coq uses `unfold + rewrite`, but the proof obligations are the same. If the Coq model is wrong (e.g., `public_hides_identity` is a bad model of non-disclosure), the Lean mirror is equally wrong. Two proofs of the same wrong statement provide no additional assurance. The claim of "independent verification" is misleading.

**Enhancement**: If the goal is cross-verification, the Lean proofs should use an independently-developed model (different predicate definitions that capture the same intended semantics) and prove the two models are equivalent. Otherwise, drop the "independent verification" claim.

---

### FINDING 7: `public_hides_identity` Is a Weak Non-Disclosure Model

**SEVERITY: HIGH**

File `C:\Users\NickV\cubie-verification\coq\CubieDualLedger.v`, lines 112-114: The predicate `public_hides_identity` checks that `pub_policy_hash != priv_real_id_hash`. This only prevents the most trivial leak -- the policy hash literally equaling the identity hash. It does not prevent: (a) the policy hash being a deterministic function of the identity hash, (b) statistical correlation between policy hashes and identities, (c) side-channel leakage, or (d) any information-theoretic notion of hiding. An adversary who knows the policy hash space can trivially map policy hashes to identities if the mapping is injective, and this predicate would still return `true`.

**Enhancement**: Model non-disclosure using an indistinguishability definition (e.g., for any two identities, the public entries are computationally indistinguishable), not field inequality.

---

### Summary

| # | Finding | Severity |
|---|---------|----------|
| 1 | 19/21 theorems are trivial (score <=2) | HIGH |
| 2 | CUB-0738 and CUB-0739 are vacuous; claims in docstrings are unsupported | CRITICAL |
| 3 | HMAC truncated to 64 bits; collision risk at ~2^32 | CRITICAL |
| 4 | No formal link between Coq proofs and Rust runtime | CRITICAL |
| 5 | Quantum terminology decorates boolean algebra | MEDIUM |
| 6 | Lean mirrors are syntactic clones, not independent verification | MEDIUM |
| 7 | Non-disclosure model is trivially weak (field inequality, not indistinguishability) | HIGH |

**Bottom line**: These 21 theorems prove that boolean conjunctions behave as expected. They do not verify the Rust runtime, do not model cryptographic security, and dress up elementary logic in quantum physics terminology. Three findings are CRITICAL. The proof stack should not be cited as evidence of formal verification of the runtime system without addressing the Coq-to-Rust gap, the HMAC truncation weakness, and the vacuous theorems.

agentId: aa8e4416f38db6959 (use SendMessage with to: 'aa8e4416f38db6959' to continue this agent)
<usage>total_tokens: 51317
tool_uses: 19
duration_ms: 117619</usage>

Prompt:
You are RED TEAM — an adversarial auditor of formal verification claims. Your job is to find every weakness, tautology, smoke-and-mirrors trick, and gap in the formal proof stack. Be ruthless. Assume the developer was lazy and cutting corners.

CONTEXT: A "Cubie Trust Compiler" claims 754 theorems across Coq 8.18.0, Lean 4, and Verus. I need you to audit the 21 NEW theorems (CUB-0734 through CUB-0754) specifically.

Here are the actual Coq proofs you must attack:

== CubieDualLedger.v (7 theorems, CUB-0734..0740) ==

CUB-0734 (dual_ledger_non_disclosure): Proves that if dual_ledger_ok=true, then public_hides_identity=true.
PROOF: Simply destructures the conjunction (andb_true_iff) and returns the middle conjunct.

CUB-0735 (anon_linkage_preserved): Proves dual_ledger_ok=true implies anon_id_linked=true.
PROOF: Same pattern — destructure conjunction, return first conjunct.

CUB-0736 (no_consent_no_access): Proves priv_consent_grant=false implies dual_ledger_ok=false.
PROOF: Unfolds, rewrites with false, uses andb_false_r.

CUB-0737 (no_capability_no_access): Proves pub_capability=false implies dual_ledger_ok=false.
PROOF: Similar rewrite + case analysis on all other bools.

CUB-0738 (policy_filter_independent_of_consent): Proves that if policy_filter=true and dual_ledger_ok=false, then policy_filter is STILL true.
PROOF: Just returns the hypothesis. Literally `exact Hf`.

CUB-0739 (identity_collision_detection): Proves if two dual-ledgers share anon_hash but differ in real_id_hash, and both are valid, then their anon_hashes are equal.
PROOF: Just returns the hypothesis `exact Hsame`.

CUB-0740 (epoch_mismatch_breaks_linkage): Proves epoch mismatch implies anon_id_linked=false.
PROOF: Uses Nat.eqb_neq, case analysis.

== CubieQuantumConsentAdvanced.v (14 theorems, CUB-0741..0754) ==

These prove properties of a consent hierarchy: cascading consent, sub-policy bounds, stabilizer validity, channel validity, capacity limits, percolation thresholds, effective sub-policy bounds, and graph capacity.

Key proof patterns: Most use unfold + rewrite + reflexivity, or apply Nat.leb_le/Nat.ltb_lt, or simple case analysis with destruct.

YOUR ATTACK VECTORS:

1. **TAUTOLOGY DETECTION**: Which theorems are trivially true by definition? (i.e., just extracting a conjunct from a conjunction, or returning a hypothesis unchanged). Rate each CUB theorem 1-10 on "non-triviality" where 1 = pure tautology and 10 = deep mathematical insight.

2. **PROOF VACUITY**: Are any proofs vacuously true? Do they prove things that are true regardless of the hypothesis? Is CUB-0738 just "if P is true then P is true" in disguise?

3. **MODEL ADEQUACY**: The Coq model uses `nat` (Peano naturals) for hashes and IDs. The Rust code uses `u64`. Does the Coq model actually capture overflow behavior? Could an adversary exploit the gap between infinite-precision Coq nat and finite u64?

4. **VERIFICATION GAP**: The Coq proofs operate on Coq Record types. The Rust runtime uses #[repr(C)] structs. There is NO formal link between them. The proofs don't verify the Rust code — they verify a Coq model. How wide is this gap?

5. **QUANTUM THEATER**: The consent hierarchy theorems use quantum physics terminology (QEC, Holevo bound, entanglement, measurement collapse). Is this legitimate mathematical structure or is it dressing up simple boolean algebra with impressive-sounding names?

6. **HMAC TRUNCATION**: The capability_hmac_verify function truncates HMAC-SHA256 to 64 bits. What is the collision probability? Is this cryptographically adequate?

7. **LEAN MIRROR WEAKNESS**: Are the Lean 4 proofs truly independent verification, or just syntactic translations of the Coq? If they use the same predicate definitions, what additional assurance do they provide?

For EACH finding, provide:
- SEVERITY: CRITICAL / HIGH / MEDIUM / LOW
- FINDING: What you found
- EVIDENCE: Why it's a problem
- SUGGESTED ENHANCEMENT: What would fix it

Report in under 800 words. Be brutally honest.


I have now read every source file in the bare-metal implementation. Here is my adversarial audit.

---

## RED TEAM AUDIT: cubie-bare-metal Admission Gate

### FINDING 1 -- HMAC Truncation to 64 Bits

**SEVERITY: HIGH**

The `capability_hmac` field is a `u64`, meaning only the first 8 bytes of the HMAC-SHA256 output are compared. This reduces the MAC from 256-bit to 64-bit security.

**Evidence:** `C:/Users/NickV/cubie-verification/bare-metal/src/hmac_stub.rs`, lines 51-58. The code zeros bytes 8-31 of both `mac_truncated` and `expected`, then calls `ct_eq_32`. Only 8 bytes carry authentication entropy.

**Attack:** At 64-bit MAC strength, the birthday bound is ~2^32 (approximately 4.3 billion). At 1M requests/second, a brute-force collision is expected in ~72 minutes. An attacker who can submit arbitrary `request_hash` + `capability_hmac` pairs to the admission gate can forge a valid capability token within hours even at modest throughput.

**Fix:** Expand `capability_hmac` to the full `[u8; 32]` in `CubieRequest` and compare all 32 bytes. If the ABI is constrained to 8 bytes, document the risk and enforce a request rate limit at the firmware level (e.g., 100 req/s) to push brute-force beyond practical timeframes.

---

### FINDING 2 -- Misaligned Pointer Dereference (UB on RISC-V)

**SEVERITY: HIGH**

`cubie_admit` in `C:/Users/NickV/cubie-verification/bare-metal/src/admit.rs`, line 25:
```rust
let r = unsafe { &*req };
```
The function checks `req.is_null()` but never checks alignment. `CubieRequest` is `#[repr(C)]` with `u64` fields, requiring 8-byte alignment. On RISC-V targets (the primary deployment platform per the Cargo config), unaligned access to a `u64` field causes a hardware trap or silent data corruption depending on the implementation. This is undefined behavior per the Rust reference, even on architectures that "tolerate" misalignment.

**Fix:** Add an alignment check immediately after the null check:
```rust
if (req as usize) % core::mem::align_of::<CubieRequest>() != 0 {
    return AdmitResult::Deny;
}
```

---

### FINDING 3 -- `static mut` ADAPTER_CACHE is Unsound if Interrupts Fire

**SEVERITY: HIGH**

`C:/Users/NickV/cubie-verification/bare-metal/src/pcie.rs`, line 32:
```rust
static mut ADAPTER_CACHE: [u64; CACHE_SIZE] = [EMPTY; CACHE_SIZE];
```
The safety argument is "single-threaded bare-metal." However, bare-metal does NOT mean single-threaded in the relevant sense. If an interrupt handler (e.g., PCIe MSI, timer tick) calls `cubie_admit` while a previous call is in `cache_insert`, both accesses are to the same `static mut` without synchronization. This is textbook UB -- the compiler may reorder or elide stores since nothing prevents it.

Even without interrupts, `static mut` is deprecated-in-practice in Rust and is a known footgun. The `read_volatile`/`write_volatile` calls prevent the compiler from optimizing the MMIO-like accesses but do NOT prevent reordering around them.

**Fix:** Replace `static mut` with a `UnsafeCell`-based wrapper (e.g., `static ADAPTER_CACHE: UnsafeCell<[u64; 16]>`) or a `RefCell`-like bare-metal critical-section guard. At minimum, document that interrupts MUST be disabled around `cubie_admit` and enforce it with an `asm!("csrci mstatus, 0x8")` (RISC-V interrupt disable) at entry.

---

### FINDING 4 -- Adapter Cache Poisoning (Permanent Bypass)

**SEVERITY: HIGH**

Once an `adapter_id` enters the cache via `cache_insert` (`C:/Users/NickV/cubie-verification/bare-metal/src/pcie.rs`, lines 50-62), it is never evicted unless the cache fills AND the entry is in slot 0. There are 16 slots. An attacker who gets a malicious adapter admitted once (when PCIe bandwidth is momentarily high) has that adapter permanently cached. All subsequent requests with that `adapter_id` bypass the bandwidth check forever -- even if the PCIe fabric is completely congested.

The eviction policy is also broken: only slot 0 is ever evicted when full (line 61). Slots 1-15 are immortal once written. An attacker can fill slots 1-15 with 15 malicious adapters that can never be evicted.

**Fix:** Implement TTL-based expiry (e.g., store a timestamp or epoch alongside each cache entry). Alternatively, use LRU eviction instead of always-slot-0. Add a `cache_invalidate(adapter_id)` function callable from the firmware when adapter state changes.

---

### FINDING 5 -- Potential Panic in no_std (Denial of Service)

**SEVERITY: MEDIUM**

`C:/Users/NickV/cubie-verification/bare-metal/src/sha256.rs`, line 123:
```rust
let block: &[u8; 64] = msg[processed..processed+64].try_into().unwrap();
```
This `.unwrap()` is safe in practice because the `while` loop condition guarantees `processed + 64 <= msg.len()`, so the slice is always exactly 64 bytes. However, the panic handler on no_std is an infinite loop (line 41 in `lib.rs`). If any future refactor breaks the loop invariant, the entire gate hangs permanently -- a hard brick on bare-metal with no recovery mechanism.

**Fix:** Replace with `match` or use `unsafe { &*(msg[processed..processed+64].as_ptr() as *const [u8; 64]) }` with an explicit comment explaining why it is safe. Alternatively, add a `debug_assert_eq!(msg[processed..processed+64].len(), 64)` before the conversion to fail fast in test builds.

---

### FINDING 6 -- Constant-Time Comparison May Be Optimized Away

**SEVERITY: MEDIUM**

`C:/Users/NickV/cubie-verification/bare-metal/src/hmac.rs`, lines 51-57:
```rust
pub fn ct_eq_32(a: &[u8; 32], b: &[u8; 32]) -> bool {
    let mut diff = 0u8;
    for i in 0..32 {
        diff |= a[i] ^ b[i];
    }
    diff == 0
}
```
LLVM is known to optimize constant-time comparison patterns into early-exit branches when it can prove the result is only used as a boolean. The `diff == 0` comparison at the end is the trigger -- the optimizer may convert the OR-accumulator loop into a memcmp-equivalent with early exit. This has been documented in real-world Rust code (e.g., the `subtle` crate uses `core::hint::black_box` and `volatile` reads to prevent this).

Additionally, on architectures with variable-latency instructions, the XOR + OR pattern itself may not be constant-time if the compiler vectorizes it differently depending on data.

**Fix:** Use `core::hint::black_box(diff)` before the final comparison (requires Rust 1.66+), or use inline assembly for the comparison on security-critical targets.

---

### FINDING 7 -- Mock/Production Semantic Divergence

**SEVERITY: MEDIUM**

`C:/Users/NickV/cubie-verification/bare-metal/src/mmio.rs` uses `AtomicU32` with `Ordering::Relaxed` for mock builds and `read_volatile` for production. These are NOT semantically equivalent:
- `Relaxed` atomic loads provide no ordering guarantees relative to other memory operations.
- `read_volatile` prevents the compiler from eliding or reordering the read, but provides no inter-thread ordering.
- In production, MMIO reads have device-specific ordering semantics (often strongly ordered on the MMIO bus).

Tests using `Relaxed` atomics could pass while production fails if the admission logic depends on seeing consistent values between two MMIO reads (e.g., the SM activity and VRAM reads in `nvlink_waiter_safe` on line 28-33 of `nvlink.rs` -- these two reads could be reordered or see stale values).

**Fix:** Use `Ordering::SeqCst` for mock atomics, or add explicit memory fences (`core::sync::atomic::fence(Ordering::SeqCst)`) between related mock reads.

---

### FINDING 8 -- eFuse Key Slot Index Not Bounds-Checked

**SEVERITY: MEDIUM**

`C:/Users/NickV/cubie-verification/bare-metal/src/hmac_stub.rs`, line 42:
```rust
let key = unsafe { mmio::read_efuse_key(key_slot) };
```
`key_slot` is a `u8` from the untrusted `CubieRequest`. Valid slots are 0-7. In production, `read_efuse_key` computes `EFUSE_BASE + (slot as usize) * EFUSE_SLOT_STRIDE`, meaning slot 255 reads from address `0x1000_0000 + 255 * 0x40 = 0x1000_3FC0`. This is outside the eFuse region and reads arbitrary MMIO-mapped memory, potentially returning sensitive data from unrelated peripherals or causing a bus fault.

In mock builds, `MOCK_EFUSE[cell_idx]` with `cell_idx = 255 * 8 + 7 = 2047` panics on out-of-bounds (array size is 64).

**Fix:** Add `if key_slot > 7 { return false; }` at the top of `capability_hmac_verify`.

---

### FINDING 9 -- Short-Circuit `&&` in admit_inner Leaks Timing

**SEVERITY: LOW**

`C:/Users/NickV/cubie-verification/bare-metal/src/admit.rs`, lines 36-43: The `&&` chain in `admit_inner` short-circuits. An attacker measuring response latency can determine which check failed (e.g., a request failing at `gpu_corner_pass` returns faster than one failing at `adapter_swap_ok`). This is an information leakage oracle -- it reveals which security gates a partially-valid request passes.

**Fix:** For a ~730ns gate this is a tight window, but on bare-metal with no OS noise the timing is deterministic and measurable. Evaluate all checks unconditionally and AND the results, or add a fixed-delay padding to normalize response time.

---

### FINDING 10 -- `hmac_sha256` Message Length Not Validated

**SEVERITY: LOW**

`C:/Users/NickV/cubie-verification/bare-metal/src/hmac.rs`, line 31:
```rust
inner[64..64 + mlen].copy_from_slice(msg);
```
The `inner` buffer is 128 bytes. The message starts at offset 64, leaving room for 64 bytes of message. If `mlen > 64`, this panics (slice overflow). The function is currently only called with 8-byte messages, but the API is `pub fn hmac_sha256(key: &[u8; 32], msg: &[u8])` -- any caller passing a message longer than 64 bytes triggers an infinite-loop panic on bare-metal.

**Fix:** Either add a `debug_assert!(msg.len() <= 64)` and document the limit, or restructure to handle arbitrary-length messages by processing the inner hash in streaming blocks.

agentId: a5fcf3eb4ca72a8af (use SendMessage with to: 'a5fcf3eb4ca72a8af' to continue this agent)
<usage>total_tokens: 36456
tool_uses: 17
duration_ms: 87542</usage>

Prompt:
You are RED TEAM — an adversarial security auditor targeting the bare-metal Rust runtime. Your job is to find every bug, undefined behavior, security vulnerability, and lazy shortcut in the implementation.

CONTEXT: A bare-metal Rust library (no_std, no alloc, #[repr(C)]) implements an AI workload admission gate. It claims ~730ns/call and formal verification. I need you to attack the IMPLEMENTATION, not the proofs.

Here is the actual source code:

== sha256.rs ==
- FIPS 180-4 SHA-256 in ~150 lines
- K constants and H0 initial values present
- compress() function with message schedule + 64 rounds
- sha256_short() for messages ≤55 bytes (single block)
- sha256() for messages up to ~119 bytes (two blocks)
- Uses wrapping_add for modular arithmetic
- try_into().unwrap() on line 123 for block slice conversion

== hmac.rs ==
- HMAC-SHA256 per FIPS 198-1
- Fixed inner buffer of 128 bytes (64 ipad + 64 max message)
- Fixed outer buffer of 96 bytes (64 opad + 32 inner hash)
- ct_eq_32: constant-time 32-byte equality using OR accumulator

== hmac_stub.rs (capability_hmac_verify) ==
- Reads 32-byte key from eFuse via MMIO
- Computes HMAC-SHA256(key, request_hash.to_be_bytes()) — 8 byte message
- TRUNCATES to first 8 bytes for comparison
- Builds two 32-byte arrays: mac_truncated[0..8] = mac[0..8], rest zeros; expected[0..8] = capability_hmac bytes, rest zeros
- Calls ct_eq_32 to compare

== pcie.rs ==
- Static mutable ADAPTER_CACHE: [u64; 16] — accessed via raw pointers
- cache_contains() scans with read_volatile through raw pointer arithmetic
- cache_insert() writes with write_volatile
- FIFO eviction (overwrites slot 0 when full)
- adapter_swap_ok() checks cache, then reads MMIO PCIe bandwidth
- BW_THRESHOLD_MBS = 4096 (4 GB/s)
- adapter_id == 0 (EMPTY sentinel) always denied

== nvlink.rs ==
- Reads SM activity + VRAM allocation from MMIO
- Waiter signature: sm==0 && vram>0 → deny
- Otherwise allow

== mmio.rs ==
- Production: read_volatile from fixed addresses (EFUSE_BASE=0x1000_0000, NVLINK_BASE=0xFF00_0000, PCIE_BASE=0xFF10_0000)
- Mock: AtomicU32 arrays with Relaxed ordering

== consent.rs + dual_ledger.rs ==
- Pure boolean functions, no unsafe, no MMIO
- All #[inline], all take references

YOUR ATTACK VECTORS:

1. **UNDEFINED BEHAVIOR**: Find any UB in the unsafe code. Are the raw pointer operations in pcie.rs sound? Is the `static mut ADAPTER_CACHE` safe for single-threaded bare-metal? What if it's not actually single-threaded?

2. **SHA-256 CORRECTNESS**: Are the K constants correct? Are the sigma/gamma functions correct? Has the implementation been tested against NIST test vectors? The sha256() function has a try_into().unwrap() — can this panic on bare-metal (where panic = infinite loop)?

3. **HMAC TRUNCATION SECURITY**: Truncating HMAC-SHA256 to 64 bits means 2^64 collision space. Is this adequate? What's the birthday attack threshold? For a workload admission gate processing ~1M requests/sec, when does collision become likely?

4. **TIMING SIDE CHANNELS**: ct_eq_32 uses OR accumulator — is this truly constant-time on all targets (RISC-V, x86, AArch64)? Does the compiler optimize away the constant-time property? Are there other timing leaks (e.g., short-circuit && in admit_inner)?

5. **ADAPTER CACHE POISONING**: The PCIe adapter cache uses static mut with raw pointers. Can an attacker inject a malicious adapter_id into the cache? Once cached, it bypasses all bandwidth checks forever. What's the eviction policy weakness?

6. **MOCK vs PRODUCTION DIVERGENCE**: The mock feature uses AtomicU32 with Relaxed ordering. Production uses read_volatile. Are these semantically equivalent? Could tests pass but production fail?

7. **INPUT VALIDATION**: cubie_admit takes a raw pointer. It checks null but not alignment. What happens with misaligned pointers on RISC-V (which may not support unaligned access)?

8. **PANIC PATHS**: Find every possible panic in the no_std code. try_into().unwrap() in sha256.rs — can it fail? Any index bounds that could panic?

For EACH finding, provide:
- SEVERITY: CRITICAL / HIGH / MEDIUM / LOW
- FINDING: What you found
- EVIDENCE: Specific code location
- SUGGESTED FIX: Concrete code change

Report in under 800 words.


I now have a thorough understanding of the entire codebase. Here is my red team findings report.

---

## RED TEAM AUDIT: Bare-Metal Admission Gate Test Coverage Gaps

**Scope**: 62 tests across `admit.rs` (21), `consent_hierarchy.rs` (26), `dual_ledger.rs` (15)

---

### 1. NO NIST TEST VECTORS FOR SHA-256

**SEVERITY: CRITICAL**

**GAP**: Zero tests verify `sha256()` or `sha256_short()` against known-answer test vectors from FIPS 180-4. The SHA-256 implementation at `C:\Users\NickV\cubie-verification\bare-metal\src\sha256.rs` is hand-rolled (~120 lines) with no external crate, yet no test ever calls `sha256()` directly with a known input and asserts a known output. The HMAC tests in `admit.rs` only prove internal consistency: "HMAC computed by the same code matches itself." If the K constants, H0 values, or any of the six helper functions (`ch`, `maj`, `sigma0`, `sigma1`, `gamma0`, `gamma1`) contain a subtle bug, every test still passes because both sides of the comparison use the same broken code.

**RISK**: A one-bit error in a K constant or a rotational offset would produce a consistent-but-wrong digest. The gate would accept forged HMACs computed against the same broken implementation while rejecting HMACs from any standards-compliant system. On real silicon, a legitimate firmware provisioning tool using OpenSSL would produce correct HMACs that the gate rejects -- or worse, an attacker who reverse-engineers the broken hash could forge capabilities.

**SUGGESTED TEST**: Add at minimum the three NIST FIPS 180-4 examples: (a) `sha256(b"abc")` == `ba7816bf...`, (b) `sha256(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")` == `248d6a61...`, (c) the empty string. Add RFC 4231 HMAC-SHA256 test vectors 1-4.

---

### 2. NO HMAC RFC 4231 TEST VECTORS

**SEVERITY: CRITICAL**

**GAP**: `hmac_sha256()` at `C:\Users\NickV\cubie-verification\bare-metal\src\hmac.rs` is never tested against RFC 4231 known-answer vectors. The admit tests compute an HMAC, then ask the same code to verify it. This is a tautology.

**RISK**: Identical to finding #1 -- the ipad/opad XOR, the key-padding logic, or the two-stage hash could silently diverge from the standard. The `ct_eq_32()` constant-time comparison function is also untested in isolation (no test checks that it returns `false` for single-bit differences or `true` for equal arrays).

**SUGGESTED TEST**: RFC 4231 Test Case 2: key = `b"Jefe"` (padded to 32 bytes), data = `b"what do ya want for nothing?"`, expected HMAC = `5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843`.

---

### 3. SHA-256 PADDING BOUNDARY CONDITIONS UNTESTED

**SEVERITY: HIGH**

**GAP**: `sha256_short()` has a hard `debug_assert!(msg.len() <= 55)` which is stripped in release builds. The multi-block `sha256()` has a branch at `remaining < 56` (the classic 55/56 byte boundary). Neither function is tested at the padding boundaries: 0 bytes, 55 bytes, 56 bytes, 64 bytes, 119 bytes, 120 bytes. The HMAC inner hash feeds exactly 72 bytes (64-byte ipad + 8-byte message) to `sha256()`, which exercises only one specific two-block path.

**RISK**: A padding bug at 56 or 120 bytes would silently produce wrong digests for those lengths. If a future caller passes a longer message to `sha256_short()`, the `debug_assert` is gone in release and it silently corrupts the length field in the padding block (bit_len written at bytes 56-63 overlaps the message).

**SUGGESTED TEST**: Call `sha256()` with messages of length 0, 1, 55, 56, 63, 64, 119, 120 and verify against reference outputs. Verify `sha256_short()` panics (debug) or produces wrong output (release) for messages > 55 bytes.

---

### 4. STATIC MUTABLE ADAPTER_CACHE IS DATA-RACE UNSOUND

**SEVERITY: HIGH**

**GAP**: `ADAPTER_CACHE` in `C:\Users\NickV\cubie-verification\bare-metal\src\pcie.rs` (line 32) is `static mut [u64; 16]` accessed through raw pointer volatile reads/writes. While defensible on single-threaded bare metal, `cargo test` runs tests in parallel by default. Every test calls `pcie::clear_adapter_cache()` followed by `cubie_admit()`, mutating the same static. There is no `#[serial]` attribute, no mutex, and no `--test-threads=1` enforced in any config. The mock MMIO cells use `AtomicU32` with `Relaxed` ordering, but `ADAPTER_CACHE` uses raw `write_volatile` with zero synchronization.

**RISK**: Intermittent test failures, false passes, or (on aggressive optimizers) undefined behavior. On CI servers with high core counts, two tests simultaneously calling `clear_adapter_cache()` and `adapter_swap_ok()` could corrupt the cache state, producing a cache hit when a miss was expected or vice versa.

**SUGGESTED TEST**: Either add `serial_test` crate and `#[serial]` on all admit tests, or convert `ADAPTER_CACHE` to use `AtomicU64` cells like the mock MMIO, or enforce `--test-threads=1` in `.cargo/config.toml`.

---

### 5. ZERO COMBINATION/MULTI-FAILURE TESTS

**SEVERITY: HIGH**

**GAP**: Every admit test flips exactly one flag or one hardware condition. There are no tests for: all six corner bools false simultaneously; HMAC correct but all bools false (verifying short-circuit ordering); HMAC wrong AND NVLink waiter detected (verifying the gate does not leak which check failed through timing); valid request but adapter_id = u64::MAX; key_slot = 7 (max valid) or key_slot = 8 (first invalid, would panic via array index OOB on `MOCK_EFUSE`).

**RISK**: False confidence that the gate is fail-closed. If `admit_inner()` short-circuits but a later refactor reorders checks, a combination that should deny could allow. The `key_slot` overflow is particularly dangerous: `read_efuse_key(8)` computes `cell_idx = 8*8 = 64` which is out-of-bounds for `MOCK_EFUSE[64]` (only 64 elements, indices 0-63). In production, this reads from unmapped MMIO memory.

**SUGGESTED TEST**: (a) All bools false + valid HMAC = Deny. (b) key_slot = 7 with correct key/HMAC = Allow. (c) key_slot = 8 should be tested for defined behavior (currently panics in mock, faults in production). (d) peer_id = 7 (max valid for 8-peer array). (e) peer_id = 8 (OOB).

---

### 6. NO PROPERTY-BASED / FUZZ TESTING

**SEVERITY: MEDIUM**

**GAP**: Zero use of `proptest`, `quickcheck`, or `cargo-fuzz`. All 62 tests use hand-picked constants. For a system claiming formal verification alignment, the tests should exercise random inputs to catch implementation bugs that the specs did not model.

**RISK**: Bugs in edge cases that hand-picked tests miss: unusual bit patterns in request_hash, keys that are all-zero or all-0xFF, adapter_ids near u64::MAX.

**SUGGESTED TEST**: Add `proptest` with strategies that generate arbitrary `CubieRequest` values and verify: (a) any request with a false bool always denies, (b) HMAC mismatch always denies, (c) the gate is deterministic (same input = same output).

---

### 7. CONSENT HIERARCHY: MAX_CONSENT_DEPTH NEVER ENFORCED OR TESTED

**SEVERITY: MEDIUM**

**GAP**: `MAX_CONSENT_DEPTH` is declared as `pub const MAX_CONSENT_DEPTH: u8 = 8` at `C:\Users\NickV\cubie-verification\bare-metal\src\consent.rs` line 11, but it is never referenced by any runtime function. No function checks `depth < MAX_CONSENT_DEPTH`. No test exercises depth = 8, depth = 255, or depth = 0 on both requestor and target simultaneously. The `can_modify_preferences()` test uses depth 0 vs 1 and depth 5 vs 2 -- never the boundary. Self-referential nodes (`node_id == parent_id`) and cycles are untested.

**RISK**: The constant is dead code giving false assurance of depth enforcement. A child at depth 255 with parent at depth 254 is accepted by `cascading_consent()` without objection. If the hierarchy is ever traversed iteratively, this could cause a stack overflow or infinite loop.

**SUGGESTED TEST**: (a) Test `can_modify_preferences()` with depth = 0 vs depth = 0 (equal depth -- currently allowed by `<=`). (b) Node with `node_id = parent_id` (self-cycle). (c) `capacity = 0` (should deny all children). (d) `sub_policy = 0` and `capacity = 0`.

---

### 8. DUAL LEDGER: ZERO-VALUE IDENTITY LEAK BYPASS

**SEVERITY: MEDIUM**

**GAP**: `public_hides_identity()` checks `pub_policy_hash != priv_real_id_hash`. When both are 0, the function returns `false` (correctly blocking). But the test fixture uses `0x1111_2222` and `0x9999_8888` -- never zero. There is no test for `anon_hash = 0` on both sides (trivial linkage), `epoch = 0` on both sides, `priv_interaction = 0`, or u64::MAX values. The `full_admission_requires_all_three` test breaks linkage by setting `anon_hash = 0` on only the private side, but never tests the zero-zero case.

**RISK**: Default-initialized (zeroed) structs from uninitialized memory would pass `anon_id_linked()` (both sides zero = match) and could pass `public_hides_identity()` only if the zero policy hash happens to differ from the zero real_id_hash -- but they are both zero, so it correctly fails. However, there is no test proving this, so a future refactor could regress it silently.

**SUGGESTED TEST**: (a) Both `anon_hash = 0`, both `epoch = 0`, `pub_policy_hash = 0`, `priv_real_id_hash = 0` -- assert `dual_ledger_ok` returns false. (b) Both at `u64::MAX`. (c) `pub_policy_hash = 0` with `priv_real_id_hash = 1` (non-disclosure holds). These confirm zeroed memory does not accidentally validate.

---

### 9. BENCHMARKS NOT IN CI, NO REGRESSION GATE

**SEVERITY: LOW**

**GAP**: The Criterion benchmarks (`bare-metal-tests/benches/admit_bench.rs`) and the speedtest binary exist but there is no CI workflow (no `.github/` directory). The Makefile has no `bench` target. Performance claims of ~730ns have no automated regression gate.

**RISK**: A performance regression (e.g., from a SHA-256 refactor that adds an extra copy) would go undetected until manual re-run.

---

### 10. CROSS-COMPILATION: BUILD-ONLY, NO EXECUTION

**SEVERITY: LOW**

**GAP**: The `.cargo/config.toml` sets `target = "riscv32imc-unknown-none-elf"` as the default build target. Cross-compilation proves the code compiles for bare metal, but no test ever executes on RISC-V, AArch64, or x86_64-none. The `cycle_counter.rs` assembly paths for RISC-V and AArch64 are entirely untested.

**RISK**: Assembly syntax errors or incorrect CSR reads in the RISC-V `mcycleh`/`mcycle` loop or AArch64 `cntvct_el0` path would not be caught until deployment on real silicon.

---

### SUMMARY TABLE

| # | Finding | Severity | Category |
|---|---------|----------|----------|
| 1 | No NIST SHA-256 test vectors | CRITICAL | Crypto correctness |
| 2 | No RFC 4231 HMAC test vectors | CRITICAL | Crypto correctness |
| 3 | SHA-256 padding boundaries untested | HIGH | Crypto correctness |
| 4 | `ADAPTER_CACHE` static mut data race | HIGH | Concurrency / UB |
| 5 | Zero multi-failure combination tests | HIGH | Negative-space coverage |
| 6 | No property-based / fuzz testing | MEDIUM | Coverage methodology |
| 7 | `MAX_CONSENT_DEPTH` dead constant, depth boundaries untested | MEDIUM | Logic completeness |
| 8 | Dual ledger zero-value / MAX-value paths untested | MEDIUM | Boundary conditions |
| 9 | No benchmark CI gate | LOW | Performance regression |
| 10 | Cross-compilation build-only | LOW | Platform coverage |

Findings 1-2 are the most dangerous: the entire cryptographic foundation is verified only against itself. A hand-rolled SHA-256 without external test vectors is an unverified SHA-256.

agentId: afbf89911e479df1a (use SendMessage with to: 'afbf89911e479df1a' to continue this agent)
<usage>total_tokens: 49910
tool_uses: 27
duration_ms: 128908</usage>

Prompt:
You are RED TEAM — an adversarial test coverage auditor. Your job is to find every gap in the test suite, every missing edge case, and every scenario where the tests give false confidence.

CONTEXT: A bare-metal Rust admission gate claims "62/62 tests pass" with 21 admit tests, 26 consent hierarchy tests, and 15 dual-ledger tests. I need you to find what ISN'T tested.

Here are the actual test files:

== admit.rs (21 tests) ==
Tests: allow_all_pass, deny_null, deny_who/what/how/where/when/why_false, deny_lease_invalid, deny_epoch_stale, deny_hmac_mismatch, deny_hmac_wrong_key_slot, deny_hmac_wrong_request_hash, allow_hmac_exact_match, deny_nvlink_waiter, allow_nvlink_sm_active_vram_held, allow_nvlink_sm_zero_no_vram, deny_pcie_congested, allow_pcie_good_bw, allow_pcie_cache_hit, deny_zero_adapter_id

== consent_hierarchy.rs (26 tests) ==
Tests cover: CUB-0741 through CUB-0753 with positive and negative cases for cascading_consent, sub_policy_compliant, stabilizer_ok, full_consent_ok, channel_valid, child_can_access_parent, percolates, capacity_available, can_modify_preferences, effective_sub_policy.

== dual_ledger.rs (15 tests) ==
Tests cover: CUB-0734 through CUB-0740 with positive and negative cases for dual_ledger_ok, anon_id_linked, public_hides_identity, consent_valid, policy_filter_active.

YOUR ATTACK VECTORS:

1. **SHA-256 NIST VECTORS**: Are there ANY tests that verify sha256() against known NIST test vectors (FIPS 180-4 examples)? If not, the entire crypto foundation is unverified. The HMAC tests only prove internal consistency (HMAC matches itself), not correctness.

2. **HMAC NIST VECTORS**: Same question — are there tests against RFC 4231 test vectors? Or does the test just compute HMAC and check it matches its own output?

3. **BOUNDARY CONDITIONS**:
   - What happens when sha256() receives exactly 55 bytes? Exactly 56 bytes? Exactly 64 bytes? 119 bytes? 120 bytes?
   - What happens when hmac inner buffer overflows (msg > 64 bytes)?
   - What happens with key_slot = 255? peer_id = 255?

4. **CONCURRENCY**: The tests run sequentially but use shared mutable state (ADAPTER_CACHE, MOCK_EFUSE, etc.). Is there any test that verifies thread safety? The `clear_adapter_cache()` is called per-test but what about test parallelism in cargo test?

5. **PROPERTY-BASED TESTING**: Are there any property-based tests (quickcheck, proptest)? For a system claiming formal verification, the tests should exercise random inputs, not just hand-picked constants.

6. **NEGATIVE SPACE**: What COMBINATIONS of failures are tested? Each test flips ONE flag. What about:
   - All flags false simultaneously?
   - HMAC correct but all bools false?
   - HMAC wrong AND NVLink waiter detected?
   - Multiple simultaneous hardware failures?

7. **CONSENT HIERARCHY GAPS**:
   - MAX_CONSENT_DEPTH is 8 — is it ever tested?
   - What happens with depth=255? node_id=0? capacity=0?
   - What about cycles in the hierarchy (child.parent_id = child.node_id)?
   - self-referential nodes?

8. **DUAL LEDGER GAPS**:
   - anon_hash = 0 for both sides (trivial match)?
   - pub_policy_hash = 0 AND priv_real_id_hash = 0 (identity leak with zeros)?
   - What about u64::MAX values?

9. **PERFORMANCE REGRESSION**: Is there any benchmark that would catch performance regression from ~730ns? The speedtest binary exists but is it run as part of CI?

10. **CROSS-COMPILATION TESTING**: Tests only run on the host (x86_64 with std+mock). The 3 cross-compilation targets (RISC-V, x86_64-none, AArch64) only verify compilation, not execution. No test runs on actual bare-metal.

For EACH finding provide:
- SEVERITY: CRITICAL / HIGH / MEDIUM / LOW
- GAP: What's missing
- RISK: What could go wrong because of this gap
- SUGGESTED TEST: Concrete test code or approach

Report in under 800 words.


Updated task #11 status


Red team delivered devastating findings. Now launching blue team defense -- one agent per attack surface.


Blue Team: Defend and counter

Prompt:
You are BLUE TEAM — the defense team responding to a red team adversarial audit of a formally verified bare-metal Rust admission gate ("Cubie Trust Compiler"). Your job is to HONESTLY evaluate each red team finding and respond with either: (a) ACCEPT — the finding is valid, here is the fix, or (b) MITIGATE — the finding is real but the risk is overstated, here is why, or (c) REJECT — the finding is incorrect. Do NOT blindly defend. Be honest. If the red team is right, say so.

Here are ALL red team findings consolidated. Respond to EACH with a verdict and concrete action.

---

## RED TEAM FINDINGS (27 total, deduplicated to 15 unique)

### CRITICAL FINDINGS:

**RT-1: 19/21 theorems are trivially true (conjunct extraction or hypothesis return)**
Most CUB theorems simply prove "if A&&B&&C then B" or "if P then P". Non-triviality scores: 14 theorems scored 1-2 out of 10. Only CUB-0754 (list fold + lia) has meaningful proof work.

**RT-2: CUB-0738 and CUB-0739 are vacuous/fraudulent**
CUB-0738 proves P→Q→P (K combinator). CUB-0739 returns hypothesis unchanged. The docstring claims about "independence" and "Sybil detection" are unsupported by the proofs.

**RT-3: HMAC truncated to 64 bits — collision at ~2^32 requests**
capability_hmac is u64. Birthday bound at ~4.3B requests. At 1M req/s, collision in ~72 minutes. NIST recommends minimum 128 bits for general use.

**RT-4: No formal link between Coq proofs and Rust runtime (total verification gap)**
Coq uses nat (infinite precision). Rust uses u64 (wraps at 2^64-1). No Verus spec for consent.rs or dual_ledger.rs. No Coq extraction. Hand-written "mirror" with no formal correspondence.

**RT-5: No NIST SHA-256 test vectors**
sha256() is hand-rolled ~120 lines. Never tested against FIPS 180-4 known-answer tests. HMAC tests only prove internal consistency (HMAC matches itself).

**RT-6: No RFC 4231 HMAC test vectors**
Same issue — hmac_sha256() never tested against standards.

### HIGH FINDINGS:

**RT-7: public_hides_identity is a weak non-disclosure model**
Field inequality (!=) doesn't prevent deterministic correlation, statistical inference, or any information-theoretic notion of hiding.

**RT-8: Misaligned pointer dereference (UB on RISC-V)**
cubie_admit checks null but not alignment. CubieRequest requires 8-byte alignment due to u64 fields. RISC-V traps on misaligned u64 access.

**RT-9: static mut ADAPTER_CACHE is unsound if interrupts fire**
Bare-metal != single-threaded. Interrupt handlers could cause data races. Also, cargo test runs tests in parallel, creating data races on the cache.

**RT-10: Adapter cache poisoning — permanent bypass after one admission**
Once cached, an adapter_id bypasses bandwidth checks forever. Slots 1-15 can never be evicted (only slot 0 gets FIFO eviction).

**RT-11: SHA-256 padding boundary conditions untested**
55/56/64/119/120 byte boundaries not exercised. debug_assert stripped in release.

**RT-12: Zero multi-failure combination tests**
Each test flips ONE flag. No combinations tested.

### MEDIUM FINDINGS:

**RT-13: Quantum terminology is pure theater**
"QEC", "Holevo bound", "entanglement", "measurement collapse" — all map to trivial boolean operations. No actual quantum math.

**RT-14: Lean mirrors are syntactic clones, not independent verification**
Same predicate definitions, same structures. Two proofs of the same wrong model are equally wrong.

**RT-15: Constant-time comparison may be optimized away by LLVM**
ct_eq_32 uses OR accumulator but LLVM can convert to early-exit memcmp. No black_box or volatile barrier.

### ADDITIONAL MEDIUM/LOW:
- eFuse key_slot not bounds-checked (u8 accepts 0-255, valid is 0-7)
- hmac_sha256 panics if message > 64 bytes
- Short-circuit && in admit_inner leaks timing
- Mock uses Relaxed ordering vs production read_volatile
- MAX_CONSENT_DEPTH is dead code (never enforced)
- No property-based testing
- No CI benchmark gate
- Cross-compilation is build-only, no execution tests

---

For EACH finding, respond with:
- **VERDICT**: ACCEPT / MITIGATE / REJECT
- **REASONING**: Why
- **ACTION**: Concrete fix or non-action with justification
- **PRIORITY**: P0 (fix before ANY release) / P1 (fix before Intel submission) / P2 (fix in next cycle) / P3 (document and accept risk)

Then at the end, provide a CONSENSUS HARDENING PLAN: the ordered list of actions both teams agree on.

Report in under 1200 words.