ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
common.h File Reference

Common declarations and data structures for cryptographic handshake. More...

Go to the source code of this file.

Data Structures

struct  crypto_handshake_context_t
 Cryptographic handshake context structure. More...
 

Macros

Authentication Requirement Flags

Flags sent in AUTH_CHALLENGE packet to indicate server requirements.

#define AUTH_REQUIRE_PASSWORD   0x01
 Server requires password authentication.
 
#define AUTH_REQUIRE_CLIENT_KEY   0x02
 Server requires client key authentication (whitelist)
 

Typedefs

typedef struct crypto_handshake_context_t crypto_handshake_context_t
 Cryptographic handshake context structure.
 

Enumerations

enum  crypto_handshake_state_t {
  CRYPTO_HANDSHAKE_DISABLED = 0 , CRYPTO_HANDSHAKE_INIT , CRYPTO_HANDSHAKE_KEY_EXCHANGE , CRYPTO_HANDSHAKE_AUTHENTICATING ,
  CRYPTO_HANDSHAKE_READY , CRYPTO_HANDSHAKE_FAILED
}
 Cryptographic handshake state enumeration. More...
 

Functions

Common Handshake Functions
asciichat_error_t crypto_handshake_init (const char *name, crypto_handshake_context_t *ctx, bool is_server)
 Initialize named crypto handshake context.
 
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_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_init_with_password (const char *name, crypto_handshake_context_t *ctx, bool is_server, const char *password)
 Initialize named crypto handshake context with password authentication.
 
void crypto_handshake_destroy (crypto_handshake_context_t *ctx)
 Cleanup crypto handshake context with secure memory wiping.
 
bool crypto_handshake_is_ready (const crypto_handshake_context_t *ctx)
 Check if handshake is complete and encryption is ready.
 
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_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_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_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.
 
Session Rekeying Functions
asciichat_error_t crypto_handshake_rekey_request (crypto_handshake_context_t *ctx, struct acip_transport *transport)
 Send REKEY_REQUEST packet (initiator side)
 
asciichat_error_t crypto_handshake_rekey_response (crypto_handshake_context_t *ctx, struct acip_transport *transport)
 Send REKEY_RESPONSE packet (responder side)
 
