Archived
a42429012e
- crates/ → engrams/ (Rust engrams live here)
- el-compiler/ added: el self-hosting compiler as an el package
- src/{compiler,lexer,parser,codegen}.el
- bootstrap/el-compiler.elc (114KB, Rust-compiled seed)
- el.toml Cargo.toml workspace paths updated
- neuron-rs cross-repo path deps fixed (were pointing to products/ instead of foundation/)
140 lines
5.3 KiB
Rust
140 lines
5.3 KiB
Rust
//! Sealed artifact format definition.
|
|
//!
|
|
//! Binary layout (big-endian):
|
|
//!
|
|
//! ```text
|
|
//! Offset Size Field
|
|
//! ────── ───── ─────────────────────────────────────────────────────────
|
|
//! 0 8 magic: b"ENGRAM01"
|
|
//! 8 2 version: u16 (currently 1)
|
|
//! 10 * JSON-encoded SealedArtifact body (algorithm_id, nonce, …)
|
|
//! ```
|
|
//!
|
|
//! The body is JSON so the format is self-describing and forward-compatible.
|
|
//! Future versions can add new fields without breaking older parsers.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Magic header bytes — identify an Engram sealed artifact.
|
|
pub const MAGIC: [u8; 8] = *b"ENGRAM01";
|
|
|
|
/// Current artifact format version.
|
|
pub const FORMAT_VERSION: u16 = 1;
|
|
|
|
// ── Configuration ─────────────────────────────────────────────────────────────
|
|
|
|
/// Which algorithm was used to seal the artifact.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub enum SealAlgorithm {
|
|
/// AES-256-GCM — current default, quantum-resistant at 256-bit.
|
|
Aes256Gcm,
|
|
/// CRYSTALS-Kyber 768 — when ml-kem crate stabilizes.
|
|
MlKem768,
|
|
/// CRYSTALS-Kyber 1024 — when ml-kem crate stabilizes.
|
|
MlKem1024,
|
|
}
|
|
|
|
impl SealAlgorithm {
|
|
pub fn id(&self) -> &'static str {
|
|
match self {
|
|
SealAlgorithm::Aes256Gcm => "aes256gcm-v1",
|
|
SealAlgorithm::MlKem768 => "mlkem768-v1",
|
|
SealAlgorithm::MlKem1024 => "mlkem1024-v1",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// How the deployment key is derived / bound.
|
|
#[derive(Debug, Clone)]
|
|
pub enum DeploymentBinding {
|
|
/// Read the seal key from an environment variable (e.g. `ENGRAM_SEAL_KEY`).
|
|
EnvironmentKey(String),
|
|
/// Bind to this machine's hostname + OS + CPU model (BLAKE3 hash of all three).
|
|
MachineFingerprint,
|
|
/// No binding — key is the zero vector. For testing only; offers no security.
|
|
None,
|
|
}
|
|
|
|
/// Configuration for the sealing operation.
|
|
#[derive(Debug, Clone)]
|
|
pub struct SealConfig {
|
|
pub algorithm: SealAlgorithm,
|
|
pub deployment_binding: DeploymentBinding,
|
|
}
|
|
|
|
impl Default for SealConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
algorithm: SealAlgorithm::Aes256Gcm,
|
|
deployment_binding: DeploymentBinding::EnvironmentKey("ENGRAM_SEAL_KEY".into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Artifact ──────────────────────────────────────────────────────────────────
|
|
|
|
/// A quantum-sealed bytecode artifact.
|
|
///
|
|
/// This is the output of `el seal` / the `prod` compilation target.
|
|
/// It is serialized to disk as: `MAGIC || VERSION_u16_be || JSON(body)`.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SealedArtifact {
|
|
/// Algorithm identifier — matches [`SealAlgorithm::id()`].
|
|
pub algorithm_id: String,
|
|
|
|
/// BLAKE3-keyed MAC over `(algorithm_id || nonce || ciphertext)`.
|
|
/// Used to detect tampering before attempting decryption.
|
|
/// In the PQ upgrade path, this becomes an ML-DSA signature.
|
|
pub signature: Vec<u8>,
|
|
|
|
/// The binding-protected symmetric key.
|
|
///
|
|
/// In the current scheme: `symmetric_key XOR BLAKE3(binding_material)`.
|
|
/// Without the deployment key, the binding material cannot be derived,
|
|
/// so the symmetric key cannot be recovered.
|
|
///
|
|
/// In the ML-KEM upgrade: this becomes the KEM-encapsulated key ciphertext.
|
|
pub encapsulated_key: Vec<u8>,
|
|
|
|
/// 96-bit AES-GCM nonce.
|
|
pub nonce: Vec<u8>,
|
|
|
|
/// Encrypted bytecode (AES-256-GCM ciphertext including the 128-bit auth tag).
|
|
pub ciphertext: Vec<u8>,
|
|
|
|
/// BLAKE3 hash of the binding material. Allows the unsealer to verify
|
|
/// it is running in the correct deployment environment before decryption.
|
|
/// `None` if [`DeploymentBinding::None`] was used.
|
|
pub deployment_fingerprint: Option<Vec<u8>>,
|
|
}
|
|
|
|
impl SealedArtifact {
|
|
/// Serialize to the on-disk wire format: `MAGIC || version_be16 || JSON`.
|
|
pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
|
|
let mut out = Vec::with_capacity(256);
|
|
out.extend_from_slice(&MAGIC);
|
|
out.extend_from_slice(&FORMAT_VERSION.to_be_bytes());
|
|
let json = serde_json::to_vec(self)?;
|
|
out.extend_from_slice(&json);
|
|
Ok(out)
|
|
}
|
|
|
|
/// Deserialize from the on-disk wire format.
|
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::SealError> {
|
|
if bytes.len() < 10 {
|
|
return Err(crate::SealError::Serialization("artifact too short".into()));
|
|
}
|
|
let magic: [u8; 8] = bytes[..8].try_into().unwrap();
|
|
if magic != MAGIC {
|
|
return Err(crate::SealError::InvalidMagic(magic));
|
|
}
|
|
// Version check (bytes 8..10) — currently we only support v1
|
|
let version = u16::from_be_bytes([bytes[8], bytes[9]]);
|
|
if version != FORMAT_VERSION {
|
|
return Err(crate::SealError::UnsupportedAlgorithm(format!("format v{version}")));
|
|
}
|
|
let body = &bytes[10..];
|
|
serde_json::from_slice(body).map_err(|e| crate::SealError::Serialization(e.to_string()))
|
|
}
|
|
}
|