ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches

🔐 Server cryptography: per-client handshake, X25519 key exchange, and session encryption management More...

Go to the source code of this file.

Functions

int server_crypto_init (void)
 
int server_crypto_handshake (client_info_t *client)
 
bool crypto_server_is_ready (uint32_t client_id)
 
const crypto_context_tcrypto_server_get_context (uint32_t client_id)
 
int crypto_server_encrypt_packet (uint32_t client_id, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len)
 
int crypto_server_decrypt_packet (uint32_t 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 (uint32_t client_id)
 

Variables

bool g_server_encryption_enabled
 Global flag indicating if server encryption is enabled.
 
private_key_t g_server_private_key
 Global server private key.
 
public_key_t g_client_whitelist []
 Global client public key whitelist.
 
size_t g_num_whitelisted_clients
 Number of whitelisted clients.
 

Detailed Description

🔐 Server cryptography: per-client handshake, X25519 key exchange, and session encryption management

  1. Initialize server crypto system and validate encryption configuration
  2. Perform cryptographic handshake with each connecting client
  3. Manage per-client crypto contexts stored in client_info_t structures
  4. Provide encryption/decryption functions for secure packet transmission
  5. Support multiple authentication modes (password, SSH key, passwordless)
  6. Integrate with client whitelist for authenticated access control

CRYPTOGRAPHIC HANDSHAKE ARCHITECTURE:

The handshake follows a multi-phase protocol:

PHASE 0: PROTOCOL NEGOTIATION:

  • Step 0a: Receive client protocol version
  • Step 0b: Send server protocol version
  • Step 0c: Receive client crypto capabilities
  • Step 0d: Select algorithms and send crypto parameters

PHASE 1: KEY EXCHANGE:

  • Step 1: Send server's ephemeral public key (X25519)
  • Server generates ephemeral key pair for this client session
  • Both sides derive shared secret using X25519 key exchange

PHASE 2: AUTHENTICATION:

  • Step 2: Receive client's public key and send auth challenge
  • Server verifies client identity (if whitelist enabled)
  • Server signs challenge with identity key (if server has identity key)
  • Step 3: Receive auth response and complete handshake

SUPPORTED AUTHENTICATION MODES:

The server supports three authentication modes:

  1. PASSWORD AUTHENTICATION:
    • Uses Argon2id key derivation from shared password
    • Both server and client derive same key from password
    • No identity keys required (password-only mode)
  2. SSH KEY AUTHENTICATION:
    • Server uses Ed25519 private key for identity verification
    • Client provides Ed25519 public key for authentication
    • Identity verification via known_hosts and whitelist
  3. PASSWORDLESS MODE:
    • Ephemeral keys only (no long-term identity)
    • Key exchange provides confidentiality but not authentication
    • Suitable for trusted networks or testing

PER-CLIENT CRYPTO CONTEXTS:

Each client has an independent crypto context stored in client_info_t:

  • crypto_handshake_ctx: Handshake state machine and cryptographic operations
  • crypto_initialized: Flag indicating handshake completion
  • Context is created during connection and cleaned up on disconnect

INTEGRATION WITH CLIENT WHITELIST:

When client whitelist is enabled:

  • Server requires client authentication during handshake
  • Client public key must be in whitelist array
  • Verification happens in crypto_handshake_server_auth_challenge()
  • Clients not in whitelist are rejected during handshake

ENCRYPTION/DECRYPTION OPERATIONS:

After handshake completion:

ALGORITHM SUPPORT:

The server currently supports:

  • Key Exchange: X25519 (Elliptic Curve Diffie-Hellman)
  • Cipher: XSalsa20-Poly1305 (Authenticated Encryption)
  • Authentication: Ed25519 (when server has identity key)
  • Key Derivation: Argon2id (for password-based authentication)
  • HMAC: HMAC-SHA256 (for additional integrity protection)

ERROR HANDLING:

Handshake errors are handled gracefully:

  • Client disconnection during handshake: Log and return error
  • Protocol mismatch: Log detailed error and disconnect client
  • Authentication failure: Log and disconnect client (whitelist rejection)
  • Network errors: Detect and handle gracefully (don't crash server)
  • Invalid packets: Validate size and format before processing

THREAD SAFETY:

Crypto operations are thread-safe:

  • Each client has independent crypto context (no shared state)
  • Socket access protected by client_state_mutex
  • Per-client encryption/decryption operations are isolated
  • Global server crypto state (g_server_private_key) read-only after init

INTEGRATION WITH OTHER MODULES:

  • main.c: Calls server_crypto_init() during server startup
  • client.c: Calls server_crypto_handshake() for each new client
  • protocol.c: Uses encryption functions for secure packet transmission
  • crypto/handshake.h: Core handshake protocol implementation
  • crypto/keys/keys.h: Key management and parsing functions

WHY THIS MODULAR DESIGN:

The original server.c mixed cryptographic operations with connection management and packet processing, making it difficult to:

  • Add new authentication methods
  • Modify handshake protocol
  • Debug cryptographic issues
  • Test encryption/decryption independently

This separation provides:

  • Clear cryptographic interface
  • Easier handshake protocol evolution
  • Better error isolation
  • Improved security auditing
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025
Version
2.0 (Post-Modularization)
See also
client.c For client lifecycle management and Crypto Module context storage
crypto/handshake.h For Handshake Module protocol implementation
crypto/keys/keys.h For key parsing and management

Definition in file src/server/crypto.c.

Function Documentation

◆ crypto_server_cleanup_client()

void crypto_server_cleanup_client ( uint32_t  client_id)

Cleanup crypto resources for a specific client

Parameters
client_idClient ID to cleanup crypto for

Definition at line 647 of file src/server/crypto.c.

647 {
648 client_info_t *client = find_client_by_id(client_id);
649 if (!client) {
650 return;
651 }
652
653 if (client->crypto_initialized) {
655 client->crypto_initialized = false;
656 log_debug("Crypto handshake cleaned up for client %u", client_id);
657 }
658}
void crypto_handshake_cleanup(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
#define log_debug(...)
Log a DEBUG message.
client_info_t * find_client_by_id(uint32_t client_id)
Per-client state structure for server-side client management.
crypto_handshake_context_t crypto_handshake_ctx

References crypto_handshake_cleanup(), client_info::crypto_handshake_ctx, client_info::crypto_initialized, find_client_by_id(), and log_debug.

◆ crypto_server_decrypt_packet()

int crypto_server_decrypt_packet ( uint32_t  client_id,
const uint8_t ciphertext,
size_t  ciphertext_len,
uint8_t plaintext,
size_t  plaintext_size,
size_t *  plaintext_len 
)

Decrypt a received packet from a specific client

Parameters
client_idClient ID that sent the packet
ciphertextEncrypted data to decrypt
ciphertext_lenLength of encrypted data
plaintextOutput buffer for decrypted data
plaintext_sizeSize of output buffer
plaintext_lenOutput length of decrypted data
Returns
0 on success, -1 on failure

Definition at line 631 of file src/server/crypto.c.

632 {
633 client_info_t *client = find_client_by_id(client_id);
634 if (!client) {
635 return -1;
636 }
637
639 ciphertext, ciphertext_len, plaintext, plaintext_size, plaintext_len);
640}
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.
bool crypto_server_is_ready(uint32_t client_id)

References crypto_decrypt_packet_or_passthrough(), client_info::crypto_handshake_ctx, crypto_server_is_ready(), and find_client_by_id().

Referenced by process_encrypted_packet().

◆ crypto_server_encrypt_packet()

int crypto_server_encrypt_packet ( uint32_t  client_id,
const uint8_t plaintext,
size_t  plaintext_len,
uint8_t ciphertext,
size_t  ciphertext_size,
size_t *  ciphertext_len 
)

Encrypt a packet for transmission to a specific client

Parameters
client_idClient ID to encrypt for
plaintextPlaintext data to encrypt
plaintext_lenLength of plaintext data
ciphertextOutput buffer for encrypted data
ciphertext_sizeSize of output buffer
ciphertext_lenOutput length of encrypted data
Returns
0 on success, -1 on failure

Definition at line 609 of file src/server/crypto.c.

610 {
611 client_info_t *client = find_client_by_id(client_id);
612 if (!client) {
613 return -1;
614 }
615
617 plaintext, plaintext_len, ciphertext, ciphertext_size, ciphertext_len);
618}
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.

References crypto_encrypt_packet_or_passthrough(), client_info::crypto_handshake_ctx, crypto_server_is_ready(), and find_client_by_id().

◆ crypto_server_get_context()

const crypto_context_t * crypto_server_get_context ( uint32_t  client_id)

Get crypto context for encryption/decryption for a specific client

Parameters
client_idClient ID to get context for
Returns
crypto context or NULL if not ready

Definition at line 585 of file src/server/crypto.c.

585 {
586 if (!crypto_server_is_ready(client_id)) {
587 return NULL;
588 }
589
590 client_info_t *client = find_client_by_id(client_id);
591 if (!client) {
592 return NULL;
593 }
594
596}
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.

References client_info::crypto_handshake_ctx, crypto_handshake_get_context(), crypto_server_is_ready(), and find_client_by_id().

◆ crypto_server_is_ready()

bool crypto_server_is_ready ( uint32_t  client_id)

Check if crypto handshake is ready for a specific client

Parameters
client_idClient ID to check
Returns
true if encryption is ready, false otherwise

Definition at line 561 of file src/server/crypto.c.

561 {
562 if (GET_OPTION(no_encrypt)) {
563 return false;
564 }
565
566 client_info_t *client = find_client_by_id(client_id);
567 if (!client) {
568 return false;
569 }
570
571 if (!client->crypto_initialized) {
572 return false;
573 }
574
575 bool ready = crypto_handshake_is_ready(&client->crypto_handshake_ctx);
576 return ready;
577}
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
Check if handshake is complete and encryption is ready.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644

References client_info::crypto_handshake_ctx, crypto_handshake_is_ready(), client_info::crypto_initialized, find_client_by_id(), and GET_OPTION.

Referenced by crypto_server_decrypt_packet(), crypto_server_encrypt_packet(), crypto_server_get_context(), and process_encrypted_packet().

◆ server_crypto_handshake()

int server_crypto_handshake ( client_info_t client)

Perform crypto handshake with client

Parameters
clientClient info structure
Returns
0 on success, -1 on failure

Definition at line 184 of file src/server/crypto.c.

184 {
185 if (GET_OPTION(no_encrypt)) {
186 log_debug("Crypto handshake skipped (disabled)");
187 return 0;
188 }
189
190 if (!client) {
191 FATAL(ERROR_CRYPTO_HANDSHAKE, "Client is NULL for crypto handshake");
192 return -1;
193 }
194
195 // Initialize crypto context for this specific client
196 const options_t *opts = options_get();
197 const char *password = opts && opts->password[0] != '\0' ? opts->password : "";
198
199 int init_result;
200 if (strlen(password) > 0) {
201 // Password provided - use password-based encryption (even if SSH key is also provided)
202 log_debug("SERVER_CRYPTO_HANDSHAKE: Using password-based encryption");
203 init_result = crypto_handshake_init_with_password(&client->crypto_handshake_ctx, true, password); // true = server
204 } else {
205 // Server has SSH key - use standard initialization
206 log_debug("SERVER_CRYPTO_HANDSHAKE: Using passwordless-based encryption");
207 init_result = crypto_handshake_init(&client->crypto_handshake_ctx, true); // true = server
208 }
209
210 if (init_result != ASCIICHAT_OK) {
211 FATAL(init_result, "Failed to initialize crypto handshake for client %u", atomic_load(&client->client_id));
212 return -1;
213 }
214 client->crypto_initialized = true;
215
216 // Set up server keys in the handshake context
218 // Copy server private key to handshake context for signing
220
221 // Extract Ed25519 public key from private key for identity
225
226 // SSH key is already configured in the handshake context above
227 // No additional setup needed - SSH keys are used only for authentication
228
229 log_debug("Server identity keys configured for client %u", atomic_load(&client->client_id));
230 }
231
232 // Set up client whitelist if specified
237 log_info("Client whitelist enabled: %zu authorized keys", g_num_whitelisted_clients);
238 }
239
240 // If --require-server-verify is set, require clients to provide identity keys
241 // This ensures only clients with Ed25519 identity keys can connect
242 if (GET_OPTION(require_server_verify)) {
244 log_info("--require-server-verify enabled: clients must provide identity keys");
245 }
246
247 log_info("Starting crypto handshake with client %u...", atomic_load(&client->client_id));
248
249 START_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
250
251 // Step 0a: Receive client's protocol version
252 log_debug("SERVER_CRYPTO_HANDSHAKE: Receiving client protocol version");
253 packet_type_t packet_type;
254 void *payload = NULL;
255 size_t payload_len = 0;
256
257 log_debug("SERVER_CRYPTO_HANDSHAKE: About to receive packet from client %u", atomic_load(&client->client_id));
258
259 // Protect socket access during crypto handshake
261 socket_t socket = client->socket;
263
264 if (socket == INVALID_SOCKET_VALUE) {
265 log_debug("SERVER_CRYPTO_HANDSHAKE: Socket is invalid for client %u", atomic_load(&client->client_id));
266 client->crypto_initialized = false;
267 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
268 return -1;
269 }
270
271 int result = receive_packet(socket, &packet_type, &payload, &payload_len);
272 log_debug("SERVER_CRYPTO_HANDSHAKE: Received packet from client %u: result=%d, type=%u",
273 atomic_load(&client->client_id), result, packet_type);
274
275 // Handle client disconnection gracefully
276 if (result != ASCIICHAT_OK) {
277 log_info("Client %u disconnected during crypto handshake (connection error)", atomic_load(&client->client_id));
278 if (payload) {
279 buffer_pool_free(NULL, payload, payload_len);
280 }
281 client->crypto_initialized = false;
282 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
283 return -1; // Return error but don't crash the server
284 }
285
286 // If client sent something other than PROTOCOL_VERSION, it means client is using --no-encrypt
287 // In this case, we need to store the packet for the caller to process and skip the handshake
288 if (packet_type != PACKET_TYPE_PROTOCOL_VERSION) {
289 log_info("Client %u sent packet type %u instead of PROTOCOL_VERSION - using unencrypted mode",
290 atomic_load(&client->client_id), packet_type);
291
292 // Store this packet in the client structure so the caller can process it
293 // This is a bit of a hack, but necessary to preserve the packet we already received
294 client->pending_packet_type = packet_type;
295 client->pending_packet_payload = payload;
296 client->pending_packet_length = payload_len;
297
298 // Mark crypto as not initialized (no encryption)
299 client->crypto_initialized = false;
300 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
301 return 0; // Success - just no encryption
302 }
303
304 if (payload_len != sizeof(protocol_version_packet_t)) {
305 log_error("Invalid protocol version packet size: %zu, expected %zu", payload_len,
307 buffer_pool_free(NULL, payload, payload_len);
308 client->crypto_initialized = false;
309 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
310 return -1;
311 }
312
313 protocol_version_packet_t client_version;
314 memcpy(&client_version, payload, sizeof(protocol_version_packet_t));
315 log_debug("SERVER_CRYPTO_HANDSHAKE: About to free payload for client %u", atomic_load(&client->client_id));
316 buffer_pool_free(NULL, payload, payload_len);
317 log_debug("SERVER_CRYPTO_HANDSHAKE: Payload freed for client %u", atomic_load(&client->client_id));
318
319 // Convert from network byte order
320 uint16_t client_proto_version = NET_TO_HOST_U16(client_version.protocol_version);
321 uint16_t client_proto_revision = NET_TO_HOST_U16(client_version.protocol_revision);
322
323 log_info("Client %u protocol version: %u.%u (encryption: %s)", atomic_load(&client->client_id), client_proto_version,
324 client_proto_revision, client_version.supports_encryption ? "yes" : "no");
325
326 log_debug("SERVER_CRYPTO_HANDSHAKE: About to check encryption support for client %u",
327 atomic_load(&client->client_id));
328
329 if (!client_version.supports_encryption) {
330 log_error("Client %u does not support encryption", atomic_load(&client->client_id));
331 log_info("Client %u disconnected - encryption not supported", atomic_load(&client->client_id));
332 client->crypto_initialized = false;
333 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
334 return -1; // Return error but don't crash the server
335 }
336
337 // Step 0b: Send our protocol version to client
338 log_debug("SERVER_CRYPTO_HANDSHAKE: About to prepare server protocol version for client %u",
339 atomic_load(&client->client_id));
340 protocol_version_packet_t server_version = {0};
341 log_debug("SERVER_CRYPTO_HANDSHAKE: Initialized server_version struct for client %u",
342 atomic_load(&client->client_id));
343 server_version.protocol_version = HOST_TO_NET_U16(1); // Protocol version 1
344 server_version.protocol_revision = HOST_TO_NET_U16(0); // Revision 0
345 server_version.supports_encryption = 1; // We support encryption
346 server_version.compression_algorithms = 0; // No compression for now
347 server_version.compression_threshold = 0;
348 server_version.feature_flags = 0;
349
350 log_debug("SERVER_CRYPTO_HANDSHAKE: About to call send_protocol_version_packet for client %u",
351 atomic_load(&client->client_id));
352 result = send_protocol_version_packet(socket, &server_version);
353 log_debug("SERVER_CRYPTO_HANDSHAKE: send_protocol_version_packet returned %d for client %u", result,
354 atomic_load(&client->client_id));
355 if (result != 0) {
356 log_error("Failed to send protocol version to client %u", atomic_load(&client->client_id));
357 log_info("Client %u disconnected - failed to send protocol version", atomic_load(&client->client_id));
358 client->crypto_initialized = false;
359 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
360 return -1; // Return error but don't crash the server
361 }
362 log_debug("SERVER_CRYPTO_HANDSHAKE: Protocol version sent successfully to client %u",
363 atomic_load(&client->client_id));
364
365 // Step 0c: Receive client's crypto capabilities
366 payload = NULL;
367 payload_len = 0;
368
369 result = receive_packet(socket, &packet_type, &payload, &payload_len);
370 if (result != ASCIICHAT_OK) {
371 log_info("Client %u disconnected during crypto capabilities exchange", atomic_load(&client->client_id));
372 if (payload) {
373 buffer_pool_free(NULL, payload, payload_len);
374 }
375 client->crypto_initialized = false;
376 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
377 return -1; // Return error but don't crash the server
378 }
379
380 if (packet_type != PACKET_TYPE_CRYPTO_CAPABILITIES) {
381 log_error("Server received packet type 0x%x (decimal %u) - Expected 0x%x (decimal %d)", packet_type, packet_type,
383 log_error("Raw packet type bytes: %02x %02x %02x %02x", (packet_type >> 0) & 0xFF, (packet_type >> 8) & 0xFF,
384 (packet_type >> 16) & 0xFF, (packet_type >> 24) & 0xFF);
385 if (payload) {
386 buffer_pool_free(NULL, payload, payload_len);
387 }
388 log_info("Client %u disconnected due to protocol mismatch in crypto capabilities", atomic_load(&client->client_id));
389 client->crypto_initialized = false;
390 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
391 return -1; // Return error but don't crash the server
392 }
393
394 if (payload_len != sizeof(crypto_capabilities_packet_t)) {
396 "Invalid crypto capabilities packet size: %zu (expected %zu) - we should disconnect this client",
397 payload_len, sizeof(crypto_capabilities_packet_t));
398 if (payload) {
399 buffer_pool_free(NULL, payload, payload_len);
400 payload = NULL;
401 }
402 client->crypto_initialized = false;
403 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
404 return -1;
405 }
406
407 if (!payload) {
408 SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Crypto capabilities payload is NULL after validation");
409 log_error("Client %u crypto capabilities payload is NULL after validation - disconnecting",
410 atomic_load(&client->client_id));
411 LOG_ERRNO_IF_SET("Crypto handshake: missing capabilities payload");
412 client->crypto_initialized = false;
413 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
414 return -1;
415 }
416
418 // NOLINTNEXTLINE(clang-analyzer-unix.cstring.NullArg) - payload checked above, FATAL never returns
419 memcpy(&client_caps, payload, sizeof(crypto_capabilities_packet_t));
420 buffer_pool_free(NULL, payload, payload_len);
421
422 // Convert from network byte order
423 uint16_t supported_kex = NET_TO_HOST_U16(client_caps.supported_kex_algorithms);
424 uint16_t supported_auth = NET_TO_HOST_U16(client_caps.supported_auth_algorithms);
425 uint16_t supported_cipher = NET_TO_HOST_U16(client_caps.supported_cipher_algorithms);
426
427 log_info("Client %u crypto capabilities: KEX=0x%04x, Auth=0x%04x, Cipher=0x%04x", atomic_load(&client->client_id),
428 supported_kex, supported_auth, supported_cipher);
429
430 // Step 0d: Select crypto algorithms and send parameters to client
431 crypto_parameters_packet_t server_params = {0};
432
433 // Select algorithms (for now, we only support X25519 + Ed25519 + XSalsa20-Poly1305)
434 server_params.selected_kex = KEX_ALGO_X25519;
436
437 // Select authentication algorithm based on server configuration
438 // Note: Password authentication is not a separate algorithm - it's a mode of operation
439 // that affects key derivation. The authentication algorithm refers to signature verification.
440 //
441 // CRITICAL: We require Ed25519 authentication if:
442 // - Server has an identity key (g_server_encryption_enabled AND g_server_private_key is Ed25519)
443 // - This is needed to send authenticated KEY_EXCHANGE_INIT with identity + signature
444 // - Client whitelist verification happens during authentication phase, not key exchange
446 // SSH key authentication (Ed25519 signatures) - server has identity key
447 server_params.selected_auth = AUTH_ALGO_ED25519;
448 } else {
449 // No signature-based authentication during key exchange
450 // Client authentication will be required during auth phase if whitelist is enabled
451 server_params.selected_auth = AUTH_ALGO_NONE;
452 }
453
454 // Set verification flag based on client whitelist
455 server_params.verification_enabled = (g_num_whitelisted_clients > 0) ? 1 : 0;
456
457 // Set crypto parameters for current algorithms
458 server_params.kex_public_key_size = CRYPTO_PUBLIC_KEY_SIZE; // X25519 public key size
459
460 // Only set auth/signature sizes if we're using authentication
461 if (server_params.selected_auth == AUTH_ALGO_ED25519) {
462 server_params.auth_public_key_size = ED25519_PUBLIC_KEY_SIZE; // Ed25519 public key size
463 server_params.signature_size = ED25519_SIGNATURE_SIZE; // Ed25519 signature size
464 } else {
465 server_params.auth_public_key_size = 0; // No authentication
466 server_params.signature_size = 0; // No signature
467 }
468
469 server_params.shared_secret_size = CRYPTO_PUBLIC_KEY_SIZE; // X25519 shared secret size
470 server_params.nonce_size = CRYPTO_NONCE_SIZE; // XSalsa20 nonce size
471 server_params.mac_size = CRYPTO_MAC_SIZE; // Poly1305 MAC size
472 server_params.hmac_size = CRYPTO_HMAC_SIZE; // HMAC-SHA256 size
473
474 log_debug("SERVER_CRYPTO_HANDSHAKE: Sending crypto parameters to client %u", atomic_load(&client->client_id));
475 result = send_crypto_parameters_packet(socket, &server_params);
476 if (result != 0) {
477 log_error("Failed to send crypto parameters to client %u", atomic_load(&client->client_id));
478 client->crypto_initialized = false;
479 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
480 return -1;
481 }
482 log_info("Server selected crypto for client %u: KEX=%u, Auth=%u, Cipher=%u", atomic_load(&client->client_id),
483 server_params.selected_kex, server_params.selected_auth, server_params.selected_cipher);
484
485 // Set the crypto parameters in the handshake context
486 result = crypto_handshake_set_parameters(&client->crypto_handshake_ctx, &server_params);
487 if (result != ASCIICHAT_OK) {
488 client->crypto_initialized = false;
489 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
490 FATAL(result, "Failed to set crypto parameters for client %u", atomic_load(&client->client_id));
491 return -1;
492 }
493
494 // Step 1: Send our public key to client
495 log_debug("About to call crypto_handshake_server_start");
496 result = crypto_handshake_server_start(&client->crypto_handshake_ctx, socket);
497 if (result != ASCIICHAT_OK) {
498 log_error("Crypto handshake start failed for client %u: %s", atomic_load(&client->client_id),
499 asciichat_error_string(result));
500 LOG_ERRNO_IF_SET("Crypto handshake: failed to send KEY_EXCHANGE_INIT");
501 client->crypto_initialized = false;
502 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
503 return -1;
504 }
505
506 // Step 2: Receive client's public key and send auth challenge
508 if (result != ASCIICHAT_OK) {
509 log_error("Crypto authentication challenge failed for client %u: %s", atomic_load(&client->client_id),
510 asciichat_error_string(result));
511 client->crypto_initialized = false;
512 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
513 return -1; // Return error to disconnect client gracefully
514 }
515
516 // Check if handshake completed during auth challenge (no authentication needed)
518 uint32_t cid = atomic_load(&client->client_id);
519 STOP_TIMER_AND_LOG("server_crypto_handshake_client_%u", log_info,
520 "Crypto handshake completed successfully for client %u (no authentication)", cid);
521 return 0;
522 }
523
524 // Step 3: Receive auth response and complete handshake
526 if (result != ASCIICHAT_OK) {
527 if (result == ERROR_NETWORK || result == ERROR_NETWORK_PROTOCOL || result == ERROR_CRYPTO_AUTH ||
528 result == ERROR_CRYPTO_VERIFICATION || result == ERROR_CRYPTO) {
529 if (result == ERROR_NETWORK) {
530 log_info("Client %u disconnected during authentication", atomic_load(&client->client_id));
531 } else {
532 log_error("Crypto authentication failed for client %u: %s", atomic_load(&client->client_id),
533 asciichat_error_string(result));
534 }
535 LOG_ERRNO_IF_SET("Crypto handshake completion failed");
536 client->crypto_initialized = false;
537 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
538 return -1;
539 }
540 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
541 FATAL(result, "Crypto authentication response failed for client %u", atomic_load(&client->client_id));
542 client->crypto_initialized = false;
543 return -1;
544 }
545
546 uint32_t cid = atomic_load(&client->client_id);
547 STOP_TIMER_AND_LOG("server_crypto_handshake_client_%u", log_info,
548 "Crypto handshake completed successfully for client %u", cid);
549
550 // Send success notification to client (encrypted channel now established)
551 log_info_client(client, "Encryption established - secure channel ready");
552 return 0;
553}
#define LOG_ERRNO_IF_SET(message)
Check if any error occurred and log it if so.
asciichat_error_t crypto_handshake_init_with_password(crypto_handshake_context_t *ctx, bool is_server, const char *password)
Initialize crypto handshake context with password authentication.
asciichat_error_t crypto_handshake_init(crypto_handshake_context_t *ctx, bool is_server)
Initialize 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.
#define HOST_TO_NET_U16(val)
Definition endian.h:101
#define NET_TO_HOST_U16(val)
Definition endian.h:116
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:151
#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.
@ ERROR_CRYPTO_HANDSHAKE
Definition error_codes.h:91
@ ERROR_NETWORK
Definition error_codes.h:69
@ ERROR_CRYPTO_VERIFICATION
Definition error_codes.h:92
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:73
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_CRYPTO_AUTH
Definition error_codes.h:90
@ ERROR_CRYPTO
Definition error_codes.h:88
@ 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.
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define START_TIMER(name_fmt,...)
Start a timer with formatted name.
Definition time.h:141
#define STOP_TIMER(name_fmt,...)
Stop a timer with formatted name and return elapsed time.
Definition time.h:165
#define STOP_TIMER_AND_LOG(timer_name, log_func, msg_fmt,...)
Stop a timer and log the result with a custom message.
Definition time.h:271
uint8_t hmac_size
HMAC size in bytes (e.g., 32 for HMAC-SHA256)
Definition packet.h:895
uint16_t supported_auth_algorithms
Supported authentication algorithms bitmask (AUTH_ALGO_*)
Definition packet.h:848
uint8_t selected_kex
Selected key exchange algorithm (KEX_ALGO_*)
Definition packet.h:875
uint16_t supported_kex_algorithms
Supported key exchange algorithms bitmask (KEX_ALGO_*)
Definition packet.h:846
#define AUTH_ALGO_ED25519
Ed25519 authentication (Edwards-curve signatures)
Definition packet.h:950
uint8_t selected_cipher
Selected cipher algorithm (CIPHER_ALGO_*)
Definition packet.h:879
uint8_t compression_threshold
Compression threshold percentage (0-100, e.g., 80 = compress if >80% size reduction)
Definition packet.h:720
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
Definition packet.h:885
uint8_t selected_auth
Selected authentication algorithm (AUTH_ALGO_*)
Definition packet.h:877
int send_protocol_version_packet(socket_t sockfd, const protocol_version_packet_t *version)
Send protocol version packet.
Definition packet.c:1016
#define KEX_ALGO_X25519
X25519 key exchange (Curve25519)
Definition packet.h:949
uint8_t verification_enabled
Server verification enabled flag (1=enabled, 0=disabled)
Definition packet.h:881
uint16_t protocol_revision
Minor protocol revision (server can be newer)
Definition packet.h:714
uint16_t protocol_version
Major protocol version (must match for compatibility)
Definition packet.h:712
uint16_t supported_cipher_algorithms
Supported cipher algorithms bitmask (CIPHER_ALGO_*)
Definition packet.h:850
uint16_t signature_size
Signature size in bytes (e.g., 64 for Ed25519, 3309 for Dilithium3)
Definition packet.h:887
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
Definition packet.c:767
#define AUTH_ALGO_NONE
No authentication (plaintext mode)
Definition packet.h:951
uint16_t feature_flags
Feature flags bitmask (FEATURE_RLE_ENCODING, etc.)
Definition packet.h:722
uint8_t nonce_size
Nonce size in bytes (e.g., 24 for XSalsa20)
Definition packet.h:891
uint8_t compression_algorithms
Supported compression algorithms bitmask (COMPRESS_ALGO_*)
Definition packet.h:718
int send_crypto_parameters_packet(socket_t sockfd, const crypto_parameters_packet_t *params)
Send crypto parameters packet.
Definition packet.c:1044
uint8_t mac_size
MAC size in bytes (e.g., 16 for Poly1305)
Definition packet.h:893
uint16_t shared_secret_size
Shared secret size in bytes (e.g., 32 for X25519)
Definition packet.h:889
uint16_t kex_public_key_size
Key exchange public key size in bytes (e.g., 32 for X25519, 1568 for Kyber1024)
Definition packet.h:883
uint8_t supports_encryption
Encryption support flag (1=support encryption, 0=plaintext only)
Definition packet.h:716
#define CIPHER_ALGO_XSALSA20_POLY1305
XSalsa20-Poly1305 authenticated encryption.
Definition packet.h:952
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:222
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:281
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:283
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED)
Definition packet.h:313
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
Definition mutex.h:140
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:52
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
Definition mutex.h:175
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[]
Global client public key whitelist.
private_key_t g_server_private_key
Global server private key.
asciichat_error_t crypto_handshake_server_complete(crypto_handshake_context_t *ctx, socket_t client_socket)
Server: Process auth response and complete handshake.
asciichat_error_t crypto_handshake_server_start(crypto_handshake_context_t *ctx, socket_t client_socket)
Server: Start crypto handshake by sending public key.
asciichat_error_t crypto_handshake_server_auth_challenge(crypto_handshake_context_t *ctx, socket_t client_socket)
Server: Process client's public key and send auth challenge.
atomic_uint client_id
mutex_t client_state_mutex
packet_type_t pending_packet_type
void * pending_packet_payload
size_t pending_packet_length
Crypto capabilities packet structure (Packet Type 14)
Definition packet.h:844
crypto_handshake_state_t state
Crypto parameters packet structure (Packet Type 15)
Definition packet.h:873
Consolidated options structure.
Definition options.h:439
char password[256]
Password string.
Definition options.h:543
Private key structure (for server –ssh-key)
Definition key_types.h:91
Protocol version negotiation packet structure (Packet Type 1)
Definition packet.h:710

