/*
 * ==================================================================================
 * Cryptographic Engine Header - AES-256-CBC encryption for file transfer
 *
 * ISA Project
 * Author: Roman Necas (xnecasr00)
 * Date: 13. October 2025
 *
 * PURPOSE:
 * This module provides AES-256-CBC encryption and decryption services for the
 * ICMP covert channel file transfer system. It implements secure key derivation
 * from the user's login using PBKDF2 and manages encryption contexts using
 * OpenSSL's EVP interface.
 *
 * ENCRYPTION ALGORITHM:
 * - Cipher: AES-256-CBC (Advanced Encryption Standard, 256-bit key, CBC mode)
 * - Key Derivation: PBKDF2-HMAC-SHA256 with 600,000 iterations
 * - Password Source: User's login name (hardcoded to "xnecasr00")
 * - Unique Per-Transfer: Each transfer uses unique IV and salt
 *
 * SECURITY PROPERTIES:
 * - Confidentiality: AES-256 provides strong encryption (128-bit security level)
 * - Key Stretching: PBKDF2 with 600,000 iterations defends against brute-force
 * - Randomness: Cryptographically secure random IVs and salts (OpenSSL RAND_bytes)
 * - Memory Safety: Secure memory wiping using OPENSSL_cleanse
 *
 * USAGE FLOW:
 * 1. Client: crypto_init() with NULL salt/IV (generates new ones)
 * 2. Client: crypto_init_encrypt() to prepare for encryption
 * 3. Client: crypto_encrypt() in loop for file chunks
 * 4. Client: crypto_finalize_encrypt() for final padding
 * 5. Client: Transmit salt/IV in START packet
 * 6. Server: crypto_init() with received salt/IV
 * 7. Server: crypto_init_decrypt() to prepare for decryption
 * 8. Server: crypto_decrypt() in loop for received chunks
 * 9. Server: crypto_finalize_decrypt() to remove padding
 * 10. Both: crypto_cleanup() to securely erase keys
 * ==================================================================================
 */

#ifndef CRYPTO_H
#define CRYPTO_H

#include "common.h"

/*
 * OpenSSL Headers
 * ---------------
 * These headers provide access to OpenSSL's cryptographic primitives.
 */
#include <openssl/evp.h>    /* EVP interface - high-level crypto API */
#include <openssl/aes.h>    /* AES constants and low-level definitions */
#include <openssl/rand.h>   /* Cryptographically secure random number generator */
#include <openssl/sha.h>    /* SHA-256 for PBKDF2 HMAC */
#include <openssl/err.h>    /* Error reporting for OpenSSL failures */

/*
 * ==================================================================================
 * CRYPTOGRAPHIC CONSTANTS
 * ==================================================================================
 */

/*
 * AES-256 Key Size (32 bytes = 256 bits)
 *
 * AES-256 uses a 256-bit (32-byte) key, providing a security level of 128 bits
 * due to the birthday paradox. This is considered secure against all known attacks
 * including quantum computers (with Grover's algorithm reducing effective security
 * to 128 bits).
 */
#define AES_KEY_SIZE 32

/*
 * AES Initialization Vector (IV) Size (16 bytes = 128 bits)
 *
 * The IV is used in CBC mode to ensure that identical plaintext blocks encrypt
 * to different ciphertext blocks. It must be:
 * - Unpredictable (cryptographically random)
 * - Unique for each encryption operation
 * - Transmitted to the receiver (included in START packet)
 *
 * The IV does NOT need to be secret, only unpredictable.
 */
#define AES_IV_SIZE 16

/*
 * AES Block Size (16 bytes = 128 bits)
 *
 * AES operates on fixed 128-bit (16-byte) blocks regardless of key size.
 * In CBC mode, the plaintext is padded to a multiple of the block size.
 * PKCS#7 padding is used automatically by OpenSSL EVP functions.
 */
#define AES_BLOCK_SIZE 16

