39 log_info(
"Server crypto handshake initialized (ephemeral keys)");
41 log_info(
"Client crypto handshake initialized");
50 if (!ctx || !params) {
82 log_debug(
"Crypto parameters set: kex_key=%u, auth_key=%u, sig=%u, "
83 "secret=%u, nonce=%u, mac=%u, hmac=%u",
98 switch (packet_type) {
120 size_t authenticated_size =
123 if (packet_size != simple_size && packet_size != authenticated_size) {
125 "Invalid KEY_EXCHANGE_INIT size: %zu (expected %zu for simple or %zu for authenticated: "
126 "kex=%u + auth=%u + sig=%u)",
144 size_t authenticated_max_size = authenticated_min_size + 1 + 40;
146 if (packet_size != simple_size &&
147 (packet_size < authenticated_min_size || packet_size > authenticated_max_size)) {
149 "Invalid KEY_EXCHANGE_RESP size: %zu (expected %zu for simple or %zu-%zu for authenticated: "
150 "kex=%u + auth=%u + sig=%u + optional GPG key ID)",
151 packet_size, simple_size, authenticated_min_size, authenticated_max_size,
161 if (packet_size != expected_size) {
172 size_t max_size = min_size + 1 + 40;
173 if (packet_size < min_size || packet_size > max_size) {
175 "Invalid AUTH_RESPONSE size: %zu (expected %zu-%zu: hmac=%u + "
176 "nonce=%u + optional GPG key ID)",
201 if (packet_size != 0) {
208 if (packet_size != 0) {
230 const char *password) {
231 if (!ctx || !password) {
286 size_t plaintext_len,
uint8_t *ciphertext,
size_t ciphertext_size,
287 size_t *ciphertext_len) {
294 ciphertext_size, ciphertext_len);
304 size_t ciphertext_len,
uint8_t *plaintext,
size_t plaintext_size,
305 size_t *plaintext_len) {
312 plaintext_size, plaintext_len);
322 const uint8_t *plaintext,
size_t plaintext_len,
323 uint8_t *ciphertext,
size_t ciphertext_size,
324 size_t *ciphertext_len) {
327 if (plaintext_len > ciphertext_size) {
328 SET_ERRNO(
ERROR_BUFFER,
"Plaintext too large for ciphertext buffer: %zu > %zu", plaintext_len, ciphertext_size);
331 memcpy(ciphertext, plaintext, plaintext_len);
332 *ciphertext_len = plaintext_len;
341 const uint8_t *ciphertext,
size_t ciphertext_len,
342 uint8_t *plaintext,
size_t plaintext_size,
343 size_t *plaintext_len) {
346 if (ciphertext_len > plaintext_size) {
347 SET_ERRNO(
ERROR_BUFFER,
"Ciphertext too large for plaintext buffer: %zu > %zu", ciphertext_len, plaintext_size);
350 memcpy(plaintext, ciphertext, ciphertext_len);
351 *plaintext_len = ciphertext_len;
379 log_info(
"Sending REKEY_REQUEST with new ephemeral X25519 public key (32 bytes)");
382 if (send_result != 0) {
387 log_debug(
"REKEY_REQUEST sent successfully, awaiting REKEY_RESPONSE");
406 log_info(
"Sending REKEY_RESPONSE with new ephemeral X25519 public key (32 bytes)");
409 if (send_result != 0) {
414 log_debug(
"REKEY_RESPONSE sent successfully, awaiting REKEY_COMPLETE");
436 size_t ciphertext_len = 0;
449 sodium_memzero(old_shared_key,
sizeof(old_shared_key));
457 log_info(
"Sending REKEY_COMPLETE (encrypted with NEW key, %zu bytes)", ciphertext_len);
459 if (send_result != 0) {
470 log_info(
"Session rekeying completed successfully (initiator side)");
486 time_t now = time(NULL);
491 "SECURITY: Rekey request rejected - too frequent (%ld sec since last, minimum %d sec required)",
505 log_info(
"Received REKEY_REQUEST with peer's new ephemeral public key (32 bytes)");
520 log_debug(
"REKEY_REQUEST processed successfully, new shared secret computed (responder side)");
545 log_info(
"Received REKEY_RESPONSE with peer's new ephemeral public key (32 bytes)");
554 log_debug(
"REKEY_RESPONSE processed successfully, new shared secret computed (initiator side)");
574 log_info(
"Received REKEY_COMPLETE packet (%zu bytes), verifying with NEW key", packet_len);
583 size_t plaintext_len = 0;
589 sodium_memzero(old_shared_key,
sizeof(old_shared_key));
597 log_info(
"REKEY_COMPLETE verified successfully, committing to new key");
605 log_info(
"Session rekeying completed successfully (responder side)");
⚠️‼️ Error and/or exit() when things go bad.
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
asciichat_error_t crypto_handshake_rekey_complete(crypto_handshake_context_t *ctx, socket_t socket)
Send REKEY_COMPLETE packet (initiator side)
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
asciichat_error_t crypto_handshake_decrypt_packet(const crypto_handshake_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len)
Decrypt a packet using the established crypto context.
asciichat_error_t crypto_handshake_process_rekey_request(crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
Process received REKEY_REQUEST packet (responder side)
asciichat_error_t crypto_handshake_validate_packet_size(const crypto_handshake_context_t *ctx, uint16_t packet_type, size_t packet_size)
Validate crypto packet size based on session parameters.
asciichat_error_t crypto_handshake_encrypt_packet(const crypto_handshake_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len)
Encrypt a packet using the established crypto context.
asciichat_error_t crypto_handshake_init_with_password(crypto_handshake_context_t *ctx, bool is_server, const char *password)
Initialize crypto handshake context with password authentication.
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
Check if handshake is complete and encryption is ready.
bool crypto_handshake_should_rekey(const crypto_handshake_context_t *ctx)
Check if rekeying should be triggered for this handshake context.
asciichat_error_t crypto_handshake_init(crypto_handshake_context_t *ctx, bool is_server)
Initialize crypto handshake context.
asciichat_error_t crypto_handshake_rekey_response(crypto_handshake_context_t *ctx, socket_t socket)
Send REKEY_RESPONSE packet (responder side)
void crypto_handshake_cleanup(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
asciichat_error_t crypto_handshake_set_parameters(crypto_handshake_context_t *ctx, const crypto_parameters_packet_t *params)
Set crypto parameters from crypto_parameters_packet_t.
asciichat_error_t crypto_handshake_process_rekey_response(crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
Process received REKEY_RESPONSE packet (initiator side)
asciichat_error_t crypto_encrypt_packet_or_passthrough(const crypto_handshake_context_t *ctx, bool crypto_ready, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len)
Encrypt with automatic passthrough if crypto not ready.
asciichat_error_t crypto_decrypt_packet_or_passthrough(const crypto_handshake_context_t *ctx, bool crypto_ready, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len)
Decrypt with automatic passthrough if crypto not ready.
asciichat_error_t crypto_handshake_process_rekey_complete(crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
Process received REKEY_COMPLETE packet (responder side)
asciichat_error_t crypto_handshake_rekey_request(crypto_handshake_context_t *ctx, socket_t socket)
Send REKEY_REQUEST packet (initiator side)
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U16(val)
#define SAFE_STRNCPY(dst, src, size)
crypto_result_t crypto_rekey_commit(crypto_context_t *ctx)
Commit to new keys after successful REKEY_COMPLETE.
void crypto_cleanup(crypto_context_t *ctx)
Cleanup crypto context with secure memory wiping.
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)
Encrypt data using XSalsa20-Poly1305.
#define ED25519_SIGNATURE_SIZE
Ed25519 signature size in bytes.
#define AUTH_CHALLENGE_SIZE
Challenge nonce size (32 bytes)
crypto_result_t crypto_init(crypto_context_t *ctx)
Initialize libsodium and crypto context.
#define MAX_AUTH_FAILED_PACKET_SIZE
Maximum AUTH_FAILED packet size (256 bytes)
const char * crypto_result_to_string(crypto_result_t result)
Convert crypto result to human-readable string.
crypto_result_t
Cryptographic operation result codes.
#define MAX_ENCRYPTED_PACKET_SIZE
Maximum encrypted packet size (64KB)
#define ED25519_PUBLIC_KEY_SIZE
Ed25519 public key size in bytes.
#define CRYPTO_SHARED_KEY_SIZE
Shared key size (X25519)
bool crypto_should_rekey(const crypto_context_t *ctx)
Check if rekeying should be triggered based on time or packet count thresholds.
void crypto_rekey_abort(crypto_context_t *ctx)
Abort rekeying and fallback to old keys.
bool crypto_is_ready(const crypto_context_t *ctx)
Check if key exchange is complete and ready for encryption.
crypto_result_t crypto_rekey_process_request(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
Process REKEY_REQUEST from peer (responder side)
#define CRYPTO_PUBLIC_KEY_SIZE
Public key size (X25519)
#define REKEY_MIN_REQUEST_INTERVAL
Minimum interval between rekey requests (60 seconds, DDoS protection)
#define ARGON2ID_SALT_SIZE
Argon2id salt size in bytes.
crypto_result_t crypto_decrypt(crypto_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext_out, size_t plaintext_out_size, size_t *plaintext_len_out)
Decrypt data using XSalsa20-Poly1305.
crypto_result_t crypto_init_with_password(crypto_context_t *ctx, const char *password)
Initialize with password-based encryption.
crypto_result_t crypto_rekey_init(crypto_context_t *ctx)
Initiate rekeying by generating new ephemeral keys.
#define AUTH_CHALLENGE_FLAGS_SIZE
Authentication flags size (1 byte)
crypto_result_t crypto_rekey_process_response(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
Process REKEY_RESPONSE from peer (initiator side)
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
uint8_t hmac_size
HMAC size in bytes (e.g., 32 for HMAC-SHA256)
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
uint16_t signature_size
Signature size in bytes (e.g., 64 for Ed25519, 3309 for Dilithium3)
int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a basic packet without encryption.
uint8_t nonce_size
Nonce size in bytes (e.g., 24 for XSalsa20)
uint8_t mac_size
MAC size in bytes (e.g., 16 for Poly1305)
uint16_t shared_secret_size
Shared secret size in bytes (e.g., 32 for X25519)
uint16_t kex_public_key_size
Key exchange public key size in bytes (e.g., 32 for X25519, 1568 for Kyber1024)
@ PACKET_TYPE_CRYPTO_AUTH_RESPONSE
Client -> Server: {HMAC[32]} (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP
Client -> Server: {client_pubkey[32]} (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_REKEY_COMPLETE
Initiator -> Responder: Empty (encrypted with NEW key, but still handshake)
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED)
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion)
@ PACKET_TYPE_CRYPTO_REKEY_REQUEST
Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED)
@ PACKET_TYPE_CRYPTO_REKEY_RESPONSE
Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Crypto capabilities packet structure (Packet Type 14)
Cryptographic context structure.
uint8_t encryption_key_size
uint8_t temp_public_key[32]
uint16_t auth_public_key_size
uint16_t private_key_size
uint8_t auth_challenge_size
time_t rekey_last_request_time
uint8_t temp_shared_key[32]
Cryptographic handshake context structure.
crypto_handshake_state_t state
bool server_uses_client_auth
crypto_context_t crypto_ctx
Crypto parameters packet structure (Packet Type 15)
Common SIMD utilities and structures.