ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
crypto/handshake/common.c
Go to the documentation of this file.
1
7#include <ascii-chat/common.h>
16#include <stdio.h>
17#include <string.h>
18
19// Initialize named crypto handshake context
21 if (!name) {
22 return SET_ERRNO(ERROR_INVALID_PARAM, "Crypto context name is required");
23 }
24
25 if (!ctx) {
26 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p", ctx);
27 }
28
29 // Register crypto context with debug naming system
30 NAMED_REGISTER_CRYPTO_CONTEXT(ctx, name, NULL);
31
32 // Zero out the context
33 memset(ctx, 0, sizeof(crypto_handshake_context_t));
34
35 // Initialize core crypto context
36#ifndef NDEBUG
37 log_info("★★★ BEFORE crypto_init()");
39#endif
41#ifndef NDEBUG
42 log_info("★★★ AFTER crypto_init(), result=%d", result);
44#endif
45 if (result != CRYPTO_OK) {
46 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize crypto context: %s", crypto_result_to_string(result));
47 }
48
50 ctx->is_server = is_server;
51 ctx->verify_server_key = false;
52 ctx->require_client_auth = false;
53 ctx->server_uses_client_auth = false; // Set to true only if authenticated packet received
54
55 // Load server keys if this is a server
56 if (is_server) {
57 log_dev("Server crypto handshake initialized (ephemeral keys)");
58 } else {
59 log_dev("Client crypto handshake initialized");
60 }
61
62 return ASCIICHAT_OK;
63}
64
65// Set crypto parameters from crypto_parameters_packet_t
67 const crypto_parameters_packet_t *params) {
68 if (!ctx || !params) {
69 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, params=%p", ctx, params);
70 }
71
72 // Client receives network byte order and must convert
73 // Server uses host byte order and must NOT convert
74 if (ctx->is_server) {
75 // Server: values are already in host byte order
76 // Update crypto context with negotiated parameters directly
81 } else {
82 // Client: convert from network byte order
83 // Update crypto context with negotiated parameters directly
88 }
89 // Update crypto context with negotiated parameters directly
90 ctx->crypto_ctx.nonce_size = params->nonce_size;
91 ctx->crypto_ctx.mac_size = params->mac_size;
92 ctx->crypto_ctx.hmac_size = params->hmac_size;
94 AUTH_CHALLENGE_SIZE; // Auth challenge size is fixed for now, could be negotiated later
96 (uint8_t)ctx->crypto_ctx.shared_key_size; // Use shared key size as encryption key size
97 ctx->crypto_ctx.private_key_size = ctx->crypto_ctx.public_key_size; // Same as public key for X25519
98 ctx->crypto_ctx.salt_size = ARGON2ID_SALT_SIZE; // Salt size doesn't change
99
100 log_debug("Crypto parameters set: kex_key=%u, auth_key=%u, sig=%u, "
101 "secret=%u, nonce=%u, mac=%u, hmac=%u",
104 ctx->crypto_ctx.hmac_size);
105
106 return ASCIICHAT_OK;
107}
108
109// Validate crypto packet size based on session parameters
111 size_t packet_size) {
112 if (!ctx) {
113 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p", ctx);
114 }
115
116 switch (packet_type) {
118 if (packet_size != sizeof(crypto_capabilities_packet_t)) {
119 // Don't return an error code, just set the errno and return the error code
120 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid crypto capabilities packet size: %zu (expected %zu)",
121 packet_size, sizeof(crypto_capabilities_packet_t));
122 }
123 break;
124
126 if (packet_size != sizeof(crypto_parameters_packet_t)) {
127 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid crypto parameters packet size: %zu (expected %zu)", packet_size,
129 }
130 break;
131
133 // Server can send either:
134 // 1. Simple format: kex_public_key_size (when server has no identity key)
135 // 2. Authenticated format: kex_public_key_size + auth_public_key_size + signature_size
136 {
137 size_t simple_size = ctx->crypto_ctx.public_key_size;
138 size_t authenticated_size =
140
141 if (packet_size != simple_size && packet_size != authenticated_size) {
143 "Invalid KEY_EXCHANGE_INIT size: %zu (expected %zu for simple or %zu for authenticated: "
144 "kex=%u + auth=%u + sig=%u)",
145 packet_size, simple_size, authenticated_size, ctx->crypto_ctx.public_key_size,
147 }
148 }
149 break;
150
152 // Client can send either:
153 // 1. Simple format: kex_public_key_size (when server has no identity key)
154 // 2. Authenticated format: kex_public_key_size + client_auth_key_size + client_sig_size + [gpg_key_id_len:1] +
155 // [gpg_key_id:0-16]
156 {
157 size_t simple_size = ctx->crypto_ctx.public_key_size;
158 // For authenticated format, use Ed25519 sizes since client has Ed25519 key
159 size_t ed25519_auth_size = ED25519_PUBLIC_KEY_SIZE; // Ed25519 public key is always 32 bytes
160 size_t ed25519_sig_size = ED25519_SIGNATURE_SIZE; // Ed25519 signature is always 64 bytes
161 size_t authenticated_min_size = ctx->crypto_ctx.public_key_size + ed25519_auth_size + ed25519_sig_size;
162 size_t authenticated_max_size = authenticated_min_size + 1 + 40; // +1 for length, +40 for max GPG key ID
163
164 if (packet_size != simple_size &&
165 (packet_size < authenticated_min_size || packet_size > authenticated_max_size)) {
167 "Invalid KEY_EXCHANGE_RESP size: %zu (expected %zu for simple or %zu-%zu for authenticated: "
168 "kex=%u + auth=%u + sig=%u + optional GPG key ID)",
169 packet_size, simple_size, authenticated_min_size, authenticated_max_size,
170 ctx->crypto_ctx.public_key_size, ed25519_auth_size, ed25519_sig_size);
171 }
172 }
173 break;
174
176 // Server sends: 1 byte auth_flags + auth_challenge_size byte nonce
177 {
178 size_t expected_size = AUTH_CHALLENGE_FLAGS_SIZE + ctx->crypto_ctx.auth_challenge_size;
179 if (packet_size != expected_size) {
180 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid AUTH_CHALLENGE size: %zu (expected %zu: flags=%d + nonce=%u)",
181 packet_size, expected_size, AUTH_CHALLENGE_FLAGS_SIZE, ctx->crypto_ctx.auth_challenge_size);
182 }
183 }
184 break;
185
187 // Client sends: hmac_size + auth_challenge_size bytes client_nonce + [gpg_key_id_len:1] + [gpg_key_id:0-40]
188 {
189 size_t min_size = ctx->crypto_ctx.hmac_size + ctx->crypto_ctx.auth_challenge_size;
190 size_t max_size = min_size + 1 + 40; // +1 for length, +40 for max GPG key ID
191 if (packet_size < min_size || packet_size > max_size) {
193 "Invalid AUTH_RESPONSE size: %zu (expected %zu-%zu: hmac=%u + "
194 "nonce=%u + optional GPG key ID)",
195 packet_size, min_size, max_size, ctx->crypto_ctx.hmac_size,
197 }
198 }
199 break;
200
202 // Variable size - just check reasonable limits
203 if (packet_size > MAX_AUTH_FAILED_PACKET_SIZE) {
204 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid AUTH_FAILED size: %zu (max %d)", packet_size,
206 }
207 break;
208
210 // Server sends: hmac_size bytes
211 if (packet_size != ctx->crypto_ctx.hmac_size) {
212 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid SERVER_AUTH_RESP size: %zu (expected %u)", packet_size,
213 ctx->crypto_ctx.hmac_size);
214 }
215 break;
216
218 // Empty packet
219 if (packet_size != 0) {
220 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid HANDSHAKE_COMPLETE size: %zu (expected 0)", packet_size);
221 }
222 break;
223
225 // Empty packet
226 if (packet_size != 0) {
227 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid NO_ENCRYPTION size: %zu (expected 0)", packet_size);
228 }
229 break;
230
232 // Variable size - check reasonable limits
233 if (packet_size > MAX_ENCRYPTED_PACKET_SIZE) { // 64KB max for encrypted packets
234 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid ENCRYPTED size: %zu (max %d)", packet_size,
236 }
237 break;
238
239 default:
240 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unknown crypto packet type: %u", packet_type);
241 }
242
243 return ASCIICHAT_OK;
244}
245
246// Initialize named crypto handshake context with password authentication
248 const char *password) {
249 if (!name) {
250 return SET_ERRNO(ERROR_INVALID_PARAM, "Crypto context name is required");
251 }
252
253 if (!ctx || !password) {
254 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: ctx=%p, password=%p", ctx, password);
255 }
256
257 // Register crypto context with debug naming system
258 NAMED_REGISTER_CRYPTO_CONTEXT(ctx, name, NULL);
259
260 // Zero out the context
261 memset(ctx, 0, sizeof(crypto_handshake_context_t));
262
263 // Initialize core crypto context with password
264 crypto_result_t result = crypto_init_with_password(&ctx->crypto_ctx, password);
265 if (result != CRYPTO_OK) {
266 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize crypto context with password: %s",
268 }
269
271 ctx->is_server = is_server;
272 ctx->verify_server_key = false;
273 ctx->require_client_auth = false;
274 ctx->server_uses_client_auth = false; // Set to true only if authenticated packet received
275 ctx->has_password = true;
276
277 // Store password temporarily (will be cleared after key derivation)
278 SAFE_STRNCPY(ctx->password, password, sizeof(ctx->password) - 1);
279
280 return ASCIICHAT_OK;
281}
282
283// Cleanup crypto handshake context
285 if (!ctx)
286 return;
287
288 // Cleanup core crypto context
290
291 // Zero out sensitive data
292 sodium_memzero(ctx, sizeof(crypto_handshake_context_t));
293}
294
295// Check if handshake is complete and encryption is ready
297 if (!ctx)
298 return false;
300}
301
302// Get the crypto context for encryption/decryption
304 if (!ctx || !crypto_handshake_is_ready(ctx))
305 return NULL;
306 return &ctx->crypto_ctx;
307}
308
309// Encrypt a packet using the established crypto context
311 size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size,
312 size_t *ciphertext_len) {
313 if (!ctx || !crypto_handshake_is_ready(ctx)) {
314 SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, ready=%d", ctx, ctx ? crypto_handshake_is_ready(ctx) : 0);
315 return ERROR_INVALID_STATE;
316 }
317
318 crypto_result_t result = crypto_encrypt((crypto_context_t *)&ctx->crypto_ctx, plaintext, plaintext_len, ciphertext,
319 ciphertext_size, ciphertext_len);
320 if (result != CRYPTO_OK) {
321 return SET_ERRNO(ERROR_NETWORK, "Failed to encrypt packet: %s", crypto_result_to_string(result));
322 }
323
324 return ASCIICHAT_OK;
325}
326
327// Decrypt a packet using the established crypto context
329 size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size,
330 size_t *plaintext_len) {
331 if (!ctx || !crypto_handshake_is_ready(ctx)) {
332 SET_ERRNO(ERROR_INVALID_STATE, "Invalid state: ctx=%p, ready=%d", ctx, ctx ? crypto_handshake_is_ready(ctx) : 0);
333 return ERROR_INVALID_STATE;
334 }
335
336 crypto_result_t result = crypto_decrypt((crypto_context_t *)&ctx->crypto_ctx, ciphertext, ciphertext_len, plaintext,
337 plaintext_size, plaintext_len);
338 if (result != CRYPTO_OK) {
339 return SET_ERRNO(ERROR_NETWORK, "Failed to decrypt packet: %s", crypto_result_to_string(result));
340 }
341
342 return ASCIICHAT_OK;
343}
344
345// Helper: Encrypt with automatic passthrough if crypto not ready
347 const uint8_t *plaintext, size_t plaintext_len,
348 uint8_t *ciphertext, size_t ciphertext_size,
349 size_t *ciphertext_len) {
350 if (!crypto_ready) {
351 // No encryption - just copy data
352 if (plaintext_len > ciphertext_size) {
353 SET_ERRNO(ERROR_BUFFER, "Plaintext too large for ciphertext buffer: %zu > %zu", plaintext_len, ciphertext_size);
354 return ERROR_BUFFER;
355 }
356 memcpy(ciphertext, plaintext, plaintext_len);
357 *ciphertext_len = plaintext_len;
358 return ASCIICHAT_OK;
359 }
360
361 return crypto_handshake_encrypt_packet(ctx, plaintext, plaintext_len, ciphertext, ciphertext_size, ciphertext_len);
362}
363
364// Helper: Decrypt with automatic passthrough if crypto not ready
366 const uint8_t *ciphertext, size_t ciphertext_len,
367 uint8_t *plaintext, size_t plaintext_size,
368 size_t *plaintext_len) {
369 if (!crypto_ready) {
370 // No encryption - just copy data
371 if (ciphertext_len > plaintext_size) {
372 SET_ERRNO(ERROR_BUFFER, "Ciphertext too large for plaintext buffer: %zu > %zu", ciphertext_len, plaintext_size);
373 return ERROR_BUFFER;
374 }
375 memcpy(plaintext, ciphertext, ciphertext_len);
376 *plaintext_len = ciphertext_len;
377 return ASCIICHAT_OK;
378 }
379
380 return crypto_handshake_decrypt_packet(ctx, ciphertext, ciphertext_len, plaintext, plaintext_size, plaintext_len);
381}
382
383// =============================================================================
384// Session Rekeying Protocol Implementation
385// =============================================================================
386
392 if (!ctx || !crypto_handshake_is_ready(ctx)) {
393 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
394 ctx ? crypto_handshake_is_ready(ctx) : 0);
395 }
396 if (!transport) {
397 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport is NULL");
398 }
399
400 // Initialize rekey process (generates new ephemeral keypair)
402 if (result != CRYPTO_OK) {
403 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize rekey: %s", crypto_result_to_string(result));
404 }
405
406 // Send REKEY_REQUEST with new ephemeral public key (32 bytes)
407 log_debug("Sending REKEY_REQUEST with new ephemeral X25519 public key (32 bytes)");
410 if (send_result != ASCIICHAT_OK) {
411 crypto_rekey_abort(&ctx->crypto_ctx); // Clean up temp keys on failure
412 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_REQUEST packet");
413 }
414
415 log_debug("REKEY_REQUEST sent successfully, awaiting REKEY_RESPONSE");
416 return ASCIICHAT_OK;
417}
418
424 if (!ctx || !crypto_handshake_is_ready(ctx)) {
425 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
426 ctx ? crypto_handshake_is_ready(ctx) : 0);
427 }
428 if (!transport) {
429 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport is NULL");
430 }
431
433 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
434 }
435
436 // Send REKEY_RESPONSE with new ephemeral public key (32 bytes)
437 log_debug("Sending REKEY_RESPONSE with new ephemeral X25519 public key (32 bytes)");
440 if (send_result != ASCIICHAT_OK) {
441 crypto_rekey_abort(&ctx->crypto_ctx); // Clean up temp keys on failure
442 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_RESPONSE packet");
443 }
444
445 log_debug("REKEY_RESPONSE sent successfully, awaiting REKEY_COMPLETE");
446 return ASCIICHAT_OK;
447}
448
455 if (!ctx || !crypto_handshake_is_ready(ctx)) {
456 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
457 ctx ? crypto_handshake_is_ready(ctx) : 0);
458 }
459 if (!transport) {
460 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport is NULL");
461 }
462
464 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
465 }
466
467 // Encrypt empty payload with NEW key to prove possession
468 uint8_t plaintext[1] = {0}; // Minimal payload
469
470 // Temporarily swap keys to encrypt with NEW key
471 uint8_t old_shared_key[CRYPTO_SHARED_KEY_SIZE];
472 memcpy(old_shared_key, ctx->crypto_ctx.shared_key, CRYPTO_SHARED_KEY_SIZE);
474
475 // Restore old key immediately after the transport send completes.
476 // The transport encrypts the packet using the active shared key, so we
477 // keep the temporary key installed only for the duration of this send.
478 asciichat_error_t send_result =
479 packet_send_via_transport(transport, PACKET_TYPE_CRYPTO_REKEY_COMPLETE, plaintext, sizeof(plaintext), 0);
480
481 memcpy(ctx->crypto_ctx.shared_key, old_shared_key, CRYPTO_SHARED_KEY_SIZE);
482 sodium_memzero(old_shared_key, sizeof(old_shared_key));
483
484 if (send_result != ASCIICHAT_OK) {
486 return SET_ERRNO(ERROR_NETWORK, "Failed to send REKEY_COMPLETE packet");
487 }
488
489 // Commit to new key (atomic switch)
490 crypto_result_t commit_result = crypto_rekey_commit(&ctx->crypto_ctx);
491 if (commit_result != CRYPTO_OK) {
492 return SET_ERRNO(ERROR_CRYPTO, "Failed to commit rekey: %s", crypto_result_to_string(commit_result));
493 }
494
495 log_debug("Session rekeying completed successfully (initiator side)");
496 return ASCIICHAT_OK;
497}
498
504 size_t packet_len) {
505 if (!ctx || !crypto_handshake_is_ready(ctx)) {
506 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
507 ctx ? crypto_handshake_is_ready(ctx) : 0);
508 }
509
510 // DDoS PROTECTION: Rate limit rekey requests
511 time_t now = time(NULL);
512 if (ctx->crypto_ctx.rekey_last_request_time > 0) {
513 time_t elapsed = now - ctx->crypto_ctx.rekey_last_request_time;
514 time_t min_request_interval_seconds = (time_t)(REKEY_MIN_REQUEST_INTERVAL / NS_PER_SEC_INT);
515 if (elapsed < min_request_interval_seconds) {
516 return SET_ERRNO(ERROR_CRYPTO,
517 "SECURITY: Rekey request rejected - too frequent (%ld sec since last, minimum %ld sec required)",
518 (long)elapsed, (long)min_request_interval_seconds);
519 }
520 }
521
522 // Update last request time
524
525 // Validate packet size (should be 32 bytes for X25519 public key)
526 if (packet_len != CRYPTO_PUBLIC_KEY_SIZE) {
527 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid REKEY_REQUEST packet size: %zu (expected %d)", packet_len,
529 }
530
531 log_debug("Received REKEY_REQUEST with peer's new ephemeral public key (32 bytes)");
532
533 // Initialize our rekey process (generates our new ephemeral keypair)
535 if (result != CRYPTO_OK) {
536 return SET_ERRNO(ERROR_CRYPTO, "Failed to initialize rekey: %s", crypto_result_to_string(result));
537 }
538
539 // Process peer's public key and compute new shared secret
540 result = crypto_rekey_process_request(&ctx->crypto_ctx, packet);
541 if (result != CRYPTO_OK) {
543 return SET_ERRNO(ERROR_CRYPTO, "Failed to process REKEY_REQUEST: %s", crypto_result_to_string(result));
544 }
545
546 log_debug("REKEY_REQUEST processed successfully, new shared secret computed (responder side)");
547 return ASCIICHAT_OK;
548}
549
555 size_t packet_len) {
556 if (!ctx || !crypto_handshake_is_ready(ctx)) {
557 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
558 ctx ? crypto_handshake_is_ready(ctx) : 0);
559 }
560
561 // Validate packet size (should be 32 bytes for X25519 public key)
562 if (packet_len != CRYPTO_PUBLIC_KEY_SIZE) {
563 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid REKEY_RESPONSE packet size: %zu (expected %d)", packet_len,
565 }
566
568 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
569 }
570
571 log_debug("Received REKEY_RESPONSE with peer's new ephemeral public key (32 bytes)");
572
573 // Process peer's public key and compute new shared secret
575 if (result != CRYPTO_OK) {
577 return SET_ERRNO(ERROR_CRYPTO, "Failed to process REKEY_RESPONSE: %s", crypto_result_to_string(result));
578 }
579
580 log_debug("REKEY_RESPONSE processed successfully, new shared secret computed (initiator side)");
581 return ASCIICHAT_OK;
582}
583
590 size_t packet_len) {
591 if (!ctx || !crypto_handshake_is_ready(ctx)) {
592 return SET_ERRNO(ERROR_INVALID_STATE, "Handshake not ready for rekeying: ctx=%p, ready=%d", ctx,
593 ctx ? crypto_handshake_is_ready(ctx) : 0);
594 }
595
597 return SET_ERRNO(ERROR_INVALID_STATE, "No rekey in progress or temp key missing");
598 }
599
600 log_debug("Received REKEY_COMPLETE packet (%zu bytes), verifying with NEW key", packet_len);
601
602 // Temporarily swap keys to decrypt with NEW key
603 uint8_t old_shared_key[CRYPTO_SHARED_KEY_SIZE];
604 memcpy(old_shared_key, ctx->crypto_ctx.shared_key, CRYPTO_SHARED_KEY_SIZE);
606
607 // Attempt to decrypt with NEW key
608 uint8_t plaintext[256];
609 size_t plaintext_len = 0;
610 crypto_result_t result =
611 crypto_decrypt(&ctx->crypto_ctx, packet, packet_len, plaintext, sizeof(plaintext), &plaintext_len);
612
613 // Restore old key immediately
614 memcpy(ctx->crypto_ctx.shared_key, old_shared_key, CRYPTO_SHARED_KEY_SIZE);
615 sodium_memzero(old_shared_key, sizeof(old_shared_key));
616
617 if (result != CRYPTO_OK) {
619 return SET_ERRNO(ERROR_CRYPTO, "REKEY_COMPLETE decryption failed (key mismatch): %s",
621 }
622
623 log_debug("REKEY_COMPLETE verified successfully, committing to new key");
624
625 // Commit to new key (atomic switch)
626 result = crypto_rekey_commit(&ctx->crypto_ctx);
627 if (result != CRYPTO_OK) {
628 return SET_ERRNO(ERROR_CRYPTO, "Failed to commit rekey: %s", crypto_result_to_string(result));
629 }
630
631 log_debug("Session rekeying completed successfully (responder side)");
632 return ASCIICHAT_OK;
633}
634
640 if (!ctx || !crypto_handshake_is_ready(ctx)) {
641 return false;
642 }
643 return crypto_should_rekey(&ctx->crypto_ctx);
644}
⚠️‼️ 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_rekey_complete(crypto_handshake_context_t *ctx, acip_transport_t *transport)
Send REKEY_COMPLETE packet (initiator side)
asciichat_error_t crypto_handshake_init(const char *name, crypto_handshake_context_t *ctx, bool is_server)
Initialize named crypto handshake context.
asciichat_error_t crypto_handshake_rekey_response(crypto_handshake_context_t *ctx, acip_transport_t *transport)
Send REKEY_RESPONSE packet (responder 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_init_with_password(const char *name, crypto_handshake_context_t *ctx, bool is_server, const char *password)
Initialize named crypto handshake context with password authentication.
void crypto_handshake_destroy(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
asciichat_error_t crypto_handshake_decrypt_packet(const crypto_handshake_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_len)
Decrypt a packet using the established crypto context.
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_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_encrypt_packet(const crypto_handshake_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_len)
Encrypt a packet using the established crypto context.
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_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_process_rekey_complete(crypto_handshake_context_t *ctx, const uint8_t *packet, size_t packet_len)
Process received REKEY_COMPLETE packet (responder side)
asciichat_error_t crypto_handshake_rekey_request(crypto_handshake_context_t *ctx, acip_transport_t *transport)
Send REKEY_REQUEST packet (initiator side)
Common declarations and data structures for cryptographic handshake.
Named object registry for debugging — log identifiable resource names.
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U16(val)
Definition endian.h:111
unsigned short uint16_t
Definition common.h:57
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
unsigned char uint8_t
Definition common.h:56
crypto_result_t crypto_rekey_commit(crypto_context_t *ctx)
Commit to new keys after successful REKEY_COMPLETE.
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)
Encrypt data using XSalsa20-Poly1305.
#define ED25519_SIGNATURE_SIZE
Ed25519 signature size in bytes.
#define AUTH_CHALLENGE_SIZE
Challenge nonce size (32 bytes)
crypto_result_t crypto_init(crypto_context_t *ctx)
Initialize libsodium and crypto context.
#define MAX_AUTH_FAILED_PACKET_SIZE
Maximum AUTH_FAILED packet size (256 bytes)
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 MAX_ENCRYPTED_PACKET_SIZE
Maximum encrypted packet size (64KB)
#define ED25519_PUBLIC_KEY_SIZE
Ed25519 public key size in bytes.
#define CRYPTO_SHARED_KEY_SIZE
Shared key size (X25519)
bool crypto_should_rekey(const crypto_context_t *ctx)
Check if rekeying should be triggered based on time or packet count thresholds.
void crypto_rekey_abort(crypto_context_t *ctx)
Abort rekeying and fallback to old keys.
bool crypto_is_ready(const crypto_context_t *ctx)
Check if key exchange is complete and ready for encryption.
crypto_result_t crypto_rekey_process_request(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
Process REKEY_REQUEST from peer (responder side)
#define CRYPTO_PUBLIC_KEY_SIZE
Public key size (X25519)
#define REKEY_MIN_REQUEST_INTERVAL
Minimum interval between rekey requests (60 seconds in nanoseconds, DDoS protection)
#define ARGON2ID_SALT_SIZE
Argon2id salt size in bytes.
crypto_result_t crypto_decrypt(crypto_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext_out, size_t plaintext_out_size, size_t *plaintext_len_out)
Decrypt data using XSalsa20-Poly1305.
crypto_result_t crypto_init_with_password(crypto_context_t *ctx, const char *password)
Initialize with password-based encryption.
void crypto_destroy(crypto_context_t *ctx)
Cleanup crypto context with secure memory wiping.
crypto_result_t crypto_rekey_init(crypto_context_t *ctx)
Initiate rekeying by generating new ephemeral keys.
#define AUTH_CHALLENGE_FLAGS_SIZE
Authentication flags size (1 byte)
crypto_result_t crypto_rekey_process_response(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
Process REKEY_RESPONSE from peer (initiator side)
#define NAMED_REGISTER_CRYPTO_CONTEXT(ctx, name, parent_ptr)
Register a crypto context with automatic format specifier.
void debug_sync_print_state(void)
Print all synchronization primitive states at once.
Definition sync.c:387
#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
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER
@ CRYPTO_HANDSHAKE_INIT
@ CRYPTO_HANDSHAKE_READY
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#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 NS_PER_SEC_INT
Definition time.h:157
@ 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_REKEY_COMPLETE
Initiator -> Responder: Empty (encrypted with NEW key, but still handshake)
Definition packet.h:339
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED)
Definition packet.h:319
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion)
Definition packet.h:333
@ PACKET_TYPE_CRYPTO_REKEY_REQUEST
Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:335
@ 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
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED)
Definition packet.h:307
@ PACKET_TYPE_CRYPTO_REKEY_RESPONSE
Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey)
Definition packet.h:337
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
Crypto capabilities packet structure (Packet Type 14)
Definition packet.h:952
Cryptographic context structure.
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
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
Definition packet.h:993
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
Transport abstraction layer for ACIP protocol.