/*
 * PBKDF2 Salt Size (32 bytes = 256 bits)
 *
 * The salt is a random value used in PBKDF2 key derivation to ensure that:
 * - The same password produces different keys in different contexts
 * - Precomputed rainbow tables cannot be used
 * - Each transfer session has a unique encryption key
 *
 * NIST recommends a minimum of 128 bits (16 bytes) for salts.
 * We use 256 bits (32 bytes) for extra security margin.
 */
#define PBKDF2_SALT_SIZE 32

/*
 * PBKDF2 Iteration Count (600,000)
 *
 * PBKDF2 iterations determine how many times the password is hashed.
 * More iterations = more CPU time to derive key = harder to brute-force.
 *
 * OWASP recommendations (2023):
 * - Minimum: 120,000 iterations for PBKDF2-HMAC-SHA256
 * - Recommended: 600,000 iterations
 *
 * This value provides strong defense against brute-force attacks while
 * remaining fast enough for legitimate use (~100ms on modern hardware).
 */
#define PBKDF2_ITERATIONS 600000

/*
 * HMAC Size (32 bytes = 256 bits)
 *
 * Size of HMAC-SHA256 output used in PBKDF2.
 * SHA-256 produces a 256-bit (32-byte) hash value.
 *
 * Note: Currently not used directly in code, but defined for completeness.
 */
#define HMAC_SIZE 32

/*
 * ==================================================================================
 * CRYPTOGRAPHIC ENGINE STRUCTURE
 * ==================================================================================
 */

/*
 * crypto_engine_t - Cryptographic state management structure
 *
 * This structure encapsulates all state needed for encryption/decryption
 * operations. It holds the encryption key, IV, and OpenSSL context.
 *
 * LIFECYCLE:
 * 1. Allocated by caller (typically on stack or in session structure)
 * 2. Initialized by crypto_init()
 * 3. Configured by crypto_init_encrypt() or crypto_init_decrypt()
 * 4. Used for crypto_encrypt() or crypto_decrypt() calls
 * 5. Finalized by crypto_finalize_encrypt() or crypto_finalize_decrypt()
 * 6. Cleaned up by crypto_cleanup() (MANDATORY - erases sensitive data)
 *
 * SECURITY NOTES:
 * - The key array contains sensitive cryptographic material
 * - Always call crypto_cleanup() to securely wipe memory
 * - Never copy or serialize this structure (key would be exposed)
 * - Keep this structure in secure memory (stack is safer than heap)
 *
 * MEMORY MANAGEMENT:
 * - ctx: Dynamically allocated by OpenSSL, freed by crypto_cleanup()
 * - key, iv, salt: Fixed-size arrays, wiped by crypto_cleanup()
 * - Structure itself: Managed by caller
 */
typedef struct {
    /*
     * OpenSSL cipher context
     *
     * This is an opaque pointer to OpenSSL's internal encryption state.
     * It maintains the cipher algorithm (AES-256-CBC), mode, key schedule,
     * and buffered data between crypto_encrypt/decrypt calls.
     *
     * Allocated by EVP_CIPHER_CTX_new() in crypto_init()
     * Freed by EVP_CIPHER_CTX_free() in crypto_cleanup()
     */
    EVP_CIPHER_CTX *ctx;

    /*
     * AES-256 encryption key (32 bytes)
     *
     * This is the actual encryption key derived from the user's login
     * using PBKDF2. It is HIGHLY SENSITIVE and must be:
     * - Never logged or printed
     * - Never written to disk
     * - Securely wiped after use (crypto_cleanup does this)
     * - Kept in memory as briefly as possible
     *
     * Derived by: PBKDF2(login, salt, 600000 iterations, SHA-256)
     */
    uint8_t key[AES_KEY_SIZE];

    /*
     * Initialization Vector (16 bytes)
     *
     * Random value used as the first "previous ciphertext block" in CBC mode.
     * Must be unique for each encryption operation but does not need to be secret.
     *
     * Generated by: RAND_bytes() (cryptographically secure)
     * Transmitted in: START packet metadata (iftp_metadata_t.iv)
     */
    uint8_t iv[AES_IV_SIZE];

    /*
     * PBKDF2 salt (32 bytes)
     *
     * Random value mixed with the password during key derivation.
     * Ensures that the same password produces different keys in different contexts.
     *
     * Generated by: RAND_bytes() (cryptographically secure)
     * Transmitted in: START packet metadata (iftp_metadata_t.salt)
     */
    uint8_t salt[PBKDF2_SALT_SIZE];

    /*
     * Initialization flag
     *
     * Set to 1 by crypto_init() after successful initialization.
     * Checked by other functions to ensure crypto_init() was called.
     * Reset to 0 by crypto_cleanup().
     */
    int initialized;

    /*
     * Encryption mode flag
     *
     * 1 = encryption mode (crypto_init_encrypt was called)
     * 0 = decryption mode (crypto_init_decrypt was called)
     *
     * Note: This field is defined but not currently used in the implementation.
     * It's kept for future use and debugging purposes.
     */
    int encrypt_mode;
} crypto_engine_t;

