ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
common.c File Reference

Common handshake functions: initialization, cleanup, encryption, rekeying. More...

Go to the source code of this file.

Functions

asciichat_error_t crypto_handshake_init (crypto_handshake_context_t *ctx, bool is_server)
 
asciichat_error_t crypto_handshake_set_parameters (crypto_handshake_context_t *ctx, const crypto_parameters_packet_t *params)
 
asciichat_error_t crypto_handshake_validate_packet_size (const crypto_handshake_context_t *ctx, uint16_t packet_type, size_t packet_size)
 
asciichat_error_t crypto_handshake_init_with_password (crypto_handshake_context_t *ctx, bool is_server, const char *password)
 
void crypto_handshake_destroy (crypto_handshake_context_t *ctx)
 
bool crypto_handshake_is_ready (const crypto_handshake_context_t *ctx)
 
const crypto_context_t * crypto_handshake_get_context (const crypto_handshake_context_t *ctx)
 
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)
 
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)
 
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)
 
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)
 
asciichat_error_t crypto_handshake_rekey_request (crypto_handshake_context_t *ctx, socket_t socket)
 
asciichat_error_t crypto_handshake_rekey_response (crypto_handshake_context_t *ctx, socket_t socket)
 
asciichat_error_t crypto_handshake_rekey_complete (crypto_handshake_context_t *ctx, socket_t socket)
 
asciichat_error_t crypto_handshake_process_rekey_request (crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
 
asciichat_error_t crypto_handshake_process_rekey_response (crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
 
asciichat_error_t crypto_handshake_process_rekey_complete (crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
 
bool crypto_handshake_should_rekey (const crypto_handshake_context_t *ctx)
 

Detailed Description

Common handshake functions: initialization, cleanup, encryption, rekeying.

Definition in file crypto/handshake/common.c.

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 
)

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

343 {
344 if (!crypto_ready) {
345 // No encryption - just copy data
346 if (ciphertext_len > plaintext_size) {
347 SET_ERRNO(ERROR_BUFFER, "Ciphertext too large for plaintext buffer: %zu > %zu", ciphertext_len, plaintext_size);
348 return ERROR_BUFFER;
349 }
350 memcpy(plaintext, ciphertext, ciphertext_len);
351 *plaintext_len = ciphertext_len;
352 return ASCIICHAT_OK;
353 }
354
355 return crypto_handshake_decrypt_packet(ctx, ciphertext, ciphertext_len, plaintext, plaintext_size, plaintext_len);
356}
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)

References crypto_handshake_decrypt_packet().

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 
)

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

324 {
325 if (!crypto_ready) {
326 // No encryption - just copy data
327 if (plaintext_len > ciphertext_size) {
328 SET_ERRNO(ERROR_BUFFER, "Plaintext too large for ciphertext buffer: %zu > %zu", plaintext_len, ciphertext_size);
329 return ERROR_BUFFER;
330 }
331 memcpy(ciphertext, plaintext, plaintext_len);
332 *ciphertext_len = plaintext_len;
333 return ASCIICHAT_OK;
334 }
335
336 return crypto_handshake_encrypt_packet(ctx, plaintext, plaintext_len, ciphertext, ciphertext_size, ciphertext_len);
337}
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)

References crypto_handshake_encrypt_packet().

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 
)

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

305 {
306 if (!ctx || !crypto_handshake_is_ready(ctx)) {
307 SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, ready=%d", ctx, ctx ? crypto_handshake_is_ready(ctx) : 0);
308 return ERROR_INVALID_STATE;
309 }
310
311 crypto_result_t result = crypto_decrypt((crypto_context_t *)&ctx->crypto_ctx, ciphertext, ciphertext_len, plaintext,
312 plaintext_size, plaintext_len);
313 if (result != CRYPTO_OK) {
314 return SET_ERRNO(ERROR_NETWORK, "Failed to decrypt packet: %s", crypto_result_to_string(result));
315 }
316
317 return ASCIICHAT_OK;
318}
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
const char * crypto_result_to_string(crypto_result_t result)
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)

References crypto_decrypt(), crypto_handshake_is_ready(), and crypto_result_to_string().

Referenced by crypto_decrypt_packet_or_passthrough().

◆ crypto_handshake_destroy()

void crypto_handshake_destroy ( crypto_handshake_context_t *  ctx)

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