References ASCIICHAT_OK, AUTH_ALGO_ED25519, AUTH_ALGO_NONE, crypto_parameters_packet_t::auth_public_key_size, buffer_pool_free(), CIPHER_ALGO_XSALSA20_POLY1305, client_info::client_id, client_info::client_state_mutex, crypto_handshake_context_t::client_whitelist, protocol_version_packet_t::compression_algorithms, protocol_version_packet_t::compression_threshold, client_info::crypto_handshake_ctx, crypto_handshake_init(), crypto_handshake_init_with_password(), CRYPTO_HANDSHAKE_READY, crypto_handshake_server_auth_challenge(), crypto_handshake_server_complete(), crypto_handshake_server_start(), crypto_handshake_set_parameters(), CRYPTO_HMAC_SIZE, client_info::crypto_initialized, CRYPTO_MAC_SIZE, CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, ED25519_SIGNATURE_SIZE, ERROR_CRYPTO, ERROR_CRYPTO_AUTH, ERROR_CRYPTO_HANDSHAKE, ERROR_CRYPTO_VERIFICATION, ERROR_NETWORK, ERROR_NETWORK_PROTOCOL, FATAL, protocol_version_packet_t::feature_flags, g_client_whitelist, g_num_whitelisted_clients, g_server_encryption_enabled, g_server_private_key, GET_OPTION, crypto_parameters_packet_t::hmac_size, HOST_TO_NET_U16, INVALID_SOCKET_VALUE, KEX_ALGO_X25519, crypto_parameters_packet_t::kex_public_key_size, public_key_t::key, KEY_TYPE_ED25519, log_debug, LOG_ERRNO_IF_SET, log_error, log_info, log_info_client, crypto_parameters_packet_t::mac_size, mutex_lock, mutex_unlock, NET_TO_HOST_U16, crypto_parameters_packet_t::nonce_size, crypto_handshake_context_t::num_whitelisted_clients, options_get(), PACKET_TYPE_CRYPTO_CAPABILITIES, PACKET_TYPE_PROTOCOL_VERSION, options_state::password, client_info::pending_packet_length, client_info::pending_packet_payload, client_info::pending_packet_type, protocol_version_packet_t::protocol_revision, protocol_version_packet_t::protocol_version, private_key_t::public_key, receive_packet(), crypto_handshake_context_t::require_client_auth, crypto_parameters_packet_t::selected_auth, crypto_parameters_packet_t::selected_cipher, crypto_parameters_packet_t::selected_kex, send_crypto_parameters_packet(), send_protocol_version_packet(), crypto_handshake_context_t::server_private_key, crypto_handshake_context_t::server_public_key, SET_ERRNO, crypto_parameters_packet_t::shared_secret_size, crypto_parameters_packet_t::signature_size, client_info::socket, START_TIMER, crypto_handshake_context_t::state, STOP_TIMER, STOP_TIMER_AND_LOG, crypto_capabilities_packet_t::supported_auth_algorithms, crypto_capabilities_packet_t::supported_cipher_algorithms, crypto_capabilities_packet_t::supported_kex_algorithms, protocol_version_packet_t::supports_encryption, public_key_t::type, private_key_t::type, and crypto_parameters_packet_t::verification_enabled.

◆ server_crypto_init()

int server_crypto_init ( void  )

Initialize server crypto system (global initialization)

Returns
0 on success, -1 on failure

Definition at line 167 of file src/server/crypto.c.

167 {
168 // Check if encryption is disabled
169 if (GET_OPTION(no_encrypt)) {
170 log_info("Encryption disabled via --no-encrypt");
171 return 0;
172 }
173
174 log_info("Server crypto system initialized (per-client contexts will be created on demand)");
175 return 0;
176}

References GET_OPTION, and log_info.