185static bool g_crypto_initialized =
false;
195 log_debug(
"CLIENT_CRYPTO_INIT: Starting crypto initialization");
196 if (g_crypto_initialized) {
197 log_debug(
"CLIENT_CRYPTO_INIT: Already initialized, cleaning up and reinitializing");
199 g_crypto_initialized =
false;
204 log_info(
"Encryption disabled via --no-encrypt");
205 log_debug(
"CLIENT_CRYPTO_INIT: Encryption disabled, returning 0");
209 log_debug(
"CLIENT_CRYPTO_INIT: Initializing crypto handshake context");
213 bool is_ssh_key =
false;
219 const char *password = opts && opts->
password[0] !=
'\0' ? opts->
password :
"";
220 const char *address = opts && opts->
address[0] !=
'\0' ? opts->
address :
"localhost";
221 const char *port = opts && opts->
port[0] !=
'\0' ? opts->
port :
"27224";
225 if (strlen(encrypt_key) > 0) {
229 if (strncmp(encrypt_key,
"gpg:", 4) != 0) {
236 log_debug(
"CLIENT_CRYPTO_INIT: Loading private key for authentication: %s", encrypt_key);
238 log_info(
"Successfully parsed SSH private key");
242 log_error(
"Failed to parse SSH key file: %s", encrypt_key);
244 log_error(
" - Wrong password for encrypted key");
245 log_error(
" - Unsupported key type (only Ed25519 is currently supported)");
248 log_error(
"Note: RSA and ECDSA keys are not yet supported");
249 log_error(
"To generate an Ed25519 key: ssh-keygen -t ed25519");
256 log_debug(
"CLIENT_CRYPTO_INIT: Using SSH key for authentication");
261 FATAL(result,
"Failed to initialize crypto handshake");
274 if (strncmp(encrypt_key,
"gpg:", 4) == 0) {
275 const char *key_id = encrypt_key + 4;
276 size_t key_id_len = strlen(key_id);
278 if (key_id_len == 8 || key_id_len == 16 || key_id_len == 40) {
280 log_debug(
"CLIENT_CRYPTO_INIT: Extracted client GPG key ID (%zu chars): %s", key_id_len,
283 log_warn(
"CLIENT_CRYPTO_INIT: Invalid GPG key ID length: %zu (expected 8, 16, or 40)", key_id_len);
295 sodium_memzero(&private_key,
sizeof(private_key));
298 if (strlen(password) > 0) {
299 log_debug(
"CLIENT_CRYPTO_INIT: Password also provided, deriving password key");
306 log_info(
"Password authentication enabled alongside SSH key");
309 }
else if (strlen(
GET_OPTION(password)) > 0) {
311 log_debug(
"CLIENT_CRYPTO_INIT: Using password authentication");
314 FATAL(result,
"Failed to initialize crypto handshake with password");
318 log_debug(
"CLIENT_CRYPTO_INIT: Using standard initialization");
321 FATAL(result,
"Failed to initialize crypto handshake");
325 log_debug(
"CLIENT_CRYPTO_INIT: crypto_handshake_init succeeded");
330 log_debug(
"CLIENT_CRYPTO_INIT: server_connection_get_ip() returned: '%s'", server_ip ? server_ip :
"NULL");
336 if (strlen(server_key) > 0) {
339 log_info(
"Server key verification enabled: %s", server_key);
344 log_info(
"--require-client-verify enabled: performing ACDS session lookup for '%s'",
GET_OPTION(session_string));
367 lookup_result.
found ?
"session not found" :
"lookup error");
372 char server_key_hex[65];
373 for (
size_t i = 0; i < 32; i++) {
376 server_key_hex[64] =
'\0';
381 log_info(
"ACDS session lookup succeeded - server identity will be verified");
382 log_debug(
"Expected server key (from ACDS): %s", server_key_hex);
385 g_crypto_initialized =
true;
386 log_info(
"Client crypto handshake initialized");
387 log_debug(
"CLIENT_CRYPTO_INIT: Initialization complete, g_crypto_initialized=true");
402 log_debug(
"Client has --no-encrypt, skipping crypto handshake");
407 if (!g_crypto_initialized) {
408 log_error(
"Crypto not initialized but server requires encryption");
409 log_error(
"Server requires encrypted connection but client has no encryption configured");
410 log_error(
"Use --key to specify a client key or --password for password authentication");
414 log_info(
"Starting crypto handshake with server...");
429 log_error(
"Failed to send protocol version to server");
433 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Protocol version sent successfully");
437 void *payload = NULL;
438 size_t payload_len = 0;
440 result =
receive_packet(socket, &packet_type, &payload, &payload_len);
442 log_error(
"Failed to receive server protocol version (got type %u)", packet_type);
443 log_error(
"Packet type 0x%x (decimal %u) - Expected 0x%x (decimal %d)", packet_type, packet_type,
445 log_error(
"This suggests a protocol mismatch or packet corruption");
446 log_error(
"Raw packet type bytes: %02x %02x %02x %02x", (packet_type >> 0) & 0xFF, (packet_type >> 8) & 0xFF,
447 (packet_type >> 16) & 0xFF, (packet_type >> 24) & 0xFF);
456 log_error(
"Invalid protocol version packet size: %zu, expected %zu", payload_len,
471 log_info(
"Server protocol version: %u.%u (encryption: %s)", server_proto_version, server_proto_revision,
475 log_error(
"Server does not support encryption");
481 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Sending crypto capabilities");
493 log_error(
"Failed to send crypto capabilities to server");
497 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Crypto capabilities sent successfully");
500 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Receiving server crypto parameters");
504 result =
receive_packet(socket, &packet_type, &payload, &payload_len);
506 log_error(
"Failed to receive server crypto parameters (got type %u)", packet_type);
515 log_error(
"Invalid crypto parameters packet size: %zu, expected %zu", payload_len,
532 log_info(
"Server crypto parameters: KEX=%u, Auth=%u, Cipher=%u (key_size=%u, auth_size=%u, sig_size=%u, "
533 "secret_size=%u, verification=%u)",
541 FATAL(result,
"Failed to set crypto parameters");
549 log_info(
"Server will verify client identity (whitelist enabled)");
565 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Protocol negotiation completed successfully");
568 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Starting key exchange");
576 FATAL(result,
"Crypto key exchange failed");
578 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Key exchange completed successfully");
585 log_warn(
"Server requires client verification but client has no identity key");
592 bool skip_interactive =
false;
600 "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
601 "@ WARNING: CLIENT AUTHENTICATION REQUIRED @\n"
602 "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
604 "The server requires client authentication (--client-keys enabled),\n"
605 "but you have not provided a client identity key with --key.\n"
607 "To connect to this server, you need to:\n"
608 " 1. Generate an Ed25519 key: ssh-keygen -t ed25519\n"
609 " 2. Add the public key to the server's --client-keys list\n"
610 " 3. Connect with: ascii-chat client --key /path/to/private/key\n");
617 log_plain(
"Connection aborted by user.");
621 log_plain(
"Warning: Continuing without client identity key (connection may fail).\n");
624 log_warn(
"Non-interactive mode: Continuing without client identity key (connection may fail)");
629 log_debug(
"CLIENT_CRYPTO: Sending auth response to server...");
630 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Starting auth response");
633 FATAL(result,
"Crypto authentication failed");
635 log_debug(
"CLIENT_CRYPTO: Auth response sent successfully");
636 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Auth response completed successfully");
641 "Crypto handshake completed successfully (no authentication)");
646 log_debug(
"CLIENT_CRYPTO_HANDSHAKE: Waiting for handshake complete message");
649 FATAL(result,
"Crypto handshake completion failed");
665 if (!g_crypto_initialized ||
GET_OPTION(no_encrypt)) {
700 size_t ciphertext_size,
size_t *ciphertext_len) {
702 ciphertext, ciphertext_size, ciphertext_len);
718 size_t plaintext_size,
size_t *plaintext_len) {
720 plaintext, plaintext_size, plaintext_len);
729 if (g_crypto_initialized) {
731 g_crypto_initialized =
false;
732 log_debug(
"Client crypto handshake cleaned up");
763 log_error(
"Cannot initiate rekey: crypto not initialized or not ready");
769 log_error(
"Cannot initiate rekey: invalid socket");
775 log_error(
"Failed to send REKEY_REQUEST: %d", result);
793 log_error(
"Cannot process rekey request: crypto not initialized or not ready");
799 log_error(
"Failed to process REKEY_REQUEST: %d", result);
815 log_error(
"Cannot send rekey response: crypto not initialized or not ready");
821 log_error(
"Cannot send rekey response: invalid socket");
827 log_error(
"Failed to send REKEY_RESPONSE: %d", result);
845 log_error(
"Cannot process rekey response: crypto not initialized or not ready");
851 log_error(
"Failed to process REKEY_RESPONSE: %d", result);
867 log_error(
"Cannot send rekey complete: crypto not initialized or not ready");
873 log_error(
"Cannot send rekey complete: invalid socket");
879 log_error(
"Failed to send REKEY_COMPLETE: %d", result);
asciichat_error_t acds_session_lookup(acds_client_t *client, const char *session_string, acds_session_lookup_result_t *result)
Look up session by string.
void acds_client_config_init_defaults(acds_client_config_t *config)
Initialize ACDS client configuration with defaults.
asciichat_error_t acds_client_connect(acds_client_t *client, const acds_client_config_t *config)
Connect to ACDS server.
void acds_client_disconnect(acds_client_t *client)
Disconnect from ACDS server.
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
ascii-chat Client Media Capture Management Interface
asciichat_error_t crypto_handshake_rekey_complete(crypto_handshake_context_t *ctx, socket_t socket)
Send REKEY_COMPLETE packet (initiator side)
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_process_rekey_request(crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
Process received REKEY_REQUEST packet (responder side)
asciichat_error_t crypto_handshake_init_with_password(crypto_handshake_context_t *ctx, bool is_server, const char *password)
Initialize crypto handshake context with password authentication.
bool crypto_handshake_is_ready(const crypto_handshake_context_t *ctx)
Check if handshake is complete and encryption is ready.
bool crypto_handshake_should_rekey(const crypto_handshake_context_t *ctx)
Check if rekeying should be triggered for this handshake context.
asciichat_error_t crypto_handshake_init(crypto_handshake_context_t *ctx, bool is_server)
Initialize crypto handshake context.
asciichat_error_t crypto_handshake_rekey_response(crypto_handshake_context_t *ctx, socket_t socket)
Send REKEY_RESPONSE packet (responder side)
void crypto_handshake_cleanup(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
asciichat_error_t crypto_handshake_set_parameters(crypto_handshake_context_t *ctx, const crypto_parameters_packet_t *params)
Set crypto parameters from crypto_parameters_packet_t.
asciichat_error_t crypto_handshake_process_rekey_response(crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
Process received REKEY_RESPONSE packet (initiator side)
asciichat_error_t crypto_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.
asciichat_error_t crypto_handshake_rekey_request(crypto_handshake_context_t *ctx, socket_t socket)
Send REKEY_REQUEST packet (initiator side)
Common declarations and data structures for cryptographic handshake.
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
#define NET_TO_HOST_U16(val)
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
void capture_cleanup()
Cleanup capture subsystem.
socket_t server_connection_get_socket()
Get current socket file descriptor.
const char * server_connection_get_ip()
Get resolved server IP address.
crypto_handshake_context_t g_crypto_ctx
Per-connection crypto handshake context.
const crypto_context_t * crypto_client_get_context(void)
Get crypto context for encryption/decryption.
bool crypto_client_should_rekey(void)
Check if session rekeying should be triggered.
void crypto_client_cleanup(void)
Cleanup crypto client resources.
int crypto_client_send_rekey_response(void)
Send REKEY_RESPONSE packet to server.
int crypto_client_initiate_rekey(void)
Initiate session rekeying (client-initiated)
int client_crypto_init(void)
Initialize client crypto handshake.
int crypto_client_encrypt_packet(const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len)
Encrypt a packet for transmission.
int client_crypto_handshake(socket_t socket)
Perform crypto handshake with server.
int crypto_client_decrypt_packet(const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len)
Decrypt a received packet.
bool crypto_client_is_ready(void)
Check if crypto handshake is ready.
int crypto_client_process_rekey_request(const uint8_t *packet, size_t packet_len)
Process received REKEY_REQUEST packet from server.
int crypto_client_process_rekey_response(const uint8_t *packet, size_t packet_len)
Process received REKEY_RESPONSE packet from server.
int crypto_client_send_rekey_complete(void)
Send REKEY_COMPLETE packet to server and commit to new key.
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_SNPRINTF(buffer, buffer_size,...)
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
const char * crypto_result_to_string(crypto_result_t result)
Convert crypto result to human-readable string.
crypto_result_t
Cryptographic operation result codes.
#define ED25519_PUBLIC_KEY_SIZE
Ed25519 public key size in bytes.
crypto_result_t crypto_derive_password_key(crypto_context_t *ctx, const char *password)
Derive key from password using Argon2id.
asciichat_error_t
Error and exit codes - unified status values (0-255)
asciichat_error_t parse_private_key(const char *key_path, private_key_t *key_out)
Parse SSH private key from file.
asciichat_error_t validate_ssh_key_file(const char *key_path)
Validate SSH key file before parsing.
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
bool log_lock_terminal(void)
Lock terminal output for exclusive access by the calling thread.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
#define log_plain(...)
Plain logging - writes to both log file and stderr without timestamps or log levels.
void log_unlock_terminal(bool previous_state)
Release terminal lock and flush buffered messages.
#define START_TIMER(name_fmt,...)
Start a timer with formatted name.
#define STOP_TIMER(name_fmt,...)
Stop a timer with formatted name and return elapsed time.
#define STOP_TIMER_AND_LOG(timer_name, log_func, msg_fmt,...)
Stop a timer and log the result with a custom message.
uint8_t preferred_auth
Preferred authentication algorithm (AUTH_ALGO_*)
uint16_t supported_auth_algorithms
Supported authentication algorithms bitmask (AUTH_ALGO_*)
uint8_t selected_kex
Selected key exchange algorithm (KEX_ALGO_*)
uint16_t supported_kex_algorithms
Supported key exchange algorithms bitmask (KEX_ALGO_*)
#define AUTH_ALGO_ED25519
Ed25519 authentication (Edwards-curve signatures)
uint8_t preferred_cipher
Preferred cipher algorithm (CIPHER_ALGO_*)
uint8_t selected_cipher
Selected cipher algorithm (CIPHER_ALGO_*)
uint8_t compression_threshold
Compression threshold percentage (0-100, e.g., 80 = compress if >80% size reduction)
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
uint8_t selected_auth
Selected authentication algorithm (AUTH_ALGO_*)
int send_protocol_version_packet(socket_t sockfd, const protocol_version_packet_t *version)
Send protocol version packet.
#define KEX_ALGO_X25519
X25519 key exchange (Curve25519)
uint8_t verification_enabled
Server verification enabled flag (1=enabled, 0=disabled)
uint16_t protocol_revision
Minor protocol revision (server can be newer)
uint16_t protocol_version
Major protocol version (must match for compatibility)
uint16_t supported_cipher_algorithms
Supported cipher algorithms bitmask (CIPHER_ALGO_*)
uint16_t signature_size
Signature size in bytes (e.g., 64 for Ed25519, 3309 for Dilithium3)
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
#define AUTH_ALGO_NONE
No authentication (plaintext mode)
uint16_t feature_flags
Feature flags bitmask (FEATURE_RLE_ENCODING, etc.)
uint8_t preferred_kex
Preferred key exchange algorithm (KEX_ALGO_*)
int send_crypto_capabilities_packet(socket_t sockfd, const crypto_capabilities_packet_t *caps)
Send crypto capabilities packet.
uint8_t requires_verification
Server verification requirement flag (1=required, 0=optional)
uint8_t compression_algorithms
Supported compression algorithms bitmask (COMPRESS_ALGO_*)
uint16_t shared_secret_size
Shared secret size in bytes (e.g., 32 for X25519)
uint16_t kex_public_key_size
Key exchange public key size in bytes (e.g., 32 for X25519, 1568 for Kyber1024)
uint8_t supports_encryption
Encryption support flag (1=support encryption, 0=plaintext only)
#define CIPHER_ALGO_XSALSA20_POLY1305
XSalsa20-Poly1305 authenticated encryption.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
int strtoint_safe(const char *str)
Safely parse string to integer with validation.
const options_t * options_get(void)
Get current options (lock-free read)
packet_type_t
Network protocol packet type enumeration.
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED)
asciichat_error_t crypto_handshake_client_auth_response(crypto_handshake_context_t *ctx, socket_t client_socket)
Client: Process auth challenge and send response.
asciichat_error_t crypto_handshake_client_key_exchange(crypto_handshake_context_t *ctx, socket_t client_socket)
Client: Process server's public key and send our public key.
asciichat_error_t crypto_handshake_client_complete(crypto_handshake_context_t *ctx, socket_t client_socket)
Client: Wait for handshake complete confirmation.
Client-side handshake functions.
⚙️ Command-line options parsing and configuration management for ascii-chat
Packet protocol implementation with encryption and compression support.
Cross-platform interactive prompting utilities.
ascii-chat Client Server Connection Management Interface
@ CONNECTION_ERROR_AUTH_FAILED
Authentication failure (no retry)
Server cryptographic operations and per-client handshake management.
ACDS client connection configuration.
char server_address[256]
ACDS server address (e.g., "discovery.ascii.chat" or "127.0.0.1")
uint32_t timeout_ms
Connection timeout in milliseconds.
uint16_t server_port
ACDS server port (default: 27225)
ACDS client connection handle.
bool found
Session exists.
uint8_t host_pubkey[32]
Host's Ed25519 public key.
Crypto capabilities packet structure (Packet Type 14)
Cryptographic context structure.
Cryptographic handshake context structure.
crypto_handshake_state_t state
bool server_uses_client_auth
public_key_t client_public_key
char expected_server_key[256]
crypto_context_t crypto_ctx
char client_gpg_key_id[41]
char server_hostname[256]
private_key_t client_private_key
Crypto parameters packet structure (Packet Type 15)
Consolidated options structure.
char port[256]
Server port number.
char password[256]
Password string.
char server_key[256]
Expected server public key (client)
char encrypt_key[256]
SSH/GPG key file path.
char address[256]
Server address (client) or bind address (server)
Private key structure (for server –ssh-key)
Protocol version negotiation packet structure (Packet Type 1)
⏱️ High-precision timing utilities using sokol_time.h and uthash
Common SIMD utilities and structures.