259 {
260 if (!ctx)
261 return;
262
263 // Cleanup core crypto context
264 crypto_destroy(&ctx->crypto_ctx);
265
266 // Zero out sensitive data
267 sodium_memzero(ctx, sizeof(crypto_handshake_context_t));
268}
void crypto_destroy(crypto_context_t *ctx)

References crypto_destroy().

Referenced by client_cleanup(), client_crypto_init(), client_generate_keypair(), 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 
)

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

287 {
288 if (!ctx || !crypto_handshake_is_ready(ctx)) {
289 SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, ready=%d", ctx, ctx ? crypto_handshake_is_ready(ctx) : 0);
290 return ERROR_INVALID_STATE;
291 }
292
293 crypto_result_t result = crypto_encrypt((crypto_context_t *)&ctx->crypto_ctx, plaintext, plaintext_len, ciphertext,
294 ciphertext_size, ciphertext_len);
295 if (result != CRYPTO_OK) {
296 return SET_ERRNO(ERROR_NETWORK, "Failed to encrypt packet: %s", crypto_result_to_string(result));
297 }
298
299 return ASCIICHAT_OK;
300}
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)

References crypto_encrypt(), crypto_handshake_is_ready(), and crypto_result_to_string().

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)

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

278 {
279 if (!ctx || !crypto_handshake_is_ready(ctx))
280 return NULL;
281 return &ctx->crypto_ctx;
282}

References crypto_handshake_is_ready().

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

◆ crypto_handshake_init()

asciichat_error_t crypto_handshake_init ( crypto_handshake_context_t *  ctx,
bool  is_server 
)

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

17 {
18 if (!ctx) {
19 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p", ctx);
20 }
21
22 // Zero out the context
23 memset(ctx, 0, sizeof(crypto_handshake_context_t));
24
25 // Initialize core crypto context
26 crypto_result_t result = crypto_init(&ctx->crypto_ctx);
27 if (result != CRYPTO_OK) {
28 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize crypto context: %s", crypto_result_to_string(result));
29 }
30
31 ctx->state = CRYPTO_HANDSHAKE_INIT;
32 ctx->is_server = is_server;
33 ctx->verify_server_key = false;
34 ctx->require_client_auth = false;
35 ctx->server_uses_client_auth = false; // Set to true only if authenticated packet received
36
37 // Load server keys if this is a server
38 if (is_server) {
39 log_dev("Server crypto handshake initialized (ephemeral keys)");
40 } else {
41 log_dev("Client crypto handshake initialized");
42 }
43
44 return ASCIICHAT_OK;
45}
crypto_result_t crypto_init(crypto_context_t *ctx)

References crypto_init(), and crypto_result_to_string().

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

◆ crypto_handshake_init_with_password()

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

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

230 {
231 if (!ctx || !password) {
232 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, password=%p", ctx, password);
233 }
234
235 // Zero out the context
236 memset(ctx, 0, sizeof(crypto_handshake_context_t));
237
238 // Initialize core crypto context with password
239 crypto_result_t result = crypto_init_with_password(&ctx->crypto_ctx, password);
240 if (result != CRYPTO_OK) {
241 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize crypto context with password: %s",
243 }
244
245 ctx->state = CRYPTO_HANDSHAKE_INIT;
246 ctx->is_server = is_server;
247 ctx->verify_server_key = false;
248 ctx->require_client_auth = false;
249 ctx->server_uses_client_auth = false; // Set to true only if authenticated packet received
250 ctx->has_password = true;
251
252 // Store password temporarily (will be cleared after key derivation)
253 SAFE_STRNCPY(ctx->password, password, sizeof(ctx->password) - 1);
254
255 return ASCIICHAT_OK;
256}
crypto_result_t crypto_init_with_password(crypto_context_t *ctx, const char *password)

References crypto_init_with_password(), and crypto_result_to_string().

Referenced by client_crypto_init(), and server_crypto_handshake().

◆ crypto_handshake_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). Verifies that the packet decrypts with the new shared secret. If successful, commits to the new key.

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

