ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
lib/crypto/handshake/server.c
Go to the documentation of this file.
1
10#include <ascii-chat/common.h>
16#include <stdio.h>
17#include <string.h>
18
19// Server: Send CRYPTO_PARAMETERS packet and set context sizes
21 acip_transport_t *transport) {
22 if (!ctx || !transport) {
23 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, transport=%p", (void *)ctx, (void *)transport);
24 }
25
26 bool has_identity_key = (ctx->server_private_key.type == KEY_TYPE_ED25519);
27
29 memset(&params, 0, sizeof(params));
30
32 params.selected_auth = has_identity_key ? AUTH_ALGO_ED25519 : AUTH_ALGO_NONE;
34 params.verification_enabled = ctx->require_client_auth ? 1 : 0;
39 params.hmac_size = 32; // SHA256 output size
40
41 if (has_identity_key) {
44 }
45
46 // Set context sizes to match what we send (host byte order)
48
49 // Convert to network byte order for the wire
50 params.kex_public_key_size = htons(params.kex_public_key_size);
51 params.auth_public_key_size = htons(params.auth_public_key_size);
52 params.signature_size = htons(params.signature_size);
53 params.shared_secret_size = htons(params.shared_secret_size);
54
55 int result =
56 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_PARAMETERS, (uint8_t *)&params, sizeof(params), 0);
57 if (result != ASCIICHAT_OK) {
58 return SET_ERRNO(ERROR_NETWORK, "Failed to send CRYPTO_PARAMETERS packet");
59 }
60
61 log_debug("[HANDSHAKE] CRYPTO_PARAMETERS sent: auth=%s, cipher=XSALSA20_POLY1305",
62 has_identity_key ? "ED25519" : "NONE");
63 return ASCIICHAT_OK;
64}
65
66// Server: Start crypto handshake by sending public key
68 log_debug("[HANDSHAKE_START] ===== ENTRY: crypto_handshake_server_start called =====");
69 log_debug("[HANDSHAKE_START] ctx=%p, transport=%p", (void *)ctx, (void *)transport);
70
71 if (!ctx || ctx->state != CRYPTO_HANDSHAKE_INIT) {
72 log_error("[HANDSHAKE_START] FAILED: Invalid state - ctx=%p, state=%d", (void *)ctx, ctx ? (int)ctx->state : -1);
73 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, state=%d", (void *)ctx, ctx ? (int)ctx->state : -1);
74 }
75 log_debug("[HANDSHAKE_START] State check OK: state=%d (CRYPTO_HANDSHAKE_INIT)", ctx->state);
76
77 int result;
78
79 // Caller is responsible for sending CRYPTO_PARAMETERS and calling
80 // crypto_handshake_set_parameters() before this function. This function
81 // only handles KEY_EXCHANGE_INIT.
82
83 // Calculate packet size based on negotiated crypto parameters
84 size_t expected_packet_size =
86
87 log_debug("SERVER_KEY_EXCHANGE: kex_size=%u, auth_size=%u, sig_size=%u, expected_size=%zu",
89 expected_packet_size);
90
91 // Check if we have an identity key to send authenticated packet
93 // Extended packet format:
94 // [ephemeral_key:kex_size][identity_key:auth_size][signature:sig_size]
95 uint8_t *extended_packet;
96 extended_packet = SAFE_MALLOC(expected_packet_size, uint8_t *);
97 if (!extended_packet) {
98 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate memory for extended packet");
99 }
100
101 // Copy ephemeral public key
102 memcpy(extended_packet, ctx->crypto_ctx.public_key, ctx->crypto_ctx.public_key_size);
103
104 // Copy identity public key
105 memcpy(extended_packet + ctx->crypto_ctx.public_key_size, ctx->server_private_key.public_key,
107
108 // DEBUG: Print identity key being sent
109 char hex[HEX_STRING_SIZE_32];
110 for (int i = 0; i < ED25519_PUBLIC_KEY_SIZE; i++) {
111 safe_snprintf(hex + i * 2, 3, "%02x", ctx->server_private_key.public_key[i]);
112 }
113 hex[HEX_STRING_SIZE_32 - 1] = '\0';
114
115 // Sign the ephemeral key with our identity key
116 log_debug("Signing ephemeral key with server identity key");
117
118 // Log key details in debug mode
119 char hex_ephemeral[65], hex_identity[65];
120 for (int i = 0; i < 32; i++) {
121 safe_snprintf(hex_ephemeral + i * 2, 3, "%02x", ctx->crypto_ctx.public_key[i]);
122 safe_snprintf(hex_identity + i * 2, 3, "%02x", ctx->server_private_key.public_key[i]);
123 }
124 hex_ephemeral[64] = hex_identity[64] = '\0';
125 log_debug("SERVER: Ephemeral key (32 bytes): %s", hex_ephemeral);
126 log_debug("SERVER: Identity public key: %s", hex_identity);
127
129 extended_packet + ctx->crypto_ctx.public_key_size +
130 ctx->crypto_ctx.auth_public_key_size) != 0) {
131 SAFE_FREE(extended_packet);
132 return SET_ERRNO(ERROR_CRYPTO, "Failed to sign ephemeral key with identity key");
133 }
134
135 log_debug("Sending authenticated KEY_EXCHANGE_INIT (%zu bytes: ephemeral + "
136 "identity + signature)",
137 expected_packet_size);
138 log_debug("[HANDSHAKE_START] Calling packet_send_via_transport(type=%d, payload_len=%zu, 0)...",
139 PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, expected_packet_size);
140 result = packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, extended_packet,
141 expected_packet_size, 0);
142 log_debug("[HANDSHAKE_START] packet_send_via_transport returned %d", result);
143 SAFE_FREE(extended_packet);
144 } else {
145 // No identity key - send just the ephemeral key
146 log_debug("Sending simple KEY_EXCHANGE_INIT (%zu bytes: ephemeral key only)", ctx->crypto_ctx.public_key_size);
147 log_debug("[HANDSHAKE_START] Calling packet_send_via_transport(type=%d, payload_len=%zu, 0)...",
151 log_debug("[HANDSHAKE_START] packet_send_via_transport returned %d", result);
152 }
153
154 if (result != ASCIICHAT_OK) {
155 log_error("[HANDSHAKE_START] FAILED: packet_send returned %d", result);
156 return SET_ERRNO(ERROR_NETWORK, "Failed to send KEY_EXCHANGE_INIT packet");
157 }
158 log_debug("[HANDSHAKE_START] ===== SUCCESS: KEY_EXCHANGE_INIT sent =====");
159
161
162 return ASCIICHAT_OK;
163}
164// Server: Process client's public key and send auth challenge
166 packet_type_t packet_type, const uint8_t *payload,
167 size_t payload_len) {
168 if (!ctx || ctx->state != CRYPTO_HANDSHAKE_KEY_EXCHANGE) {
169 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, state=%d", ctx, ctx ? ctx->state : -1);
170 }
171 if (!transport) {
172 return SET_ERRNO(ERROR_INVALID_PARAM, "transport is NULL");
173 }
174
175 // Note: Packet already received by ACIP handler
176 int result;
177
178 // Check if client sent NO_ENCRYPTION response
179 if (packet_type == PACKET_TYPE_CRYPTO_NO_ENCRYPTION) {
180
181 // Send AUTH_FAILED to inform client (though they already know)
182 auth_failure_packet_t failure = {0};
183 failure.reason_flags = 0; // No specific auth failure, just encryption mismatch
184 int send_result =
185 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_FAILED, &failure, sizeof(failure), 0);
186 if (send_result != 0) {
187 return SET_ERRNO(ERROR_NETWORK, "Failed to send AUTH_FAILED packet");
188 }
189
190 return SET_ERRNO(ERROR_CRYPTO, "SECURITY: Client sent NO_ENCRYPTION response - encryption mode "
191 "mismatch. Server requires encryption, but "
192 "client has --no-encrypt. Use matching encryption settings on "
193 "both client and server");
194 }
195
196 // Verify packet type
197 if (packet_type != PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP) {
198 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Expected KEY_EXCHANGE_RESPONSE, got packet type %d", packet_type);
199 }
200
201 // Verify payload size - client can send either simple or authenticated format
202 // Simple: kex_public_key_size bytes
203 // Authenticated: public_key_size + client_auth_key_size + client_sig_size bytes
204 size_t simple_size = ctx->crypto_ctx.public_key_size;
205 // Ed25519 public key is always 32 bytes
206 // Ed25519 signature is always 64 bytes
208
209 bool client_sent_identity = false;
210 uint8_t *client_ephemeral_key = SAFE_MALLOC(ctx->crypto_ctx.public_key_size, uint8_t *);
211 uint8_t *client_identity_key = SAFE_MALLOC(ED25519_PUBLIC_KEY_SIZE, uint8_t *);
212 uint8_t *client_signature = SAFE_MALLOC(ED25519_SIGNATURE_SIZE, uint8_t *);
213
214 if (!client_ephemeral_key || !client_identity_key || !client_signature) {
215 if (client_ephemeral_key)
216 SAFE_FREE(client_ephemeral_key);
217 if (client_identity_key)
218 SAFE_FREE(client_identity_key);
219 if (client_signature)
220 SAFE_FREE(client_signature);
221 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate memory for client keys");
222 }
223
224 // Validate packet size using session parameters
225 asciichat_error_t validation_result =
227 if (validation_result != ASCIICHAT_OK) {
228 // Payload ownership remains with caller
229 SAFE_FREE(client_ephemeral_key);
230 SAFE_FREE(client_identity_key);
231 SAFE_FREE(client_signature);
232 return validation_result;
233 }
234
235 // Handle both authenticated and non-authenticated responses
236 // authenticated_size is now a minimum - packet may be larger with GPG key ID
237 if (payload_len >= authenticated_size) {
238 // Authenticated format (may have optional GPG key ID):
239 // [ephemeral:kex_size][identity:auth_size][signature:sig_size][gpg_key_id_len:1][gpg_key_id:0-16]
240 memcpy(client_ephemeral_key, payload, ctx->crypto_ctx.public_key_size);
241 memcpy(client_identity_key, payload + ctx->crypto_ctx.public_key_size, ED25519_PUBLIC_KEY_SIZE);
242 memcpy(client_signature, payload + ctx->crypto_ctx.public_key_size + ED25519_PUBLIC_KEY_SIZE,
244 client_sent_identity = true;
245 ctx->client_sent_identity = true;
246
247 // Extract GPG key ID if present
248 const char *client_gpg_key_id = NULL;
249 char gpg_key_id_buffer[41] = {0};
251 if (payload_len > gpg_offset) {
252 uint8_t gpg_key_id_len = payload[gpg_offset];
253 if (gpg_key_id_len > 0 && gpg_key_id_len <= 40 && payload_len >= gpg_offset + 1 + gpg_key_id_len) {
254 memcpy(gpg_key_id_buffer, payload + gpg_offset + 1, gpg_key_id_len);
255 gpg_key_id_buffer[gpg_key_id_len] = '\0';
256 client_gpg_key_id = gpg_key_id_buffer;
257 log_debug("Extracted client GPG key ID from KEY_EXCHANGE_RESPONSE: %s", client_gpg_key_id);
258 }
259 }
260
261 // Check if client sent a null identity key (all zeros)
262 bool client_has_null_identity = true;
263 for (size_t i = 0; i < ED25519_PUBLIC_KEY_SIZE; i++) {
264 if (client_identity_key[i] != 0) {
265 client_has_null_identity = false;
266 break;
267 }
268 }
269
270 if (client_has_null_identity) {
271 // Client has no identity key - this is allowed for servers without client authentication
272 log_debug("Client sent null identity key - no client authentication required");
273 client_sent_identity = false;
274 ctx->client_sent_identity = false;
275 log_warn("Client connected without identity authentication");
276 } else {
277 // Client has a real identity key
278 // If server didn't specify --client-keys, skip signature verification
279 // (server doesn't care about client identity verification)
280 if (!ctx->require_client_auth) {
281 log_info("Skipping client signature verification (no --client-keys specified)");
282 log_warn("Connection is encrypted but client identity is NOT verified");
283 } else {
284 // Verify client's signature
285 log_debug("Verifying client's signature");
286 // Pass client's GPG key ID if available
287 if (ed25519_verify_signature(client_identity_key, client_ephemeral_key, ctx->crypto_ctx.public_key_size,
288 client_signature, client_gpg_key_id) != 0) {
289 // Send AUTH_FAILED with specific reason
290 auth_failure_packet_t failure = {0};
292 int send_result =
293 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_FAILED, &failure, sizeof(failure), 0);
294 if (send_result != 0) {
295 return SET_ERRNO(ERROR_NETWORK, "Failed to send AUTH_FAILED packet");
296 }
297
298 return SET_ERRNO(ERROR_CRYPTO, "Client signature verification FAILED - rejecting connection");
299 }
300 }
301 }
302
303 // Store the verified client identity for whitelist checking (only if client has identity)
304 if (client_sent_identity) {
306 memcpy(ctx->client_ed25519_key.key, client_identity_key, ctx->crypto_ctx.public_key_size);
307 }
308 } else if (payload_len == simple_size) {
309 // Non-authenticated format: [ephemeral:public_key_size] only
310 log_debug("Client sent non-authenticated response (%zu bytes)", payload_len);
311 memcpy(client_ephemeral_key, payload, ctx->crypto_ctx.public_key_size);
312 client_sent_identity = false;
313 ctx->client_sent_identity = false;
314 log_warn("Client connected without identity authentication");
315 } else {
316 // Payload ownership remains with caller
317 SAFE_FREE(client_ephemeral_key);
318 SAFE_FREE(client_identity_key);
319 SAFE_FREE(client_signature);
321 "Invalid client key response size: %zu bytes (expected %zu for "
322 "authenticated or %zu for simple)",
323 payload_len, authenticated_size, simple_size);
324 }
325
326 // Extract pointers for compatibility with existing code
327 const uint8_t *client_x25519 = client_ephemeral_key;
328 const uint8_t *client_ed25519 = client_sent_identity ? client_identity_key : NULL;
329
330 // Check client Ed25519 key against whitelist if client provided one and
331 // whitelist is enabled
332 if (client_sent_identity && ctx->require_client_auth && ctx->client_whitelist && ctx->num_whitelisted_clients > 0) {
333 bool key_found = false;
334
335 // Debug: print client's Ed25519 identity key
336 char client_ed25519_hex[HEX_STRING_SIZE_32];
337 for (int i = 0; i < ED25519_PUBLIC_KEY_SIZE; i++) {
338 safe_snprintf(client_ed25519_hex + i * 2, 3, "%02x", client_ed25519[i]);
339 }
340 log_debug("Client Ed25519 identity key: %s", client_ed25519_hex);
341
342 // Compare against whitelist (direct Ed25519 comparison - no conversion!)
343 for (size_t i = 0; i < ctx->num_whitelisted_clients; i++) {
344 // Debug: print whitelist Ed25519 key
345 char whitelist_ed25519_hex[HEX_STRING_SIZE_32];
346 for (int j = 0; j < ED25519_PUBLIC_KEY_SIZE; j++) {
347 safe_snprintf(whitelist_ed25519_hex + j * 2, 3, "%02x", ctx->client_whitelist[i].key[j]);
348 }
349 log_debug("Whitelist[%zu] Ed25519 key: %s", i, whitelist_ed25519_hex);
350
351 // Direct comparison of Ed25519 keys (constant-time to prevent timing
352 // attacks)
353 if (sodium_memcmp(client_ed25519, ctx->client_whitelist[i].key, ED25519_PUBLIC_KEY_SIZE) == 0) {
354 key_found = true;
355 ctx->client_ed25519_key_verified = true;
356
357 // Store the client's Ed25519 key for signature verification
358 memcpy(&ctx->client_ed25519_key, &ctx->client_whitelist[i], sizeof(public_key_t));
359
360 log_debug("Client Ed25519 key authorized (whitelist entry %zu)", i);
361 if (strlen(ctx->client_whitelist[i].comment) > 0) {
362 log_info("Client identity: %s", ctx->client_whitelist[i].comment);
363 }
364 break;
365 }
366 }
367
368 if (!key_found) {
369 SET_ERRNO(ERROR_CRYPTO_AUTH, "Client Ed25519 key not in whitelist - rejecting connection");
370 // Don't send AUTH_FAILED here - wait until server_complete
371 // Just mark that the key was rejected
372 ctx->client_ed25519_key_verified = false;
373 }
374 } else if (client_sent_identity) {
375 // No whitelist checking - just store the client's Ed25519 key for later
376 // (Already stored at line 313-314, but keep this for clarity)
377 ctx->client_ed25519_key_verified = false;
378 }
379
380 // Set peer's X25519 encryption key - this also derives the shared secret
381 crypto_result_t crypto_result = crypto_set_peer_public_key(&ctx->crypto_ctx, client_x25519);
382 if (crypto_result != CRYPTO_OK) {
383 return SET_ERRNO(ERROR_CRYPTO, "Failed to set peer public key and derive shared secret: %s",
384 crypto_result_to_string(crypto_result));
385 }
386
387 // Clean up allocated memory
388 SAFE_FREE(client_ephemeral_key);
389 SAFE_FREE(client_identity_key);
390 SAFE_FREE(client_signature);
391
392 // Do authentication challenge if client provided identity key OR server
393 // requires password
394 if (client_sent_identity || ctx->crypto_ctx.has_password || ctx->require_client_auth) {
395 // Generate nonce and store it in the context
396 crypto_result = crypto_generate_nonce(ctx->crypto_ctx.auth_nonce);
397 if (crypto_result != CRYPTO_OK) {
398 return SET_ERRNO(ERROR_CRYPTO, "Failed to generate nonce: %s", crypto_result_to_string(crypto_result));
399 }
400
401 // Prepare AUTH_CHALLENGE packet: 1 byte flags + auth_challenge_size byte nonce
402 size_t challenge_packet_size = AUTH_CHALLENGE_FLAGS_SIZE + ctx->crypto_ctx.auth_challenge_size;
403 uint8_t challenge_packet[1 + HMAC_SHA256_SIZE]; // Maximum size buffer
404 uint8_t auth_flags = 0;
405
406 // Set flags based on server requirements
407 if (ctx->crypto_ctx.has_password) {
408 auth_flags |= AUTH_REQUIRE_PASSWORD;
409 }
410 if (ctx->require_client_auth) {
411 auth_flags |= AUTH_REQUIRE_CLIENT_KEY;
412 }
413
414 challenge_packet[0] = auth_flags;
415 memcpy(challenge_packet + 1, ctx->crypto_ctx.auth_nonce, ctx->crypto_ctx.auth_challenge_size);
416
417 // Send AUTH_CHALLENGE with flags + nonce (challenge_packet_size bytes)
418 result = packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, challenge_packet,
419 challenge_packet_size, 0);
420 if (result != 0) {
421 return SET_ERRNO(ERROR_NETWORK, "Failed to send AUTH_CHALLENGE packet");
422 }
423
425 } else {
426 // No authentication needed - skip to completion
427 log_debug("Skipping authentication (no password and client has no identity key)");
428
429 // Send HANDSHAKE_COMPLETE immediately
431 if (result != 0) {
432 return SET_ERRNO(ERROR_NETWORK, "Failed to send HANDSHAKE_COMPLETE packet");
433 }
434
436 ctx->crypto_ctx.handshake_complete = true; // Mark crypto context as ready for rekeying
437 log_debug("Crypto handshake completed successfully (no authentication)");
438 }
439
440 return ASCIICHAT_OK;
441}
442// Server: Process auth response and complete handshake
444 packet_type_t packet_type, const uint8_t *payload,
445 size_t payload_len) {
446 if (!ctx || ctx->state != CRYPTO_HANDSHAKE_AUTHENTICATING) {
447 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, state=%d", ctx, ctx ? ctx->state : -1);
448 }
449 if (!transport) {
450 return SET_ERRNO(ERROR_INVALID_PARAM, "transport is NULL");
451 }
452
453 // Note: Packet already received by ACIP handler
454 int result;
455
456 // Verify packet type
457 if (packet_type != PACKET_TYPE_CRYPTO_AUTH_RESPONSE) {
458 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Expected AUTH_RESPONSE, got packet type %d", packet_type);
459 }
460
461 // Verify password if required
462 if (ctx->crypto_ctx.has_password) {
463 // Validate packet size using session parameters
464 asciichat_error_t validation_result =
466 if (validation_result != ASCIICHAT_OK) {
467 return validation_result;
468 }
469
470 // Ensure shared secret is derived before password verification
471 // This is critical for password HMAC verification which binds to the shared secret
473 SET_ERRNO(ERROR_CRYPTO, "Password authentication failed - key exchange not complete");
474
475 // Send AUTH_FAILED with specific reason
476 auth_failure_packet_t failure = {0};
478 if (ctx->require_client_auth) {
480 }
481 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_FAILED, &failure, sizeof(failure), 0);
482 return ERROR_NETWORK;
483 }
484
485 // Verify password HMAC (binds to DH shared_secret to prevent MITM)
486 log_debug("Verifying password HMAC: has_password=%d, key_exchange_complete=%d", ctx->crypto_ctx.has_password,
488 if (!payload) {
489 SET_ERRNO(ERROR_INVALID_PARAM, "Payload is NULL in password authentication");
490 return ERROR_CRYPTO;
491 }
492 if (!crypto_verify_auth_response(&ctx->crypto_ctx, ctx->crypto_ctx.auth_nonce, payload)) {
493 log_debug("Password HMAC verification failed");
494 // Enhanced error message when both password and whitelist are required
495 if (ctx->require_client_auth) {
497 "Password authentication failed - incorrect password (server also requires whitelisted client key)");
498 } else {
499 SET_ERRNO(ERROR_CRYPTO, "Password authentication failed - incorrect password");
500 }
501
502 // Send AUTH_FAILED with specific reason
503 auth_failure_packet_t failure = {0};
505 if (ctx->require_client_auth) {
507 }
508 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_FAILED, &failure, sizeof(failure), 0);
509 return ERROR_NETWORK;
510 }
511
512 // Extract client challenge nonce for mutual authentication
513 // Use ctx->crypto_ctx.hmac_size and ctx->crypto_ctx.auth_challenge_size (negotiated during handshake)
515 log_info("Password authentication successful");
516 } else {
517 // Ed25519 signature auth (payload format: signature + client_nonce + gpg_key_id_len + gpg_key_id)
518 size_t expected_min_signature_size =
519 ctx->crypto_ctx.signature_size + ctx->crypto_ctx.auth_challenge_size + 1; // +1 for gpg_key_id_len byte
520 size_t expected_password_size = ctx->crypto_ctx.hmac_size + ctx->crypto_ctx.auth_challenge_size;
521 if (!payload) {
522 SET_ERRNO(ERROR_INVALID_PARAM, "Payload is NULL in signature authentication");
523 return ERROR_CRYPTO;
524 }
525 if (payload_len >= expected_min_signature_size) {
526 // Signature + client nonce + optional GPG key ID - VERIFY the signature on the challenge nonce
527 const uint8_t *signature = payload;
528 const uint8_t *client_nonce = payload + ctx->crypto_ctx.signature_size;
529
530 // Extract GPG key ID if present
531 size_t gpg_offset = ctx->crypto_ctx.signature_size + ctx->crypto_ctx.auth_challenge_size;
532 uint8_t gpg_key_id_len = payload[gpg_offset];
533 const char *client_gpg_key_id = NULL;
534 char gpg_key_id_buffer[41] = {0};
535
536 if (gpg_key_id_len > 0 && gpg_key_id_len <= 40) {
537 // Validate packet has enough bytes for GPG key ID
538 if (payload_len >= gpg_offset + 1 + gpg_key_id_len) {
539 memcpy(gpg_key_id_buffer, payload + gpg_offset + 1, gpg_key_id_len);
540 gpg_key_id_buffer[gpg_key_id_len] = '\0';
541 client_gpg_key_id = gpg_key_id_buffer;
542 log_debug("Extracted client GPG key ID from AUTH_RESPONSE: %s", client_gpg_key_id);
543 }
544 }
545
546 // Actually verify the Ed25519/GPG signature on the challenge nonce
547 // This was missing, allowing authentication bypass
549 // Use unified verification function that handles both Ed25519 and GPG keys
550 // This matches the KEY_EXCHANGE verification path
551 log_debug("Verifying %s signature on challenge nonce",
552 ctx->client_ed25519_key.type == KEY_TYPE_GPG ? "GPG" : "Ed25519");
553
556 client_gpg_key_id // GPG key ID for fallback verification
557 );
558
559 if (verify_result != ASCIICHAT_OK) {
560 auth_failure_packet_t failure = {0};
562 SET_ERRNO(ERROR_CRYPTO_AUTH, "%s signature verification failed on challenge nonce",
563 ctx->client_ed25519_key.type == KEY_TYPE_GPG ? "GPG" : "Ed25519");
564 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_FAILED, &failure, sizeof(failure), 0);
565 return ERROR_CRYPTO_AUTH;
566 }
567 log_debug("%s signature on challenge nonce verified successfully",
568 ctx->client_ed25519_key.type == KEY_TYPE_GPG ? "GPG" : "Ed25519");
569 }
570
571 memcpy(ctx->client_challenge_nonce, client_nonce, ctx->crypto_ctx.auth_challenge_size);
572 } else if (payload_len == expected_password_size) {
573 // Just client nonce (legacy or password-only mode without password enabled)
574 // Use ctx->crypto_ctx.hmac_size and ctx->crypto_ctx.auth_challenge_size (negotiated during handshake)
576 } else {
577 // Validate packet size using session parameters
578 asciichat_error_t validation_result =
580 if (validation_result != ASCIICHAT_OK) {
581 return validation_result;
582 }
583 }
584 }
585
586 // Verify client key if required (whitelist)
587 if (ctx->require_client_auth) {
588 if (!ctx->client_ed25519_key_verified) {
589 // Send AUTH_FAILED with specific reason
590 auth_failure_packet_t failure = {0};
591
592 // Check if client provided a key but it wasn't in whitelist
593 if (ctx->client_sent_identity) {
594 SET_ERRNO(ERROR_CRYPTO_AUTH, "Client key authentication failed - your key is not in the server's whitelist");
596 } else {
597 SET_ERRNO(ERROR_CRYPTO_AUTH, "Client key authentication failed - client did not provide a key");
599 }
600
601 if (ctx->crypto_ctx.has_password) {
602 // Password was verified, but key was not
603 SET_ERRNO(ERROR_CRYPTO_AUTH, "Note: Password was correct, but client key is required");
604 }
605 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_AUTH_FAILED, &failure, sizeof(failure), 0);
606 return ERROR_NETWORK;
607 }
608 log_info("Client key authentication successful (whitelist verified)");
609 if (strlen(ctx->client_ed25519_key.comment) > 0) {
610 log_info("Authenticated client: %s", ctx->client_ed25519_key.comment);
611 }
612 }
613
614 // Send SERVER_AUTH_RESPONSE with server's HMAC for mutual authentication
615 // Bind to DH shared_secret to prevent MITM (even if attacker knows password)
616 // Allocate buffer using negotiated hmac_size (should match AUTH_HMAC_SIZE)
617 uint8_t server_hmac[HMAC_SHA256_SIZE]; // Maximum size, actual size is ctx->hmac_size
618 crypto_result_t crypto_result =
620 if (crypto_result != CRYPTO_OK) {
621 SET_ERRNO(ERROR_CRYPTO, "Failed to compute server HMAC for mutual authentication: %s",
622 crypto_result_to_string(crypto_result));
623 return ERROR_NETWORK;
624 }
625
626 log_debug("Sending SERVER_AUTH_RESPONSE packet with server HMAC (%u bytes) "
627 "for mutual authentication",
628 ctx->crypto_ctx.hmac_size);
629 result = packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, server_hmac,
630 ctx->crypto_ctx.hmac_size, 0);
631 if (result != ASCIICHAT_OK) {
632 SET_ERRNO(ERROR_NETWORK, "Failed to send SERVER_AUTH_RESPONSE packet");
633 return ERROR_NETWORK;
634 }
635
637 log_debug("Crypto handshake completed successfully (mutual authentication)");
638
639 return ASCIICHAT_OK;
640}
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
⚙️ Common definitions, error codes, macros, and types shared throughout the application
asciichat_error_t crypto_handshake_validate_packet_size(const crypto_handshake_context_t *ctx, uint16_t packet_type, size_t packet_size)
Validate crypto packet size based on session parameters.
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 AUTH_REQUIRE_PASSWORD
Server requires password authentication.
#define AUTH_REQUIRE_CLIENT_KEY
Server requires client key authentication (whitelist)
GPG operations - main header.
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
crypto_result_t crypto_set_peer_public_key(crypto_context_t *ctx, const uint8_t *peer_public_key)
Set peer's public key and compute shared secret (step 2 of handshake)
#define ED25519_SIGNATURE_SIZE
Ed25519 signature size in bytes.
crypto_result_t crypto_compute_auth_response(const crypto_context_t *ctx, const uint8_t nonce[32], uint8_t hmac_out[32])
Compute authentication response HMAC bound to DH shared_secret.
#define HMAC_SHA256_SIZE
HMAC-SHA256 output size in bytes.
crypto_result_t crypto_generate_nonce(uint8_t nonce[32])
Generate random nonce for authentication.
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.
#define CRYPTO_SHARED_KEY_SIZE
Shared key size (X25519)
#define XSALSA20_NONCE_SIZE
XSalsa20 nonce size in bytes.
#define HEX_STRING_SIZE_32
Hex string size for 32-byte values (64 hex chars + null terminator)
#define AUTH_CHALLENGE_FLAGS_SIZE
Authentication flags size (1 byte)
bool crypto_verify_auth_response(const crypto_context_t *ctx, const uint8_t nonce[32], const uint8_t expected_hmac[32])
Verify authentication response HMAC bound to DH shared_secret.
#define POLY1305_MAC_SIZE
Poly1305 MAC size in bytes.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:77
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CRYPTO_AUTH
Definition error_codes.h:98
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM
@ CRYPTO_HANDSHAKE_AUTHENTICATING
@ CRYPTO_HANDSHAKE_INIT
@ CRYPTO_HANDSHAKE_KEY_EXCHANGE
@ CRYPTO_HANDSHAKE_READY
uint8_t key[32]
Definition key_types.h:71
asciichat_error_t ed25519_verify_signature(const uint8_t public_key[32], const uint8_t *message, size_t message_len, const uint8_t signature[64], const char *gpg_key_id)
Verify an Ed25519 signature.
Definition ssh_keys.c:1126
char comment[256]
Definition key_types.h:72
key_type_t type
Definition key_types.h:92
uint8_t public_key[32]
Definition key_types.h:99
asciichat_error_t ed25519_sign_message(const private_key_t *key, const uint8_t *message, size_t message_len, uint8_t signature[64])
Sign a message with Ed25519 (uses SSH agent if available, otherwise in-memory key)
Definition ssh_keys.c:1030
key_type_t type
Definition key_types.h:70
@ KEY_TYPE_ED25519
Definition key_types.h:52
@ KEY_TYPE_GPG
Definition key_types.h:54
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#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 AUTH_ALGO_ED25519
Ed25519 authentication (Edwards-curve signatures)
Definition packet.h:1065
#define KEX_ALGO_X25519
X25519 key exchange (Curve25519)
Definition packet.h:1064
#define AUTH_ALGO_NONE
No authentication (plaintext mode)
Definition packet.h:1066
#define CIPHER_ALGO_XSALSA20_POLY1305
XSalsa20-Poly1305 authenticated encryption.
Definition packet.h:1067
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
@ AUTH_FAIL_PASSWORD_INCORRECT
Password verification failed (incorrect password)
Definition packet.h:765
@ AUTH_FAIL_CLIENT_KEY_REQUIRED
Server requires client key but client didn't provide one.
Definition packet.h:767
@ AUTH_FAIL_CLIENT_KEY_REJECTED
Client key not in whitelist (access denied)
Definition packet.h:769
@ AUTH_FAIL_SIGNATURE_INVALID
Client signature verification failed (invalid signature)
Definition packet.h:771
@ PACKET_TYPE_CRYPTO_AUTH_RESPONSE
Client -> Server: {HMAC[32]} (UNENCRYPTED)
Definition packet.h:317
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED)
Definition packet.h:323
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED)
Definition packet.h:311
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP
Client -> Server: {client_pubkey[32]} (UNENCRYPTED)
Definition packet.h:313
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED)
Definition packet.h:319
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED)
Definition packet.h:321
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED)
Definition packet.h:325
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED)
Definition packet.h:315
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED)
Definition packet.h:309
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
Server-side handshake functions.
asciichat_error_t crypto_handshake_server_send_parameters(crypto_handshake_context_t *ctx, acip_transport_t *transport)
Server: Send CRYPTO_PARAMETERS and set handshake context sizes.
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.
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41
ACIP shared/bidirectional packet sending functions.
Transport instance structure.
Definition transport.h:214
Authentication failure packet structure.
Definition packet.h:782
uint8_t reason_flags
Bitmask of auth_failure_reason_t values indicating failure causes.
Definition packet.h:784
Cryptographic handshake 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
Public key structure.
Definition key_types.h:69
Transport abstraction layer for ACIP protocol.