asciichat_error_t crypto_handshake_rekey_complete (crypto_handshake_context_t *ctx, struct acip_transport *transport)
 Send REKEY_COMPLETE packet (initiator side)
 
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_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_handshake_process_rekey_complete (crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
 Process received REKEY_COMPLETE packet (responder side)
 
bool crypto_handshake_should_rekey (const crypto_handshake_context_t *ctx)
 Check if rekeying should be triggered for this handshake context.
 

Detailed Description

Common declarations and data structures for cryptographic handshake.

Definition in file crypto/handshake/common.h.

Macro Definition Documentation

◆ AUTH_REQUIRE_CLIENT_KEY

#define AUTH_REQUIRE_CLIENT_KEY   0x02

Server requires client key authentication (whitelist)

Definition at line 28 of file crypto/handshake/common.h.

◆ AUTH_REQUIRE_PASSWORD

#define AUTH_REQUIRE_PASSWORD   0x01

Server requires password authentication.

Definition at line 26 of file crypto/handshake/common.h.

Function Documentation

◆ crypto_decrypt_packet_or_passthrough()

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.

Parameters
ctxHandshake context
crypto_readyTrue if crypto is ready, false to passthrough
ciphertextCiphertext or plaintext data to decrypt
ciphertext_lenLength of ciphertext/plaintext data
plaintextOutput buffer for plaintext
plaintext_sizeSize of output buffer
plaintext_lenOutput parameter for actual plaintext length
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 365 of file crypto/handshake/common.c.

368 {
369 if (!crypto_ready) {
370 // No encryption - just copy data
371 if (ciphertext_len > plaintext_size) {
372 SET_ERRNO(ERROR_BUFFER, "Ciphertext too large for plaintext buffer: %zu > %zu", ciphertext_len, plaintext_size);
373 return ERROR_BUFFER;
374 }
375 memcpy(plaintext, ciphertext, ciphertext_len);
376 *plaintext_len = ciphertext_len;
377 return ASCIICHAT_OK;
378 }
379
380 return crypto_handshake_decrypt_packet(ctx, ciphertext, ciphertext_len, plaintext, plaintext_size, plaintext_len);
381}
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.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_BUFFER

References ASCIICHAT_OK, crypto_handshake_decrypt_packet(), ERROR_BUFFER, and SET_ERRNO.

Referenced by crypto_client_decrypt_packet(), and crypto_server_decrypt_packet().

◆ crypto_encrypt_packet_or_passthrough()

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.

Parameters
ctxHandshake context
crypto_readyTrue if crypto is ready, false to passthrough
plaintextPlaintext data to encrypt
plaintext_lenLength of plaintext data
ciphertextOutput buffer for ciphertext or plaintext (if passthrough)
ciphertext_sizeSize of output buffer
ciphertext_lenOutput parameter for actual length
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 346 of file crypto/handshake/common.c.

349 {
350 if (!crypto_ready) {
351 // No encryption - just copy data
352 if (plaintext_len > ciphertext_size) {
353 SET_ERRNO(ERROR_BUFFER, "Plaintext too large for ciphertext buffer: %zu > %zu", plaintext_len, ciphertext_size);
354 return ERROR_BUFFER;
355 }
356 memcpy(ciphertext, plaintext, plaintext_len);
357 *ciphertext_len = plaintext_len;
358 return ASCIICHAT_OK;
359 }
360
361 return crypto_handshake_encrypt_packet(ctx, plaintext, plaintext_len, ciphertext, ciphertext_size, ciphertext_len);
362}
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.

References ASCIICHAT_OK, crypto_handshake_encrypt_packet(), ERROR_BUFFER, and SET_ERRNO.

Referenced by crypto_client_encrypt_packet(), and crypto_server_encrypt_packet().

◆ crypto_handshake_decrypt_packet()

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.

Parameters
ctxHandshake context (must be ready)
ciphertextCiphertext data to decrypt
ciphertext_lenLength of ciphertext data
plaintextOutput buffer for plaintext
plaintext_sizeSize of output buffer
plaintext_lenOutput parameter for actual plaintext length
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 328 of file crypto/handshake/common.c.

330 {
331 if (!ctx || !crypto_handshake_is_ready(ctx)) {
332 SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, ready=%d", ctx, ctx ? crypto_handshake_is_ready(ctx) : 0);
333 return ERROR_INVALID_STATE;
334 }
335
336 crypto_result_t result = crypto_decrypt((crypto_context_t *)&ctx->crypto_ctx, ciphertext, ciphertext_len, plaintext,
337 plaintext_size, plaintext_len);
338 if (result != CRYPTO_OK) {
339 return SET_ERRNO(ERROR_NETWORK, "Failed to decrypt packet: %s", crypto_result_to_string(result));
340 }
341
342 return ASCIICHAT_OK;
343}
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
Check if handshake is complete and encryption is ready.
const char * crypto_result_to_string(crypto_result_t result)
Convert crypto result to human-readable string.
crypto_result_t
Cryptographic operation result codes.
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.
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:77
Cryptographic context structure.

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_decrypt(), crypto_handshake_is_ready(), CRYPTO_OK, crypto_result_to_string(), ERROR_INVALID_STATE, ERROR_NETWORK, and SET_ERRNO.

Referenced by crypto_decrypt_packet_or_passthrough().

◆ crypto_handshake_destroy()

void crypto_handshake_destroy ( crypto_handshake_context_t *  ctx)

Cleanup crypto handshake context with secure memory wiping.

Parameters
ctxHandshake context to cleanup

Definition at line 284 of file crypto/handshake/common.c.

284 {
285 if (!ctx)
286 return;
287
288 // Cleanup core crypto context
290
291 // Zero out sensitive data
292 sodium_memzero(ctx, sizeof(crypto_handshake_context_t));
293}
void crypto_destroy(crypto_context_t *ctx)
Cleanup crypto context with secure memory wiping.
Cryptographic handshake context structure.

References crypto_handshake_context_t::crypto_ctx, and crypto_destroy().

Referenced by acds_client_disconnect(), client_cleanup(), client_crypto_init(), client_generate_keypair(), client_handle_crypto_parameters(), client_handle_key_exchange_init(), crypto_client_cleanup(), crypto_server_cleanup_client(), remove_client(), and server_connection_close().

◆ crypto_handshake_encrypt_packet()

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.

Parameters
ctxHandshake context (must be ready)
plaintextPlaintext data to encrypt
plaintext_lenLength of plaintext data
ciphertextOutput buffer for ciphertext
ciphertext_sizeSize of output buffer
ciphertext_lenOutput parameter for actual ciphertext length
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 310 of file crypto/handshake/common.c.

312 {
313 if (!ctx || !crypto_handshake_is_ready(ctx)) {
314 SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, ready=%d", ctx, ctx ? crypto_handshake_is_ready(ctx) : 0);
315 return ERROR_INVALID_STATE;
316 }
317
318 crypto_result_t result = crypto_encrypt((crypto_context_t *)&ctx->crypto_ctx, plaintext, plaintext_len, ciphertext,
319 ciphertext_size, ciphertext_len);
320 if (result != CRYPTO_OK) {
321 return SET_ERRNO(ERROR_NETWORK, "Failed to encrypt packet: %s", crypto_result_to_string(result));
322 }
323
324 return ASCIICHAT_OK;
325}
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.

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_encrypt(), crypto_handshake_is_ready(), CRYPTO_OK, crypto_result_to_string(), ERROR_INVALID_STATE, ERROR_NETWORK, and SET_ERRNO.

Referenced by crypto_encrypt_packet_or_passthrough().

◆ crypto_handshake_get_context()

const crypto_context_t * crypto_handshake_get_context ( const crypto_handshake_context_t *  ctx)

Get the crypto context for encryption/decryption.

Parameters
ctxHandshake context
Returns
Pointer to crypto context, or NULL if ctx is NULL

Definition at line 303 of file crypto/handshake/common.c.

303 {
304 if (!ctx || !crypto_handshake_is_ready(ctx))
305 return NULL;
306 return &ctx->crypto_ctx;
307}

References crypto_handshake_context_t::crypto_ctx, and crypto_handshake_is_ready().

Referenced by broadcast_server_state_to_all_clients(), crypto_client_get_context(), crypto_server_get_context(), disconnect_client_for_bad_data(), and server_crypto_handshake().

◆ crypto_handshake_init()

asciichat_error_t crypto_handshake_init ( const char *  name,
crypto_handshake_context_t *  ctx,
bool  is_server 
)

Initialize named crypto handshake context.

Parameters
nameDebug name for the crypto context (typically "crypto_client_<id>")
ctxHandshake context to initialize (must not be NULL)
is_serverTrue if this is the server side, false for client
Returns
ASCIICHAT_OK on success, error code on failure

The context is automatically registered with the debug naming system.

Definition at line 20 of file crypto/handshake/common.c.

20 {
21 if (!name) {
22 return SET_ERRNO(ERROR_INVALID_PARAM, "Crypto context name is required");
23 }
24
25 if (!ctx) {
26 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p", ctx);
27 }
28
29 // Register crypto context with debug naming system
30 NAMED_REGISTER_CRYPTO_CONTEXT(ctx, name, NULL);
31
32 // Zero out the context
33 memset(ctx, 0, sizeof(crypto_handshake_context_t));
34
35 // Initialize core crypto context
36#ifndef NDEBUG
37 log_info("★★★ BEFORE crypto_init()");
39#endif
41#ifndef NDEBUG
42 log_info("★★★ AFTER crypto_init(), result=%d", result);
44#endif
45 if (result != CRYPTO_OK) {
46 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize crypto context: %s", crypto_result_to_string(result));
47 }
48
50 ctx->is_server = is_server;
51 ctx->verify_server_key = false;
52 ctx->require_client_auth = false;
53 ctx->server_uses_client_auth = false; // Set to true only if authenticated packet received
54
55 // Load server keys if this is a server
56 if (is_server) {
57 log_dev("Server crypto handshake initialized (ephemeral keys)");
58 } else {
59 log_dev("Client crypto handshake initialized");
60 }
61
62 return ASCIICHAT_OK;
63}
crypto_result_t crypto_init(crypto_context_t *ctx)
Initialize libsodium and crypto context.
#define NAMED_REGISTER_CRYPTO_CONTEXT(ctx, name, parent_ptr)
Register a crypto context with automatic format specifier.
void debug_sync_print_state(void)
Print all synchronization primitive states at once.
Definition sync.c:387
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM
@ CRYPTO_HANDSHAKE_INIT
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
crypto_handshake_state_t state

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, CRYPTO_HANDSHAKE_INIT, crypto_init(), CRYPTO_OK, crypto_result_to_string(), debug_sync_print_state(), ERROR_CRYPTO, ERROR_INVALID_PARAM, crypto_handshake_context_t::is_server, log_dev, log_info, NAMED_REGISTER_CRYPTO_CONTEXT, crypto_handshake_context_t::require_client_auth, crypto_handshake_context_t::server_uses_client_auth, SET_ERRNO, crypto_handshake_context_t::state, and crypto_handshake_context_t::verify_server_key.

Referenced by acds_client_handler(), acds_websocket_client_handler(), client_crypto_init(), client_generate_keypair(), client_handle_crypto_parameters(), client_handle_key_exchange_init(), discovery_session_start(), and server_crypto_handshake().

◆ crypto_handshake_init_with_password()

asciichat_error_t crypto_handshake_init_with_password ( const char *  name,
crypto_handshake_context_t *  ctx,
bool  is_server,
const char *  password 
)

Initialize named crypto handshake context with password authentication.

Parameters
nameDebug name for the crypto context (typically "crypto_client_<id>")
ctxHandshake context to initialize (must not be NULL)
is_serverTrue if this is the server side, false for client
passwordPassword for authentication (must meet length requirements)
Returns
ASCIICHAT_OK on success, error code on failure

The context is automatically registered with the debug naming system.

Definition at line 247 of file crypto/handshake/common.c.

248 {
249 if (!name) {
250 return SET_ERRNO(ERROR_INVALID_PARAM, "Crypto context name is required");
251 }
252
253 if (!ctx || !password) {
254 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, password=%p", ctx, password);
255 }
256
257 // Register crypto context with debug naming system
258 NAMED_REGISTER_CRYPTO_CONTEXT(ctx, name, NULL);
259
260 // Zero out the context
261 memset(ctx, 0, sizeof(crypto_handshake_context_t));
262
263 // Initialize core crypto context with password
264 crypto_result_t result = crypto_init_with_password(&ctx->crypto_ctx, password);
265 if (result != CRYPTO_OK) {
266 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize crypto context with password: %s",
268 }
269
271 ctx->is_server = is_server;
272 ctx->verify_server_key = false;
273 ctx->require_client_auth = false;
274 ctx->server_uses_client_auth = false; // Set to true only if authenticated packet received
275 ctx->has_password = true;
276
277 // Store password temporarily (will be cleared after key derivation)
278 SAFE_STRNCPY(ctx->password, password, sizeof(ctx->password) - 1);
279
280 return ASCIICHAT_OK;
281}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
crypto_result_t crypto_init_with_password(crypto_context_t *ctx, const char *password)
Initialize with password-based encryption.

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, CRYPTO_HANDSHAKE_INIT, crypto_init_with_password(), CRYPTO_OK, crypto_result_to_string(), ERROR_CRYPTO, ERROR_INVALID_PARAM, crypto_handshake_context_t::has_password, crypto_handshake_context_t::is_server, NAMED_REGISTER_CRYPTO_CONTEXT, crypto_handshake_context_t::password, crypto_handshake_context_t::require_client_auth, SAFE_STRNCPY, crypto_handshake_context_t::server_uses_client_auth, SET_ERRNO, crypto_handshake_context_t::state, and crypto_handshake_context_t::verify_server_key.

Referenced by client_crypto_init(), and server_crypto_handshake().

◆ crypto_handshake_is_ready()

bool crypto_handshake_is_ready ( const crypto_handshake_context_t *  ctx)

Check if handshake is complete and encryption is ready.

Parameters
ctxHandshake context
Returns
true if handshake is complete and encryption is ready, false otherwise

Definition at line 296 of file crypto/handshake/common.c.

296 {
297 if (!ctx)
298 return false;
300}
bool crypto_is_ready(const crypto_context_t *ctx)
Check if key exchange is complete and ready for encryption.
@ CRYPTO_HANDSHAKE_READY

References crypto_handshake_context_t::crypto_ctx, CRYPTO_HANDSHAKE_READY, crypto_is_ready(), and crypto_handshake_context_t::state.

Referenced by add_client(), client_send_thread_func(), crypto_client_is_ready(), crypto_handshake_decrypt_packet(), crypto_handshake_encrypt_packet(), crypto_handshake_get_context(), crypto_handshake_process_rekey_complete(), crypto_handshake_process_rekey_request(), crypto_handshake_process_rekey_response(), crypto_handshake_rekey_complete(), crypto_handshake_rekey_request(), crypto_handshake_rekey_response(), crypto_handshake_should_rekey(), and crypto_server_is_ready().

◆ crypto_handshake_process_rekey_complete()

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)

