/* * ================================================================================== * 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 /* EVP interface - high-level crypto API */ #include /* AES constants and low-level definitions */ #include /* Cryptographically secure random number generator */ #include /* SHA-256 for PBKDF2 HMAC */ #include /* 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 */