#![crate_name = "qfe"]
use std::sync::Arc; use std::error::Error;
use std::fmt;
use sha2::{Sha512, Digest};
pub mod zkp;
pub use zkp::{ZkpValidityResponse, establish_zkp_sqs};
pub type Sha512Hash = [u8; 64];
use chacha20poly1305::{
aead::{Aead, AeadInPlace, KeyInit, Nonce}, ChaCha20Poly1305, Key, };
use pqcrypto::kem::mlkem1024;
use pqcrypto::traits::kem::SharedSecret;
use hkdf::Hkdf;
use rand::RngCore;
const SQS_COMPONENTS_V2_INFO: &[u8] = b"QFE_SQS_COMPONENTS_V2";
const SQS_AEAD_KEY_V1_INFO: &[u8] = b"QFE_AEAD_KEY_V1";
const SQS_SALT_CONTEXT_V1: &[u8] = b"QFE_SQS_SALT_CONTEXT_V1";
#[derive(Debug, Clone)] pub struct QfeEncryptedMessage {
pub nonce: Vec<u8>, pub ciphertext: Vec<u8>,
}
const PHI: f64 = 1.618033988749895;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QfeError {
SqsEstablishmentFailed(String),
EncodingFailed(String),
DecodingFailed(String),
InvalidUtf8(std::string::FromUtf8Error),
FrameInvalid,
SqsMissing,
InvalidSignature,
InternalError(String),
}
impl fmt::Display for QfeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
QfeError::SqsEstablishmentFailed(s) => write!(f, "SQS Establishment Failed: {}", s),
QfeError::EncodingFailed(s) => write!(f, "Encoding Failed: {}", s),
QfeError::DecodingFailed(s) => write!(f, "Decoding Failed: {}", s),
QfeError::InvalidUtf8(e) => write!(f, "Decoded data is not valid UTF-8: {}", e),
QfeError::FrameInvalid => write!(f, "Operation failed: Frame is in an invalid state"),
QfeError::SqsMissing => write!(f, "Operation failed: SQS component is missing"),
QfeError::InvalidSignature => write!(f, "Message signature verification failed"),
QfeError::InternalError(s) => write!(f, "Internal QFE error: {}", s),
}
}
}
impl Error for QfeError {}
#[derive(Clone, Default, PartialEq)] pub struct Sqs {
pub aead_key: Key, pattern_type: PatternType,
pub components: Vec<u8>,
validation: bool,
pub participant_a_id: String,
pub participant_b_id: String,
}
impl fmt::Debug for Sqs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let components_prefix = self.components.get(..4) .map(|slice| format!("{:02x}{:02x}{:02x}{:02x}", slice[0], slice[1], slice[2], slice[3]))
.unwrap_or_else(|| "[]".to_string()); let aead_key_prefix = format!("{:02x}{:02x}..", self.aead_key[0], self.aead_key[1]);
f.debug_struct("Sqs")
.field("aead_key_prefix", &aead_key_prefix)
.field("pattern_type", &self.pattern_type)
.field("components_len", &self.components.len())
.field("components_prefix", &components_prefix)
.field("validation", &self.validation)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct Frame {
id: String,
sqs_component: Option<Arc<Sqs>>,
pub validation_status: bool,
pub zkp_witness: Option<Vec<u8>>,
pub zkp_secret_scalar: Option<curve25519_dalek::Scalar>,
}
#[derive(Debug, Default, Clone, PartialEq)]
enum PatternType {
#[default]
Sqs, }
impl Frame {
pub fn initialize(id: String) -> Self {
Frame {
id,
sqs_component: None,
validation_status: true, zkp_witness: None,
zkp_secret_scalar: None,
}
}
pub fn has_sqs(&self) -> bool {
self.sqs_component.is_some() && self.validation_status
}
pub fn get_sqs(&self) -> Option<&Arc<Sqs>> {
if !self.validation_status { return None; }
self.sqs_component.as_ref()
}
pub fn id(&self) -> &str {
&self.id
}
pub fn is_valid(&self) -> bool {
self.validation_status
}
pub fn encode_aead(
&self,
plaintext: &[u8],
associated_data: Option<&[u8]>
) -> Result<QfeEncryptedMessage, QfeError> {
if !self.is_valid() { return Err(QfeError::FrameInvalid); }
let sqs = self.get_sqs().ok_or(QfeError::SqsMissing)?;
let key = &sqs.aead_key;
let cipher = ChaCha20Poly1305::new(key);
let mut nonce_bytes = [0u8; 12];
rand::rng().fill_bytes(&mut nonce_bytes);
let nonce = Nonce::<ChaCha20Poly1305>::from_slice(&nonce_bytes);
let _ciphertext = cipher.encrypt(nonce, plaintext)
.map_err(|e| QfeError::EncodingFailed(format!("AEAD encryption error: {}", e)))?;
let key = &sqs.aead_key;
let cipher = ChaCha20Poly1305::new(key);
let mut nonce_bytes = [0u8; 12];
rand::rng().fill_bytes(&mut nonce_bytes);
let nonce = Nonce::<ChaCha20Poly1305>::from_slice(&nonce_bytes);
let mut buffer = Vec::with_capacity(plaintext.len() + 16); buffer.extend_from_slice(plaintext);
let tag = cipher.encrypt_in_place_detached(
nonce,
associated_data.unwrap_or(&[]), &mut buffer
)
.map_err(|e| QfeError::EncodingFailed(format!("AEAD encryption error: {}", e)))?;
buffer.extend_from_slice(tag.as_slice());
Ok(QfeEncryptedMessage {
nonce: nonce_bytes.to_vec(), ciphertext: buffer, })
}
pub fn decode_aead(
&mut self, encrypted_message: &QfeEncryptedMessage,
associated_data: Option<&[u8]>
) -> Result<Vec<u8>, QfeError> {
if !self.is_valid() { return Err(QfeError::FrameInvalid); } let sqs = self.get_sqs().ok_or(QfeError::SqsMissing)?;
let key = &sqs.aead_key;
let cipher = ChaCha20Poly1305::new(key);
if encrypted_message.nonce.len() != 12 {
self.validation_status = false;
return Err(QfeError::DecodingFailed("Invalid nonce length received".to_string()));
}
let nonce = Nonce::<ChaCha20Poly1305>::from_slice(&encrypted_message.nonce);
let mut buffer = encrypted_message.ciphertext.clone();
if buffer.len() < 16 { self.validation_status = false;
return Err(QfeError::DecodingFailed("Ciphertext too short to contain tag".to_string()));
}
let tag_offset = buffer.len() - 16;
let (ciphertext_slice_mut, tag_slice) = buffer.split_at_mut(tag_offset);
let tag = chacha20poly1305::Tag::from_slice(tag_slice);
let decrypt_result = cipher.decrypt_in_place_detached(
nonce,
associated_data.unwrap_or(&[]), ciphertext_slice_mut, tag );
match decrypt_result {
Ok(()) => {
Ok(ciphertext_slice_mut.to_vec()) }
Err(e) => {
self.validation_status = false; Err(QfeError::DecodingFailed(format!("AEAD decryption/authentication failed: {}", e)))
}
}
}
pub fn calculate_sqs_fingerprint(&self) -> Result<String, QfeError> {
if !self.is_valid() { return Err(QfeError::FrameInvalid); }
let sqs = self.get_sqs().ok_or(QfeError::SqsMissing)?;
let mut hasher = Sha512::new(); hasher.update(b"QFE_SQS_FINGERPRINT_V1");
let mut ids = [sqs.participant_a_id.as_str(), sqs.participant_b_id.as_str()];
ids.sort_unstable();
hasher.update(ids[0].as_bytes());
hasher.update(ids[1].as_bytes());
hasher.update(&sqs.components);
let full_hash: [u8; 64] = hasher.finalize().into();
let fingerprint = format!(
"{:02x}{:02x}{:02x}{:02x}",
full_hash[0], full_hash[1], full_hash[2], full_hash[3]
);
Ok(fingerprint)
}
}
pub fn setup_qfe_pair(
id_a: String,
id_b: String,
context_string: &str, ) -> Result<(Frame, Frame), QfeError> {
let mut frame_a = Frame::initialize(id_a);
let mut frame_b = Frame::initialize(id_b);
establish_sqs_kem(&mut frame_a, &mut frame_b, context_string)?;
Ok((frame_a, frame_b))
}
#[deprecated(since="0.3.0", note="Use establish_sqs_kem for secure key establishment.")]
pub fn establish_sqs(_frame_a: &mut Frame, _frame_b: &mut Frame) -> Result<(), QfeError> {
Err(QfeError::InternalError("establish_sqs is deprecated, use establish_sqs_kem".to_string()))
}
pub fn establish_sqs_kem(
frame_a: &mut Frame, frame_b: &mut Frame, context_string: &str ) -> Result<(), QfeError> {
if !frame_a.validation_status || !frame_b.validation_status {
return Err(QfeError::FrameInvalid);
}
if frame_a.sqs_component.is_some() || frame_b.sqs_component.is_some() {
return Err(QfeError::SqsEstablishmentFailed(
"SQS already established".to_string(),
));
}
let (pk_a, sk_a) = mlkem1024::keypair();
let (shared_secret_ss_b, ciphertext_c) = mlkem1024::encapsulate(&pk_a);
let shared_secret_ss_a = mlkem1024::decapsulate(&ciphertext_c, &sk_a);
if shared_secret_ss_a != shared_secret_ss_b {
frame_a.validation_status = false;
frame_b.validation_status = false;
return Err(QfeError::SqsEstablishmentFailed(
"CRITICAL: ML-KEM shared secrets mismatch!".to_string()
));
}
let ikm = shared_secret_ss_a.as_bytes();
let mut salt_hasher = Sha512::new();
salt_hasher.update(SQS_SALT_CONTEXT_V1);
let mut ids = [frame_a.id().as_bytes(), frame_b.id().as_bytes()];
ids.sort_unstable();
salt_hasher.update(ids[0]);
salt_hasher.update(ids[1]);
salt_hasher.update(context_string.as_bytes());
let salt = salt_hasher.finalize();
let hk = Hkdf::<Sha512>::new(Some(salt.as_slice()), ikm);
let mut sqs_components_okm = [0u8; 64];
hk.expand(SQS_COMPONENTS_V2_INFO, &mut sqs_components_okm)
.map_err(|e| QfeError::SqsEstablishmentFailed(format!("HKDF-Expand error for components: {}", e)))?;
let mut aead_key_okm = Key::default(); hk.expand(SQS_AEAD_KEY_V1_INFO, &mut aead_key_okm)
.map_err(|e| QfeError::SqsEstablishmentFailed(format!("HKDF-Expand error for AEAD key: {}", e)))?;
let sqs = Arc::new(Sqs {
pattern_type: PatternType::Sqs,
components: sqs_components_okm.to_vec(),
aead_key: aead_key_okm, validation: true,
participant_a_id: frame_a.id.clone(),
participant_b_id: frame_b.id.clone(),
});
frame_a.sqs_component = Some(Arc::clone(&sqs));
frame_b.sqs_component = Some(sqs);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn setup_frames_for_signing(id_a: &str, id_b: &str) -> (Frame, Frame) {
let mut frame_a = Frame::initialize(id_a.to_string());
let mut frame_b = Frame::initialize(id_b.to_string());
establish_sqs_kem(&mut frame_a, &mut frame_b, "test_context")
.expect("SQS setup failed during test helper execution");
(frame_a, frame_b)
}
mod aead_tests {
use super::*;
fn setup_frames_for_aead() -> (Frame, Frame) {
setup_frames_for_signing("AEAD_A", "AEAD_B") }
#[test]
fn test_aead_encode_decode_success_no_ad() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"This message is secret and authentic (no AD).";
let associated_data = None;
let encrypted_msg_res = frame_a.encode_aead(plaintext, associated_data);
assert!(encrypted_msg_res.is_ok());
let encrypted_msg = encrypted_msg_res.unwrap();
assert_eq!(encrypted_msg.nonce.len(), 12);
assert!(encrypted_msg.ciphertext.len() >= plaintext.len() + 16);
let decoded_res = frame_b.decode_aead(&encrypted_msg, associated_data);
assert!(decoded_res.is_ok());
let decoded_plaintext = decoded_res.unwrap();
assert_eq!(decoded_plaintext, plaintext);
assert!(frame_b.is_valid()); }
#[test]
fn test_aead_encode_decode_success_with_ad() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"This message is secret and authentic (with AD).";
let associated_data = Some(b"Important Context" as &[u8]);
let encrypted_msg = frame_a.encode_aead(plaintext, associated_data)
.expect("Encoding with AD failed");
let decoded_plaintext = frame_b.decode_aead(&encrypted_msg, associated_data)
.expect("Decoding with AD failed");
assert_eq!(decoded_plaintext, plaintext);
assert!(frame_b.is_valid());
}
#[test]
fn test_aead_encode_decode_empty_message() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b""; let associated_data = Some(b"Context for empty message" as &[u8]);
let encrypted_msg = frame_a.encode_aead(plaintext, associated_data)
.expect("Encoding empty message failed");
assert!(encrypted_msg.ciphertext.len() == 16);
let decoded_plaintext = frame_b.decode_aead(&encrypted_msg, associated_data)
.expect("Decoding empty message failed");
assert_eq!(decoded_plaintext, plaintext);
assert!(decoded_plaintext.is_empty());
assert!(frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_tampered_ciphertext() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"Do not tamper!";
let mut encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
if encrypted_msg.ciphertext.len() > 16 { encrypted_msg.ciphertext[0] ^= 0xAA;
} else if !encrypted_msg.ciphertext.is_empty() {
encrypted_msg.ciphertext[0] ^= 0xAA; }
let decoded_res = frame_b.decode_aead(&encrypted_msg, None);
assert!(decoded_res.is_err());
let err = decoded_res.unwrap_err();
assert!(matches!(err, QfeError::DecodingFailed(_)));
assert!(err.to_string().contains("AEAD decryption/authentication failed"));
assert!(!frame_b.is_valid()); }
#[test]
fn test_aead_decode_fails_tampered_tag() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"Do not tamper tag!";
let mut encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
let ct_len = encrypted_msg.ciphertext.len();
if ct_len >= 16 {
encrypted_msg.ciphertext[ct_len - 1] ^= 0xAA; }
let decoded_res = frame_b.decode_aead(&encrypted_msg, None);
assert!(decoded_res.is_err());
assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_tampered_nonce() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"Cannot reuse nonces";
let mut encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
if !encrypted_msg.nonce.is_empty() {
encrypted_msg.nonce[0] ^= 0xAA;
}
let decoded_res = frame_b.decode_aead(&encrypted_msg, None);
assert!(decoded_res.is_err());
assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_wrong_ad_string_mismatch() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"AD must match";
let associated_data = Some(b"Correct AD" as &[u8]);
let wrong_associated_data = Some(b"Wrong AD" as &[u8]);
let encrypted_msg = frame_a.encode_aead(plaintext, associated_data).unwrap();
let decoded_res = frame_b.decode_aead(&encrypted_msg, wrong_associated_data);
assert!(decoded_res.is_err());
let err = decoded_res.unwrap_err();
println!("Wrong AD (String Mismatch) Decode Error: {:?}", err); assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for AD string mismatch");
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_wrong_ad_none_vs_some() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"AD must match";
let associated_data = Some(b"Correct AD" as &[u8]);
let encrypted_msg = frame_a.encode_aead(plaintext, associated_data).unwrap();
let decoded_res_no_ad = frame_b.decode_aead(&encrypted_msg, None);
assert!(decoded_res_no_ad.is_err());
let err = decoded_res_no_ad.unwrap_err();
println!("Wrong AD (None vs Some) Decode Error: {:?}", err); assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for AD mismatch (None vs Some)");
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_nonce_too_short() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"Nonce length matters";
let encrypted_msg_ok = frame_a.encode_aead(plaintext, None).unwrap();
let mut bad_nonce_msg = encrypted_msg_ok.clone();
bad_nonce_msg.nonce = vec![0u8; 11];
let decoded_res = frame_b.decode_aead(&bad_nonce_msg, None);
assert!(decoded_res.is_err());
let err = decoded_res.unwrap_err();
println!("Wrong Nonce Length (11) Decode Error: {:?}", err); assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for Nonce Length 11");
assert!(err.to_string().contains("Invalid nonce length received"), "Incorrect error message for short nonce");
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_nonce_too_long() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"Nonce length matters";
let encrypted_msg_ok = frame_a.encode_aead(plaintext, None).unwrap();
let mut bad_nonce_msg = encrypted_msg_ok.clone();
bad_nonce_msg.nonce = vec![0u8; 13];
let decoded_res_13 = frame_b.decode_aead(&bad_nonce_msg, None);
assert!(decoded_res_13.is_err());
let err = decoded_res_13.unwrap_err();
println!("Wrong Nonce Length (13) Decode Error: {:?}", err); assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for Nonce Length 13");
assert!(err.to_string().contains("Invalid nonce length received"), "Incorrect error message for long nonce");
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_ad_mismatch_encode_none_decode_some() {
let (frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"AD mismatch 2";
let associated_data = Some(b"Some AD" as &[u8]);
let encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
let decoded_res = frame_b.decode_aead(&encrypted_msg, associated_data);
assert!(decoded_res.is_err());
assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
assert!(!frame_b.is_valid());
}
#[test]
fn test_aead_decode_fails_wrong_sqs() {
let (frame_a, _frame_b) = setup_frames_for_aead();
let (mut frame_c, _frame_d) = setup_frames_for_signing("AEAD_C", "AEAD_D");
assert_ne!(
frame_a.get_sqs().unwrap().components,
frame_c.get_sqs().unwrap().components
);
let plaintext = b"Message from A";
let encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
let decoded_res = frame_c.decode_aead(&encrypted_msg, None);
assert!(decoded_res.is_err());
assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
assert!(!frame_c.is_valid()); }
#[test]
fn test_aead_encode_decode_no_sqs() {
let frame_a_no_sqs = Frame::initialize("NoSQS_AEAD_A".to_string());
let mut frame_b_no_sqs = Frame::initialize("NoSQS_AEAD_B".to_string());
let plaintext = b"Cannot encrypt";
let dummy_encrypted = QfeEncryptedMessage { nonce: vec![0;12], ciphertext: vec![0;32]};
let enc_res = frame_a_no_sqs.encode_aead(plaintext, None);
assert!(enc_res.is_err());
assert!(matches!(enc_res.unwrap_err(), QfeError::SqsMissing));
let dec_res = frame_b_no_sqs.decode_aead(&dummy_encrypted, None);
assert!(dec_res.is_err());
assert!(matches!(dec_res.unwrap_err(), QfeError::SqsMissing));
}
#[test]
fn test_aead_encode_decode_invalid_frame() {
let (mut frame_a, mut frame_b) = setup_frames_for_aead();
let plaintext = b"Invalid frame test";
let encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
frame_a.validation_status = false;
frame_b.validation_status = false;
let enc_res = frame_a.encode_aead(plaintext, None);
assert!(enc_res.is_err());
assert!(matches!(enc_res.unwrap_err(), QfeError::FrameInvalid));
let dec_res = frame_b.decode_aead(&encrypted_msg, None);
assert!(dec_res.is_err());
assert!(matches!(dec_res.unwrap_err(), QfeError::FrameInvalid));
}
}
}