Parameters
ctxCrypto handshake context (must be ready)
packetEncrypted packet (empty payload, encrypted with NEW key)
packet_lenPacket length
Returns
ASCIICHAT_OK on success, error code on failure

Process received REKEY_COMPLETE packet (responder side). Verifies that the packet decrypts with the new shared secret. If successful, commits to the new key.

Definition at line 589 of file crypto/handshake/common.c.

590 {
591 if (!ctx || !crypto_handshake_is_ready(ctx)) {
592 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
593 ctx ? crypto_handshake_is_ready(ctx) : 0);
594 }
595
597 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
598 }
599
600 log_debug("Received REKEY_COMPLETE packet (%zu bytes), verifying with NEW key", packet_len);
601
602 // Temporarily swap keys to decrypt with NEW key
603 uint8_t old_shared_key[CRYPTO_SHARED_KEY_SIZE];
604 memcpy(old_shared_key, ctx->crypto_ctx.shared_key, CRYPTO_SHARED_KEY_SIZE);
606
607 // Attempt to decrypt with NEW key
608 uint8_t plaintext[256];
609 size_t plaintext_len = 0;
610 crypto_result_t result =
611 crypto_decrypt(&ctx->crypto_ctx, packet, packet_len, plaintext, sizeof(plaintext), &plaintext_len);
612
613 // Restore old key immediately
614 memcpy(ctx->crypto_ctx.shared_key, old_shared_key, CRYPTO_SHARED_KEY_SIZE);
615 sodium_memzero(old_shared_key, sizeof(old_shared_key));
616
617 if (result != CRYPTO_OK) {
619 return SET_ERRNO(ERROR_CRYPTO, "REKEY_COMPLETE decryption failed (key mismatch): %s",
621 }
622
623 log_debug("REKEY_COMPLETE verified successfully, committing to new key");
624
625 // Commit to new key (atomic switch)
626 result = crypto_rekey_commit(&ctx->crypto_ctx);
627 if (result != CRYPTO_OK) {
628 return SET_ERRNO(ERROR_CRYPTO, "Failed to commit rekey: %s", crypto_result_to_string(result));
629 }
630
631 log_debug("Session rekeying completed successfully (responder side)");
632 return ASCIICHAT_OK;
633}
unsigned char uint8_t
Definition common.h:56
crypto_result_t crypto_rekey_commit(crypto_context_t *ctx)
Commit to new keys after successful REKEY_COMPLETE.
#define CRYPTO_SHARED_KEY_SIZE
Shared key size (X25519)
void crypto_rekey_abort(crypto_context_t *ctx)
Abort rekeying and fallback to old keys.
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_decrypt(), crypto_handshake_is_ready(), CRYPTO_OK, crypto_rekey_abort(), crypto_rekey_commit(), crypto_result_to_string(), CRYPTO_SHARED_KEY_SIZE, ERROR_CRYPTO, ERROR_INVALID_STATE, crypto_context_t::has_temp_key, log_debug, crypto_context_t::rekey_in_progress, SET_ERRNO, crypto_context_t::shared_key, and crypto_context_t::temp_shared_key.