565 {
566 if (!ctx || !crypto_handshake_is_ready(ctx)) {
567 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
568 ctx ? crypto_handshake_is_ready(ctx) : 0);
569 }
570
571 if (!ctx->crypto_ctx.rekey_in_progress || !ctx->crypto_ctx.has_temp_key) {
572 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
573 }
574
575 log_debug("Received REKEY_COMPLETE packet (%zu bytes), verifying with NEW key", packet_len);
576
577 // Temporarily swap keys to decrypt with NEW key
578 uint8_t old_shared_key[CRYPTO_SHARED_KEY_SIZE];
579 memcpy(old_shared_key, ctx->crypto_ctx.shared_key, CRYPTO_SHARED_KEY_SIZE);
580 memcpy(ctx->crypto_ctx.shared_key, ctx->crypto_ctx.temp_shared_key, CRYPTO_SHARED_KEY_SIZE);
581
582 // Attempt to decrypt with NEW key
583 uint8_t plaintext[256];
584 size_t plaintext_len = 0;
585 crypto_result_t result =
586 crypto_decrypt(&ctx->crypto_ctx, packet, packet_len, plaintext, sizeof(plaintext), &plaintext_len);
587
588 // Restore old key immediately
589 memcpy(ctx->crypto_ctx.shared_key, old_shared_key, CRYPTO_SHARED_KEY_SIZE);
590 sodium_memzero(old_shared_key, sizeof(old_shared_key));
591
592 if (result != CRYPTO_OK) {
593 crypto_rekey_abort(&ctx->crypto_ctx);
594 return SET_ERRNO(ERROR_CRYPTO, "REKEY_COMPLETE decryption failed (key mismatch): %s",
596 }
597
598 log_debug("REKEY_COMPLETE verified successfully, committing to new key");
599
600 // Commit to new key (atomic switch)
601 result = crypto_rekey_commit(&ctx->crypto_ctx);
602 if (result != CRYPTO_OK) {
603 return SET_ERRNO(ERROR_CRYPTO, "Failed to commit rekey: %s", crypto_result_to_string(result));
604 }
605
606 log_debug("Session rekeying completed successfully (responder side)");
607 return ASCIICHAT_OK;
608}
crypto_result_t crypto_rekey_commit(crypto_context_t *ctx)
void crypto_rekey_abort(crypto_context_t *ctx)

References crypto_decrypt(), crypto_handshake_is_ready(), crypto_rekey_abort(), crypto_rekey_commit(), and crypto_result_to_string().

◆ 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). Extracts peer's new ephemeral public key and computes new shared secret.

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

479 {
480 if (!ctx || !crypto_handshake_is_ready(ctx)) {
481 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
482 ctx ? crypto_handshake_is_ready(ctx) : 0);
483 }
484
485 // DDoS PROTECTION: Rate limit rekey requests
486 time_t now = time(NULL);
487 if (ctx->crypto_ctx.rekey_last_request_time > 0) {
488 time_t elapsed = now - ctx->crypto_ctx.rekey_last_request_time;
489 time_t min_request_interval_seconds = (time_t)(REKEY_MIN_REQUEST_INTERVAL / NS_PER_SEC_INT);
490 if (elapsed < min_request_interval_seconds) {
491 return SET_ERRNO(ERROR_CRYPTO,
492 "SECURITY: Rekey request rejected - too frequent (%ld sec since last, minimum %ld sec required)",
493 (long)elapsed, (long)min_request_interval_seconds);
494 }
495 }
496
497 // Update last request time
498 ctx->crypto_ctx.rekey_last_request_time = now;
499
500 // Validate packet size (should be 32 bytes for X25519 public key)
501 if (packet_len != CRYPTO_PUBLIC_KEY_SIZE) {
502 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid REKEY_REQUEST packet size: %zu (expected %d)", packet_len,
503 CRYPTO_PUBLIC_KEY_SIZE);
504 }
505
506 log_debug("Received REKEY_REQUEST with peer's new ephemeral public key (32 bytes)");
507
508 // Initialize our rekey process (generates our new ephemeral keypair)
509 crypto_result_t result = crypto_rekey_init(&ctx->crypto_ctx);
510 if (result != CRYPTO_OK) {
511 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize rekey: %s", crypto_result_to_string(result));
512 }
513
514 // Process peer's public key and compute new shared secret
515 result = crypto_rekey_process_request(&ctx->crypto_ctx, packet);
516 if (result != CRYPTO_OK) {
517 crypto_rekey_abort(&ctx->crypto_ctx);
518 return SET_ERRNO(ERROR_CRYPTO, "Failed to process REKEY_REQUEST: %s", crypto_result_to_string(result));
519 }
520
521 log_debug("REKEY_REQUEST processed successfully, new shared secret computed (responder side)");
522 return ASCIICHAT_OK;
523}
crypto_result_t crypto_rekey_process_request(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
crypto_result_t crypto_rekey_init(crypto_context_t *ctx)

References crypto_handshake_is_ready(), crypto_rekey_abort(), crypto_rekey_init(), crypto_rekey_process_request(), and crypto_result_to_string().

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). Extracts peer's new ephemeral public key and computes new shared secret.

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

