//! Cipher example — AES-256-GCM AEAD encryption/decryption.
//!
//! Run with: cargo run --example cipher -p native-ossl

use native_ossl::cipher::{AeadDecryptCtx, AeadEncryptCtx, CipherAlg};
use native_ossl::rand::Rand;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // ── AES-256-GCM (AEAD) ───────────────────────────────────────────────────

    let alg = CipherAlg::fetch(c"AES-256-GCM", None)?;
    println!("AES-256-GCM key length: {} bytes", alg.key_len());
    println!("AES-256-GCM IV length:  {} bytes", alg.iv_len());

    let mut key = [0u8; 32];
    let mut nonce = [0u8; 12];
    Rand::fill(&mut key)?;
    Rand::fill(&mut nonce)?;

    let plaintext = b"secret message - keep it safe";
    let aad = b"additional authenticated data";

    // Encrypt.
    let mut enc = AeadEncryptCtx::new(&alg, &key, &nonce, None)?;
    enc.set_aad(aad)?;
    let mut ciphertext = vec![0u8; plaintext.len()];
    let n = enc.update(plaintext, &mut ciphertext)?;
    enc.finalize(&mut ciphertext[n..])?;
    let mut tag = [0u8; 16];
    enc.tag(&mut tag)?;

    println!(
        "Ciphertext ({} bytes): {}",
        ciphertext.len(),
        native_ossl::util::hex_encode(&ciphertext)
    );
    println!(
        "GCM tag:               {}",
        native_ossl::util::hex_encode(tag)
    );

    // Decrypt and authenticate.
    let mut dec = AeadDecryptCtx::new(&alg, &key, &nonce, None)?;
    dec.set_aad(aad)?;
    dec.set_tag(&tag)?;
    let mut recovered = vec![0u8; ciphertext.len()];
    let n = dec.update(&ciphertext, &mut recovered)?;
    dec.finalize(&mut recovered[n..])?;

    assert_eq!(&recovered, plaintext);
    println!("AES-256-GCM round-trip: OK");

    // ── Authentication failure ────────────────────────────────────────────────

    let mut dec2 = AeadDecryptCtx::new(&alg, &key, &nonce, None)?;
    dec2.set_aad(aad)?;
    let mut bad_tag = tag;
    bad_tag[0] ^= 0xff;
    dec2.set_tag(&bad_tag)?;
    let mut buf = vec![0u8; ciphertext.len()];
    let _ = dec2.update(&ciphertext, &mut buf);
    let auth_err = dec2.finalize(&mut buf[..0]);
    assert!(auth_err.is_err(), "tampered tag must fail");
    println!("Tampered tag rejected: OK");

    Ok(())
}