◆ crypto_handshake_process_rekey_request()

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)

Parameters
ctxCrypto handshake context (must be ready)
packetPacket payload (32-byte public key)
packet_lenPacket length (should be 32)
Returns
ASCIICHAT_OK on success, error code on failure

Process received REKEY_REQUEST packet (responder side). Extracts peer's new ephemeral public key and computes new shared secret.

Definition at line 503 of file crypto/handshake/common.c.

504 {
505 if (!ctx || !crypto_handshake_is_ready(ctx)) {
506 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
507 ctx ? crypto_handshake_is_ready(ctx) : 0);
508 }
509
510 // DDoS PROTECTION: Rate limit rekey requests
511 time_t now = time(NULL);
512 if (ctx->crypto_ctx.rekey_last_request_time > 0) {
513 time_t elapsed = now - ctx->crypto_ctx.rekey_last_request_time;
514 time_t min_request_interval_seconds = (time_t)(REKEY_MIN_REQUEST_INTERVAL / NS_PER_SEC_INT);
515 if (elapsed < min_request_interval_seconds) {
516 return SET_ERRNO(ERROR_CRYPTO,
517 "SECURITY: Rekey request rejected - too frequent (%ld sec since last, minimum %ld sec required)",
518 (long)elapsed, (long)min_request_interval_seconds);
519 }
520 }
521
522 // Update last request time
524
525 // Validate packet size (should be 32 bytes for X25519 public key)
526 if (packet_len != CRYPTO_PUBLIC_KEY_SIZE) {
527 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid REKEY_REQUEST packet size: %zu (expected %d)", packet_len,
529 }
530
531 log_debug("Received REKEY_REQUEST with peer's new ephemeral public key (32 bytes)");
532
533 // Initialize our rekey process (generates our new ephemeral keypair)
535 if (result != CRYPTO_OK) {
536 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize rekey: %s", crypto_result_to_string(result));
537 }
538
539 // Process peer's public key and compute new shared secret
540 result = crypto_rekey_process_request(&ctx->crypto_ctx, packet);
541 if (result != CRYPTO_OK) {
543 return SET_ERRNO(ERROR_CRYPTO, "Failed to process REKEY_REQUEST: %s", crypto_result_to_string(result));
544 }
545
546 log_debug("REKEY_REQUEST processed successfully, new shared secret computed (responder side)");
547 return ASCIICHAT_OK;
548}
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 in nanoseconds, DDoS protection)
crypto_result_t crypto_rekey_init(crypto_context_t *ctx)
Initiate rekeying by generating new ephemeral keys.
#define NS_PER_SEC_INT
Definition time.h:157

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_handshake_is_ready(), CRYPTO_OK, CRYPTO_PUBLIC_KEY_SIZE, crypto_rekey_abort(), crypto_rekey_init(), crypto_rekey_process_request(), crypto_result_to_string(), ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, log_debug, NS_PER_SEC_INT, crypto_context_t::rekey_last_request_time, REKEY_MIN_REQUEST_INTERVAL, and SET_ERRNO.