530 {
531 if (!ctx || !crypto_handshake_is_ready(ctx)) {
532 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
533 ctx ? crypto_handshake_is_ready(ctx) : 0);
534 }
535
536 // Validate packet size (should be 32 bytes for X25519 public key)
537 if (packet_len != CRYPTO_PUBLIC_KEY_SIZE) {
538 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid REKEY_RESPONSE packet size: %zu (expected %d)", packet_len,
539 CRYPTO_PUBLIC_KEY_SIZE);
540 }
541
542 if (!ctx->crypto_ctx.rekey_in_progress || !ctx->crypto_ctx.has_temp_key) {
543 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
544 }
545
546 log_debug("Received REKEY_RESPONSE with peer's new ephemeral public key (32 bytes)");
547
548 // Process peer's public key and compute new shared secret
549 crypto_result_t result = crypto_rekey_process_response(&ctx->crypto_ctx, packet);
550 if (result != CRYPTO_OK) {
551 crypto_rekey_abort(&ctx->crypto_ctx);
552 return SET_ERRNO(ERROR_CRYPTO, "Failed to process REKEY_RESPONSE: %s", crypto_result_to_string(result));
553 }
554
555 log_debug("REKEY_RESPONSE processed successfully, new shared secret computed (initiator side)");
556 return ASCIICHAT_OK;
557}
crypto_result_t crypto_rekey_process_response(crypto_context_t *ctx, const uint8_t *peer_new_public_key)

References crypto_handshake_is_ready(), crypto_rekey_abort(), crypto_rekey_process_response(), and crypto_result_to_string().

Referenced by crypto_client_process_rekey_response().

◆ crypto_handshake_rekey_complete()

asciichat_error_t crypto_handshake_rekey_complete ( crypto_handshake_context_t *  ctx,
socket_t  socket 
)

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 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
429 if (!ctx->crypto_ctx.rekey_in_progress || !ctx->crypto_ctx.has_temp_key) {
430 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
431 }
432
433 // Encrypt empty payload with NEW key to prove possession
434 uint8_t plaintext[1] = {0}; // Minimal payload
435 uint8_t ciphertext[256]; // Sufficient for nonce + MAC + minimal payload
436 size_t ciphertext_len = 0;
437
438 // Temporarily swap keys to encrypt with NEW key
439 uint8_t old_shared_key[CRYPTO_SHARED_KEY_SIZE];
440 memcpy(old_shared_key, ctx->crypto_ctx.shared_key, CRYPTO_SHARED_KEY_SIZE);
441 memcpy(ctx->crypto_ctx.shared_key, ctx->crypto_ctx.temp_shared_key, CRYPTO_SHARED_KEY_SIZE);
442
443 // Encrypt with NEW key
444 crypto_result_t result =
445 crypto_encrypt(&ctx->crypto_ctx, plaintext, sizeof(plaintext), ciphertext, sizeof(ciphertext), &ciphertext_len);
446
447 // Restore old key immediately (commit will happen after successful send)
448 memcpy(ctx->crypto_ctx.shared_key, old_shared_key, CRYPTO_SHARED_KEY_SIZE);
449 sodium_memzero(old_shared_key, sizeof(old_shared_key));
450
451 if (result != CRYPTO_OK) {
452 crypto_rekey_abort(&ctx->crypto_ctx);
453 return SET_ERRNO(ERROR_CRYPTO, "Failed to encrypt REKEY_COMPLETE: %s", crypto_result_to_string(result));
454 }
455
456 // Send encrypted REKEY_COMPLETE
457 log_debug("Sending REKEY_COMPLETE (encrypted with NEW key, %zu bytes)", ciphertext_len);
458 int send_result = send_packet(socket, PACKET_TYPE_CRYPTO_REKEY_COMPLETE, ciphertext, ciphertext_len);
459 if (send_result != 0) {
460 crypto_rekey_abort(&ctx->crypto_ctx);
461 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_COMPLETE packet");
462 }
463
464 // Commit to new key (atomic switch)
465 result = crypto_rekey_commit(&ctx->crypto_ctx);
466 if (result != CRYPTO_OK) {
467 return SET_ERRNO(ERROR_CRYPTO, "Failed to commit rekey: %s", crypto_result_to_string(result));
468 }
469
470 log_debug("Session rekeying completed successfully (initiator side)");
471 return ASCIICHAT_OK;
472}
int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a basic packet without encryption.
Definition packet.c:753

