178 {
179 if (GET_OPTION(no_encrypt)) {
180 log_debug("Crypto handshake skipped (disabled)");
181 return 0;
182 }
183
184 if (!client) {
185 FATAL(ERROR_CRYPTO_HANDSHAKE, "Client is NULL for crypto handshake");
186 return -1;
187 }
188
189
191 const char *password = opts && opts->password[0] != '\0' ? opts->password : "";
192
193 int init_result;
194 if (strlen(password) > 0) {
195
196 log_debug("SERVER_CRYPTO_HANDSHAKE: Using password-based encryption");
198 } else {
199
200 log_debug("SERVER_CRYPTO_HANDSHAKE: Using passwordless-based encryption");
202 }
203
204 if (init_result != ASCIICHAT_OK) {
205 FATAL(init_result, "Failed to initialize crypto handshake for client %u", atomic_load(&client->client_id));
206 return -1;
207 }
208
209 client->crypto_initialized = false;
210
211
213
214 memcpy(&client->crypto_handshake_ctx.server_private_key, &
g_server_private_key,
sizeof(private_key_t));
215
216
217 client->crypto_handshake_ctx.server_public_key.type = KEY_TYPE_ED25519;
219 ED25519_PUBLIC_KEY_SIZE);
220
221
222
223
224 log_debug("Server identity keys configured for client %u", atomic_load(&client->client_id));
225 }
226
227
229 client->crypto_handshake_ctx.require_client_auth = true;
233 }
234
235
236
237 if (GET_OPTION(require_server_verify)) {
238 client->crypto_handshake_ctx.require_client_auth = true;
239 log_info("--require-server-verify enabled: clients must provide identity keys");
240 }
241
242 log_debug("Starting crypto handshake with client %u...", atomic_load(&client->client_id));
243
244 START_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
245
246
247 log_debug("SERVER_CRYPTO_HANDSHAKE: Receiving client protocol version");
248 packet_type_t packet_type;
249 void *payload = NULL;
250 size_t payload_len = 0;
251
252 log_debug("SERVER_CRYPTO_HANDSHAKE: About to receive packet from client %u", atomic_load(&client->client_id));
253
254
255 mutex_lock(&client->client_state_mutex);
257 mutex_unlock(&client->client_state_mutex);
258
259 if (socket == INVALID_SOCKET_VALUE) {
260 log_debug("SERVER_CRYPTO_HANDSHAKE: Socket is invalid for client %u", atomic_load(&client->client_id));
261 client->crypto_initialized = false;
262 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
263 return -1;
264 }
265
266 int result =
receive_packet(socket, &packet_type, &payload, &payload_len);
267 log_debug("SERVER_CRYPTO_HANDSHAKE: Received packet from client %u: result=%d, type=%u",
268 atomic_load(&client->client_id), result, packet_type);
269
270
271 if (result != ASCIICHAT_OK) {
272 log_info("Client %u disconnected during crypto handshake (connection error)", atomic_load(&client->client_id));
273 if (payload) {
275 }
276 client->crypto_initialized = false;
277 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
278 return -1;
279 }
280
281
282
283 if (packet_type != PACKET_TYPE_PROTOCOL_VERSION) {
284 log_info("Client %u sent packet type %u instead of PROTOCOL_VERSION - using unencrypted mode",
285 atomic_load(&client->client_id), packet_type);
286
287
288
289 client->pending_packet_type = packet_type;
290 client->pending_packet_payload = payload;
291 client->pending_packet_length = payload_len;
292
293
294 client->crypto_initialized = false;
295 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
296 return 0;
297 }
298
299 if (payload_len != sizeof(protocol_version_packet_t)) {
300 log_error("Invalid protocol version packet size: %zu, expected %zu", payload_len,
301 sizeof(protocol_version_packet_t));
303 client->crypto_initialized = false;
304 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
305 return -1;
306 }
307
308 protocol_version_packet_t client_version;
309 memcpy(&client_version, payload, sizeof(protocol_version_packet_t));
310 log_debug("SERVER_CRYPTO_HANDSHAKE: About to free payload for client %u", atomic_load(&client->client_id));
312 log_debug("SERVER_CRYPTO_HANDSHAKE: Payload freed for client %u", atomic_load(&client->client_id));
313
314
315 uint16_t client_proto_version = NET_TO_HOST_U16(client_version.protocol_version);
316 uint16_t client_proto_revision = NET_TO_HOST_U16(client_version.protocol_revision);
317
318 uint8_t client_mode = client_version.supports_encryption;
319 log_info("Client %u protocol version: %u.%u (crypto mode: 0x%02x)", atomic_load(&client->client_id),
320 client_proto_version, client_proto_revision, client_mode);
321
322 log_debug("SERVER_CRYPTO_HANDSHAKE: About to check encryption support for client %u",
323 atomic_load(&client->client_id));
324
325
326 if (client_mode == ACIP_CRYPTO_NONE) {
327 log_error("Client %u does not support any crypto protocol", atomic_load(&client->client_id));
328 log_info("Client %u disconnected - crypto protocol required", atomic_load(&client->client_id));
329 client->crypto_initialized = false;
330 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
331 return -1;
332 }
333
334
335 client->encrypt_data = ACIP_CRYPTO_HAS_ENCRYPT(client_mode);
336
337
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);
344 server_version.protocol_revision = HOST_TO_NET_U16(0);
345 server_version.supports_encryption = client_mode;
346 server_version.compression_algorithms = 0;
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));
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;
361 }
362 log_debug("SERVER_CRYPTO_HANDSHAKE: Protocol version sent successfully to client %u",
363 atomic_load(&client->client_id));
364
365
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) {
374 }
375 client->crypto_initialized = false;
376 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
377 return -1;
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,
382 PACKET_TYPE_CRYPTO_CAPABILITIES, PACKET_TYPE_CRYPTO_CAPABILITIES);
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) {
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;
392 }
393
394 if (payload_len != sizeof(crypto_capabilities_packet_t)) {
395 SET_ERRNO(ERROR_NETWORK_PROTOCOL,
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) {
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
417
418 crypto_capabilities_packet_t client_caps;
419 memcpy(&client_caps, payload, sizeof(crypto_capabilities_packet_t));
420
421
423 payload = NULL;
424
425
426 uint16_t supported_kex = NET_TO_HOST_U16(client_caps.supported_kex_algorithms);
427 uint16_t supported_auth = NET_TO_HOST_U16(client_caps.supported_auth_algorithms);
428 uint16_t supported_cipher = NET_TO_HOST_U16(client_caps.supported_cipher_algorithms);
429
430 log_debug("Client %u crypto capabilities: KEX=0x%04x, Auth=0x%04x, Cipher=0x%04x", atomic_load(&client->client_id),
431 supported_kex, supported_auth, supported_cipher);
432
433
434 crypto_parameters_packet_t server_params = {0};
435
436
437 server_params.selected_kex = KEX_ALGO_X25519;
438 server_params.selected_cipher =
439 ACIP_CRYPTO_HAS_ENCRYPT(client_mode) ? CIPHER_ALGO_XSALSA20_POLY1305 : CIPHER_ALGO_NONE;
440
441
442
443
444
445
446
447
448
450
451 server_params.selected_auth = AUTH_ALGO_ED25519;
452 } else {
453
454
455 server_params.selected_auth = AUTH_ALGO_NONE;
456 }
457
458
460
461
462 server_params.kex_public_key_size = CRYPTO_PUBLIC_KEY_SIZE;
463
464
465 if (server_params.selected_auth == AUTH_ALGO_ED25519) {
466 server_params.auth_public_key_size = ED25519_PUBLIC_KEY_SIZE;
467 server_params.signature_size = ED25519_SIGNATURE_SIZE;
468 } else {
469 server_params.auth_public_key_size = 0;
470 server_params.signature_size = 0;
471 }
472
473 server_params.shared_secret_size = CRYPTO_PUBLIC_KEY_SIZE;
474 server_params.nonce_size = CRYPTO_NONCE_SIZE;
475 server_params.mac_size = CRYPTO_MAC_SIZE;
476 server_params.hmac_size = CRYPTO_HMAC_SIZE;
477
478 log_debug("SERVER_CRYPTO_HANDSHAKE: Sending crypto parameters to client %u", atomic_load(&client->client_id));
480 if (result != 0) {
481 log_error("Failed to send crypto parameters to client %u", atomic_load(&client->client_id));
482 client->crypto_initialized = false;
483 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
484 return -1;
485 }
486 log_debug("Server selected crypto for client %u: KEX=%u, Auth=%u, Cipher=%u", atomic_load(&client->client_id),
487 server_params.selected_kex, server_params.selected_auth, server_params.selected_cipher);
488
489
491 if (result != ASCIICHAT_OK) {
492 client->crypto_initialized = false;
493 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
494 FATAL(result, "Failed to set crypto parameters for client %u", atomic_load(&client->client_id));
495 return -1;
496 }
497
498
499
500
502 log_debug("SERVER_CRYPTO_HANDSHAKE: Waiting for optional CRYPTO_CLIENT_HELLO from client %u",
503 atomic_load(&client->client_id));
504
505
506 packet_type_t hello_packet_type;
507 uint8_t *hello_payload = NULL;
508 size_t hello_payload_len = 0;
509
510 result =
receive_packet(socket, &hello_packet_type, (
void **)&hello_payload, &hello_payload_len);
511 if (result != ASCIICHAT_OK) {
512 log_info("Client %u disconnected during handshake initialization", atomic_load(&client->client_id));
513 if (hello_payload) {
515 }
516 client->crypto_initialized = false;
517 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
518 return -1;
519 }
520
521 if (hello_packet_type == PACKET_TYPE_CRYPTO_CLIENT_HELLO) {
522
523 if (hello_payload_len != ED25519_PUBLIC_KEY_SIZE) {
524 log_error("Invalid CRYPTO_CLIENT_HELLO size: %zu (expected %d)", hello_payload_len, ED25519_PUBLIC_KEY_SIZE);
526 client->crypto_initialized = false;
527 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
528 return -1;
529 }
530
531 const uint8_t *expected_server_key = hello_payload;
532
533
534 char hex_expected[65];
536 log_debug("Client %u expects server key: %s", atomic_load(&client->client_id), hex_expected);
537
538
539 bool key_found = false;
541 if (sodium_memcmp(expected_server_key,
g_server_identity_keys[i].public_key, ED25519_PUBLIC_KEY_SIZE) == 0) {
542
543 memcpy(&client->crypto_handshake_ctx.server_private_key, &
g_server_identity_keys[i],
sizeof(private_key_t));
544
545 char hex_selected[65];
547 log_debug("Client %u selected server identity key #%zu: %s", atomic_load(&client->client_id), i + 1,
548 hex_selected);
549
550 key_found = true;
551 break;
552 }
553 }
554
555 if (!key_found) {
556
557 log_error("Client %u requested unknown server key %s - rejecting connection", atomic_load(&client->client_id),
558 hex_expected);
560 client->crypto_initialized = false;
561 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
562 return -1;
563 }
564
566 } else {
567
568
570 log_error("Client %u must send CRYPTO_CLIENT_HELLO when server has multiple keys (got packet type %u)",
571 atomic_load(&client->client_id), hello_packet_type);
573 client->crypto_initialized = false;
574 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
575 return -1;
576 }
577
578
579
580 log_debug("Client %u is using old protocol (no CLIENT_HELLO) - using default key",
581 atomic_load(&client->client_id));
582
583
584 client->pending_packet_type = hello_packet_type;
585 client->pending_packet_payload = hello_payload;
586 client->pending_packet_length = hello_payload_len;
587 }
588 }
589
590
591 log_debug("About to call crypto_handshake_server_start");
593 if (result != ASCIICHAT_OK) {
594 log_error("Crypto handshake start failed for client %u: %s", atomic_load(&client->client_id),
595 asciichat_error_string(result));
596 LOG_ERRNO_IF_SET("Crypto handshake: failed to send KEY_EXCHANGE_INIT");
597 client->crypto_initialized = false;
598 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
599 return -1;
600 }
601
602
604 if (result != ASCIICHAT_OK) {
605 log_error("Crypto authentication challenge failed for client %u: %s", atomic_load(&client->client_id),
606 asciichat_error_string(result));
607 client->crypto_initialized = false;
608 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
609 return -1;
610 }
611
612
613 if (client->crypto_handshake_ctx.state == CRYPTO_HANDSHAKE_READY) {
614 uint32_t cid = atomic_load(&client->client_id);
615 client->crypto_initialized = true;
616
617 client->crypto_handshake_ctx.crypto_ctx.encrypt_data = client->encrypt_data;
618 STOP_TIMER_AND_LOG(debug, 100 * NS_PER_MS_INT, "server_crypto_handshake_client_%u",
619 "Crypto handshake completed successfully for client %u (no authentication)", cid);
620 return 0;
621 }
622
623
625 if (result != ASCIICHAT_OK) {
626 if (result == ERROR_NETWORK || result == ERROR_NETWORK_PROTOCOL || result == ERROR_CRYPTO_AUTH ||
627 result == ERROR_CRYPTO_VERIFICATION || result == ERROR_CRYPTO) {
628 if (result == ERROR_NETWORK) {
629 log_info("Client %u disconnected during authentication", atomic_load(&client->client_id));
630 } else {
631 log_error("Crypto authentication failed for client %u: %s", atomic_load(&client->client_id),
632 asciichat_error_string(result));
633 }
634 LOG_ERRNO_IF_SET("Crypto handshake completion failed");
635 client->crypto_initialized = false;
636 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
637 return -1;
638 }
639 STOP_TIMER("server_crypto_handshake_client_%u", atomic_load(&client->client_id));
640 FATAL(result, "Crypto authentication response failed for client %u", atomic_load(&client->client_id));
641 client->crypto_initialized = false;
642 return -1;
643 }
644
645 uint32_t cid = atomic_load(&client->client_id);
646 client->crypto_initialized = true;
647
648 client->crypto_handshake_ctx.crypto_ctx.encrypt_data = client->encrypt_data;
649 STOP_TIMER_AND_LOG(debug, 100 * NS_PER_MS_INT, "server_crypto_handshake_client_%u",
650 "Crypto handshake completed successfully for client %u", cid);
651
652
653 log_info_client(client, "Encryption established - secure channel ready");
654 return 0;
655}
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
asciichat_error_t crypto_handshake_init_with_password(crypto_handshake_context_t *ctx, bool is_server, const char *password)
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)
void pubkey_to_hex(const uint8_t pubkey[32], char hex_out[65])
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.
asciichat_error_t crypto_handshake_server_auth_challenge_socket(crypto_handshake_context_t *ctx, socket_t client_socket)
Legacy wrapper: Auth challenge using socket (TCP clients only)
asciichat_error_t crypto_handshake_server_start_socket(crypto_handshake_context_t *ctx, socket_t client_socket)
Legacy wrapper: Start handshake using socket (TCP clients only)
asciichat_error_t crypto_handshake_server_complete_socket(crypto_handshake_context_t *ctx, socket_t client_socket)
Legacy wrapper: Complete handshake using socket (TCP clients only)
int send_protocol_version_packet(socket_t sockfd, const protocol_version_packet_t *version)
Send protocol version packet.
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
int send_crypto_parameters_packet(socket_t sockfd, const crypto_parameters_packet_t *params)
Send crypto parameters packet.
const options_t * options_get(void)