Referenced by crypto_client_process_rekey_request().

◆ crypto_handshake_process_rekey_response()

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)

Parameters
ctxCrypto handshake context (must be ready)
packetPacket payload (32-byte public key)
packet_lenPacket length (should be 32)
Returns
ASCIICHAT_OK on success, error code on failure

Process received REKEY_RESPONSE packet (initiator side). Extracts peer's new ephemeral public key and computes new shared secret.

Definition at line 554 of file crypto/handshake/common.c.

555 {
556 if (!ctx || !crypto_handshake_is_ready(ctx)) {
557 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
558 ctx ? crypto_handshake_is_ready(ctx) : 0);
559 }
560
561 // Validate packet size (should be 32 bytes for X25519 public key)
562 if (packet_len != CRYPTO_PUBLIC_KEY_SIZE) {
563 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid REKEY_RESPONSE packet size: %zu (expected %d)", packet_len,
565 }
566
568 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
569 }
570
571 log_debug("Received REKEY_RESPONSE with peer's new ephemeral public key (32 bytes)");
572
573 // Process peer's public key and compute new shared secret
575 if (result != CRYPTO_OK) {
577 return SET_ERRNO(ERROR_CRYPTO, "Failed to process REKEY_RESPONSE: %s", crypto_result_to_string(result));
578 }
579
580 log_debug("REKEY_RESPONSE processed successfully, new shared secret computed (initiator side)");
581 return ASCIICHAT_OK;
582}
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)

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_handshake_is_ready(), CRYPTO_OK, CRYPTO_PUBLIC_KEY_SIZE, crypto_rekey_abort(), crypto_rekey_process_response(), crypto_result_to_string(), ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, crypto_context_t::has_temp_key, log_debug, crypto_context_t::rekey_in_progress, and SET_ERRNO.

Referenced by crypto_client_process_rekey_response().

◆ crypto_handshake_rekey_complete()

asciichat_error_t crypto_handshake_rekey_complete ( crypto_handshake_context_t *  ctx,
acip_transport_t *  transport 
)

Send REKEY_COMPLETE packet (initiator side)

Parameters
ctxCrypto handshake context (must be ready)
transportTransport to send on
Returns
ASCIICHAT_OK on success, error code on failure

Send REKEY_COMPLETE packet (initiator side). Note: This packet is encrypted with the new shared secret. It proves that both sides have computed the same shared secret.

Definition at line 454 of file crypto/handshake/common.c.

454 {
455 if (!ctx || !crypto_handshake_is_ready(ctx)) {
456 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
457 ctx ? crypto_handshake_is_ready(ctx) : 0);
458 }
459 if (!transport) {
460 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport is NULL");
461 }
462
464 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
465 }
466
467 // Encrypt empty payload with NEW key to prove possession
468 uint8_t plaintext[1] = {0}; // Minimal payload
469
470 // Temporarily swap keys to encrypt with NEW key
471 uint8_t old_shared_key[CRYPTO_SHARED_KEY_SIZE];
472 memcpy(old_shared_key, ctx->crypto_ctx.shared_key, CRYPTO_SHARED_KEY_SIZE);
474
475 // Restore old key immediately after the transport send completes.
476 // The transport encrypts the packet using the active shared key, so we
477 // keep the temporary key installed only for the duration of this send.
478 asciichat_error_t send_result =
479 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_REKEY_COMPLETE, plaintext, sizeof(plaintext), 0);
480
481 memcpy(ctx->crypto_ctx.shared_key, old_shared_key, CRYPTO_SHARED_KEY_SIZE);
482 sodium_memzero(old_shared_key, sizeof(old_shared_key));
483
484 if (send_result != ASCIICHAT_OK) {
486 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_COMPLETE packet");
487 }
488
489 // Commit to new key (atomic switch)
490 crypto_result_t commit_result = crypto_rekey_commit(&ctx->crypto_ctx);
491 if (commit_result != CRYPTO_OK) {
492 return SET_ERRNO(ERROR_CRYPTO, "Failed to commit rekey: %s", crypto_result_to_string(commit_result));
493 }
494
495 log_debug("Session rekeying completed successfully (initiator side)");
496 return ASCIICHAT_OK;
497}
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ PACKET_TYPE_CRYPTO_REKEY_COMPLETE
Initiator -> Responder: Empty (encrypted with NEW key, but still handshake)
Definition packet.h:339
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_handshake_is_ready(), CRYPTO_OK, crypto_rekey_abort(), crypto_rekey_commit(), crypto_result_to_string(), CRYPTO_SHARED_KEY_SIZE, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_NETWORK, crypto_context_t::has_temp_key, log_debug, packet_send_via_transport(), PACKET_TYPE_CRYPTO_REKEY_COMPLETE, crypto_context_t::rekey_in_progress, SET_ERRNO, crypto_context_t::shared_key, and crypto_context_t::temp_shared_key.