References crypto_encrypt(), crypto_handshake_is_ready(), crypto_rekey_abort(), crypto_rekey_commit(), crypto_result_to_string(), and send_packet().

Referenced by crypto_client_send_rekey_complete().

◆ crypto_handshake_rekey_request()

asciichat_error_t crypto_handshake_rekey_request ( crypto_handshake_context_t *  ctx,
socket_t  socket 
)

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

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

366 {
367 if (!ctx || !crypto_handshake_is_ready(ctx)) {
368 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
369 ctx ? crypto_handshake_is_ready(ctx) : 0);
370 }
371
372 // Initialize rekey process (generates new ephemeral keypair)
373 crypto_result_t result = crypto_rekey_init(&ctx->crypto_ctx);
374 if (result != CRYPTO_OK) {
375 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize rekey: %s", crypto_result_to_string(result));
376 }
377
378 // Send REKEY_REQUEST with new ephemeral public key (32 bytes)
379 log_debug("Sending REKEY_REQUEST with new ephemeral X25519 public key (32 bytes)");
380 int send_result =
381 send_packet(socket, PACKET_TYPE_CRYPTO_REKEY_REQUEST, ctx->crypto_ctx.temp_public_key, CRYPTO_PUBLIC_KEY_SIZE);
382 if (send_result != 0) {
383 crypto_rekey_abort(&ctx->crypto_ctx); // Clean up temp keys on failure
384 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_REQUEST packet");
385 }
386
387 log_debug("REKEY_REQUEST sent successfully, awaiting REKEY_RESPONSE");
388 return ASCIICHAT_OK;
389}

References crypto_handshake_is_ready(), crypto_rekey_abort(), crypto_rekey_init(), crypto_result_to_string(), and send_packet().

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,
socket_t  socket 
)

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

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

395 {
396 if (!ctx || !crypto_handshake_is_ready(ctx)) {
397 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
398 ctx ? crypto_handshake_is_ready(ctx) : 0);
399 }
400
401 if (!ctx->crypto_ctx.rekey_in_progress || !ctx->crypto_ctx.has_temp_key) {
402 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
403 }
404
405 // Send REKEY_RESPONSE with new ephemeral public key (32 bytes)
406 log_debug("Sending REKEY_RESPONSE with new ephemeral X25519 public key (32 bytes)");
407 int send_result =
408 send_packet(socket, PACKET_TYPE_CRYPTO_REKEY_RESPONSE, ctx->crypto_ctx.temp_public_key, CRYPTO_PUBLIC_KEY_SIZE);
409 if (send_result != 0) {
410 crypto_rekey_abort(&ctx->crypto_ctx); // Clean up temp keys on failure
411 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_RESPONSE packet");
412 }
413
414 log_debug("REKEY_RESPONSE sent successfully, awaiting REKEY_COMPLETE");
415 return ASCIICHAT_OK;
416}

References crypto_handshake_is_ready(), crypto_rekey_abort(), and send_packet().

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 
)

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

