cttps 0.1.2

Crypto Transfer Protocol Secure (CTTPS) - A high-performance secure transport protocol using X25519 and AES-256-GCM.
use ring::{agreement, aead, rand, hkdf};
use anyhow::{Result, anyhow};

pub struct CttpsCrypto {
    opening_key: aead::LessSafeKey,
    sealing_key: aead::LessSafeKey,
}

impl CttpsCrypto {
    pub fn new(shared_secret: &[u8], transcript_hash: &[u8]) -> Result<Self> {
        if shared_secret.len() != 32 {
            return Err(anyhow!("Shared secret must be 32 bytes"));
        }

        // Use HKDF-SHA256 to derive the actual AES keys from the shared secret and transcript.
        // This ensures that if the handshake was modified, the keys won't match.
        let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]);
        let prk = salt.extract(shared_secret);
        
        // Derive two 256-bit keys: one for client->server and one for server->client.
        // For simplicity in this implementation, we'll derive one master key and use it for both,
        // but with different "info" strings to ensure separation.
        
        let info = [transcript_hash];
        let okm = prk.expand(&info, hkdf::HKDF_SHA256)
            .map_err(|_| anyhow!("HKDF expansion failed"))?;
        
        let mut session_key = [0u8; 32];
        okm.fill(&mut session_key).map_err(|_| anyhow!("Failed to fill session key"))?;

        let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, &session_key)
            .map_err(|_| anyhow!("Failed to create AEAD key"))?;
        let opening_key = aead::LessSafeKey::new(unbound_key);
        
        let unbound_key_seal = aead::UnboundKey::new(&aead::AES_256_GCM, &session_key)
            .map_err(|_| anyhow!("Failed to create AEAD key"))?;
        let sealing_key = aead::LessSafeKey::new(unbound_key_seal);

        Ok(Self {
            opening_key,
            sealing_key,
        })
    }

    pub fn seal(&self, nonce_bytes: [u8; 12], payload: &mut Vec<u8>) -> Result<Vec<u8>> {
        let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
        let aad = aead::Aad::empty();
        
        self.sealing_key
            .seal_in_place_append_tag(nonce, aad, payload)
            .map_err(|_| anyhow!("Encryption failed"))?;
            
        Ok(payload.clone())
    }

    pub fn open<'a>(&self, nonce_bytes: [u8; 12], encrypted_payload_with_tag: &'a mut [u8]) -> Result<&'a [u8]> {
        let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
        let aad = aead::Aad::empty();
        
        let decrypted = self.opening_key
            .open_in_place(nonce, aad, encrypted_payload_with_tag)
            .map_err(|_| anyhow!("Decryption failed - integrity check failed"))?;
            
        Ok(decrypted)
    }
}

pub fn generate_keypair() -> (agreement::EphemeralPrivateKey, Vec<u8>) {
    let rng = rand::SystemRandom::new();
    let private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng).unwrap();
    let public_key = private_key.compute_public_key().unwrap();
    (private_key, public_key.as_ref().to_vec())
}

pub fn derive_shared_secret(my_private_key: agreement::EphemeralPrivateKey, peer_public_key: &[u8]) -> Result<Vec<u8>> {
    let peer_public_key = agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key);
    
    agreement::agree_ephemeral(
        my_private_key,
        &peer_public_key,
        |shared_secret| {
            Ok(shared_secret.to_vec())
        }
    ).map_err(|_| anyhow!("Agreement failed"))?
}

/// Computes a transcript hash for the handshake to prevent MITM/tampering.
pub fn compute_transcript_hash(client_pub: &[u8], server_pub: &[u8]) -> Vec<u8> {
    use ring::digest;
    let mut ctx = digest::Context::new(&digest::SHA256);
    ctx.update(client_pub);
    ctx.update(server_pub);
    ctx.finish().as_ref().to_vec()
}