Referenced by crypto_client_send_rekey_complete().

◆ crypto_handshake_rekey_request()

asciichat_error_t crypto_handshake_rekey_request ( crypto_handshake_context_t *  ctx,
acip_transport_t *  transport 
)

Send REKEY_REQUEST packet (initiator side)

Parameters
ctxCrypto handshake context (must be ready)
transportTransport to send on
Returns
ASCIICHAT_OK on success, error code on failure

Send REKEY_REQUEST packet (initiator side). Sends the initiator's new ephemeral public key to the peer.

Definition at line 391 of file crypto/handshake/common.c.

391 {
392 if (!ctx || !crypto_handshake_is_ready(ctx)) {
393 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
394 ctx ? crypto_handshake_is_ready(ctx) : 0);
395 }
396 if (!transport) {
397 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport is NULL");
398 }
399
400 // Initialize rekey process (generates new ephemeral keypair)
402 if (result != CRYPTO_OK) {
403 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize rekey: %s", crypto_result_to_string(result));
404 }
405
406 // Send REKEY_REQUEST with new ephemeral public key (32 bytes)
407 log_debug("Sending REKEY_REQUEST with new ephemeral X25519 public key (32 bytes)");
410 if (send_result != ASCIICHAT_OK) {
411 crypto_rekey_abort(&ctx->crypto_ctx); // Clean up temp keys on failure
412 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_REQUEST packet");
413 }
414
415 log_debug("REKEY_REQUEST sent successfully, awaiting REKEY_RESPONSE");
416 return ASCIICHAT_OK;
417}
@ PACKET_TYPE_CRYPTO_REKEY_REQUEST
Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:335

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_handshake_is_ready(), CRYPTO_OK, CRYPTO_PUBLIC_KEY_SIZE, crypto_rekey_abort(), crypto_rekey_init(), crypto_result_to_string(), ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_NETWORK, log_debug, packet_send_via_transport(), PACKET_TYPE_CRYPTO_REKEY_REQUEST, SET_ERRNO, and crypto_context_t::temp_public_key.

Referenced by client_send_thread_func(), and crypto_client_initiate_rekey().

◆ crypto_handshake_rekey_response()

asciichat_error_t crypto_handshake_rekey_response ( crypto_handshake_context_t *  ctx,
acip_transport_t *  transport 
)

Send REKEY_RESPONSE packet (responder side)

Parameters
ctxCrypto handshake context (must be ready)
transportTransport to send on
Returns
ASCIICHAT_OK on success, error code on failure

Send REKEY_RESPONSE packet (responder side). Sends the responder's new ephemeral public key to the peer.

Definition at line 423 of file crypto/handshake/common.c.

423 {
424 if (!ctx || !crypto_handshake_is_ready(ctx)) {
425 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
426 ctx ? crypto_handshake_is_ready(ctx) : 0);
427 }
428 if (!transport) {
429 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport is NULL");
430 }
431
433 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
434 }
435
436 // Send REKEY_RESPONSE with new ephemeral public key (32 bytes)
437 log_debug("Sending REKEY_RESPONSE with new ephemeral X25519 public key (32 bytes)");
440 if (send_result != ASCIICHAT_OK) {
441 crypto_rekey_abort(&ctx->crypto_ctx); // Clean up temp keys on failure
442 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_RESPONSE packet");
443 }
444
445 log_debug("REKEY_RESPONSE sent successfully, awaiting REKEY_COMPLETE");
446 return ASCIICHAT_OK;
447}
@ PACKET_TYPE_CRYPTO_REKEY_RESPONSE
Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:337

References ASCIICHAT_OK, crypto_handshake_context_t::crypto_ctx, crypto_handshake_is_ready(), CRYPTO_PUBLIC_KEY_SIZE, crypto_rekey_abort(), ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_NETWORK, crypto_context_t::has_temp_key, log_debug, packet_send_via_transport(), PACKET_TYPE_CRYPTO_REKEY_RESPONSE, crypto_context_t::rekey_in_progress, SET_ERRNO, and crypto_context_t::temp_public_key.

Referenced by crypto_client_send_rekey_response().

◆ crypto_handshake_set_parameters()

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.

Parameters
ctxHandshake context
paramsNegotiated crypto parameters (from capabilities negotiation)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 66 of file crypto/handshake/common.c.

67 {
68 if (!ctx || !params) {
69 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, params=%p", ctx, params);
70 }
71
72 // Client receives network byte order and must convert
73 // Server uses host byte order and must NOT convert
74 if (ctx->is_server) {
75 // Server: values are already in host byte order
76 // Update crypto context with negotiated parameters directly
81 } else {
82 // Client: convert from network byte order
83 // Update crypto context with negotiated parameters directly
88 }
89 // Update crypto context with negotiated parameters directly
90 ctx->crypto_ctx.nonce_size = params->nonce_size;
91 ctx->crypto_ctx.mac_size = params->mac_size;
92 ctx->crypto_ctx.hmac_size = params->hmac_size;
94 AUTH_CHALLENGE_SIZE; // Auth challenge size is fixed for now, could be negotiated later
96 (uint8_t)ctx->crypto_ctx.shared_key_size; // Use shared key size as encryption key size
97 ctx->crypto_ctx.private_key_size = ctx->crypto_ctx.public_key_size; // Same as public key for X25519
98 ctx->crypto_ctx.salt_size = ARGON2ID_SALT_SIZE; // Salt size doesn't change
99
100 log_debug("Crypto parameters set: kex_key=%u, auth_key=%u, sig=%u, "
101 "secret=%u, nonce=%u, mac=%u, hmac=%u",
104 ctx->crypto_ctx.hmac_size);
105
106 return ASCIICHAT_OK;
107}
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define AUTH_CHALLENGE_SIZE
Challenge nonce size (32 bytes)
#define ARGON2ID_SALT_SIZE
Argon2id salt size in bytes.
uint8_t hmac_size
HMAC size in bytes (e.g., 32 for HMAC-SHA256)
Definition packet.h:1003
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
Definition packet.h:993
uint16_t signature_size
Signature size in bytes (e.g., 64 for Ed25519, 3309 for Dilithium3)
Definition packet.h:995
uint8_t nonce_size
Nonce size in bytes (e.g., 24 for XSalsa20)
Definition packet.h:999
uint8_t mac_size
MAC size in bytes (e.g., 16 for Poly1305)
Definition packet.h:1001
uint16_t shared_secret_size
Shared secret size in bytes (e.g., 32 for X25519)
Definition packet.h:997
uint16_t kex_public_key_size
Key exchange public key size in bytes (e.g., 32 for X25519, 1568 for Kyber1024)
Definition packet.h:991