/*
 * ==================================================================================
 * CORE CRYPTOGRAPHIC FUNCTIONS
 * ==================================================================================
 */

/*
 * crypto_init - Initialize cryptographic engine
 *
 * Prepares the crypto_engine_t structure for encryption or decryption.
 * Derives the AES key from the login using PBKDF2 with the provided salt.
 *
 * ALGORITHM:
 * 1. Allocate OpenSSL cipher context (EVP_CIPHER_CTX_new)
 * 2. Generate or use provided salt (RAND_bytes if NULL)
 * 3. Generate or use provided IV (RAND_bytes if NULL)
 * 4. Derive key: key = PBKDF2(login, salt, 600000, SHA-256)
 * 5. Mark structure as initialized
 *
 * @param crypto: Pointer to crypto_engine_t structure to initialize
 * @param login: User's login name (password for key derivation)
 * @param salt: Salt for PBKDF2 (NULL = generate new random salt)
 * @param iv: Initialization vector (NULL = generate new random IV)
 *
 * @return: 0 on success, -1 on error
 *
 * USAGE:
 * - Client: Pass NULL for salt and IV to generate new ones
 * - Server: Pass received salt and IV from START packet
 */
int crypto_init(crypto_engine_t *crypto, const char *login, const uint8_t *salt, const uint8_t *iv);

/*
 * crypto_init_encrypt - Configure engine for encryption
 *
 * Sets up the cipher context for encryption operations (AES-256-CBC encrypt mode).
 * Must be called after crypto_init() and before crypto_encrypt().
 *
 * ALGORITHM:
 * Calls EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)
 *
 * @param crypto: Initialized crypto_engine_t structure
 * @return: 0 on success, -1 on error
 *
 * USAGE: Client calls this before encrypting file data
 */
int crypto_init_encrypt(crypto_engine_t *crypto);

/*
 * crypto_init_decrypt - Configure engine for decryption
 *
 * Sets up the cipher context for decryption operations (AES-256-CBC decrypt mode).
 * Must be called after crypto_init() and before crypto_decrypt().
 *
 * ALGORITHM:
 * Calls EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)
 *
 * @param crypto: Initialized crypto_engine_t structure
 * @return: 0 on success, -1 on error
 *
 * USAGE: Server calls this before decrypting received data
 */
int crypto_init_decrypt(crypto_engine_t *crypto);

/*
 * crypto_encrypt - Encrypt data chunk
 *
 * Encrypts a chunk of plaintext data using AES-256-CBC.
 * This function can be called multiple times for streaming encryption.
 *
 * ALGORITHM:
 * Calls EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)
 *
 * IMPORTANT NOTES:
 * - Output may be shorter than input due to buffering (AES works on 16-byte blocks)
 * - Always call crypto_finalize_encrypt() after last chunk to get final data
 * - ciphertext buffer must be at least plaintext_len + AES_BLOCK_SIZE bytes
 *
 * @param crypto: Initialized crypto engine in encrypt mode
 * @param plaintext: Input data to encrypt
 * @param plaintext_len: Length of plaintext data
 * @param ciphertext: Output buffer for encrypted data
 * @param ciphertext_len: Output parameter - actual bytes written
 *
 * @return: 0 on success, -1 on error
 */
