Projects/3BIT/winter-semester/ISA/include/crypto.h
2026-04-14 19:28:46 +02:00

521 lines
18 KiB
C

/*
* ==================================================================================
* 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 */