References ARGON2ID_SALT_SIZE, ASCIICHAT_OK, AUTH_CHALLENGE_SIZE, crypto_context_t::auth_challenge_size, crypto_context_t::auth_public_key_size, crypto_parameters_packet_t::auth_public_key_size, crypto_handshake_context_t::crypto_ctx, crypto_context_t::encryption_key_size, ERROR_INVALID_PARAM, crypto_context_t::hmac_size, crypto_parameters_packet_t::hmac_size, crypto_handshake_context_t::is_server, crypto_parameters_packet_t::kex_public_key_size, log_debug, crypto_context_t::mac_size, crypto_parameters_packet_t::mac_size, NET_TO_HOST_U16, crypto_context_t::nonce_size, crypto_parameters_packet_t::nonce_size, crypto_context_t::private_key_size, crypto_context_t::public_key_size, crypto_context_t::salt_size, SET_ERRNO, crypto_context_t::shared_key_size, crypto_parameters_packet_t::shared_secret_size, crypto_context_t::signature_size, and crypto_parameters_packet_t::signature_size.

Referenced by client_crypto_handshake(), client_handle_crypto_parameters(), crypto_handshake_server_send_parameters(), discovery_session_start(), and server_crypto_handshake().

◆ crypto_handshake_should_rekey()

bool crypto_handshake_should_rekey ( const crypto_handshake_context_t *  ctx)

Check if rekeying should be triggered for this handshake context.

Parameters
ctxCrypto handshake context
Returns
true if rekey should be initiated, false otherwise

Check if rekeying should be triggered for this handshake context. Wrapper around crypto_should_rekey() for handshake context.

Definition at line 639 of file crypto/handshake/common.c.

639 {
640 if (!ctx || !crypto_handshake_is_ready(ctx)) {
641 return false;
642 }
643 return crypto_should_rekey(&ctx->crypto_ctx);
644}
bool crypto_should_rekey(const crypto_context_t *ctx)
Check if rekeying should be triggered based on time or packet count thresholds.

References crypto_handshake_context_t::crypto_ctx, crypto_handshake_is_ready(), and crypto_should_rekey().

Referenced by client_send_thread_func(), and crypto_client_should_rekey().

◆ crypto_handshake_validate_packet_size()

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.

Parameters
ctxHandshake context (must have parameters set)
packet_typePacket type to validate
packet_sizeActual packet size received
Returns
ASCIICHAT_OK if valid, error code on failure

Definition at line 110 of file crypto/handshake/common.c.

