use crate::chain::{recompute_chain, ChainAssembler, ChainError};
use crate::types::{OperationEvent, Receipt};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DilithiumSignature(pub Vec<u8>);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct KyberCiphertext(pub Vec<u8>);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DilithiumPublicKey(pub Vec<u8>);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct KyberPublicKey(pub Vec<u8>);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PqcSeal {
pub signature: DilithiumSignature,
pub ciphertext: KyberCiphertext,
pub key_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct PqcReceipt {
pub base: Receipt,
pub pqc_seal: PqcSeal,
}
#[derive(Debug, thiserror::Error)]
pub enum PqcError {
#[error("PQC Signing failed: {0}")]
Signing(String),
#[error("PQC Verification failed: {0}")]
Verification(String),
#[error("KEM Encapsulation failed: {0}")]
Encapsulation(String),
#[error("Chain error: {0}")]
Chain(#[from] ChainError),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
}
pub struct QuantumResistantAssembler {
inner: ChainAssembler,
dilithium_sk: Vec<u8>, kyber_pk: KyberPublicKey,
}
impl QuantumResistantAssembler {
pub fn new(dilithium_sk: Vec<u8>, kyber_pk: KyberPublicKey) -> Self {
Self {
inner: ChainAssembler::new(),
dilithium_sk,
kyber_pk,
}
}
pub fn append(&mut self, event: OperationEvent) -> Result<(), PqcError> {
self.inner.append(event).map_err(PqcError::Chain)
}
pub fn finalize(self) -> Result<PqcReceipt, PqcError> {
let base_receipt = self.inner.finalize();
let (ciphertext, _shared_secret) = mock_kyber_encapsulate(&self.kyber_pk)?;
let receipt_hash = base_receipt.chain_hash.clone();
let mut message = Vec::new();
message.extend_from_slice(receipt_hash.as_hex().as_bytes());
message.extend_from_slice(&ciphertext.0);
let signature = mock_dilithium_sign(&self.dilithium_sk, &message)?;
let seal = PqcSeal {
signature,
ciphertext,
key_id: "pqc-key-v1-alpha".to_string(),
};
Ok(PqcReceipt {
base: base_receipt,
pqc_seal: seal,
})
}
}
pub fn verify_pqc_receipt(
receipt: &PqcReceipt,
dilithium_pk: &DilithiumPublicKey,
) -> Result<(), PqcError> {
let recomputed = recompute_chain(&receipt.base.events)?;
if recomputed != receipt.base.chain_hash {
return Err(PqcError::Verification(
"Classical chain hash mismatch".into(),
));
}
let mut message = Vec::new();
message.extend_from_slice(receipt.base.chain_hash.as_hex().as_bytes());
message.extend_from_slice(&receipt.pqc_seal.ciphertext.0);
mock_dilithium_verify(dilithium_pk, &message, &receipt.pqc_seal.signature)?;
Ok(())
}
fn mock_dilithium_sign(_sk: &[u8], message: &[u8]) -> Result<DilithiumSignature, PqcError> {
let mut hasher = blake3::Hasher::new();
hasher.update(b"DILITHIUM-SIGN-MOCK");
hasher.update(message);
let sig = hasher.finalize();
Ok(DilithiumSignature(sig.as_bytes().to_vec()))
}
fn mock_dilithium_verify(
_pk: &DilithiumPublicKey,
message: &[u8],
signature: &DilithiumSignature,
) -> Result<(), PqcError> {
let expected = mock_dilithium_sign(&[], message)?;
if expected == *signature {
Ok(())
} else {
Err(PqcError::Verification("Dilithium signature invalid".into()))
}
}
fn mock_kyber_encapsulate(_pk: &KyberPublicKey) -> Result<(KyberCiphertext, Vec<u8>), PqcError> {
let ciphertext = KyberCiphertext(b"MOCK-KYBER-CIPHERTEXT".to_vec());
let shared_secret = b"MOCK-SHARED-SECRET".to_vec();
Ok((ciphertext, shared_secret))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::ObjectRef;
fn test_event(seq: u64) -> OperationEvent {
OperationEvent {
id: format!("e{}", seq),
seq,
event_type: "test.pqc".to_string(),
objects: vec![ObjectRef {
id: "obj".to_string(),
obj_type: "artifact".to_string(),
qualifier: None,
}],
payload_commitment: Blake3Hash::from_bytes(b"pqc-payload"),
}
}
#[test]
fn test_quantum_resistant_flow() {
let sk = b"mock-sk".to_vec();
let pk = KyberPublicKey(b"mock-pk".to_vec());
let mut assembler = QuantumResistantAssembler::new(sk, pk);
assembler.append(test_event(0)).unwrap();
assembler.append(test_event(1)).unwrap();
let pqc_receipt = assembler.finalize().unwrap();
let d_pk = DilithiumPublicKey(b"mock-pk".to_vec());
verify_pqc_receipt(&pqc_receipt, &d_pk).expect("PQC verification should pass");
tracing::info!(
"Quantum-Resistant Receipt Verified: {}",
pqc_receipt.base.chain_hash
);
}
#[test]
fn test_tamper_detection() {
let sk = b"mock-sk".to_vec();
let pk = KyberPublicKey(b"mock-pk".to_vec());
let mut assembler = QuantumResistantAssembler::new(sk, pk);
assembler.append(test_event(0)).unwrap();
let mut pqc_receipt = assembler.finalize().unwrap();
pqc_receipt.base.events[0].event_type = "tampered".to_string();
let d_pk = DilithiumPublicKey(b"mock-pk".to_vec());
let result = verify_pqc_receipt(&pqc_receipt, &d_pk);
assert!(result.is_err(), "Verification should fail after tampering");
}
}