int crypto_encrypt(crypto_engine_t *crypto, const uint8_t *plaintext, int plaintext_len,
                   uint8_t *ciphertext, int *ciphertext_len);

/*
 * crypto_decrypt - Decrypt data chunk
 *
 * Decrypts a chunk of ciphertext data using AES-256-CBC.
 * This function can be called multiple times for streaming decryption.
 *
 * ALGORITHM:
 * Calls EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)
 *
 * IMPORTANT NOTES:
 * - Output may be shorter than input due to buffering
 * - Always call crypto_finalize_decrypt() after last chunk to get final data
 * - Finalize will fail if padding is incorrect (wrong key or corrupted data)
 *
 * @param crypto: Initialized crypto engine in decrypt mode
 * @param ciphertext: Input encrypted data
 * @param ciphertext_len: Length of ciphertext data
 * @param plaintext: Output buffer for decrypted data
 * @param plaintext_len: Output parameter - actual bytes written
 *
 * @return: 0 on success, -1 on error
 */
int crypto_decrypt(crypto_engine_t *crypto, const uint8_t *ciphertext, int ciphertext_len,
                   uint8_t *plaintext, int *plaintext_len);

/*
 * crypto_finalize_encrypt - Complete encryption and get final block
 *
 * Finalizes the encryption process by applying PKCS#7 padding and outputting
 * the final ciphertext block. Must be called after all crypto_encrypt() calls.
 *
 * ALGORITHM:
 * Calls EVP_EncryptFinal_ex(ctx, output, output_len)
 *
 * PADDING:
 * AES-CBC requires the plaintext to be a multiple of 16 bytes.
 * PKCS#7 padding adds 1-16 bytes:
 * - If plaintext is already aligned, adds full 16-byte block
 * - Padding value = number of padding bytes
 * - Example: "Hello" (5 bytes) → "Hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
 *
 * @param crypto: Crypto engine that was used for encryption
 * @param output: Buffer for final encrypted bytes (padding block)
 * @param output_len: Output parameter - bytes written (0-16)
 *
 * @return: 0 on success, -1 on error
 */
int crypto_finalize_encrypt(crypto_engine_t *crypto, uint8_t *output, int *output_len);

/*
 * crypto_finalize_decrypt - Complete decryption and validate padding
 *
 * Finalizes the decryption process by removing PKCS#7 padding and outputting
 * the final plaintext bytes. Must be called after all crypto_decrypt() calls.
 *
 * ALGORITHM:
 * Calls EVP_DecryptFinal_ex(ctx, output, output_len)
 *
 * VALIDATION:
 * This function validates PKCS#7 padding:
 * - Checks that padding bytes are correct
 * - Returns error if padding is invalid
 * - Invalid padding usually means wrong key or corrupted data
 *
 * @param crypto: Crypto engine that was used for decryption
 * @param output: Buffer for final decrypted bytes
 * @param output_len: Output parameter - bytes written
 *
 * @return: 0 on success, -1 on error (wrong key or corrupted data)
 */
int crypto_finalize_decrypt(crypto_engine_t *crypto, uint8_t *output, int *output_len);

/*
 * crypto_get_salt - Retrieve salt from crypto engine
 *
 * Copies the salt value from the crypto engine to provided buffer.
 * Used by client to get the generated salt for transmission in START packet.
 *
 * @param crypto: Initialized crypto engine
 * @param salt: Output buffer (must be PBKDF2_SALT_SIZE bytes)
 */
void crypto_get_salt(const crypto_engine_t *crypto, uint8_t *salt);