111 {
112 if (!ctx) {
113 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p", ctx);
114 }
115
116 switch (packet_type) {
118 if (packet_size != sizeof(crypto_capabilities_packet_t)) {
119 // Don't return an error code, just set the errno and return the error code
120 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid crypto capabilities packet size: %zu (expected %zu)",
121 packet_size, sizeof(crypto_capabilities_packet_t));
122 }
123 break;
124
126 if (packet_size != sizeof(crypto_parameters_packet_t)) {
127 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid crypto parameters packet size: %zu (expected %zu)", packet_size,
129 }
130 break;
131
133 // Server can send either:
134 // 1. Simple format: kex_public_key_size (when server has no identity key)
135 // 2. Authenticated format: kex_public_key_size + auth_public_key_size + signature_size
136 {
137 size_t simple_size = ctx->crypto_ctx.public_key_size;
138 size_t authenticated_size =
140
141 if (packet_size != simple_size && packet_size != authenticated_size) {
143 "Invalid KEY_EXCHANGE_INIT size: %zu (expected %zu for simple or %zu for authenticated: "
144 "kex=%u + auth=%u + sig=%u)",
145 packet_size, simple_size, authenticated_size, ctx->crypto_ctx.public_key_size,
147 }
148 }
149 break;
150
152 // Client can send either:
153 // 1. Simple format: kex_public_key_size (when server has no identity key)
154 // 2. Authenticated format: kex_public_key_size + client_auth_key_size + client_sig_size + [gpg_key_id_len:1] +
155 // [gpg_key_id:0-16]
156 {
157 size_t simple_size = ctx->crypto_ctx.public_key_size;
158 // For authenticated format, use Ed25519 sizes since client has Ed25519 key
159 size_t ed25519_auth_size = ED25519_PUBLIC_KEY_SIZE; // Ed25519 public key is always 32 bytes
160 size_t ed25519_sig_size = ED25519_SIGNATURE_SIZE; // Ed25519 signature is always 64 bytes
161 size_t authenticated_min_size = ctx->crypto_ctx.public_key_size + ed25519_auth_size + ed25519_sig_size;
162 size_t authenticated_max_size = authenticated_min_size + 1 + 40; // +1 for length, +40 for max GPG key ID
163
164 if (packet_size != simple_size &&
165 (packet_size < authenticated_min_size || packet_size > authenticated_max_size)) {
167 "Invalid KEY_EXCHANGE_RESP size: %zu (expected %zu for simple or %zu-%zu for authenticated: "
168 "kex=%u + auth=%u + sig=%u + optional GPG key ID)",
169 packet_size, simple_size, authenticated_min_size, authenticated_max_size,
170 ctx->crypto_ctx.public_key_size, ed25519_auth_size, ed25519_sig_size);
171 }
172 }
173 break;
174
176 // Server sends: 1 byte auth_flags + auth_challenge_size byte nonce
177 {
178 size_t expected_size = AUTH_CHALLENGE_FLAGS_SIZE + ctx->crypto_ctx.auth_challenge_size;
179 if (packet_size != expected_size) {
180 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid AUTH_CHALLENGE size: %zu (expected %zu: flags=%d + nonce=%u)",
181 packet_size, expected_size, AUTH_CHALLENGE_FLAGS_SIZE, ctx->crypto_ctx.auth_challenge_size);
182 }
183 }
184 break;
185
187 // Client sends: hmac_size + auth_challenge_size bytes client_nonce + [gpg_key_id_len:1] + [gpg_key_id:0-40]
188 {
189 size_t min_size = ctx->crypto_ctx.hmac_size + ctx->crypto_ctx.auth_challenge_size;
190 size_t max_size = min_size + 1 + 40; // +1 for length, +40 for max GPG key ID
191 if (packet_size < min_size || packet_size > max_size) {
193 "Invalid AUTH_RESPONSE size: %zu (expected %zu-%zu: hmac=%u + "
194 "nonce=%u + optional GPG key ID)",
195 packet_size, min_size, max_size, ctx->crypto_ctx.hmac_size,
197 }
198 }
199 break;
200
202 // Variable size - just check reasonable limits
203 if (packet_size > MAX_AUTH_FAILED_PACKET_SIZE) {
204 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid AUTH_FAILED size: %zu (max %d)", packet_size,
206 }
207 break;
208
210 // Server sends: hmac_size bytes
211 if (packet_size != ctx->crypto_ctx.hmac_size) {
212 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid SERVER_AUTH_RESP size: %zu (expected %u)", packet_size,
213 ctx->crypto_ctx.hmac_size);
214 }
215 break;
216
218 // Empty packet
219 if (packet_size != 0) {
220 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid HANDSHAKE_COMPLETE size: %zu (expected 0)", packet_size);
221 }
222 break;
223
225 // Empty packet
226 if (packet_size != 0) {
227 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid NO_ENCRYPTION size: %zu (expected 0)", packet_size);
228 }
229 break;
230
232 // Variable size - check reasonable limits
233 if (packet_size > MAX_ENCRYPTED_PACKET_SIZE) { // 64KB max for encrypted packets
234 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid ENCRYPTED size: %zu (max %d)", packet_size,
236 }
237 break;
238
239 default:
240 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unknown crypto packet type: %u", packet_type);
241 }
242
243 return ASCIICHAT_OK;
244}
#define ED25519_SIGNATURE_SIZE
Ed25519 signature size in bytes.
#define MAX_AUTH_FAILED_PACKET_SIZE
Maximum AUTH_FAILED packet size (256 bytes)
#define MAX_ENCRYPTED_PACKET_SIZE
Maximum encrypted packet size (64KB)
#define ED25519_PUBLIC_KEY_SIZE
Ed25519 public key size in bytes.
#define AUTH_CHALLENGE_FLAGS_SIZE
Authentication flags size (1 byte)
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ PACKET_TYPE_CRYPTO_AUTH_RESPONSE
Client -> Server: {HMAC[32]} (UNENCRYPTED)
Definition packet.h:317
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED)
Definition packet.h:323
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED)
Definition packet.h:311
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP
Client -> Server: {client_pubkey[32]} (UNENCRYPTED)
Definition packet.h:313
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED)
Definition packet.h:319
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion)
Definition packet.h:333
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED)
Definition packet.h:321
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED)
Definition packet.h:325
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED)
Definition packet.h:315
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED)
Definition packet.h:309
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED)
Definition packet.h:307
Crypto capabilities packet structure (Packet Type 14)
Definition packet.h:952
Crypto parameters packet structure (Packet Type 15)
Definition packet.h:981

References ASCIICHAT_OK, AUTH_CHALLENGE_FLAGS_SIZE, crypto_context_t::auth_challenge_size, crypto_context_t::auth_public_key_size, crypto_handshake_context_t::crypto_ctx, ED25519_PUBLIC_KEY_SIZE, ED25519_SIGNATURE_SIZE, ERROR_INVALID_PARAM, ERROR_NETWORK_PROTOCOL, crypto_context_t::hmac_size, MAX_AUTH_FAILED_PACKET_SIZE, MAX_ENCRYPTED_PACKET_SIZE, PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, PACKET_TYPE_CRYPTO_AUTH_FAILED, PACKET_TYPE_CRYPTO_AUTH_RESPONSE, PACKET_TYPE_CRYPTO_CAPABILITIES, PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE, PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP, PACKET_TYPE_CRYPTO_NO_ENCRYPTION, PACKET_TYPE_CRYPTO_PARAMETERS, PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, PACKET_TYPE_ENCRYPTED, crypto_context_t::public_key_size, SET_ERRNO, and crypto_context_t::signature_size.

Referenced by crypto_handshake_client_auth_response(), crypto_handshake_client_key_exchange(), crypto_handshake_server_auth_challenge(), and crypto_handshake_server_complete().