ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
src/server/crypto.c
Go to the documentation of this file.
1
133#include "main.h"
134#include "client.h"
135#include "crypto.h"
136
138#include <ascii-chat/options/rcu.h> // For RCU-based options access
139#include <ascii-chat/common.h>
146#include <ascii-chat/network/mdns/discovery.h> // For pubkey_to_hex
147#include <ascii-chat/util/time.h>
149
150#include <string.h>
151#include <stdio.h>
152#include <sodium.h>
153
154// Per-client crypto contexts are now stored in client_info_t structure
155// No global crypto context needed
156
163 // Only skip handshake if BOTH encryption and authentication are disabled
164 bool no_encrypt = GET_OPTION(no_encrypt);
165 bool no_auth = GET_OPTION(no_auth);
166
167 if (no_encrypt && no_auth) {
168 log_info("ACIP handshake disabled (both encryption and authentication disabled)");
169 return 0;
170 }
171
172 log_debug("Server crypto system initialized (per-client contexts will be created on demand)");
173 return 0;
174}
175
183 if (!client) {
184 FATAL(ERROR_CRYPTO_HANDSHAKE, "Client is NULL for crypto handshake");
185 return -1;
186 }
187
188 // Only skip handshake if BOTH encryption and authentication are disabled
189 bool no_encrypt = GET_OPTION(no_encrypt);
190 bool no_auth = GET_OPTION(no_auth);
191
192 if (no_encrypt && no_auth) {
193 log_debug("Crypto handshake skipped (ACIP_CRYPTO_NONE: both encryption and authentication disabled)");
194 return 0;
195 }
196
197 // Initialize crypto context for this specific client
198 const options_t *opts = options_get();
199 const char *password = opts && opts->password[0] != '\0' ? opts->password : "";
200
201 // Create debug name with client_id
202 char crypto_name[64];
203 SAFE_SNPRINTF(crypto_name, sizeof(crypto_name), "crypto_client_%s", client->client_id);
204
205 int init_result;
206 if (strlen(password) > 0) {
207 // Password provided - use password-based encryption (even if SSH key is also provided)
208 log_debug("SERVER_CRYPTO_HANDSHAKE: Using password-based encryption");
209 init_result = crypto_handshake_init_with_password(crypto_name, &client->crypto_handshake_ctx, true,
210 password); // true = server
211 } else {
212 // Server has SSH key - use standard initialization
213 log_debug("SERVER_CRYPTO_HANDSHAKE: Using passwordless-based encryption");
214 init_result = crypto_handshake_init(crypto_name, &client->crypto_handshake_ctx, true); // true = server
215 }
216
217 if (init_result != ASCIICHAT_OK) {
218 FATAL(init_result, "Failed to initialize crypto handshake for client %u", client->client_id);
219 return -1;
220 }
221 // Do NOT set crypto_initialized here - it must be set after handshake completes
222 client->crypto_initialized = false;
223
224 // Set up server keys in the handshake context
226 // Copy server private key to handshake context for signing
228
229 // Extract Ed25519 public key from private key for identity
233
234 // SSH key is already configured in the handshake context above
235 // No additional setup needed - SSH keys are used only for authentication
236
237 log_debug("Server identity keys configured for client %s", client->client_id);
238 }
239
240 // Set up client whitelist if specified
245 log_info("Client whitelist enabled: %zu authorized keys", g_num_whitelisted_clients);
246 }
247
248 // If --require-server-verify is set, require clients to provide identity keys
249 // This ensures only clients with Ed25519 identity keys can connect
250 if (GET_OPTION(require_server_verify)) {
252 log_info("--require-server-verify enabled: clients must provide identity keys");
253 }
254
255 log_debug("Starting crypto handshake with client %u...", client->client_id);
256
257 START_TIMER("server_crypto_handshake_client_%s", client->client_id);
258
259 // Step 0a: Receive client's protocol version
260 log_debug("SERVER_CRYPTO_HANDSHAKE: Receiving client protocol version");
261 packet_type_t packet_type;
262 void *payload = NULL;
263 size_t payload_len = 0;
264
265 log_debug("SERVER_CRYPTO_HANDSHAKE: About to receive packet from client %s", client->client_id);
266
267 // Protect socket access during crypto handshake
269 socket_t socket = client->socket;
271
272 if (socket == INVALID_SOCKET_VALUE) {
273 log_debug("SERVER_CRYPTO_HANDSHAKE: Socket is invalid for client %u", client->client_id);
274 client->crypto_initialized = false;
275 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
276 return -1;
277 }
278
279 int result = receive_packet(socket, &packet_type, &payload, &payload_len);
280 log_debug("SERVER_CRYPTO_HANDSHAKE: Received packet from client %u: result=%d, type=%u", client->client_id, result,
281 packet_type);
282
283 // Handle client disconnection gracefully
284 if (result != ASCIICHAT_OK) {
285 log_info("Client %s disconnected during crypto handshake (connection error)", client->client_id);
286 if (payload) {
287 buffer_pool_free(NULL, payload, payload_len);
288 }
289 client->crypto_initialized = false;
290 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
291 return -1; // Return error but don't crash the server
292 }
293
294 // If client sent something other than PROTOCOL_VERSION, it means client is using --no-encrypt
295 // In this case, we need to store the packet for the caller to process and skip the handshake
296 if (packet_type != PACKET_TYPE_PROTOCOL_VERSION) {
297 log_info("Client %u sent packet type %u instead of PROTOCOL_VERSION - using unencrypted mode", client->client_id,
298 packet_type);
299
300 // Store this packet in the client structure so the caller can process it
301 // This is a bit of a hack, but necessary to preserve the packet we already received
302 client->pending_packet_type = packet_type;
303 client->pending_packet_payload = payload;
304 client->pending_packet_length = payload_len;
305
306 // Mark crypto as not initialized (no encryption)
307 client->crypto_initialized = false;
308 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
309 return 0; // Success - just no encryption
310 }
311
312 if (payload_len != sizeof(protocol_version_packet_t)) {
313 log_error("Invalid protocol version packet size: %zu, expected %zu", payload_len,
315 buffer_pool_free(NULL, payload, payload_len);
316 client->crypto_initialized = false;
317 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
318 return -1;
319 }
320
321 protocol_version_packet_t client_version;
322 memcpy(&client_version, payload, sizeof(protocol_version_packet_t));
323 log_debug("SERVER_CRYPTO_HANDSHAKE: About to free payload for client %s", client->client_id);
324 buffer_pool_free(NULL, payload, payload_len);
325 log_debug("SERVER_CRYPTO_HANDSHAKE: Payload freed for client %s", client->client_id);
326
327 // Convert from network byte order
328 uint16_t client_proto_version = NET_TO_HOST_U16(client_version.protocol_version);
329 uint16_t client_proto_revision = NET_TO_HOST_U16(client_version.protocol_revision);
330
331 uint8_t client_mode = client_version.supports_encryption;
332 log_info("Client %s protocol version: %u.%u (crypto mode: 0x%02x)", client->client_id, client_proto_version,
333 client_proto_revision, client_mode);
334
335 log_debug("SERVER_CRYPTO_HANDSHAKE: About to check encryption support for client %u", client->client_id);
336
337 // Reject ACIP_CRYPTO_NONE (plaintext only) - at minimum, ACIP handshake must run
338 if (client_mode == ACIP_CRYPTO_NONE) {
339 log_error("Client %s does not support any crypto protocol", client->client_id);
340 log_info("Client %s disconnected - crypto protocol required", client->client_id);
341 client->crypto_initialized = false;
342 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
343 return -1; // Return error but don't crash the server
344 }
345
346 // Store encryption mode for setting encrypt_data flag later
347 client->encrypt_data = ACIP_CRYPTO_HAS_ENCRYPT(client_mode);
348
349 // Step 0b: Send our protocol version to client
350 log_debug("SERVER_CRYPTO_HANDSHAKE: About to prepare server protocol version for client %u", client->client_id);
351 protocol_version_packet_t server_version = {0};
352 log_debug("SERVER_CRYPTO_HANDSHAKE: Initialized server_version struct for client %u", client->client_id);
353 server_version.protocol_version = HOST_TO_NET_U16(1); // Protocol version 1
354 server_version.protocol_revision = HOST_TO_NET_U16(0); // Revision 0
355 server_version.supports_encryption = client_mode; // Echo client's mode
356 server_version.compression_algorithms = 0; // No compression for now
357 server_version.compression_threshold = 0;
358 server_version.feature_flags = 0;
359
360 log_debug("SERVER_CRYPTO_HANDSHAKE: About to call send_protocol_version_packet for client %u", client->client_id);
361 result = send_protocol_version_packet(socket, &server_version);
362 log_debug("SERVER_CRYPTO_HANDSHAKE: send_protocol_version_packet returned %d for client %u", result,
363 client->client_id);
364 if (result != 0) {
365 log_error("Failed to send protocol version to client %u", client->client_id);
366 log_info("Client %s disconnected - failed to send protocol version", client->client_id);
367 client->crypto_initialized = false;
368 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
369 return -1; // Return error but don't crash the server
370 }
371 log_debug("SERVER_CRYPTO_HANDSHAKE: Protocol version sent successfully to client %u", client->client_id);
372
373 // Step 0c: Receive client's crypto capabilities
374 payload = NULL;
375 payload_len = 0;
376
377 result = receive_packet(socket, &packet_type, &payload, &payload_len);
378 if (result != ASCIICHAT_OK) {
379 log_info("Client %s disconnected during crypto capabilities exchange", client->client_id);
380 if (payload) {
381 buffer_pool_free(NULL, payload, payload_len);
382 }
383 client->crypto_initialized = false;
384 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
385 return -1; // Return error but don't crash the server
386 }
387
388 if (packet_type != PACKET_TYPE_CRYPTO_CAPABILITIES) {
389 log_error("Server received packet type 0x%x (decimal %u) - Expected 0x%x (decimal %d)", packet_type, packet_type,
391 log_error("Raw packet type bytes: %02x %02x %02x %02x", (packet_type >> 0) & 0xFF, (packet_type >> 8) & 0xFF,
392 (packet_type >> 16) & 0xFF, (packet_type >> 24) & 0xFF);
393 if (payload) {
394 buffer_pool_free(NULL, payload, payload_len);
395 }
396 log_info("Client %s disconnected due to protocol mismatch in crypto capabilities", client->client_id);
397 client->crypto_initialized = false;
398 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
399 return -1; // Return error but don't crash the server
400 }
401
402 if (payload_len != sizeof(crypto_capabilities_packet_t)) {
404 "Invalid crypto capabilities packet size: %zu (expected %zu) - we should disconnect this client",
405 payload_len, sizeof(crypto_capabilities_packet_t));
406 if (payload) {
407 buffer_pool_free(NULL, payload, payload_len);
408 payload = NULL;
409 }
410 client->crypto_initialized = false;
411 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
412 return -1;
413 }
414
415 if (!payload) {
416 SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Crypto capabilities payload is NULL after validation");
417 log_error("Client %u crypto capabilities payload is NULL after validation - disconnecting", client->client_id);
418 LOG_ERRNO_IF_SET("Crypto handshake: missing capabilities payload");
419 client->crypto_initialized = false;
420 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
421 return -1;
422 }
423
424 // Copy and validate payload immediately after NULL check to ensure analyzer understands safety
426 memcpy(&client_caps, payload, sizeof(crypto_capabilities_packet_t));
427
428 // Free the buffer after use
429 buffer_pool_free(NULL, payload, payload_len);
430 payload = NULL; // Clear reference to prevent use-after-free
431
432 // Convert from network byte order
433 uint16_t supported_kex = NET_TO_HOST_U16(client_caps.supported_kex_algorithms);
434 uint16_t supported_auth = NET_TO_HOST_U16(client_caps.supported_auth_algorithms);
435 uint16_t supported_cipher = NET_TO_HOST_U16(client_caps.supported_cipher_algorithms);
436
437 log_debug("Client %s crypto capabilities: KEX=0x%04x, Auth=0x%04x, Cipher=0x%04x", client->client_id, supported_kex,
438 supported_auth, supported_cipher);
439
440 // Step 0d: Select crypto algorithms and send parameters to client
441 crypto_parameters_packet_t server_params = {0};
442
443 // Select algorithms (for now, we only support X25519 + Ed25519 + XSalsa20-Poly1305)
444 server_params.selected_kex = KEX_ALGO_X25519;
445 server_params.selected_cipher =
447
448 // Select authentication algorithm based on server configuration
449 // Note: Password authentication is not a separate algorithm - it's a mode of operation
450 // that affects key derivation. The authentication algorithm refers to signature verification.
451 //
452 // We require Ed25519 authentication if:
453 // - Server has an identity key (g_server_encryption_enabled AND g_server_private_key is Ed25519)
454 // - This is needed to send authenticated KEY_EXCHANGE_INIT with identity + signature
455 // - Client whitelist verification happens during authentication phase, not key exchange
457 // SSH key authentication (Ed25519 signatures) - server has identity key
458 server_params.selected_auth = AUTH_ALGO_ED25519;
459 } else {
460 // No signature-based authentication during key exchange
461 // Client authentication will be required during auth phase if whitelist is enabled
462 server_params.selected_auth = AUTH_ALGO_NONE;
463 }
464
465 // Set verification flag based on client whitelist
466 server_params.verification_enabled = (g_num_whitelisted_clients > 0) ? 1 : 0;
467
468 // Set crypto parameters for current algorithms
469 server_params.kex_public_key_size = CRYPTO_PUBLIC_KEY_SIZE; // X25519 public key size
470
471 // Only set auth/signature sizes if we're using authentication
472 if (server_params.selected_auth == AUTH_ALGO_ED25519) {
473 server_params.auth_public_key_size = ED25519_PUBLIC_KEY_SIZE; // Ed25519 public key size
474 server_params.signature_size = ED25519_SIGNATURE_SIZE; // Ed25519 signature size
475 } else {
476 server_params.auth_public_key_size = 0; // No authentication
477 server_params.signature_size = 0; // No signature
478 }
479
480 server_params.shared_secret_size = CRYPTO_PUBLIC_KEY_SIZE; // X25519 shared secret size
481 server_params.nonce_size = CRYPTO_NONCE_SIZE; // XSalsa20 nonce size
482 server_params.mac_size = CRYPTO_MAC_SIZE; // Poly1305 MAC size
483 server_params.hmac_size = CRYPTO_HMAC_SIZE; // HMAC-SHA256 size
484
485 // Send negotiated crypto parameters to client
486 log_debug("SERVER_CRYPTO_HANDSHAKE: Sending crypto parameters to client %s", client->client_id);
487 result = send_crypto_parameters_packet(socket, &server_params);
488 if (result != 0) {
489 log_error("Failed to send crypto parameters to client %u", client->client_id);
490 client->crypto_initialized = false;
491 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
492 return -1;
493 }
494 log_debug("Server selected crypto for client %s: KEX=%u, Auth=%u, Cipher=%u", client->client_id,
495 server_params.selected_kex, server_params.selected_auth, server_params.selected_cipher);
496
497 // Set the crypto parameters in the handshake context so that
498 // crypto_handshake_server_start knows parameters were already sent
499 result = crypto_handshake_set_parameters(&client->crypto_handshake_ctx, &server_params);
500 if (result != ASCIICHAT_OK) {
501 client->crypto_initialized = false;
502 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
503 FATAL(result, "Failed to set crypto parameters for client %u", client->client_id);
504 return -1;
505 }
506
507 // Step 0.5: Receive CRYPTO_CLIENT_HELLO when the client pins a server key.
508 // The capability flag lets the server distinguish clients that will send
509 // the hello from clients that are waiting for KEY_EXCHANGE_INIT.
510 bool client_requires_server_verification = client_caps.requires_verification != 0;
511 if (g_num_server_identity_keys > 0 && client_requires_server_verification) {
512 log_debug("SERVER_CRYPTO_HANDSHAKE: Waiting for optional CRYPTO_CLIENT_HELLO from client %u", client->client_id);
513
514 // Try to receive CLIENT_HELLO packet
515 packet_type_t hello_packet_type;
516 uint8_t *hello_payload = NULL;
517 size_t hello_payload_len = 0;
518
519 result = receive_packet(socket, &hello_packet_type, (void **)&hello_payload, &hello_payload_len);
520 if (result != ASCIICHAT_OK) {
521 log_info("Client %s disconnected during handshake initialization", client->client_id);
522 if (hello_payload) {
523 buffer_pool_free(NULL, hello_payload, hello_payload_len);
524 }
525 client->crypto_initialized = false;
526 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
527 return -1;
528 }
529
530 if (hello_packet_type == PACKET_TYPE_CRYPTO_CLIENT_HELLO) {
531 // Client sent expected server key - select matching key
532 if (hello_payload_len != ED25519_PUBLIC_KEY_SIZE) {
533 log_error("Invalid CRYPTO_CLIENT_HELLO size: %zu (expected %d)", hello_payload_len, ED25519_PUBLIC_KEY_SIZE);
534 buffer_pool_free(NULL, hello_payload, hello_payload_len);
535 client->crypto_initialized = false;
536 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
537 return -1;
538 }
539
540 const uint8_t *expected_server_key = hello_payload;
541
542 // Log the expected key for debugging
543 char hex_expected[65];
544 pubkey_to_hex(expected_server_key, hex_expected);
545 log_debug("Client %s expects server key: %s", client->client_id, hex_expected);
546
547 // Search g_server_identity_keys[] for matching public key
548 bool key_found = false;
549 for (size_t i = 0; i < g_num_server_identity_keys; i++) {
550 if (sodium_memcmp(expected_server_key, g_server_identity_keys[i].public_key, ED25519_PUBLIC_KEY_SIZE) == 0) {
551 // Found matching key - use this one for handshake
553
554 char hex_selected[65];
555 pubkey_to_hex(g_server_identity_keys[i].public_key, hex_selected);
556 log_debug("Client %s selected server identity key #%zu: %s", client->client_id, i + 1, hex_selected);
557
558 key_found = true;
559 break;
560 }
561 }
562
563 if (!key_found) {
564 // Client requested a key we don't have - reject connection
565 log_error("Client %s requested unknown server key %s - rejecting connection", client->client_id, hex_expected);
566 buffer_pool_free(NULL, hello_payload, hello_payload_len);
567 client->crypto_initialized = false;
568 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
569 return -1;
570 }
571
572 buffer_pool_free(NULL, hello_payload, hello_payload_len);
573 } else {
574 // Client didn't send CLIENT_HELLO
575 // This is ONLY acceptable for single-key servers (backward compatibility)
577 log_error("Client %u must send CRYPTO_CLIENT_HELLO when server has multiple keys (got packet type %u)",
578 client->client_id, hello_packet_type);
579 buffer_pool_free(NULL, hello_payload, hello_payload_len);
580 client->crypto_initialized = false;
581 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
582 return -1;
583 }
584
585 // Single-key server: Old client didn't send CLIENT_HELLO
586 // Store this packet for later processing (it's probably the first handshake packet)
587 log_debug("Client %u is using old protocol (no CLIENT_HELLO) - using default key", client->client_id);
588
589 // Store pending packet so we can process it after KEY_EXCHANGE_INIT
590 client->pending_packet_type = hello_packet_type;
591 client->pending_packet_payload = hello_payload;
592 client->pending_packet_length = hello_payload_len;
593 }
594 }
595
596 // Use the client's persistent transport for handshake (created in add_client before this call).
597 // The transport starts with NULL crypto_ctx; after handshake we set it.
598 acip_transport_t *transport = client->transport;
599 if (!transport) {
600 log_error("No transport available for client %u handshake", client->client_id);
601 client->crypto_initialized = false;
602 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
603 return -1;
604 }
605
606 // Step 1: Send our public key to client
607 log_debug("About to call crypto_handshake_server_start");
608 result = crypto_handshake_server_start(&client->crypto_handshake_ctx, transport);
609 if (result != ASCIICHAT_OK) {
610 log_error("Crypto handshake start failed for client %u: %s", client->client_id, asciichat_error_string(result));
611 LOG_ERRNO_IF_SET("Crypto handshake: failed to send KEY_EXCHANGE_INIT");
612 client->crypto_initialized = false;
613 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
614 return -1;
615 }
616
617 // Step 2: Receive client's public key and send auth challenge
618 {
619 packet_type_t step2_type;
620 uint8_t *step2_payload = NULL;
621 size_t step2_len = 0;
622 int recv_result = receive_packet(socket, &step2_type, (void **)&step2_payload, &step2_len);
623 if (recv_result != ASCIICHAT_OK) {
624 log_error("Failed to receive KEY_EXCHANGE_RESPONSE from client %u", client->client_id);
625 client->crypto_initialized = false;
626 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
627 return -1;
628 }
629 result = crypto_handshake_server_auth_challenge(&client->crypto_handshake_ctx, transport, step2_type, step2_payload,
630 step2_len);
631 buffer_pool_free(NULL, step2_payload, step2_len);
632 }
633 if (result != ASCIICHAT_OK) {
634 log_error("Crypto authentication challenge failed for client %u: %s", client->client_id,
635 asciichat_error_string(result));
636 client->crypto_initialized = false;
637 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
638 return -1; // Return error to disconnect client gracefully
639 }
640
641 // Check if handshake completed during auth challenge (no authentication needed)
643 const char *cid = client->client_id;
644 client->crypto_initialized = true;
645 // Propagate encryption flag to crypto context
647 // Set crypto context on the persistent transport now that handshake is complete
649 STOP_TIMER_AND_LOG(debug, 100 * NS_PER_MS_INT, "server_crypto_handshake_client_%s",
650 "Crypto handshake completed successfully for client %s (no authentication)", cid);
651 return 0;
652 }
653
654 // Step 3: Receive auth response and complete handshake
655 {
656 packet_type_t step3_type = 0;
657 uint8_t *step3_payload = NULL;
658 size_t step3_len = 0;
659 int recv_result = receive_packet(socket, &step3_type, (void **)&step3_payload, &step3_len);
660 if (recv_result != ASCIICHAT_OK) {
661 log_info("Client %s disconnected during authentication", client->client_id);
662 client->crypto_initialized = false;
663 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
664 return -1;
665 }
666 result = crypto_handshake_server_complete(&client->crypto_handshake_ctx, transport, step3_type, step3_payload,
667 step3_len);
668 buffer_pool_free(NULL, step3_payload, step3_len);
669 }
670 if (result != ASCIICHAT_OK) {
671 if (result == ERROR_NETWORK || result == ERROR_NETWORK_PROTOCOL || result == ERROR_CRYPTO_AUTH ||
672 result == ERROR_CRYPTO_VERIFICATION || result == ERROR_CRYPTO) {
673 if (result == ERROR_NETWORK) {
674 log_info("Client %s disconnected during authentication", client->client_id);
675 } else {
676 log_error("Crypto authentication failed for client %s: %s", client->client_id, asciichat_error_string(result));
677 }
678 LOG_ERRNO_IF_SET("Crypto handshake completion failed");
679 client->crypto_initialized = false;
680 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
681 return -1;
682 }
683 STOP_TIMER("server_crypto_handshake_client_%s", client->client_id);
684 FATAL(result, "Crypto authentication response failed for client %s", client->client_id);
685 client->crypto_initialized = false;
686 return -1;
687 }
688
689 const char *cid = client->client_id;
690 client->crypto_initialized = true;
691 // Propagate encryption flag to crypto context
693 // Set crypto context on the persistent transport now that handshake is complete
695 STOP_TIMER_AND_LOG(debug, 100 * NS_PER_MS_INT, "server_crypto_handshake_client_%s",
696 "Crypto handshake completed successfully for client %s", cid);
697
698 // Send success notification to client (encrypted channel now established)
699 log_info_client(client, "Encryption established - secure channel ready");
700 return 0;
701}
702
709bool crypto_server_is_ready(const char *client_id) {
710 if (GET_OPTION(no_encrypt)) {
711 return false;
712 }
713
714 client_info_t *client = find_client_by_id(client_id);
715 if (!client) {
716 return false;
717 }
718
719 if (!client->crypto_initialized) {
720 return false;
721 }
722
723 bool ready = crypto_handshake_is_ready(&client->crypto_handshake_ctx);
724 return ready;
725}
726
733const crypto_context_t *crypto_server_get_context(const char *client_id) {
734 if (!crypto_server_is_ready(client_id)) {
735 return NULL;
736 }
737
738 client_info_t *client = find_client_by_id(client_id);
739 if (!client) {
740 return NULL;
741 }
742
744}
745
757int crypto_server_encrypt_packet(const char *client_id, const uint8_t *plaintext, size_t plaintext_len,
758 uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len) {
759 client_info_t *client = find_client_by_id(client_id);
760 if (!client) {
761 return -1;
762 }
763
765 plaintext, plaintext_len, ciphertext, ciphertext_size, ciphertext_len);
766}
767
779int crypto_server_decrypt_packet(const char *client_id, const uint8_t *ciphertext, size_t ciphertext_len,
780 uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len) {
781 client_info_t *client = find_client_by_id(client_id);
782 if (!client) {
783 return -1;
784 }
785
787 ciphertext, ciphertext_len, plaintext, plaintext_size, plaintext_len);
788}
789
795void crypto_server_cleanup_client(const char *client_id) {
796 client_info_t *client = find_client_by_id(client_id);
797 if (!client) {
798 return;
799 }
800
801 if (client->crypto_initialized) {
803 client->crypto_initialized = false;
804 log_debug("Crypto handshake cleaned up for client %s", client_id);
805 }
806}
#define LOG_ERRNO_IF_SET(message)
Check if any error occurred and log it if so.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
asciichat_error_t crypto_handshake_init(const char *name, crypto_handshake_context_t *ctx, bool is_server)
Initialize named crypto handshake context.
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_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.
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_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.
Common declarations and data structures for cryptographic handshake.
void pubkey_to_hex(const uint8_t pubkey[32], char hex_out[65])
Convert Ed25519 public key to hex string.
Definition discovery.c:49
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define NET_TO_HOST_U16(val)
Definition endian.h:111
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
unsigned short uint16_t
Definition common.h:57
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:158
unsigned char uint8_t
Definition common.h:56
#define ED25519_SIGNATURE_SIZE
Ed25519 signature size in bytes.
#define CRYPTO_NONCE_SIZE
Nonce size (XSalsa20)
#define CRYPTO_HMAC_SIZE
HMAC size (HMAC-SHA256)
#define ED25519_PUBLIC_KEY_SIZE
Ed25519 public key size in bytes.
#define CRYPTO_MAC_SIZE
MAC size (Poly1305)
#define CRYPTO_PUBLIC_KEY_SIZE
Public key size (X25519)
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_CRYPTO_HANDSHAKE
Definition error_codes.h:99
@ ERROR_NETWORK
Definition error_codes.h:77
@ ERROR_CRYPTO_VERIFICATION
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CRYPTO_AUTH
Definition error_codes.h:98
@ ERROR_CRYPTO
Definition error_codes.h:96
@ CRYPTO_HANDSHAKE_READY
uint8_t key[32]
Definition key_types.h:71
key_type_t type
Definition key_types.h:92
uint8_t public_key[32]
Definition key_types.h:99
key_type_t type
Definition key_types.h:70
@ KEY_TYPE_ED25519
Definition key_types.h:52
#define log_info_client(client, fmt,...)
Server sends INFO log message to client.
Definition network/log.h:69
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define STOP_TIMER_AND_LOG(log_level, threshold_ns, timer_name, msg_fmt,...)
Stop a timer and log the result with a custom message.
Definition time.h:612
#define START_TIMER(name_fmt,...)
Start a timer with formatted name.
Definition time.h:333
#define NS_PER_MS_INT
Definition time.h:156
#define STOP_TIMER(name_fmt,...)
Stop a timer with formatted name and return elapsed time.
Definition time.h:403
#define AUTH_ALGO_ED25519
Ed25519 authentication (Edwards-curve signatures)
Definition packet.h:1065
#define ACIP_CRYPTO_NONE
No encryption, no authentication (plaintext only)
Definition packet.h:1082
int send_protocol_version_packet(socket_t sockfd, const protocol_version_packet_t *version)
Send protocol version packet.
Definition packet.c:1119
#define KEX_ALGO_X25519
X25519 key exchange (Curve25519)
Definition packet.h:1064
#define ACIP_CRYPTO_HAS_ENCRYPT(mode)
Check if crypto mode includes payload encryption.
Definition packet.h:1092
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
Definition packet.c:797
#define AUTH_ALGO_NONE
No authentication (plaintext mode)
Definition packet.h:1066
#define CIPHER_ALGO_NONE
No encryption (plaintext mode)
Definition packet.h:1068
int send_crypto_parameters_packet(socket_t sockfd, const crypto_parameters_packet_t *params)
Send crypto parameters packet.
Definition packet.c:1147
#define CIPHER_ALGO_XSALSA20_POLY1305
XSalsa20-Poly1305 authenticated encryption.
Definition packet.h:1067
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:496
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
@ PACKET_TYPE_CRYPTO_CLIENT_HELLO
Client -> Server: Expected server key fingerprint for multi-key selection (UNENCRYPTED)
Definition packet.h:299
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:288
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED)
Definition packet.h:307
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
size_t g_num_whitelisted_clients
Number of whitelisted clients.
bool g_server_encryption_enabled
Global flag indicating if server encryption is enabled.
public_key_t g_client_whitelist[MAX_CLIENTS]
Global client public key whitelist.
private_key_t g_server_private_key
Global server private key (first identity key, for backward compatibility)
private_key_t g_server_identity_keys[MAX_IDENTITY_KEYS]
Global server identity keys array (multi-key support)
size_t g_num_server_identity_keys
Number of loaded server identity keys.
int socket_t
Server-side handshake functions.
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
asciichat_error_t crypto_handshake_server_complete(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Server: Process auth response and complete handshake.
asciichat_error_t crypto_handshake_server_auth_challenge(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Server: Process client's public key and send auth challenge.
asciichat_error_t crypto_handshake_server_start(crypto_handshake_context_t *ctx, acip_transport_t *transport)
Server: Start crypto handshake by sending public key.
ascii-chat Server Mode Entry Point Header
client_info_t * find_client_by_id(const char *client_id)
Fast O(1) client lookup by ID using hash table.
bool crypto_server_is_ready(const char *client_id)
int crypto_server_decrypt_packet(const char *client_id, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len)
void crypto_server_cleanup_client(const char *client_id)
const crypto_context_t * crypto_server_get_context(const char *client_id)
int crypto_server_encrypt_packet(const char *client_id, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len)
int server_crypto_init(void)
int server_crypto_handshake(client_info_t *client)
Server cryptographic operations and per-client handshake management.
Client-side H.265 media capture and encoding pipeline.
Transport instance structure.
Definition transport.h:214
crypto_context_t * crypto_ctx
Optional encryption context.
Definition transport.h:216
Per-client state structure for server-side client management.
crypto_handshake_context_t crypto_handshake_ctx
char client_id[MAX_CLIENT_ID_LEN]
Crypto capabilities packet structure (Packet Type 14)
Definition packet.h:952
uint16_t supported_auth_algorithms
Supported authentication algorithms bitmask (AUTH_ALGO_*)
Definition packet.h:956
uint16_t supported_kex_algorithms
Supported key exchange algorithms bitmask (KEX_ALGO_*)
Definition packet.h:954
uint16_t supported_cipher_algorithms
Supported cipher algorithms bitmask (CIPHER_ALGO_*)
Definition packet.h:958
uint8_t requires_verification
Server verification requirement flag (1=required, 0=optional)
Definition packet.h:960
Cryptographic context structure.
crypto_handshake_state_t state
Crypto parameters packet structure (Packet Type 15)
Definition packet.h:981
uint8_t hmac_size
HMAC size in bytes (e.g., 32 for HMAC-SHA256)
Definition packet.h:1003
uint8_t selected_kex
Selected key exchange algorithm (KEX_ALGO_*)
Definition packet.h:983
uint8_t selected_cipher
Selected cipher algorithm (CIPHER_ALGO_*)
Definition packet.h:987
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
Definition packet.h:993
uint8_t selected_auth
Selected authentication algorithm (AUTH_ALGO_*)
Definition packet.h:985
uint8_t verification_enabled
Server verification enabled flag (1=enabled, 0=disabled)
Definition packet.h:989
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
Consolidated options structure.
char password[256]
Password string.
Private key structure (for server –ssh-key)
Definition key_types.h:91
Protocol version negotiation packet structure (Packet Type 1)
Definition packet.h:818
uint8_t compression_threshold
Compression threshold percentage (0-100, e.g., 80 = compress if >80% size reduction)
Definition packet.h:828
uint16_t protocol_revision
Minor protocol revision (server can be newer)
Definition packet.h:822
uint16_t protocol_version
Major protocol version (must match for compatibility)
Definition packet.h:820
uint16_t feature_flags
Feature flags bitmask (FEATURE_RLE_ENCODING, etc.)
Definition packet.h:830
uint8_t compression_algorithms
Supported compression algorithms bitmask (COMPRESS_ALGO_*)
Definition packet.h:826
uint8_t supports_encryption
Encryption & authentication mode bitmask (ACIP_CRYPTO_* constants: NONE, ENCRYPT, AUTH,...
Definition packet.h:824
⏱️ High-precision timing utilities using sokol_time.h and uthash
Transport abstraction layer for ACIP protocol.