/*
 * crypto_get_iv - Retrieve IV from crypto engine
 *
 * Copies the IV value from the crypto engine to provided buffer.
 * Used by client to get the generated IV for transmission in START packet.
 *
 * @param crypto: Initialized crypto engine
 * @param iv: Output buffer (must be AES_IV_SIZE bytes)
 */
void crypto_get_iv(const crypto_engine_t *crypto, uint8_t *iv);

/*
 * crypto_cleanup - Securely erase and free crypto engine resources
 *
 * Cleans up all resources used by the crypto engine and securely wipes
 * all sensitive cryptographic material from memory.
 *
 * SECURITY:
 * This function is CRITICAL for security. It:
 * - Frees OpenSSL cipher context
 * - Overwrites key with zeros (OPENSSL_cleanse)
 * - Overwrites IV with zeros
 * - Overwrites salt with zeros
 * - Marks structure as uninitialized
 *
 * MANDATORY: Always call this function when done with crypto operations.
 * Failure to call this leaves sensitive key material in memory.
 *
 * @param crypto: Crypto engine to clean up
 */
void crypto_cleanup(crypto_engine_t *crypto);

/*
 * ==================================================================================
 * SECURE MEMORY OPERATIONS
 * ==================================================================================
 */

/*
 * crypto_secure_zero - Securely wipe memory
 *
 * Overwrites memory with zeros in a way that cannot be optimized away by
 * the compiler. Uses OPENSSL_cleanse() internally.
 *
 * USE CASE: Wiping sensitive data (passwords, keys) from memory
 *
 * @param ptr: Pointer to memory to wipe
 * @param size: Number of bytes to wipe
 */
void crypto_secure_zero(void *ptr, size_t size);

/*
 * crypto_secure_compare - Constant-time memory comparison
 *
 * Compares two memory regions in constant time to prevent timing attacks.
 * Uses CRYPTO_memcmp() internally.
 *
 * USE CASE: Comparing authentication tags, MACs, or passwords
 * DO NOT USE: For general-purpose comparisons (it's slower than memcmp)
 *
 * @param a: First memory region
 * @param b: Second memory region
 * @param size: Number of bytes to compare
 * @return: 0 if equal, non-zero if different
 */
int crypto_secure_compare(const void *a, const void *b, size_t size);

/*
 * ==================================================================================
 * KEY DERIVATION
 * ==================================================================================
 */

/*
 * crypto_derive_key - Derive cryptographic key from password using PBKDF2
 *
 * Generic key derivation function. This is a lower-level interface to PBKDF2.
 * Most code should use crypto_init() instead, which calls this internally.
 *
 * ALGORITHM: PBKDF2-HMAC-SHA256
 * key = PBKDF2(password, salt, iterations, SHA-256)
 *
 * @param password: Password/passphrase to derive from
 * @param salt: Salt value (randomness)
 * @param salt_len: Length of salt in bytes
 * @param key: Output buffer for derived key
 * @param key_len: Desired key length in bytes
 * @param iterations: Number of PBKDF2 iterations (600000 recommended)
 *
 * @return: 0 on success, -1 on error
 */
int crypto_derive_key(const char *password, const uint8_t *salt, size_t salt_len,
                      uint8_t *key, size_t key_len, int iterations);

/*
 * ==================================================================================
 * RANDOM NUMBER GENERATION
 * ==================================================================================
 */

/*
 * crypto_random_bytes - Generate cryptographically secure random bytes
 *
 * Uses OpenSSL's RAND_bytes() to generate high-quality random data.
 * This is suitable for cryptographic use (keys, IVs, salts, nonces).
 *
 * SECURITY: Uses system entropy sources (e.g., /dev/urandom on Linux)
 *
 * @param buffer: Output buffer for random bytes
 * @param size: Number of random bytes to generate
 *
 * @return: 0 on success, -1 on error
 */
int crypto_random_bytes(uint8_t *buffer, size_t size);

#endif /* CRYPTO_H */
