Skip to main content

qfe/
lib.rs

1#![crate_name = "qfe"]
2//! # Qualitative Frame Entanglement (QFE) - Experimental Secure Communication Framework
3//!
4//! **Current Version Date:** April 7, 2025
5//!
6//! **NOTE:** This library is an experimental simulation framework. While it now incorporates
7//! standard, vetted cryptographic primitives for core security functions (PQC KEM, HKDF, AEAD),
8//! the overall integrated system has not undergone formal security audits. Use with caution
9//! and primarily for research or educational purposes.
10//!
11//! ## Overview
12//!
13//! The QFE framework provides Rust types and functions to simulate secure communication
14//! between participants represented as `Frame` instances. Originally conceived from
15//! foundational principles, the implementation has evolved significantly to prioritize robust
16//! security using modern cryptographic standards.
17//!
18//! The core security flow involves:
19//! 1.  **Frame Initialization:** Participants create `Frame` instances with unique internal
20//!     states derived deterministically from IDs and seeds using SHA-512.
21//! 2.  **Post-Quantum Key Establishment:** A shared secret context (`Sqs`) is established
22//!     using the `establish_sqs_kem` function. This function simulates key exchange
23//!     using the **ML-KEM-1024 (Kyber)** algorithm (a NIST standard for Post-Quantum
24//!     Cryptography), providing resistance against known quantum computer attacks for the
25//!     initial shared secret.
26//! 3.  **Key Derivation:** The **HKDF-SHA512** key derivation function is used with the
27//!     ML-KEM shared secret (and contextual information like participant IDs) to derive
28//!     robust session keys, including a specific key for authenticated encryption. These
29//!     keys are stored within the `Sqs` struct shared between the participant `Frame`s.
30//! 4.  **Authenticated Encryption:** Subsequent communication requiring confidentiality,
31//!     integrity, and authenticity is handled by the `encode_aead` and `decode_aead`
32//!     methods. These methods utilize the **ChaCha20-Poly1305 AEAD** cipher, keyed with
33//!     the key derived during the HKDF step. Correct usage requires generating a **unique
34//!     nonce** for each message.
35//! 5.  **Zero-Knowledge Proofs (Optional):** The library includes experimental ZKP
36//!     features within the `zkp` module (e.g., Schnorr's protocol) that can be bound
37//!     to the established session context.
38//!
39//! ## Security Model
40//!
41//! The security of this revised QFE implementation relies directly on the standard
42//! cryptographic hardness assumptions of the underlying primitives:
43//! - **ML-KEM-1024:** Security based on the hardness of the Module-LWE problem (Post-Quantum Secure).
44//! - **HKDF-SHA512:** Security as a standard Key Derivation Function based on HMAC-SHA512.
45//! - **ChaCha20-Poly1305:** Standard AEAD security (Confidentiality, Integrity, Authenticity).
46//! - **SHA-512:** Standard hash function properties.
47//!
48//! **Important Considerations:**
49//! - **Nonce Reuse:** Nonce uniqueness *must* be maintained for AEAD security. Reusing a nonce with the same key breaks ChaCha20-Poly1305 security guarantees. The current implementation uses random nonces.
50//! - **Key Exchange MitM:** The base ML-KEM protocol requires the public key and ciphertext to be exchanged. This exchange is vulnerable to Man-in-the-Middle (MitM) attacks if performed over an insecure channel. Protection against MitM requires either an underlying authenticated channel or out-of-band verification (e.g., comparing `Sqs` fingerprints after establishment).
51//! - **Experimental Status:** As mentioned, the integrated system requires further analysis and review.
52//!
53//! ## Example Workflow
54//!
55//! ```rust,no_run
56//! use qfe::{Frame, establish_sqs_kem, setup_qfe_pair}; // Assuming setup_qfe_pair uses KEM
57//!
58//! fn main() -> Result<(), Box<dyn std::error::Error>> {
59//!     // 1. Setup Frames and SQS using KEM + HKDF
60//!     let alice_id = "Alice_Main".to_string();
61//!     let bob_id = "Bob_Main".to_string();
62//!     let context = "example_session_v1";
63//!     // Use setup_qfe_pair (assuming it's updated for KEM and context)
64//!     let (mut frame_a, mut frame_b) = setup_qfe_pair(
65//!         alice_id.clone(), // Seed for Alice's Frame init
66//!         bob_id.clone(),   // Seed for Bob's Frame init
67//!         context                 // Context string for HKDF salt
68//!     )?;
69//!
70//!     // (Optional but Recommended) Compare SQS fingerprints out-of-band
71//!     let fp_a = frame_a.calculate_sqs_fingerprint()?;
72//!     let fp_b = frame_b.calculate_sqs_fingerprint()?;
73//!     assert_eq!(fp_a, fp_b, "Fingerprint mismatch indicates potential MitM!");
74//!     println!("SQS Fingerprints match: {}", fp_a);
75//!
76//!     // 2. Alice Encrypts a message for Bob
77//!     let plaintext = b"Secret message from Alice!";
78//!     let associated_data = Some(b"message_id_001" as &[u8]);
79//!     let encrypted_msg = frame_a.encode_aead(plaintext, associated_data)?;
80//!     println!("Alice sends encrypted message (len: {})", encrypted_msg.ciphertext.len());
81//!
82//!     // 3. Bob Decrypts the message
83//!     let decoded_plaintext = frame_b.decode_aead(&encrypted_msg, associated_data)?;
84//!     println!("Bob received: {}", String::from_utf8_lossy(&decoded_plaintext));
85//!     assert_eq!(plaintext.as_slice(), decoded_plaintext.as_slice());
86//!
87//!     Ok(())
88//! }
89//! ```
90
91use std::sync::Arc; // For shared ownership of Sqs
92use std::error::Error;
93use std::fmt;
94use sha2::{Sha512, Digest};
95
96pub mod zkp;
97pub use zkp::{ZkpValidityResponse, establish_zkp_sqs};
98pub type Sha512Hash = [u8; 64];
99
100use chacha20poly1305::{
101    aead::{Aead, AeadInPlace, KeyInit, Nonce}, // Import necessary traits and types
102    ChaCha20Poly1305, // The AEAD cipher implementation
103    Key, // Type alias for the 32-byte key
104};
105use pqcrypto::kem::mlkem1024;
106use pqcrypto::traits::kem::SharedSecret;
107use hkdf::Hkdf;
108use rand::RngCore;
109
110const SQS_COMPONENTS_V2_INFO: &[u8] = b"QFE_SQS_COMPONENTS_V2";
111const SQS_AEAD_KEY_V1_INFO: &[u8] = b"QFE_AEAD_KEY_V1";
112const SQS_SALT_CONTEXT_V1: &[u8] = b"QFE_SQS_SALT_CONTEXT_V1";
113
114/// Structure to hold the result of AEAD encryption.
115#[derive(Debug, Clone)] // PartialEq, Eq, Hash might be tricky with Vec<u8>
116pub struct QfeEncryptedMessage {
117    /// Nonce used for encryption (12 bytes for ChaCha20Poly1305).
118    /// Must be unique per message per key. MUST be sent with ciphertext.
119    pub nonce: Vec<u8>, // Store as Vec<u8> for flexibility, convert to Nonce type on use
120    /// Ciphertext including the 16-byte authentication tag appended at the end.
121    pub ciphertext: Vec<u8>,
122}
123
124// // --- Constants derived from Framework Core Mathematics ---
125// Primary Scale: φ (phi)
126const PHI: f64 = 1.618033988749895;
127
128// --- Core Data Structures ---
129
130/// Custom error types for the QFE library operations.
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub enum QfeError {
133    SqsEstablishmentFailed(String),
134    EncodingFailed(String),
135    DecodingFailed(String),
136    InvalidUtf8(std::string::FromUtf8Error),
137    FrameInvalid,
138    SqsMissing,
139    InvalidSignature,
140    InternalError(String),
141}
142
143// Display and Error impl remain the same...
144impl fmt::Display for QfeError {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        match self {
147            QfeError::SqsEstablishmentFailed(s) => write!(f, "SQS Establishment Failed: {}", s),
148            QfeError::EncodingFailed(s) => write!(f, "Encoding Failed: {}", s),
149            QfeError::DecodingFailed(s) => write!(f, "Decoding Failed: {}", s),
150            QfeError::InvalidUtf8(e) => write!(f, "Decoded data is not valid UTF-8: {}", e),
151            QfeError::FrameInvalid => write!(f, "Operation failed: Frame is in an invalid state"),
152            QfeError::SqsMissing => write!(f, "Operation failed: SQS component is missing"),
153            QfeError::InvalidSignature => write!(f, "Message signature verification failed"),
154            QfeError::InternalError(s) => write!(f, "Internal QFE error: {}", s),
155        }
156    }
157}
158impl Error for QfeError {}
159
160
161/// Represents the established shared state (secret key and context) between two Frames.
162#[derive(Clone, Default, PartialEq)] // SQS components (Vec<u8>) make Eq complex, PartialEq is fine.
163pub struct Sqs {
164    /// Specific 32-byte key for AEAD (ChaCha20Poly1305) derived via HKDF-SHA512.
165    pub aead_key: Key, // Key is [u8; 32]
166    pattern_type: PatternType,
167    /// The core shared secret (SHA-512 hash output) derived from the interaction.
168    // Note: components are now 64 bytes long.
169    pub components: Vec<u8>,
170    /// Internal validation status determined during creation.
171    validation: bool,
172    /// Identifier of the first participant establishing this SQS.
173    pub participant_a_id: String,
174    /// Identifier of the second participant establishing this SQS.
175    pub participant_b_id: String,
176}
177
178// Debug impl for Sqs remains the same (still uses DefaultHasher for display hash)
179impl fmt::Debug for Sqs {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        let components_prefix = self.components.get(..4) // Get first 4 bytes safely
182            .map(|slice| format!("{:02x}{:02x}{:02x}{:02x}", slice[0], slice[1], slice[2], slice[3]))
183            .unwrap_or_else(|| "[]".to_string()); // Handle empty or short components
184        let aead_key_prefix = format!("{:02x}{:02x}..", self.aead_key[0], self.aead_key[1]);
185        f.debug_struct("Sqs")
186         .field("aead_key_prefix", &aead_key_prefix)
187         .field("pattern_type", &self.pattern_type)
188         .field("components_len", &self.components.len())
189         .field("components_prefix", &components_prefix)
190         .field("validation", &self.validation)
191         .finish()
192    }
193}
194
195
196/// Represents a participant Frame (e.g., Sender A, Receiver B).
197#[derive(Debug, Clone)]
198pub struct Frame {
199    id: String,
200    sqs_component: Option<Arc<Sqs>>,
201    pub validation_status: bool,
202    pub zkp_witness: Option<Vec<u8>>,
203    pub zkp_secret_scalar: Option<curve25519_dalek::Scalar>,
204}
205
206/// Represents different types of patterns P(n) = Ω(x) × R(Ω).
207#[derive(Debug, Default, Clone, PartialEq)]
208enum PatternType {
209    #[default]
210    Sqs, // Shared Qualitative Structure
211    // Future types: Information, Transformation, etc.
212}
213
214// --- QFE Algorithm Implementation ---
215
216impl Frame {
217    /// Initializes a new `Frame` instance.
218    ///
219    /// Creates a frame with a unique internal state derived deterministically
220    /// from the provided identifier and seed. The frame starts in a valid state
221    /// without any established shared state (`SQS`).
222    ///
223    /// # Arguments
224    /// * `id`: A `String` identifier for the frame.
225    /// * `initial_seed`: A `u64` seed value used to generate the frame's unique internal state.
226    ///
227    /// # Returns
228    /// * A new `Frame` instance.
229    pub fn initialize(id: String) -> Self {
230        Frame {
231            id,
232            // internal_patterns: Vec::new(),
233            sqs_component: None,
234            validation_status: true, // Starts valid
235            zkp_witness: None,
236            zkp_secret_scalar: None,
237        }
238    }
239
240    /// Checks if a shared state (`SQS`) has been successfully established for this frame
241    /// and if the frame is currently in a valid state.
242    ///
243    /// # Returns
244    /// * `true` if an `SQS` is present and `validation_status` is true, `false` otherwise.
245    pub fn has_sqs(&self) -> bool {
246        self.sqs_component.is_some() && self.validation_status
247    }
248
249    /// Returns an immutable reference to the established shared state (`SQS`) data, if available.
250    ///
251    /// Returns `None` if no `SQS` has been established or if the frame is invalid.
252    /// The `SQS` is shared using an `Arc`, so multiple frames can hold references.
253    ///
254    /// # Returns
255    /// * `Some(&Arc<SQS>)` if an SQS exists and the frame is valid.
256    /// * `None` otherwise.
257    pub fn get_sqs(&self) -> Option<&Arc<Sqs>> {
258         if !self.validation_status { return None; }
259        self.sqs_component.as_ref()
260    }
261
262    /// Gets the frame's unique ID.
263    pub fn id(&self) -> &str {
264        &self.id
265    }
266
267
268    /// Checks if the frame is currently considered valid.
269    ///
270    /// The validation status can become `false` if operations like `decode` detect
271    /// inconsistencies or tampering.
272    ///
273    /// # Returns
274    /// * `true` if the frame's `validation_status` is true, `false` otherwise.
275     pub fn is_valid(&self) -> bool {
276        self.validation_status
277    }
278
279    /// Encrypts a message using ChaCha20-Poly1305 AEAD with the frame's SQS context.
280    ///
281    /// Generates a unique random nonce for each encryption. The nonce is included
282    /// in the returned `QfeEncryptedMessage`.
283    ///
284    /// # Arguments
285    /// * `plaintext`: The message bytes to encrypt.
286    /// * `associated_data`: Optional data to authenticate but not encrypt.
287    ///
288    /// # Returns
289    /// * `Ok(QfeEncryptedMessage)` containing the nonce and ciphertext+tag.
290    /// * `Err(QfeError)` if SQS is missing, frame is invalid, or key derivation fails.
291    pub fn encode_aead(
292        &self,
293        plaintext: &[u8],
294        associated_data: Option<&[u8]>
295    ) -> Result<QfeEncryptedMessage, QfeError> {
296        if !self.is_valid() { return Err(QfeError::FrameInvalid); }
297        let sqs = self.get_sqs().ok_or(QfeError::SqsMissing)?;
298
299        // 1. Derive AEAD key from SQS
300        let key = &sqs.aead_key;
301        let cipher = ChaCha20Poly1305::new(key);
302
303        // 2. Generate a unique Nonce (12 bytes for ChaCha20Poly1305)
304        // Using OsRngNonce requires enabling the 'std' feature potentially, or using OsRng directly
305        let mut nonce_bytes = [0u8; 12];
306        rand::rng().fill_bytes(&mut nonce_bytes);
307        let nonce = Nonce::<ChaCha20Poly1305>::from_slice(&nonce_bytes); // Create Nonce type
308
309        // 3. Encrypt the data
310        let _ciphertext = cipher.encrypt(nonce, plaintext)
311            .map_err(|e| QfeError::EncodingFailed(format!("AEAD encryption error: {}", e)))?;
312
313        // Include associated data in the encryption process if provided
314        // Note: AEAD encrypt methods often take plaintext as AsRef<[u8]>, AD separately.
315        // Re-check chacha20poly1305 docs: `encrypt` doesn't directly take AD.
316        // We need `encrypt_in_place` or construct the call differently if AD is used.
317        // Let's use `encrypt_in_place_detached` for clarity with AD.
318
319        // --- Revised Encryption Logic with AD ---
320        let key = &sqs.aead_key;
321        let cipher = ChaCha20Poly1305::new(key);
322        let mut nonce_bytes = [0u8; 12];
323        rand::rng().fill_bytes(&mut nonce_bytes);
324        let nonce = Nonce::<ChaCha20Poly1305>::from_slice(&nonce_bytes);
325
326        // Use encrypt_in_place_detached requires a mutable buffer
327        let mut buffer = Vec::with_capacity(plaintext.len() + 16); // Space for plaintext + tag
328        buffer.extend_from_slice(plaintext);
329
330        let tag = cipher.encrypt_in_place_detached(
331                nonce,
332                associated_data.unwrap_or(&[]), // Pass AD here
333                &mut buffer
334            )
335            .map_err(|e| QfeError::EncodingFailed(format!("AEAD encryption error: {}", e)))?;
336
337        // Append the tag to the ciphertext in the buffer
338        buffer.extend_from_slice(tag.as_slice());
339
340        Ok(QfeEncryptedMessage {
341            nonce: nonce_bytes.to_vec(), // Store the raw nonce bytes
342            ciphertext: buffer, // Ciphertext now includes the tag
343        })
344    }
345
346    /// Decrypts a message using ChaCha20-Poly1305 AEAD with the frame's SQS context.
347    ///
348    /// Verifies the integrity and authenticity using the tag included in the ciphertext.
349    /// Marks the frame invalid if decryption fails.
350    ///
351    /// # Arguments
352    /// * `encrypted_message`: The `QfeEncryptedMessage` containing nonce and ciphertext+tag.
353    /// * `associated_data`: Optional associated data that must match the data used during encryption.
354    ///
355    /// # Returns
356    /// * `Ok(Vec<u8>)` containing the original plaintext if decryption and verification succeed.
357    /// * `Err(QfeError)` if SQS is missing, frame is invalid, key derivation fails, or
358    ///   decryption/authentication fails (`QfeError::DecodingFailed`).
359    pub fn decode_aead(
360        &mut self, // Changed to &mut self to allow setting validation_status on failure
361        encrypted_message: &QfeEncryptedMessage,
362        associated_data: Option<&[u8]>
363    ) -> Result<Vec<u8>, QfeError> {
364         if !self.is_valid() { return Err(QfeError::FrameInvalid); } // Check before attempt
365         let sqs = self.get_sqs().ok_or(QfeError::SqsMissing)?;
366
367         // 1. Derive AEAD key from SQS
368         let key = &sqs.aead_key;
369         let cipher = ChaCha20Poly1305::new(key);
370
371         // 2. Get Nonce from message
372         if encrypted_message.nonce.len() != 12 {
373             self.validation_status = false;
374             return Err(QfeError::DecodingFailed("Invalid nonce length received".to_string()));
375         }
376         let nonce = Nonce::<ChaCha20Poly1305>::from_slice(&encrypted_message.nonce);
377
378         // 3. Decrypt the data (includes authenticity check)
379         // Use decrypt_in_place_detached if using that for encryption
380         let mut buffer = encrypted_message.ciphertext.clone(); // Clone to decrypt in place
381
382         // Need to split buffer into ciphertext and tag
383         if buffer.len() < 16 { // Check if buffer is large enough for the tag
384              self.validation_status = false;
385              return Err(QfeError::DecodingFailed("Ciphertext too short to contain tag".to_string()));
386         }
387         let tag_offset = buffer.len() - 16;
388         let (ciphertext_slice_mut, tag_slice) = buffer.split_at_mut(tag_offset);
389         let tag = chacha20poly1305::Tag::from_slice(tag_slice);
390
391
392         let decrypt_result = cipher.decrypt_in_place_detached(
393             nonce,
394             associated_data.unwrap_or(&[]), // Pass AD here
395             ciphertext_slice_mut, // Provide mutable ciphertext slice
396             tag // Provide the tag separately
397         );
398
399         match decrypt_result {
400             Ok(()) => {
401                 // Decryption successful, buffer now contains plaintext
402                 // Truncate buffer to remove decrypted padding/tag space if necessary (inplace should handle this)
403                 Ok(ciphertext_slice_mut.to_vec()) // Return the plaintext part
404             }
405             Err(e) => {
406                 self.validation_status = false; // Mark frame invalid on decryption failure
407                 Err(QfeError::DecodingFailed(format!("AEAD decryption/authentication failed: {}", e)))
408             }
409         }
410    }
411
412    /// Calculates a short, human-readable fingerprint for the established SQS.
413    ///
414    /// This fingerprint should be compared out-of-band with the other participant's
415    /// fingerprint after `establish_sqs` completes successfully. A match provides
416    /// strong evidence against a Man-in-the-Middle (MitM) attack during the SQS exchange.
417    ///
418    /// The fingerprint is derived by hashing the SQS components, phase lock, and
419    /// participant IDs in a deterministic order.
420    ///
421    /// # Returns
422    /// * `Ok(String)` containing a short hexadecimal fingerprint (e.g., 8 characters).
423    /// * `Err(QfeError::SqsMissing)` if no SQS has been established for this frame.
424    /// * `Err(QfeError::FrameInvalid)` if the frame is in an invalid state.
425    pub fn calculate_sqs_fingerprint(&self) -> Result<String, QfeError> {
426        if !self.is_valid() { return Err(QfeError::FrameInvalid); }
427        let sqs = self.get_sqs().ok_or(QfeError::SqsMissing)?;
428
429        let mut hasher = Sha512::new(); // Use strong hash for fingerprint base
430        hasher.update(b"QFE_SQS_FINGERPRINT_V1"); // Domain separation
431
432        // Include participant IDs, sorted for consistency
433        let mut ids = [sqs.participant_a_id.as_str(), sqs.participant_b_id.as_str()];
434        ids.sort_unstable();
435        hasher.update(ids[0].as_bytes());
436        hasher.update(ids[1].as_bytes());
437
438        // Include core SQS data
439        hasher.update(&sqs.components);
440
441        let full_hash: [u8; 64] = hasher.finalize().into();
442
443        // Take the first 4 bytes (8 hex chars) for a short fingerprint
444        // Use data encoding crate for hex? No, keep deps minimal. Format manually.
445        let fingerprint = format!(
446            "{:02x}{:02x}{:02x}{:02x}",
447            full_hash[0], full_hash[1], full_hash[2], full_hash[3]
448        );
449
450        Ok(fingerprint)
451    }
452}
453
454// --- Public API Functions ---
455
456/// Sets up two Frames (A and B) and establishes a shared state (`SQS`) between them.
457///
458/// This function simplifies the common initialization workflow. It initializes two frames
459/// based on the provided IDs and seeds, then performs the key exchange process
460/// to establish the `SQS`.
461///
462/// # Arguments
463/// * `id_a`: A `String` identifier for the first frame (Frame A).
464/// * `seed_a`: A `u64` seed used for generating Frame A's internal state.
465/// * `id_b`: A `String` identifier for the second frame (Frame B).
466/// * `seed_b`: A `u64` seed used for generating Frame B's internal state.
467///
468/// # Returns
469/// * `Ok((Frame, Frame))` containing the two initialized `Frame` instances, each holding
470///   a reference to the same successfully established `SQS`.
471/// * `Err(QfeError)` if frame initialization fails or if the `SQS` establishment
472///   process fails (e.g., due to internal validation checks or simulated interaction errors).
473///   Specific error reasons can be found in the `QfeError::SqsEstablishmentFailed` variant.
474///   Sets up two Frames (A and B) and establishes a shared state (`SQS`) using ML-KEM.
475pub fn setup_qfe_pair(
476    id_a: String,
477    id_b: String,
478    context_string: &str, // Add context string needed for KDF salt
479) -> Result<(Frame, Frame), QfeError> {
480    let mut frame_a = Frame::initialize(id_a);
481    let mut frame_b = Frame::initialize(id_b);
482    // Use the new KEM-based establishment function
483    establish_sqs_kem(&mut frame_a, &mut frame_b, context_string)?;
484    Ok((frame_a, frame_b))
485}
486
487/// Performs the interactive shared state (`SQS`) establishment process between two Frames.
488///
489/// This function simulates the key exchange. It should typically be called after
490/// `Frame::initialize` for two frames that intend to communicate securely.
491/// Use [`setup_qfe_pair`] for a simpler setup.
492///
493/// # Arguments
494/// * `frame_a`: A mutable reference to the first participating `Frame`.
495/// * `frame_b`: A mutable reference to the second participating `Frame`.
496///
497/// # Returns
498/// * `Ok(())` if the `SQS` is successfully established and validated between the two frames.
499///   Both input frames will be updated internally with a reference to the shared `SQS`.
500/// * `Err(QfeError)` if the process fails. This can happen if:
501///     - Either frame is already invalid (`QfeError::FrameInvalid`).
502///     - An SQS is already established for one or both frames (`QfeError::SqsEstablishmentFailed`).
503///     - The simulated interaction fails internal checks (`QfeError::SqsEstablishmentFailed`).
504///
505/// Performs the interactive shared state (`SQS`) establishment process between two Frames.
506/// This simulates the key exchange. After calling this successfully, it is **highly recommended**
507/// that both participants call [`Frame::calculate_sqs_fingerprint`] and compare the results
508/// via an independent, authenticated channel (e.g., voice call, in person) to verify
509/// the absence of a Man-in-the-Middle attack before using the SQS for sensitive communication.
510///
511/// (Rest of documentation comment remains the same)
512#[deprecated(since="0.3.0", note="Use establish_sqs_kem for secure key establishment.")]
513pub fn establish_sqs(_frame_a: &mut Frame, _frame_b: &mut Frame) -> Result<(), QfeError> {
514    // Keep the old implementation here, or just return an error/panic
515     Err(QfeError::InternalError("establish_sqs is deprecated, use establish_sqs_kem".to_string()))
516    // Or copy the old implementation if needed for compatibility during transition
517}
518
519/// Establishes a shared state (SQS) using ML-KEM-1024 key exchange and HKDF.
520/// This replaces the previous custom SQS establishment logic.
521///
522/// Simulates the KEM process:
523/// 1. Alice generates a keypair (pk_A, sk_A).
524/// 2. Bob uses pk_A to encapsulate -> (ciphertext_C, shared_secret_ss_B).
525/// 3. Alice uses sk_A to decapsulate ciphertext_C -> shared_secret_ss_A.
526/// 4. Both parties use the shared secret (ss_A == ss_B) as IKM for HKDF-SHA512.
527/// 5. HKDF derives the final `Sqs.components`.
528///
529/// # Arguments
530/// * `frame_a` (Alice): Mutable reference to the initiating frame.
531/// * `frame_b` (Bob): Mutable reference to the responding frame.
532/// * `context_string`: A string for domain separation used in HKDF salt.
533///
534/// # Returns
535/// * `Ok(())` on success, updating both frames with the derived `Arc<Sqs>`.
536/// * `Err(QfeError)` on failure (e.g., frame invalid, KEM error, HKDF error).
537pub fn establish_sqs_kem(
538    frame_a: &mut Frame, // Alice
539    frame_b: &mut Frame, // Bob
540    context_string: &str // Context for HKDF salt
541) -> Result<(), QfeError> {
542    if !frame_a.validation_status || !frame_b.validation_status {
543        return Err(QfeError::FrameInvalid);
544    }
545    if frame_a.sqs_component.is_some() || frame_b.sqs_component.is_some() {
546        return Err(QfeError::SqsEstablishmentFailed(
547            "SQS already established".to_string(),
548        ));
549    }
550
551    // --- Simulate ML-KEM Exchange ---
552    // 1. Alice generates keypair
553    // In a real protocol, sk_a is kept secret, pk_a is sent to Bob.
554    let (pk_a, sk_a) = mlkem1024::keypair();
555
556    // 2. Bob receives pk_a, encapsulates -> (shared_secret_b, ciphertext)
557    // In a real protocol, Bob receives pk_a over the network.
558    // Encapsulate returns Result, handle potential errors.
559    let (shared_secret_ss_b, ciphertext_c) = mlkem1024::encapsulate(&pk_a);
560
561    // 3. Alice receives ciphertext_c, decapsulates -> shared_secret_a
562    // In a real protocol, Alice receives ciphertext_c over the network.
563    // Decapsulate returns Result, handle potential errors.
564    let shared_secret_ss_a = mlkem1024::decapsulate(&ciphertext_c, &sk_a);
565
566    // --- Verification (Crucial!) ---
567    // Check that the shared secrets match. In theory they always should if KEM is correct.
568    // The pqcrypto shared secret types should implement PartialEq.
569    if shared_secret_ss_a != shared_secret_ss_b {
570        // This indicates a critical failure in the KEM library or logic.
571        frame_a.validation_status = false;
572        frame_b.validation_status = false;
573        return Err(QfeError::SqsEstablishmentFailed(
574            "CRITICAL: ML-KEM shared secrets mismatch!".to_string()
575        ));
576    }
577    // Use ss_a (or ss_b) as the Input Keying Material (IKM) for HKDF
578    let ikm = shared_secret_ss_a.as_bytes();
579
580    // --- HKDF for SQS Components Derivation ---
581    // 1. Derive Salt for HKDF
582    let mut salt_hasher = Sha512::new();
583    salt_hasher.update(SQS_SALT_CONTEXT_V1);
584    let mut ids = [frame_a.id().as_bytes(), frame_b.id().as_bytes()];
585    ids.sort_unstable();
586    salt_hasher.update(ids[0]);
587    salt_hasher.update(ids[1]);
588    salt_hasher.update(context_string.as_bytes());
589    let salt = salt_hasher.finalize();
590
591    // 2. HKDF-Extract using SHA-512 -> Pseudorandom Key (PRK)
592    let hk = Hkdf::<Sha512>::new(Some(salt.as_slice()), ikm);
593
594    // 3. HKDF-Expand for SQS components (64 bytes)
595    let mut sqs_components_okm = [0u8; 64];
596    hk.expand(SQS_COMPONENTS_V2_INFO, &mut sqs_components_okm)
597        .map_err(|e| QfeError::SqsEstablishmentFailed(format!("HKDF-Expand error for components: {}", e)))?;
598
599    // 4. HKDF-Expand for AEAD key (32 bytes) using DIFFERENT info
600    let mut aead_key_okm = Key::default(); // Key is [u8; 32]
601    hk.expand(SQS_AEAD_KEY_V1_INFO, &mut aead_key_okm)
602        .map_err(|e| QfeError::SqsEstablishmentFailed(format!("HKDF-Expand error for AEAD key: {}", e)))?;
603
604
605    // --- Create and Store SQS ---
606    let sqs = Arc::new(Sqs {
607        pattern_type: PatternType::Sqs,
608        components: sqs_components_okm.to_vec(),
609        aead_key: aead_key_okm, // Store the derived AEAD key
610        validation: true,
611        participant_a_id: frame_a.id.clone(),
612        participant_b_id: frame_b.id.clone(),
613    });
614
615    frame_a.sqs_component = Some(Arc::clone(&sqs));
616    frame_b.sqs_component = Some(sqs);
617
618    Ok(())
619}
620
621//
622// --- Unit Tests ---
623#[cfg(test)]
624mod tests {
625    use super::*;
626
627    // Helper to setup two frames with established & conceptually authenticated SQS
628    // Re-using helper from encoding tests is fine if it exists, otherwise define here.
629    fn setup_frames_for_signing(id_a: &str, id_b: &str) -> (Frame, Frame) {
630        // Initialize frames using the derived seeds (this now uses the updated initialize function)
631        let mut frame_a = Frame::initialize(id_a.to_string());
632        let mut frame_b = Frame::initialize(id_b.to_string());
633
634        // Establish SQS (this uses the updated establish_sqs logic internally)
635        establish_sqs_kem(&mut frame_a, &mut frame_b, "test_context")
636            .expect("SQS setup failed during test helper execution");
637
638        (frame_a, frame_b)
639    }
640
641    mod aead_tests {
642        use super::*; // Import from parent `tests` module (and thus lib root)
643
644        // Helper function specific to AEAD tests
645        fn setup_frames_for_aead() -> (Frame, Frame) {
646            // Use the existing updated helper or redefine if needed
647            setup_frames_for_signing("AEAD_A", "AEAD_B") // Re-use helper is fine
648        }
649
650        #[test]
651        fn test_aead_encode_decode_success_no_ad() {
652            let (frame_a, mut frame_b) = setup_frames_for_aead();
653            let plaintext = b"This message is secret and authentic (no AD).";
654            let associated_data = None; // No associated data
655
656            // Encode
657            let encrypted_msg_res = frame_a.encode_aead(plaintext, associated_data);
658            assert!(encrypted_msg_res.is_ok());
659            let encrypted_msg = encrypted_msg_res.unwrap();
660
661            // Nonce should be 12 bytes, Ciphertext > plaintext + 16 bytes (tag)
662            assert_eq!(encrypted_msg.nonce.len(), 12);
663            assert!(encrypted_msg.ciphertext.len() >= plaintext.len() + 16);
664
665            // Decode
666            let decoded_res = frame_b.decode_aead(&encrypted_msg, associated_data);
667            assert!(decoded_res.is_ok());
668            let decoded_plaintext = decoded_res.unwrap();
669
670            // Verify correctness
671            assert_eq!(decoded_plaintext, plaintext);
672            assert!(frame_b.is_valid()); // Frame B should remain valid
673        }
674
675        #[test]
676        fn test_aead_encode_decode_success_with_ad() {
677            let (frame_a, mut frame_b) = setup_frames_for_aead();
678            let plaintext = b"This message is secret and authentic (with AD).";
679            let associated_data = Some(b"Important Context" as &[u8]);
680
681            // Encode
682            let encrypted_msg = frame_a.encode_aead(plaintext, associated_data)
683                                   .expect("Encoding with AD failed");
684
685            // Decode
686            let decoded_plaintext = frame_b.decode_aead(&encrypted_msg, associated_data)
687                                        .expect("Decoding with AD failed");
688
689            // Verify correctness
690            assert_eq!(decoded_plaintext, plaintext);
691            assert!(frame_b.is_valid());
692        }
693
694        #[test]
695        fn test_aead_encode_decode_empty_message() {
696            let (frame_a, mut frame_b) = setup_frames_for_aead();
697            let plaintext = b""; // Empty plaintext
698            let associated_data = Some(b"Context for empty message" as &[u8]);
699
700            // Encode
701            let encrypted_msg = frame_a.encode_aead(plaintext, associated_data)
702                                   .expect("Encoding empty message failed");
703            assert!(encrypted_msg.ciphertext.len() == 16); // Empty plaintext -> only tag remains
704
705            // Decode
706            let decoded_plaintext = frame_b.decode_aead(&encrypted_msg, associated_data)
707                                        .expect("Decoding empty message failed");
708
709            // Verify correctness
710            assert_eq!(decoded_plaintext, plaintext);
711            assert!(decoded_plaintext.is_empty());
712            assert!(frame_b.is_valid());
713        }
714
715
716        #[test]
717        fn test_aead_decode_fails_tampered_ciphertext() {
718            let (frame_a, mut frame_b) = setup_frames_for_aead();
719            let plaintext = b"Do not tamper!";
720            let mut encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
721
722            // Tamper ciphertext (avoiding the tag at the end)
723            if encrypted_msg.ciphertext.len() > 16 { // Ensure there's ciphertext before tag
724                encrypted_msg.ciphertext[0] ^= 0xAA;
725            } else if !encrypted_msg.ciphertext.is_empty() {
726                 encrypted_msg.ciphertext[0] ^= 0xAA; // Tamper tag if only tag exists
727            }
728
729
730            // Attempt Decode
731            let decoded_res = frame_b.decode_aead(&encrypted_msg, None);
732            assert!(decoded_res.is_err());
733            let err = decoded_res.unwrap_err();
734            assert!(matches!(err, QfeError::DecodingFailed(_)));
735            assert!(err.to_string().contains("AEAD decryption/authentication failed"));
736            assert!(!frame_b.is_valid()); // Frame should be invalidated
737        }
738
739        #[test]
740        fn test_aead_decode_fails_tampered_tag() {
741            let (frame_a, mut frame_b) = setup_frames_for_aead();
742            let plaintext = b"Do not tamper tag!";
743            let mut encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
744
745            // Tamper tag (last 16 bytes)
746            let ct_len = encrypted_msg.ciphertext.len();
747            if ct_len >= 16 {
748                 encrypted_msg.ciphertext[ct_len - 1] ^= 0xAA; // Flip last byte of tag
749            }
750
751            // Attempt Decode
752            let decoded_res = frame_b.decode_aead(&encrypted_msg, None);
753            assert!(decoded_res.is_err());
754            assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
755            assert!(!frame_b.is_valid());
756        }
757
758
759        #[test]
760        fn test_aead_decode_fails_tampered_nonce() {
761            let (frame_a, mut frame_b) = setup_frames_for_aead();
762            let plaintext = b"Cannot reuse nonces";
763            let mut encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
764
765            // Tamper nonce
766            if !encrypted_msg.nonce.is_empty() {
767                encrypted_msg.nonce[0] ^= 0xAA;
768            }
769
770            // Attempt Decode
771            let decoded_res = frame_b.decode_aead(&encrypted_msg, None);
772            // Decryption will proceed but likely produce garbage or fail tag check
773            assert!(decoded_res.is_err());
774            assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
775            assert!(!frame_b.is_valid());
776        }
777
778        // In mod aead_tests
779
780        // Test AD mismatch (string vs string)
781        #[test]
782        fn test_aead_decode_fails_wrong_ad_string_mismatch() {
783            let (frame_a, mut frame_b) = setup_frames_for_aead();
784            let plaintext = b"AD must match";
785            let associated_data = Some(b"Correct AD" as &[u8]);
786            let wrong_associated_data = Some(b"Wrong AD" as &[u8]);
787
788            let encrypted_msg = frame_a.encode_aead(plaintext, associated_data).unwrap();
789
790            // Attempt decode with WRONG AD string
791            let decoded_res = frame_b.decode_aead(&encrypted_msg, wrong_associated_data);
792            assert!(decoded_res.is_err());
793            let err = decoded_res.unwrap_err();
794            println!("Wrong AD (String Mismatch) Decode Error: {:?}", err); // Optional debug
795            assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for AD string mismatch");
796            assert!(!frame_b.is_valid());
797        }
798
799        // Test AD mismatch (None vs Some)
800        #[test]
801        fn test_aead_decode_fails_wrong_ad_none_vs_some() {
802            let (frame_a, mut frame_b) = setup_frames_for_aead();
803            let plaintext = b"AD must match";
804            let associated_data = Some(b"Correct AD" as &[u8]);
805
806            let encrypted_msg = frame_a.encode_aead(plaintext, associated_data).unwrap();
807
808            // Attempt decode with NO AD (when encoded with AD)
809            let decoded_res_no_ad = frame_b.decode_aead(&encrypted_msg, None);
810            assert!(decoded_res_no_ad.is_err());
811            let err = decoded_res_no_ad.unwrap_err();
812            println!("Wrong AD (None vs Some) Decode Error: {:?}", err); // Optional debug
813            assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for AD mismatch (None vs Some)");
814            assert!(!frame_b.is_valid());
815        }
816
817        // Test Nonce length too short
818        #[test]
819        fn test_aead_decode_fails_nonce_too_short() {
820            let (frame_a, mut frame_b) = setup_frames_for_aead();
821            let plaintext = b"Nonce length matters";
822            let encrypted_msg_ok = frame_a.encode_aead(plaintext, None).unwrap();
823
824            let mut bad_nonce_msg = encrypted_msg_ok.clone();
825            bad_nonce_msg.nonce = vec![0u8; 11]; // 11 bytes
826
827            let decoded_res = frame_b.decode_aead(&bad_nonce_msg, None);
828            assert!(decoded_res.is_err());
829            let err = decoded_res.unwrap_err();
830            println!("Wrong Nonce Length (11) Decode Error: {:?}", err); // Optional debug
831            assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for Nonce Length 11");
832            // Check the specific error message associated with the nonce length check
833            assert!(err.to_string().contains("Invalid nonce length received"), "Incorrect error message for short nonce");
834            assert!(!frame_b.is_valid());
835        }
836
837        // Test Nonce length too long
838        #[test]
839        fn test_aead_decode_fails_nonce_too_long() {
840            let (frame_a, mut frame_b) = setup_frames_for_aead();
841            let plaintext = b"Nonce length matters";
842            let encrypted_msg_ok = frame_a.encode_aead(plaintext, None).unwrap();
843
844            let mut bad_nonce_msg = encrypted_msg_ok.clone();
845            bad_nonce_msg.nonce = vec![0u8; 13]; // 13 bytes
846
847            let decoded_res_13 = frame_b.decode_aead(&bad_nonce_msg, None);
848            assert!(decoded_res_13.is_err());
849            let err = decoded_res_13.unwrap_err();
850            println!("Wrong Nonce Length (13) Decode Error: {:?}", err); // Optional debug
851            assert!(matches!(err, QfeError::DecodingFailed(_)), "Expected DecodingFailed for Nonce Length 13");
852            assert!(err.to_string().contains("Invalid nonce length received"), "Incorrect error message for long nonce");
853            assert!(!frame_b.is_valid());
854        }
855
856        #[test]
857        fn test_aead_decode_fails_ad_mismatch_encode_none_decode_some() {
858            let (frame_a, mut frame_b) = setup_frames_for_aead();
859            let plaintext = b"AD mismatch 2";
860            let associated_data = Some(b"Some AD" as &[u8]);
861
862            // Encode with NO AD
863            let encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
864
865             // Attempt decode with SOME AD (when encoded with None)
866             let decoded_res = frame_b.decode_aead(&encrypted_msg, associated_data);
867             assert!(decoded_res.is_err());
868             assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
869             assert!(!frame_b.is_valid());
870        }
871
872
873        #[test]
874        fn test_aead_decode_fails_wrong_sqs() {
875            // Setup A-B with SQS1
876            let (frame_a, _frame_b) = setup_frames_for_aead();
877            // Setup C-D with SQS2
878            let (mut frame_c, _frame_d) = setup_frames_for_signing("AEAD_C", "AEAD_D"); // Use different IDs
879
880            // Ensure SQS differ
881            assert_ne!(
882                frame_a.get_sqs().unwrap().components,
883                frame_c.get_sqs().unwrap().components
884            );
885
886            let plaintext = b"Message from A";
887            // A encodes with SQS1
888            let encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap();
889
890            // C tries to decode with SQS2 (wrong key)
891            let decoded_res = frame_c.decode_aead(&encrypted_msg, None);
892            assert!(decoded_res.is_err());
893            assert!(matches!(decoded_res.unwrap_err(), QfeError::DecodingFailed(_)));
894            assert!(!frame_c.is_valid()); // Frame C should be invalidated
895        }
896
897        #[test]
898        fn test_aead_encode_decode_no_sqs() {
899            let frame_a_no_sqs = Frame::initialize("NoSQS_AEAD_A".to_string());
900            let mut frame_b_no_sqs = Frame::initialize("NoSQS_AEAD_B".to_string());
901            let plaintext = b"Cannot encrypt";
902            let dummy_encrypted = QfeEncryptedMessage { nonce: vec![0;12], ciphertext: vec![0;32]};
903
904            let enc_res = frame_a_no_sqs.encode_aead(plaintext, None);
905            assert!(enc_res.is_err());
906            assert!(matches!(enc_res.unwrap_err(), QfeError::SqsMissing));
907
908            let dec_res = frame_b_no_sqs.decode_aead(&dummy_encrypted, None);
909            assert!(dec_res.is_err());
910            assert!(matches!(dec_res.unwrap_err(), QfeError::SqsMissing));
911        }
912
913        #[test]
914        fn test_aead_encode_decode_invalid_frame() {
915            let (mut frame_a, mut frame_b) = setup_frames_for_aead();
916            let plaintext = b"Invalid frame test";
917            let encrypted_msg = frame_a.encode_aead(plaintext, None).unwrap(); // Encode while valid
918
919            // Invalidate frames
920            frame_a.validation_status = false;
921            frame_b.validation_status = false;
922
923            // Try encoding with invalid frame
924            let enc_res = frame_a.encode_aead(plaintext, None);
925            assert!(enc_res.is_err());
926            assert!(matches!(enc_res.unwrap_err(), QfeError::FrameInvalid));
927
928             // Try decoding with invalid frame
929             let dec_res = frame_b.decode_aead(&encrypted_msg, None);
930             assert!(dec_res.is_err());
931             assert!(matches!(dec_res.unwrap_err(), QfeError::FrameInvalid));
932        }
933
934    }
935}