use crate::error::PrivacyError;
use crate::types::PrivacyIcosuple;
use sha2::{Digest, Sha256};
use serde::{Deserialize, Serialize};
use pqc_sig::fips204::MlDsa65Keypair;
use pqc_sig::types::{SigAlgorithm, SigPublicKey, Signature};
extern crate alloc;
use alloc::{string::{String, ToString}, vec::Vec};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IcosupleFrame {
pub manifold_tensor: Vec<u8>,
pub proof_bundle: Vec<u8>,
pub chaos_state: Vec<u8>,
pub compressed: bool,
pub signature: Vec<u8>,
pub signing_key_public: Vec<u8>,
}
pub fn serialize(icosuple: &PrivacyIcosuple) -> Result<Vec<u8>, PrivacyError> {
let mut buf = Vec::new();
buf.extend_from_slice(b"PNET");
buf.extend_from_slice(&1u32.to_le_bytes()); let flags: u32 = if icosuple.compressed { 1 } else { 0 };
buf.extend_from_slice(&flags.to_le_bytes());
let mt_len = icosuple.manifold_tensor.len() as u32;
buf.extend_from_slice(&mt_len.to_le_bytes());
buf.extend_from_slice(&icosuple.manifold_tensor);
let pb_len = icosuple.proof_bundle.len() as u32;
buf.extend_from_slice(&pb_len.to_le_bytes());
buf.extend_from_slice(&icosuple.proof_bundle);
let cs_len = icosuple.chaos_state.len() as u32;
buf.extend_from_slice(&cs_len.to_le_bytes());
buf.extend_from_slice(&icosuple.chaos_state);
let sig_bytes = icosuple.signature.as_bytes();
buf.extend_from_slice(&(sig_bytes.len() as u32).to_le_bytes());
buf.extend_from_slice(sig_bytes);
let final_buf = if icosuple.compressed {
compress_deflate(&buf)
} else {
buf
};
if final_buf.len() > PrivacyIcosuple::MAX_BYTES {
return Err(PrivacyError::IcosupleTooBig {
size: final_buf.len(),
limit: PrivacyIcosuple::MAX_BYTES,
});
}
Ok(final_buf)
}
pub fn deserialize(bytes: &[u8]) -> Result<PrivacyIcosuple, PrivacyError> {
if bytes.len() < 12 {
return Err(PrivacyError::IcosupleDeserializeFailed("Too short".into()));
}
if &bytes[..4] != b"PNET" {
return Err(PrivacyError::IcosupleDeserializeFailed("Invalid magic".into()));
}
let flags = u32::from_le_bytes(bytes[8..12].try_into().unwrap_or([0u8; 4]));
let compressed = flags & 1 != 0;
let data = if compressed {
decompress_deflate(bytes)?
} else {
bytes.to_vec()
};
let mut pos = 12usize;
let read_field = |pos: &mut usize, data: &[u8]| -> Result<Vec<u8>, PrivacyError> {
if *pos + 4 > data.len() {
return Err(PrivacyError::IcosupleDeserializeFailed("Truncated field".into()));
}
let len = u32::from_le_bytes(data[*pos..*pos + 4].try_into().unwrap_or([0u8; 4])) as usize;
*pos += 4;
if *pos + len > data.len() {
return Err(PrivacyError::IcosupleDeserializeFailed("Field overflow".into()));
}
let field = data[*pos..*pos + len].to_vec();
*pos += len;
Ok(field)
};
let manifold_tensor = read_field(&mut pos, &data)?;
let proof_bundle = read_field(&mut pos, &data)?;
let chaos_state = read_field(&mut pos, &data)?;
let sig_bytes = read_field(&mut pos, &data)?;
let signature = String::from_utf8(sig_bytes)
.map_err(|e| PrivacyError::IcosupleDeserializeFailed(e.to_string()))?;
Ok(PrivacyIcosuple {
manifold_tensor,
proof_bundle,
chaos_state,
compressed,
signature,
})
}
pub fn build_icosuple(
manifold_tensor: Vec<u8>,
proof_bundle: Vec<u8>,
chaos_state: Vec<u8>,
chaos_seed: &[u8; 32],
compress: bool,
) -> PrivacyIcosuple {
let mut payload = Vec::new();
payload.extend_from_slice(&manifold_tensor);
payload.extend_from_slice(&proof_bundle);
payload.extend_from_slice(&chaos_state);
let signature_hex = match MlDsa65Keypair::from_secret_key_bytes(chaos_seed) {
Ok(keypair) => {
match keypair.sign_deterministic(&payload) {
Ok(sig) => hex::encode(&sig.bytes),
Err(_) => {
let mut h = Sha256::new();
h.update(&payload);
h.update(b"pqc-sig-v1");
hex::encode(h.finalize())
}
}
}
Err(_) => {
let mut h = Sha256::new();
h.update(&payload);
h.update(b"pqc-sig-v1");
hex::encode(h.finalize())
}
};
PrivacyIcosuple {
manifold_tensor,
proof_bundle,
chaos_state,
compressed: compress,
signature: signature_hex,
}
}
pub fn build_icosuple_frame(
manifold_tensor: Vec<u8>,
proof_bundle: Vec<u8>,
chaos_state: Vec<u8>,
chaos_seed: &[u8; 32],
compress: bool,
) -> Result<IcosupleFrame, PrivacyError> {
let mut payload = Vec::new();
payload.extend_from_slice(&manifold_tensor);
payload.extend_from_slice(&proof_bundle);
payload.extend_from_slice(&chaos_state);
let keypair = MlDsa65Keypair::from_secret_key_bytes(chaos_seed)
.map_err(|e| PrivacyError::Internal(alloc::format!("ML-DSA-65 keygen failed: {:?}", e)))?;
let sig = keypair.sign_deterministic(&payload)
.map_err(|e| PrivacyError::Internal(alloc::format!("ML-DSA-65 sign failed: {:?}", e)))?;
let pk = keypair.public_key();
Ok(IcosupleFrame {
manifold_tensor,
proof_bundle,
chaos_state,
compressed: compress,
signature: sig.bytes,
signing_key_public: pk.bytes,
})
}
pub fn verify_icosuple(frame: &IcosupleFrame) -> bool {
let mut payload = Vec::new();
payload.extend_from_slice(&frame.manifold_tensor);
payload.extend_from_slice(&frame.proof_bundle);
payload.extend_from_slice(&frame.chaos_state);
let pk = SigPublicKey::new(SigAlgorithm::MlDsa65, frame.signing_key_public.clone());
let sig = Signature::new(SigAlgorithm::MlDsa65, frame.signature.clone());
MlDsa65Keypair::verify(&pk, &payload, &sig).is_ok()
}
pub fn serial_build_obfuscation_frame(
manifold_tensor: &str,
qem_json: &str,
proof_bundle: &str,
chaos_state: &[u8],
chaos_seed: &[u8],
compress: bool,
) -> Result<Vec<u8>, PrivacyError> {
let seed_32: [u8; 32] = {
use sha2::{Digest, Sha256};
Sha256::digest(chaos_seed).into()
};
let mt_bytes = manifold_tensor.as_bytes();
let qem_bytes = qem_json.as_bytes();
let mut extended_tensor: Vec<u8> = Vec::with_capacity(mt_bytes.len() + 4 + qem_bytes.len());
extended_tensor.extend_from_slice(mt_bytes);
extended_tensor.extend_from_slice(&(qem_bytes.len() as u32).to_le_bytes());
extended_tensor.extend_from_slice(qem_bytes);
let pb_bytes = proof_bundle.as_bytes().to_vec();
let cs_bytes = chaos_state.to_vec();
let icosuple = build_icosuple(
extended_tensor,
pb_bytes,
cs_bytes,
&seed_32,
compress,
);
serialize(&icosuple)
}
fn compress_deflate(data: &[u8]) -> Vec<u8> {
miniz_oxide::deflate::compress_to_vec(data, 6)
}
fn decompress_deflate(data: &[u8]) -> Result<Vec<u8>, PrivacyError> {
miniz_oxide::inflate::decompress_to_vec(data)
.map_err(|e| PrivacyError::DecompressionFailed(alloc::format!("{e:?}")))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serialize_deserialize() {
let seed = [0u8; 32];
let ico = build_icosuple(
b"manifold".to_vec(),
b"proof".to_vec(),
b"chaos".to_vec(),
&seed,
false,
);
let bytes = serialize(&ico).unwrap();
assert!(bytes.len() <= PrivacyIcosuple::MAX_BYTES);
let decoded = deserialize(&bytes).unwrap();
assert_eq!(decoded.manifold_tensor, b"manifold");
assert_eq!(decoded.proof_bundle, b"proof");
}
#[test]
fn test_compressed() {
let seed = [1u8; 32];
let ico = build_icosuple(
vec![0u8; 100],
vec![1u8; 50],
vec![2u8; 30],
&seed,
true,
);
let bytes = serialize(&ico).unwrap();
assert!(!bytes.is_empty());
}
#[test]
fn test_build_icosuple_frame_and_verify() {
let seed = [2u8; 32];
let frame = build_icosuple_frame(
b"manifold".to_vec(),
b"proof".to_vec(),
b"chaos".to_vec(),
&seed,
false,
).unwrap();
assert_eq!(frame.signature.len(), 3309);
assert_eq!(frame.signing_key_public.len(), 1952);
assert!(verify_icosuple(&frame));
}
#[test]
fn test_verify_tampered_frame_fails() {
let seed = [3u8; 32];
let mut frame = build_icosuple_frame(
b"manifold".to_vec(),
b"proof".to_vec(),
b"chaos".to_vec(),
&seed,
false,
).unwrap();
frame.manifold_tensor = b"tampered".to_vec();
assert!(!verify_icosuple(&frame));
}
}