521 lines
18 KiB
C
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 */
|