# path: cdp/Cargo.toml
[package]
name = "cdp"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0 OR MIT"
description = "Coherent Data Protocol (CDP) over the UOR/PrimeOS hologram with FFI-backed proofs"
repository = "https://example.com/uor/cdp"
readme = "README.md"
categories = ["encoding", "network-programming", "cryptography"]
keywords = ["UOR", "PrimeOS", "CDP", "hologram", "FFI"]
[features]
default = ["std", "ffi-uor"]
std = []
# Use the Rust wrapper crate `uorffi` (recommended)
ffi-uor = ["dep:uorffi"]
# Optional async integration layers
tokio = ["dep:tokio"]
quic = ["dep:quinn", "tokio"]
[dependencies]
anyhow = "1"
thiserror = "1"
zerocopy = "0.7"
bytes = "1"
serde = { version = "1", features = ["derive"], optional = true }
uorffi = { version = "0.1", optional = true, package = "uorffi" }
tokio = { version = "1", optional = true, features = ["rt-multi-thread", "macros", "io-util"] }
quinn = { version = "0.11", optional = true }
[dev-dependencies]
rand = "0.8"
hex = "0.4"
# path: cdp/README.md
# CDP — Coherent Data Protocol (Rust)
This crate implements the **Coherent Data Protocol (CDP)** on top of the UOR/PrimeOS hologram,
delegating all invariants and proofs to the Lean core via the **Rust FFI wrapper (`uorffi`)**.
## Design (normative; RFC 2119)
* CDP **MUST** segment payloads into windows of size `PAGES * BYTES`, where `PAGES = 48` and `BYTES = 256`.
These constants **MUST** be taken from the FFI (`uorffi::pages()`, `uorffi::bytes()`).
* A window **MUST** be **closed** by the canonical closure routine provided by the FFI before hashing or transmission.
* For each window, implementations **MUST** compute per-page hashes and a Merkle **root** via the FFI.
* A **UOR-ID** **MUST** be constructed with the Merkle root and the layout order; receivers **MUST** verify it.
* CDP frames **MUST** fail-closed: any mismatch in closure, hash, root, or ID **MUST** be rejected.
* Protocol handshake consists of `HELLO`, `SELECT`, `ATTEST`, followed by `DATA` frames carrying the CEF record.
(This crate provides a stable wire structure and encoder/decoder for these frames.)
## Public API surface
```rust
use cdp::{CdpConfig, CdpFrame, encode_segment, verify_frame};
See examples/pack.rs
for a minimal pack/verify demonstration.
Enable --features quic
to get a QUIC stream multiplexer with CDP framing. The transport is thin:
it frames CDP messages and calls the same FFI-backed verifiers.
All cryptographic/arithmetical invariants are executed by the Lean-proof-backed FFI via uorffi
.
This crate does not re-implement those checks; it only frames bytes on the wire.
```rust
// path: cdp/src/lib.rs
#![deny(missing_docs)]
//! CDP — Coherent Data Protocol over the UOR/PrimeOS hologram.
//!
//! This crate frames bytes into **coherent** windows and defers all invariants (closure, hashing,
//! Merkle root, UOR-ID, R96) to the Lean-backed FFI via the Rust wrapper `uorffi`.
//!
//! ## Quickstart
//! ```no_run
//! use cdp::{encode_segment, verify_frame, CdpConfig};
//! let cfg = CdpConfig::default();
//! let mut segment = [0u8; 12_288]; // fill with your data (<= 12_288)
//! let frame = encode_segment(&cfg, &segment).unwrap();
//! verify_frame(&cfg, &frame).unwrap();
//! ```
mod types;
mod wire;
mod ffi; // the adapter over the `uorffi` crate
mod verify;
mod codec;
pub use types::{CdpError, CdpConfig, CdpFrame, CefRecord, HandshakeHello, HandshakeSelect, HandshakeAttest};
pub use verify::{encode_segment, verify_frame};
pub use codec::{encode_message, decode_message, Message};
#[cfg(feature = "quic")]
pub mod quic; // transport integrations (optional)
// path: cdp/src/types.rs
use thiserror::Error;
use zerocopy::{FromBytes, AsBytes};
use serde::{Serialize, Deserialize};
/// CDP configuration.
#[derive(Clone, Debug)]
pub struct CdpConfig {
/// true => cycle-major (0), false => page-major (1)
pub order_cycle_major: bool,
}
impl Default for CdpConfig {
fn default() -> Self { Self { order_cycle_major: true } }
}
/// CDP error kinds.
#[derive(Debug, Error)]
pub enum CdpError {
#[error("FFI error: {0}")]
Ffi(String),
#[error("Merkle root mismatch")]
RootMismatch,
#[error("Closure failure")]
ClosureFailure,
#[error("R96 checksum mismatch (expected {expected}, got {got})")]
R96Mismatch { expected: u32, got: u32 },
#[error("Malformed frame: {0}")]
Malformed(&'static str),
}
/// Coherent Exchange Format (CEF) record (base profile).
#[repr(C)]
#[derive(Clone, Copy, FromBytes, AsBytes)]
pub struct CefRecord {
/// Version (1)
pub version: u8,
/// Layout order: 0 = cycle-major, 1 = page-major
pub order: u8,
/// Total length in bytes (always 12288 for base profile)
pub length_be: u16,
/// 48 * 256 payload (closed)
pub payload: [u8; 48 * 256],
/// Page-closure witness (reserved; zero in base profile)
pub pcw: [u8; 48],
/// Leaf count (256)
pub leaf_count_be: u32,
/// Merkle root (SHA-256)
pub root: [u8; 32],
}
impl Default for CefRecord {
fn default() -> Self {
Self {
version: 1,
order: 0,
length_be: (48u16 * 256u16).to_be(),
payload: [0; 48*256],
pcw: [0; 48],
leaf_count_be: (256u32).to_be(),
root: [0; 32],
}
}
}
/// A complete CDP frame: UOR-ID + CEF + optional R96 checksum.
#[derive(Clone)]
pub struct CdpFrame {
/// UOR-ID bytes (encoded by FFI)
pub uor_id: Vec<u8>,
/// CEF record
pub cef: CefRecord,
/// Optional integrity checksum over payload (sum of R96 classes mod 96)
pub r96_sum: u32,
}
/// HELLO message (capabilities advertisement).
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct HandshakeHello {
/// Protocol version (1)
pub version: u8,
/// Supported layout orders (bit 0: cycle-major, bit 1: page-major)
pub layout_caps: u8,
}
/// SELECT message (parameter selection).
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct HandshakeSelect {
/// Chosen layout order (0 or 1)
pub order: u8,
}
/// ATTEST message (integrity attestation).
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct HandshakeAttest {
/// Echoed Merkle root to be used for the next DATA frame (optional)
pub next_root: Option<[u8; 32]>,
}
// path: cdp/src/wire.rs
//! Wire-level CDP message encoding.
//! Stable on-the-wire envelopes for HELLO/SELECT/ATTEST/DATA.
use bytes::{BytesMut, BufMut, Bytes};
use crate::types::*;
const MSG_HELLO: u8 = 0x01;
const MSG_SELECT: u8 = 0x02;
const MSG_ATTEST: u8 = 0x03;
const MSG_DATA: u8 = 0x04;
/// A framed CDP message.
#[derive(Clone)]
pub enum Message {
/// HELLO {version, layout_caps}
Hello(HandshakeHello),
/// SELECT {order}
Select(HandshakeSelect),
/// ATTEST {next_root?}
Attest(HandshakeAttest),
/// DATA {UOR-ID, CEF, r96_sum}
Data(CdpFrame),
}
pub fn encode_message(msg: &Message) -> Bytes {
let mut out = BytesMut::new();
match msg {
Message::Hello(h) => {
out.put_u8(MSG_HELLO);
out.put_u8(h.version);
out.put_u8(h.layout_caps);
}
Message::Select(s) => {
out.put_u8(MSG_SELECT);
out.put_u8(s.order);
}
Message::Attest(a) => {
out.put_u8(MSG_ATTEST);
match a.next_root {
None => { out.put_u8(0); }
Some(root) => { out.put_u8(1); out.extend_from_slice(&root); }
}
}
Message::Data(f) => {
out.put_u8(MSG_DATA);
// UOR-ID (length-prefixed)
out.put_u16(f.uor_id.len() as u16);
out.extend_from_slice(&f.uor_id);
// CEF
out.extend_from_slice(f.cef.as_bytes());
// r96 checksum (u8; modulo 96)
out.put_u8((f.r96_sum % 96) as u8);
}
}
out.freeze()
}
pub fn decode_message(mut bytes: Bytes) -> anyhow::Result<Message> {
use anyhow::{ensure, bail};
ensure!(bytes.len() >= 1, "empty frame");
let tag = bytes[0];
bytes.advance(1);
match tag {
MSG_HELLO => {
anyhow::ensure!(bytes.len() >= 2, "HELLO too short");
Ok(Message::Hello(HandshakeHello{ version: bytes[0], layout_caps: bytes[1] }))
}
MSG_SELECT => {
anyhow::ensure!(bytes.len() >= 1, "SELECT too short");
Ok(Message::Select(HandshakeSelect{ order: bytes[0] }))
}
MSG_ATTEST => {
anyhow::ensure!(bytes.len() >= 1, "ATTEST too short");
let flag = bytes[0]; bytes.advance(1);
if flag == 0 {
Ok(Message::Attest(HandshakeAttest{ next_root: None }))
} else {
anyhow::ensure!(bytes.len() >= 32, "ATTEST root missing");
let mut root = [0u8; 32];
root.copy_from_slice(&bytes[..32]);
Ok(Message::Attest(HandshakeAttest{ next_root: Some(root) }))
}
}
MSG_DATA => {
use zerocopy::FromBytes;
anyhow::ensure!(bytes.len() >= 2, "DATA too short");
let id_len = u16::from_be_bytes([bytes[0], bytes[1]]) as usize;
bytes.advance(2);
anyhow::ensure!(bytes.len() >= id_len, "UOR-ID truncated");
let uor_id = bytes.split_to(id_len).to_vec();
anyhow::ensure!(bytes.len() >= core::mem::size_of::<CefRecord>(), "CEF truncated");
let mut cef = CefRecord::default();
let cef_bytes_len = core::mem::size_of::<CefRecord>();
cef.as_bytes_mut().copy_from_slice(&bytes[..cef_bytes_len]);
bytes.advance(cef_bytes_len);
anyhow::ensure!(bytes.len() >= 1, "missing R96 sum");
let r96_sum = bytes[0] as u32;
Ok(Message::Data(CdpFrame{ uor_id, cef, r96_sum }))
}
_ => bail!("unknown tag: {tag:#x}"),
}
}
// path: cdp/src/ffi.rs
//! Adapter over the Rust FFI wrapper crate (`uorffi`).
//!
//! All methods here are thin delegations. If you need to mock for tests,
//! implement `HoloCore` for your own struct.
use crate::types::CdpError;
/// Abstraction over the FFI to allow mocking in tests.
pub trait HoloCore: Clone + Send + Sync + 'static {
fn pages(&self) -> u32;
fn bytes(&self) -> u32;
fn rclasses(&self) -> u32;
fn r96(&self, b: u8) -> u8;
/// Close both axes in-place; returns Ok on success.
fn pad_close_48x256(&self, buf: &mut [u8; 48*256]) -> Result<(), CdpError>;
/// Fill leaf hashes (256 * 32) and root (32) using canonical SHA-256.
fn page_hashes_sha256(&self, payload_48x256: &[u8; 48*256], leaf_hashes_out: &mut [u8; 256*32]);
fn merkle_root_sha256(&self, leaf_hashes: &[u8; 256*32], root_out: &mut [u8; 32]);
/// Encode UOR-ID into `out`, returns used length.
fn uor_id_encode(&self, order: u8, root32: &[u8; 32], w_alg: u8, cw: &[u8], out: &mut [u8]) -> Result<usize, CdpError>;
}
#[derive(Clone, Default)]
pub struct UorFfiCore;
#[cfg(feature = "ffi-uor")]
impl HoloCore for UorFfiCore {
fn pages(&self) -> u32 { uorffi::pages() }
fn bytes(&self) -> u32 { uorffi::bytes() }
fn rclasses(&self) -> u32 { uorffi::rclasses() }
fn r96(&self, b: u8) -> u8 { uorffi::r96(b) }
fn pad_close_48x256(&self, buf: &mut [u8; 48*256]) -> Result<(), CdpError> {
if uorffi::pad_close_48x256(buf.as_mut_ptr()) { Ok(()) } else { Err(CdpError::ClosureFailure) }
}
fn page_hashes_sha256(&self, payload: &[u8; 48*256], leaf_hashes_out: &mut [u8; 256*32]) {
uorffi::page_hashes_sha256(payload.as_ptr(), leaf_hashes_out.as_mut_ptr());
}
fn merkle_root_sha256(&self, leaf_hashes: &[u8; 256*32], root_out: &mut [u8; 32]) {
uorffi::merkle_root_sha256(leaf_hashes.as_ptr(), root_out.as_mut_ptr());
}
fn uor_id_encode(&self, order: u8, root32: &[u8; 32], w_alg: u8, cw: &[u8], out: &mut [u8]) -> Result<usize, CdpError> {
let mut out_len: u16 = 0;
let ok = uorffi::uor_id_encode(order, root32.as_ptr(), w_alg, if cw.is_empty() { core::ptr::null() } else { cw.as_ptr() }, cw.len() as u8, out.as_mut_ptr(), &mut out_len as *mut u16);
if ok { Ok(out_len as usize) } else { Err(CdpError::Ffi("uor_id_encode".into())) }
}
}
/// Simple R96 checksum helper (sum of classes mod 96).
pub(crate) fn r96_checksum<F: HoloCore>(ffi: &F, bytes: &[u8]) -> u32 {
let mut acc: u32 = 0;
for &b in bytes {
acc = (acc + (ffi.r96(b) as u32)) % 96;
}
acc
}
// path: cdp/src/verify.rs
//! Encode/verify routines that call into the FFI.
use crate::{types::*, ffi::{HoloCore, UorFfiCore, r96_checksum}};
/// Encode a 12,288-byte segment into a CDP frame (UOR-ID + CEF + r96_sum).
///
/// *Closes* the payload via FFI, computes per-page hashes → Merkle root,
/// then builds UOR-ID and CEF. Fails-closed on any error.
///
/// The `segment` **MUST** be exactly `48*256` bytes.
pub fn encode_segment(cfg: &CdpConfig, segment: &[u8; 48*256]) -> Result<CdpFrame, CdpError> {
encode_with::<UorFfiCore>(cfg, segment, &UorFfiCore::default())
}
/// Same as `encode_segment`, but allows a custom FFI core (mock/testing).
pub fn encode_with<F: HoloCore>(cfg: &CdpConfig, segment: &[u8; 48*256], core: &F) -> Result<CdpFrame, CdpError> {
// Sanity: confirm FFI constants (fail fast if drift)
if core.pages() != 48 || core.bytes() != 256 || core.rclasses() != 96 {
return Err(CdpError::Ffi("dimension mismatch".into()));
}
// (1) Copy and close
let mut payload = *segment;
core.pad_close_48x256(&mut payload)?;
// (2) Hash pages → leaf set (256 × 32)
let mut leaves = [0u8; 256 * 32];
core.page_hashes_sha256(&payload, &mut leaves);
// (3) Merkle root
let mut root = [0u8; 32];
core.merkle_root_sha256(&leaves, &mut root);
// (4) UOR-ID
let order: u8 = if cfg.order_cycle_major { 0 } else { 1 };
let mut id_buf = [0u8; 96];
let used = core.uor_id_encode(order, &root, /*w_alg=*/1, &[], &mut id_buf)?;
let uor_id = id_buf[..used].to_vec();
// (5) CEF
let mut cef = CefRecord::default();
cef.order = order;
cef.payload.copy_from_slice(&payload);
cef.root.copy_from_slice(&root);
// (6) Optional checksum
let r96_sum = r96_checksum(core, &cef.payload);
Ok(CdpFrame { uor_id, cef, r96_sum })
}
/// Verify a CDP frame (fail-closed).
///
/// Recomputes closure and Merkle root via FFI and compares to the frame.
/// Optionally verifies the R96 checksum (always-on in this reference impl).
pub fn verify_frame(cfg: &CdpConfig, frame: &CdpFrame) -> Result<(), CdpError> {
verify_with::<UorFfiCore>(cfg, frame, &UorFfiCore::default())
}
/// Same as `verify_frame`, but allows a custom FFI core (mock/testing).
pub fn verify_with<F: HoloCore>(_cfg: &CdpConfig, frame: &CdpFrame, core: &F) -> Result<(), CdpError> {
if core.pages() != 48 || core.bytes() != 256 || core.rclasses() != 96 {
return Err(CdpError::Ffi("dimension mismatch".into()));
}
// Recompute root
let mut leaves = [0u8; 256 * 32];
core.page_hashes_sha256(&frame.cef.payload, &mut leaves);
let mut root = [0u8; 32];
core.merkle_root_sha256(&leaves, &mut root);
if root != frame.cef.root {
return Err(CdpError::RootMismatch);
}
// Re-run closure on a copy (must succeed without changing root)
let mut tmp = frame.cef.payload;
core.pad_close_48x256(&mut tmp)?;
// Verify R96 checksum
let got = r96_checksum(core, &frame.cef.payload);
if got != (frame.r96_sum % 96) {
return Err(CdpError::R96Mismatch { expected: frame.r96_sum % 96, got });
}
Ok(())
}
// path: cdp/src/codec.rs
//! Streaming codec helpers (stateless).
pub use crate::wire::{Message, encode_message, decode_message};
// path: cdp/src/quic.rs
//! Optional QUIC transport: frames CDP messages on a bidirectional stream.
use anyhow::Result;
use bytes::Bytes;
use quinn::{RecvStream, SendStream};
use crate::wire::{Message, encode_message, decode_message};
/// Send a CDP message over a QUIC stream with length-prefix framing.
pub async fn send(mut send: SendStream, msg: &Message) -> Result<()> {
let bytes = encode_message(msg);
let len = bytes.len() as u32;
send.write_all(&len.to_be_bytes()).await?;
send.write_all(&bytes).await?;
send.flush().await?;
Ok(())
}
/// Receive a CDP message over a QUIC stream with length-prefix framing.
pub async fn recv(mut recv: RecvStream) -> Result<Message> {
use tokio::io::AsyncReadExt;
let mut len_buf = [0u8; 4];
recv.read_exact(&mut len_buf).await?;
let len = u32::from_be_bytes(len_buf) as usize;
let mut buf = vec![0u8; len];
recv.read_exact(&mut buf).await?;
decode_message(Bytes::from(buf)).map_err(Into::into)
}
// path: cdp/examples/pack.rs
//! Minimal example: pack + verify a single window.
use cdp::{CdpConfig, encode_segment, verify_frame};
fn main() -> anyhow::Result<()> {
let cfg = CdpConfig::default();
// In practice, fill this with your payload; it will be closed by FFI.
let mut segment = [0u8; 48*256];
for (i, b) in segment.iter_mut().enumerate() { *b = (i % 251) as u8; }
let frame = encode_segment(&cfg, &segment)?;
println!("UOR-ID ({} bytes)", frame.uor_id.len());
println!("Root: {}", hex::encode(frame.cef.root));
verify_frame(&cfg, &frame)?;
println!("verify: OK");
Ok(())
}
// path: cdp/tests/integration.rs
use cdp::{CdpConfig, encode_segment, verify_frame};
#[test]
fn pack_and_verify_roundtrip() {
let cfg = CdpConfig::default();
let mut segment = [0u8; 48*256];
for (i, b) in segment.iter_mut().enumerate() { *b = (i % 251) as u8; }
let frame = encode_segment(&cfg, &segment).expect("encode");
verify_frame(&cfg, &frame).expect("verify");
assert_eq!(frame.cef.version, 1);
assert!(matches!(frame.cef.order, 0 | 1));
assert_eq!(u16::from_be(frame.cef.length_be), 12_288);
assert_eq!(u32::from_be(frame.cef.leaf_count_be), 256);
}
# path: cdp/.gitignore
/target
**/*.rs.bk
Cargo.lock
This CDP crate expects a Rust wrapper crate uorffi
exposing these functions:
// Expected `uorffi` API (thin wrappers over Lean-exported C symbols)
pub fn pages() -> u32; // 48
pub fn bytes() -> u32; // 256
pub fn rclasses() -> u32; // 96
pub fn r96(b: u8) -> u8; // classify byte into 0..95
pub fn pad_close_48x256(buf: *mut u8) -> bool; // true on success
pub fn page_hashes_sha256(payload: *const u8, leaf_hashes_out: *mut u8);
pub fn merkle_root_sha256(leaves: *const u8, root_out: *mut u8);
pub fn uor_id_encode(order: u8,
root32: *const u8,
w_alg: u8,
cw_ptr: *const u8,
cw_len: u8,
out_buf: *mut u8,
out_len: *mut u16) -> bool;
If your current wrapper only has part of these, you can still use this CDP crate by providing a small compatibility layer inside uorffi
that forwards to the Lean exports.