49 {
50 if (!ctx || !params) {
51 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, params=%p", ctx, params);
52 }
53
54 // Client receives network byte order and must convert
55 // Server uses host byte order and must NOT convert
56 if (ctx->is_server) {
57 // Server: values are already in host byte order
58 // Update crypto context with negotiated parameters directly
59 ctx->crypto_ctx.public_key_size = params->kex_public_key_size;
60 ctx->crypto_ctx.auth_public_key_size = params->auth_public_key_size;
61 ctx->crypto_ctx.shared_key_size = params->shared_secret_size;
62 ctx->crypto_ctx.signature_size = params->signature_size;
63 } else {
64 // Client: convert from network byte order
65 // Update crypto context with negotiated parameters directly
66 ctx->crypto_ctx.public_key_size = NET_TO_HOST_U16(params->kex_public_key_size);
67 ctx->crypto_ctx.auth_public_key_size = NET_TO_HOST_U16(params->auth_public_key_size);
68 ctx->crypto_ctx.shared_key_size = NET_TO_HOST_U16(params->shared_secret_size);
69 ctx->crypto_ctx.signature_size = NET_TO_HOST_U16(params->signature_size);
70 }
71 // Update crypto context with negotiated parameters directly
72 ctx->crypto_ctx.nonce_size = params->nonce_size;
73 ctx->crypto_ctx.mac_size = params->mac_size;
74 ctx->crypto_ctx.hmac_size = params->hmac_size;
75 ctx->crypto_ctx.auth_challenge_size =
76 AUTH_CHALLENGE_SIZE; // Auth challenge size is fixed for now, could be negotiated later
77 ctx->crypto_ctx.encryption_key_size =
78 (uint8_t)ctx->crypto_ctx.shared_key_size; // Use shared key size as encryption key size
79 ctx->crypto_ctx.private_key_size = ctx->crypto_ctx.public_key_size; // Same as public key for X25519
80 ctx->crypto_ctx.salt_size = ARGON2ID_SALT_SIZE; // Salt size doesn't change
81
82 log_debug("Crypto parameters set: kex_key=%u, auth_key=%u, sig=%u, "
83 "secret=%u, nonce=%u, mac=%u, hmac=%u",
84 ctx->crypto_ctx.public_key_size, ctx->crypto_ctx.auth_public_key_size, ctx->crypto_ctx.signature_size,
85 ctx->crypto_ctx.shared_key_size, ctx->crypto_ctx.nonce_size, ctx->crypto_ctx.mac_size,
86 ctx->crypto_ctx.hmac_size);
87
88 return ASCIICHAT_OK;
89}

Referenced by client_crypto_handshake(), 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. Wrapper around crypto_should_rekey() for handshake context.

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

614 {
615 if (!ctx || !crypto_handshake_is_ready(ctx)) {
616 return false;
617 }
618 return crypto_should_rekey(&ctx->crypto_ctx);
619}
bool crypto_should_rekey(const crypto_context_t *ctx)

References 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 
)

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

93 {
94 if (!ctx) {
95 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p", ctx);
96 }
97
98 switch (packet_type) {
99 case PACKET_TYPE_CRYPTO_CAPABILITIES:
100 if (packet_size != sizeof(crypto_capabilities_packet_t)) {
101 // Don't return an error code, just set the errno and return the error code
102 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid crypto capabilities packet size: %zu (expected %zu)",
103 packet_size, sizeof(crypto_capabilities_packet_t));
104 }
105 break;
106
107 case PACKET_TYPE_CRYPTO_PARAMETERS:
108 if (packet_size != sizeof(crypto_parameters_packet_t)) {
109 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid crypto parameters packet size: %zu (expected %zu)", packet_size,
110 sizeof(crypto_parameters_packet_t));
111 }
112 break;
113
114 case PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT:
115 // Server can send either:
116 // 1. Simple format: kex_public_key_size (when server has no identity key)
117 // 2. Authenticated format: kex_public_key_size + auth_public_key_size + signature_size
118 {
119 size_t simple_size = ctx->crypto_ctx.public_key_size;
120 size_t authenticated_size =
121 ctx->crypto_ctx.public_key_size + ctx->crypto_ctx.auth_public_key_size + ctx->crypto_ctx.signature_size;
122
123 if (packet_size != simple_size && packet_size != authenticated_size) {
124 return SET_ERRNO(ERROR_NETWORK_PROTOCOL,
125 "Invalid KEY_EXCHANGE_INIT size: %zu (expected %zu for simple or %zu for authenticated: "
126 "kex=%u + auth=%u + sig=%u)",
127 packet_size, simple_size, authenticated_size, ctx->crypto_ctx.public_key_size,
128 ctx->crypto_ctx.auth_public_key_size, ctx->crypto_ctx.signature_size);
129 }
130 }
131 break;
132
133 case PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP:
134 // Client can send either:
135 // 1. Simple format: kex_public_key_size (when server has no identity key)
136 // 2. Authenticated format: kex_public_key_size + client_auth_key_size + client_sig_size + [gpg_key_id_len:1] +
137 // [gpg_key_id:0-16]
138 {
139 size_t simple_size = ctx->crypto_ctx.public_key_size;
140 // For authenticated format, use Ed25519 sizes since client has Ed25519 key
141 size_t ed25519_auth_size = ED25519_PUBLIC_KEY_SIZE; // Ed25519 public key is always 32 bytes
142 size_t ed25519_sig_size = ED25519_SIGNATURE_SIZE; // Ed25519 signature is always 64 bytes
143 size_t authenticated_min_size = ctx->crypto_ctx.public_key_size + ed25519_auth_size + ed25519_sig_size;
144 size_t authenticated_max_size = authenticated_min_size + 1 + 40; // +1 for length, +40 for max GPG key ID
145
146 if (packet_size != simple_size &&
147 (packet_size < authenticated_min_size || packet_size > authenticated_max_size)) {
148 return SET_ERRNO(ERROR_NETWORK_PROTOCOL,
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,
152 ctx->crypto_ctx.public_key_size, ed25519_auth_size, ed25519_sig_size);
153 }
154 }
155 break;
156
157 case PACKET_TYPE_CRYPTO_AUTH_CHALLENGE:
158 // Server sends: 1 byte auth_flags + auth_challenge_size byte nonce
159 {
160 size_t expected_size = AUTH_CHALLENGE_FLAGS_SIZE + ctx->crypto_ctx.auth_challenge_size;
161 if (packet_size != expected_size) {
162 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid AUTH_CHALLENGE size: %zu (expected %zu: flags=%d + nonce=%u)",
163 packet_size, expected_size, AUTH_CHALLENGE_FLAGS_SIZE, ctx->crypto_ctx.auth_challenge_size);
164 }
165 }
166 break;
167
168 case PACKET_TYPE_CRYPTO_AUTH_RESPONSE:
169 // Client sends: hmac_size + auth_challenge_size bytes client_nonce + [gpg_key_id_len:1] + [gpg_key_id:0-40]
170 {
171 size_t min_size = ctx->crypto_ctx.hmac_size + ctx->crypto_ctx.auth_challenge_size;
172 size_t max_size = min_size + 1 + 40; // +1 for length, +40 for max GPG key ID
173 if (packet_size < min_size || packet_size > max_size) {
174 return SET_ERRNO(ERROR_NETWORK_PROTOCOL,
175 "Invalid AUTH_RESPONSE size: %zu (expected %zu-%zu: hmac=%u + "
176 "nonce=%u + optional GPG key ID)",
177 packet_size, min_size, max_size, ctx->crypto_ctx.hmac_size,
178 ctx->crypto_ctx.auth_challenge_size);
179 }
180 }
181 break;
182
183 case PACKET_TYPE_CRYPTO_AUTH_FAILED:
184 // Variable size - just check reasonable limits
185 if (packet_size > MAX_AUTH_FAILED_PACKET_SIZE) {
186 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid AUTH_FAILED size: %zu (max %d)", packet_size,
187 MAX_AUTH_FAILED_PACKET_SIZE);
188 }
189 break;
190
191 case PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP:
192 // Server sends: hmac_size bytes
193 if (packet_size != ctx->crypto_ctx.hmac_size) {
194 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid SERVER_AUTH_RESP size: %zu (expected %u)", packet_size,
195 ctx->crypto_ctx.hmac_size);
196 }
197 break;
198
199 case PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE:
200 // Empty packet
201 if (packet_size != 0) {
202 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid HANDSHAKE_COMPLETE size: %zu (expected 0)", packet_size);
203 }
204 break;
205
206 case PACKET_TYPE_CRYPTO_NO_ENCRYPTION:
207 // Empty packet
208 if (packet_size != 0) {
209 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid NO_ENCRYPTION size: %zu (expected 0)", packet_size);
210 }
211 break;
212
213 case PACKET_TYPE_ENCRYPTED:
214 // Variable size - check reasonable limits
215 if (packet_size > MAX_ENCRYPTED_PACKET_SIZE) { // 64KB max for encrypted packets
216 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid ENCRYPTED size: %zu (max %d)", packet_size,
217 MAX_ENCRYPTED_PACKET_SIZE);
218 }
219 break;
220
221 default:
222 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unknown crypto packet type: %u", packet_type);
223 }
224
225 return ASCIICHAT_OK;
226}

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