7#include <ascii-chat/crypto/crypto.h>
8#include <ascii-chat/common.h>
9#include <ascii-chat/asciichat_errno.h>
10#include <ascii-chat/tests/test_env.h>
11#include <ascii-chat/util/time.h>
17#include <ascii-chat/platform/init.h>
20static _Atomic
bool g_libsodium_initialized =
false;
22static static_mutex_t g_libsodium_init_mutex = STATIC_MUTEX_INIT;
27static const uint32_t CRYPTO_PACKET_PUBLIC_KEY = 100;
28static const uint32_t CRYPTO_PACKET_ENCRYPTED_DATA = 102;
29static const uint32_t CRYPTO_PACKET_AUTH_CHALLENGE = 103;
30static const uint32_t CRYPTO_PACKET_AUTH_RESPONSE = 104;
37static crypto_result_t init_libsodium(
void) {
39 if (atomic_load(&g_libsodium_initialized)) {
45 static_mutex_lock(&g_libsodium_init_mutex);
48 if (atomic_load(&g_libsodium_initialized)) {
49 static_mutex_unlock(&g_libsodium_init_mutex);
54 if (sodium_init() < 0) {
55 static_mutex_unlock(&g_libsodium_init_mutex);
56 SET_ERRNO(ERROR_CRYPTO,
"Failed to initialize libsodium");
57 return CRYPTO_ERROR_LIBSODIUM;
60 atomic_store(&g_libsodium_initialized,
true);
61 static_mutex_unlock(&g_libsodium_init_mutex);
66static void generate_nonce(crypto_context_t *ctx, uint8_t *nonce_out) {
75 SAFE_MEMCPY(nonce_out, 16, ctx->session_id, 16);
76 uint64_t counter = ctx->nonce_counter++;
77 size_t counter_size = ctx->nonce_size - 16;
78 SAFE_MEMCPY(nonce_out + 16, counter_size, &counter,
79 (counter_size <
sizeof(counter)) ? counter_size : sizeof(counter));
83static void secure_memzero(
void *ptr,
size_t len) {
84 sodium_memzero(ptr, len);
93 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: ctx=%p", ctx);
94 return CRYPTO_ERROR_INVALID_PARAMS;
98 crypto_result_t result = init_libsodium();
99 if (result != CRYPTO_OK) {
104 secure_memzero(ctx,
sizeof(crypto_context_t));
108 if (result != CRYPTO_OK) {
112 ctx->initialized =
true;
113 ctx->has_password =
false;
114 ctx->key_exchange_complete =
false;
115 ctx->peer_key_received =
false;
116 ctx->handshake_complete =
false;
117 ctx->encrypt_data =
true;
118 ctx->nonce_counter = 1;
119 ctx->bytes_encrypted = 0;
120 ctx->bytes_decrypted = 0;
123 ctx->nonce_size = XSALSA20_NONCE_SIZE;
124 ctx->mac_size = POLY1305_MAC_SIZE;
125 ctx->hmac_size = HMAC_SHA256_SIZE;
126 ctx->encryption_key_size = SECRETBOX_KEY_SIZE;
127 ctx->auth_challenge_size = AUTH_CHALLENGE_SIZE;
128 ctx->public_key_size = X25519_KEY_SIZE;
129 ctx->private_key_size = X25519_KEY_SIZE;
130 ctx->shared_key_size = X25519_KEY_SIZE;
131 ctx->salt_size = ARGON2ID_SALT_SIZE;
132 ctx->signature_size = ED25519_SIGNATURE_SIZE;
135 randombytes_buf(ctx->session_id,
sizeof(ctx->session_id));
138 ctx->rekey_packet_count = 0;
139 ctx->rekey_last_time = time(NULL);
140 ctx->rekey_last_request_time = 0;
141 ctx->rekey_in_progress =
false;
142 ctx->rekey_failure_count = 0;
143 ctx->has_temp_key =
false;
144 ctx->rekey_count = 0;
149 if (is_test_environment()) {
150 ctx->rekey_packet_threshold = REKEY_TEST_PACKET_THRESHOLD;
151 ctx->rekey_time_threshold = REKEY_TEST_TIME_THRESHOLD;
152 char duration_str[32];
153 format_duration_s((
double)ctx->rekey_time_threshold, duration_str,
sizeof(duration_str));
154 log_dev(
"Crypto context initialized with X25519 key exchange (TEST MODE rekey thresholds: %llu packets, %s)",
155 (
unsigned long long)ctx->rekey_packet_threshold, duration_str);
157 ctx->rekey_packet_threshold = REKEY_DEFAULT_PACKET_THRESHOLD;
158 ctx->rekey_time_threshold = REKEY_DEFAULT_TIME_THRESHOLD;
159 char duration_str[32];
160 format_duration_s((
double)ctx->rekey_time_threshold, duration_str,
sizeof(duration_str));
161 log_dev(
"Crypto context initialized with X25519 key exchange (rekey thresholds: %llu packets, %s)",
162 (
unsigned long long)ctx->rekey_packet_threshold, duration_str);
168 if (!ctx || !password) {
169 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: ctx=%p, password=%p", ctx, password);
170 return CRYPTO_ERROR_INVALID_PARAMS;
173 if (strlen(password) == 0) {
174 SET_ERRNO(ERROR_INVALID_PARAM,
"Password cannot be empty");
175 return CRYPTO_ERROR_INVALID_PARAMS;
180 if (result != CRYPTO_OK) {
186 if (result != CRYPTO_OK) {
191 ctx->has_password =
true;
193 log_dev(
"Crypto context initialized with password-based encryption");
198 if (!ctx || !ctx->initialized) {
203 secure_memzero(ctx->private_key,
sizeof(ctx->private_key));
204 secure_memzero(ctx->shared_key,
sizeof(ctx->shared_key));
205 secure_memzero(ctx->password_key,
sizeof(ctx->password_key));
206 secure_memzero(ctx->password_salt,
sizeof(ctx->password_salt));
208 log_debug(
"Crypto context cleaned up (encrypted: %lu bytes, decrypted: %lu bytes)", ctx->bytes_encrypted,
209 ctx->bytes_decrypted);
212 secure_memzero(ctx,
sizeof(crypto_context_t));
217 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_generate_keypair: NULL context");
218 return CRYPTO_ERROR_INVALID_PARAMS;
222 if (crypto_box_keypair(ctx->public_key, ctx->private_key) != 0) {
223 SET_ERRNO(ERROR_CRYPTO,
"Failed to generate X25519 key pair");
224 return CRYPTO_ERROR_KEY_GENERATION;
227 log_debug(
"Generated X25519 key pair for key exchange");
236 if (!ctx || !ctx->initialized || !public_key_out) {
237 SET_ERRNO(ERROR_INVALID_PARAM,
238 "crypto_get_public_key: Invalid parameters (ctx=%p, initialized=%d, public_key_out=%p)", ctx,
239 ctx ? ctx->initialized : 0, public_key_out);
240 return CRYPTO_ERROR_INVALID_PARAMS;
244 size_t copy_size = (ctx->public_key_size <= X25519_KEY_SIZE) ? ctx->public_key_size : X25519_KEY_SIZE;
245 SAFE_MEMCPY(public_key_out, copy_size, ctx->public_key, copy_size);
250 if (!ctx || !ctx->initialized || !peer_public_key) {
251 SET_ERRNO(ERROR_INVALID_PARAM,
252 "crypto_set_peer_public_key: Invalid parameters (ctx=%p, initialized=%d, peer_public_key=%p)", ctx,
253 ctx ? ctx->initialized : 0, peer_public_key);
254 return CRYPTO_ERROR_INVALID_PARAMS;
259 size_t copy_size = (ctx->public_key_size <= X25519_KEY_SIZE) ? ctx->public_key_size : X25519_KEY_SIZE;
260 SAFE_MEMCPY(ctx->peer_public_key, copy_size, peer_public_key, copy_size);
261 ctx->peer_key_received =
true;
264 if (crypto_box_beforenm(ctx->shared_key, peer_public_key, ctx->private_key) != 0) {
265 SET_ERRNO(ERROR_CRYPTO,
"Failed to compute shared secret from peer public key");
266 return CRYPTO_ERROR_KEY_GENERATION;
269 ctx->key_exchange_complete =
true;
271 log_debug(
"Key exchange completed - shared secret computed");
276 if (!ctx || !ctx->initialized) {
281 return ctx->key_exchange_complete || ctx->has_password;
290 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_validate_password: Password is NULL");
291 return CRYPTO_ERROR_INVALID_PARAMS;
294 size_t password_len = strlen(password);
296 if (password_len < MIN_PASSWORD_LENGTH) {
297 SET_ERRNO(ERROR_INVALID_PARAM,
"Password too short (minimum %d characters, got %zu)", MIN_PASSWORD_LENGTH,
299 return CRYPTO_ERROR_INVALID_PARAMS;
302 if (password_len > MAX_PASSWORD_LENGTH) {
303 SET_ERRNO(ERROR_INVALID_PARAM,
"Password too long (maximum %d characters, got %zu)", MAX_PASSWORD_LENGTH,
305 return CRYPTO_ERROR_INVALID_PARAMS;
312 if (!ctx || !ctx->initialized || !password) {
313 SET_ERRNO(ERROR_INVALID_PARAM,
314 "crypto_derive_password_key: Invalid parameters (ctx=%p, initialized=%d, password=%p)", ctx,
315 ctx ? ctx->initialized : 0, password);
316 return CRYPTO_ERROR_INVALID_PARAMS;
321 if (validation_result != CRYPTO_OK) {
322 return validation_result;
328 const char *deterministic_salt =
"ascii-chat-password-salt-v1";
329 size_t salt_str_len = strlen(deterministic_salt);
332 memset(ctx->password_salt, 0, ctx->salt_size);
335 memcpy(ctx->password_salt, deterministic_salt, (salt_str_len < ctx->salt_size) ? salt_str_len : ctx->salt_size);
338 if (crypto_pwhash(ctx->password_key, ctx->encryption_key_size, password, strlen(password), ctx->password_salt,
339 crypto_pwhash_OPSLIMIT_INTERACTIVE,
340 crypto_pwhash_MEMLIMIT_INTERACTIVE,
341 crypto_pwhash_ALG_DEFAULT) != 0) {
342 SET_ERRNO(ERROR_CRYPTO,
"Password key derivation failed - possibly out of memory");
343 return CRYPTO_ERROR_PASSWORD_DERIVATION;
346 log_dev(
"Password key derived successfully using Argon2id with deterministic salt");
351 if (!ctx || !ctx->initialized || !ctx->has_password || !password) {
355 uint8_t test_key[SECRETBOX_KEY_SIZE];
359 const char *deterministic_salt =
"ascii-chat-password-salt-v1";
360 uint8_t salt[ARGON2ID_SALT_SIZE];
361 size_t salt_str_len = strlen(deterministic_salt);
364 memset(salt, 0, ARGON2ID_SALT_SIZE);
367 memcpy(salt, deterministic_salt, (salt_str_len < ctx->salt_size) ? salt_str_len : ctx->salt_size);
370 if (crypto_pwhash(test_key, ctx->encryption_key_size, password, strlen(password), salt,
371 crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE,
372 crypto_pwhash_ALG_DEFAULT) != 0) {
373 secure_memzero(test_key,
sizeof(test_key));
378 bool match = (sodium_memcmp(test_key, ctx->password_key, ctx->encryption_key_size) == 0);
380 secure_memzero(test_key,
sizeof(test_key));
386 uint8_t encryption_key[SECRETBOX_KEY_SIZE]) {
387 if (!password || !encryption_key) {
388 SET_ERRNO(ERROR_INVALID_PARAM,
389 "crypto_derive_password_encryption_key: Invalid parameters (password=%p, encryption_key=%p)", password,
391 return CRYPTO_ERROR_INVALID_PARAMS;
396 if (validation_result != CRYPTO_OK) {
397 return validation_result;
402 const char *deterministic_salt =
"ascii-chat-password-salt-v1";
403 uint8_t salt[ARGON2ID_SALT_SIZE];
404 size_t salt_str_len = strlen(deterministic_salt);
407 memset(salt, 0, ARGON2ID_SALT_SIZE);
410 memcpy(salt, deterministic_salt, (salt_str_len < ARGON2ID_SALT_SIZE) ? salt_str_len : ARGON2ID_SALT_SIZE);
413 if (crypto_pwhash(encryption_key, SECRETBOX_KEY_SIZE, password, strlen(password), salt,
414 crypto_pwhash_OPSLIMIT_INTERACTIVE,
415 crypto_pwhash_MEMLIMIT_INTERACTIVE,
416 crypto_pwhash_ALG_DEFAULT) != 0) {
417 SET_ERRNO(ERROR_CRYPTO,
"Password encryption key derivation failed - possibly out of memory");
418 return CRYPTO_ERROR_PASSWORD_DERIVATION;
421 log_dev(
"Password encryption key derived successfully using Argon2id");
429crypto_result_t
crypto_encrypt(crypto_context_t *ctx,
const uint8_t *plaintext,
size_t plaintext_len,
430 uint8_t *ciphertext_out,
size_t ciphertext_out_size,
size_t *ciphertext_len_out) {
431 if (!ctx || !ctx->initialized || !plaintext || !ciphertext_out || !ciphertext_len_out) {
432 SET_ERRNO(ERROR_INVALID_PARAM,
433 "Invalid parameters: ctx=%p, initialized=%d, plaintext=%p, ciphertext_out=%p, ciphertext_len_out=%p", ctx,
434 ctx ? ctx->initialized : 0, plaintext, ciphertext_out, ciphertext_len_out);
435 return CRYPTO_ERROR_INVALID_PARAMS;
438 if (plaintext_len == 0 || plaintext_len > CRYPTO_MAX_PLAINTEXT_SIZE) {
439 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid plaintext length: %zu (max: %d)", plaintext_len, CRYPTO_MAX_PLAINTEXT_SIZE);
440 return CRYPTO_ERROR_INVALID_PARAMS;
444 SET_ERRNO(ERROR_CRYPTO,
"Crypto context not ready for encryption");
445 return CRYPTO_ERROR_KEY_EXCHANGE_INCOMPLETE;
449 size_t required_size = plaintext_len + ctx->nonce_size + ctx->mac_size;
450 if (ciphertext_out_size < required_size) {
451 SET_ERRNO(ERROR_BUFFER,
"Ciphertext buffer too small: %zu < %zu", ciphertext_out_size, required_size);
452 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
459 if (ctx->nonce_counter == 0 || ctx->nonce_counter == UINT64_MAX) {
460 SET_ERRNO(ERROR_CRYPTO,
"Nonce counter exhausted - key rotation required");
461 return CRYPTO_ERROR_NONCE_EXHAUSTED;
465 uint8_t nonce[XSALSA20_NONCE_SIZE];
466 generate_nonce(ctx, nonce);
467 SAFE_MEMCPY(ciphertext_out, ctx->nonce_size, nonce, ctx->nonce_size);
470 const uint8_t *encryption_key = NULL;
471 if (ctx->key_exchange_complete) {
472 encryption_key = ctx->shared_key;
473 }
else if (ctx->has_password) {
474 encryption_key = ctx->password_key;
476 SET_ERRNO(ERROR_CRYPTO,
"No encryption key available");
477 return CRYPTO_ERROR_KEY_EXCHANGE_INCOMPLETE;
481 if (crypto_secretbox_easy(ciphertext_out + ctx->nonce_size, plaintext, plaintext_len, nonce, encryption_key) != 0) {
482 SET_ERRNO(ERROR_CRYPTO,
"Encryption failed");
483 return CRYPTO_ERROR_ENCRYPTION;
486 *ciphertext_len_out = required_size;
487 ctx->bytes_encrypted += plaintext_len;
490 ctx->rekey_packet_count++;
495crypto_result_t
crypto_decrypt(crypto_context_t *ctx,
const uint8_t *ciphertext,
size_t ciphertext_len,
496 uint8_t *plaintext_out,
size_t plaintext_out_size,
size_t *plaintext_len_out) {
497 if (!ctx || !ctx->initialized || !ciphertext || !plaintext_out || !plaintext_len_out) {
498 SET_ERRNO(ERROR_INVALID_PARAM,
499 "Invalid parameters: ctx=%p, initialized=%d, ciphertext=%p, plaintext_out=%p, plaintext_len_out=%p", ctx,
500 ctx ? ctx->initialized : 0, ciphertext, plaintext_out, plaintext_len_out);
501 return CRYPTO_ERROR_INVALID_PARAMS;
505 SET_ERRNO(ERROR_CRYPTO,
"Crypto context not ready for decryption");
506 return CRYPTO_ERROR_KEY_EXCHANGE_INCOMPLETE;
510 size_t min_ciphertext_size = ctx->nonce_size + ctx->mac_size;
511 if (ciphertext_len < min_ciphertext_size) {
512 SET_ERRNO(ERROR_INVALID_PARAM,
"Ciphertext too small: %zu < %zu", ciphertext_len, min_ciphertext_size);
513 return CRYPTO_ERROR_INVALID_PARAMS;
516 size_t plaintext_len = ciphertext_len - ctx->nonce_size - ctx->mac_size;
517 if (plaintext_out_size < plaintext_len) {
518 SET_ERRNO(ERROR_BUFFER,
"Plaintext buffer too small: %zu < %zu", plaintext_out_size, plaintext_len);
519 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
523 const uint8_t *nonce = ciphertext;
524 const uint8_t *encrypted_data = ciphertext + ctx->nonce_size;
527 const uint8_t *decryption_key = NULL;
528 if (ctx->key_exchange_complete) {
529 decryption_key = ctx->shared_key;
530 }
else if (ctx->has_password) {
531 decryption_key = ctx->password_key;
533 SET_ERRNO(ERROR_CRYPTO,
"No decryption key available");
534 return CRYPTO_ERROR_KEY_EXCHANGE_INCOMPLETE;
538 if (crypto_secretbox_open_easy(plaintext_out, encrypted_data, ciphertext_len - ctx->nonce_size, nonce,
539 decryption_key) != 0) {
540 SET_ERRNO(ERROR_CRYPTO,
"Decryption failed - invalid MAC or corrupted data");
541 return CRYPTO_ERROR_INVALID_MAC;
544 *plaintext_len_out = plaintext_len;
545 ctx->bytes_decrypted += plaintext_len;
555static const char *
const g_crypto_result_strings[] = {
557 "Initialization failed",
558 "Invalid parameters",
559 "Memory allocation failed",
561 "Key generation failed",
562 "Password derivation failed",
565 "Invalid MAC or corrupted data",
567 "Key exchange not complete",
568 "Nonce counter exhausted",
569 "Rekey already in progress",
571 "Rekey rate limited",
573#define CRYPTO_RESULT_STRING_COUNT (sizeof(g_crypto_result_strings) / sizeof(g_crypto_result_strings[0]))
578 return g_crypto_result_strings[idx];
580 return "Unknown error";
588 if (!ctx->initialized) {
589 SAFE_SNPRINTF(status_buffer,
buffer_size,
"Not initialized");
594 "Initialized: %s, Password: %s, Key Exchange: %s, Ready: %s, "
595 "Encrypted: %" PRIu64
" bytes, Decrypted: %" PRIu64
" bytes, Nonce: %" PRIu64,
596 ctx->initialized ?
"yes" :
"no", ctx->has_password ?
"yes" :
"no",
597 ctx->key_exchange_complete ?
"complete" :
"incomplete",
crypto_is_ready(ctx) ?
"yes" :
"no",
598 ctx->bytes_encrypted, ctx->bytes_decrypted, ctx->nonce_counter);
605 return sodium_memcmp(lhs, rhs, len) == 0;
609 if (!buffer || len == 0) {
610 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_random_bytes: Invalid parameters (buffer=%p, len=%zu)", buffer, len);
611 return CRYPTO_ERROR_INVALID_PARAMS;
614 crypto_result_t result = init_libsodium();
615 if (result != CRYPTO_OK) {
619 randombytes_buf(buffer, len);
628 size_t *packet_len_out) {
629 if (!ctx || !ctx->initialized || !packet_out || !packet_len_out) {
630 SET_ERRNO(ERROR_INVALID_PARAM,
631 "crypto_create_public_key_packet: Invalid parameters (ctx=%p, initialized=%d, packet_out=%p, "
632 "packet_len_out=%p)",
633 ctx, ctx ? ctx->initialized : 0, packet_out, packet_len_out);
634 return CRYPTO_ERROR_INVALID_PARAMS;
637 size_t required_size =
sizeof(uint32_t) + ctx->public_key_size;
638 if (packet_size < required_size) {
639 SET_ERRNO(ERROR_BUFFER,
"crypto_create_public_key_packet: Buffer too small (size=%zu, required=%zu)", packet_size,
641 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
645 uint32_t packet_type = CRYPTO_PACKET_PUBLIC_KEY;
646 SAFE_MEMCPY(packet_out,
sizeof(packet_type), &packet_type,
sizeof(packet_type));
648 size_t copy_size = (ctx->public_key_size <= X25519_KEY_SIZE) ? ctx->public_key_size : X25519_KEY_SIZE;
649 SAFE_MEMCPY(packet_out +
sizeof(packet_type), copy_size, ctx->public_key, copy_size);
651 *packet_len_out = required_size;
656 if (!ctx || !ctx->initialized || !packet) {
657 SET_ERRNO(ERROR_INVALID_PARAM,
658 "crypto_process_public_key_packet: Invalid parameters (ctx=%p, initialized=%d, packet=%p)", ctx,
659 ctx ? ctx->initialized : 0, packet);
660 return CRYPTO_ERROR_INVALID_PARAMS;
663 size_t expected_size =
sizeof(uint32_t) + ctx->public_key_size;
664 if (packet_len != expected_size) {
665 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_public_key_packet: Invalid packet size (expected=%zu, got=%zu)",
666 expected_size, packet_len);
667 return CRYPTO_ERROR_INVALID_PARAMS;
671 uint32_t packet_type;
672 SAFE_MEMCPY(&packet_type,
sizeof(packet_type), packet,
sizeof(packet_type));
674 if (packet_type != CRYPTO_PACKET_PUBLIC_KEY) {
675 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_public_key_packet: Invalid packet type (expected=%u, got=%u)",
676 CRYPTO_PACKET_PUBLIC_KEY, packet_type);
677 return CRYPTO_ERROR_INVALID_PARAMS;
680 const uint8_t *peer_public_key = packet +
sizeof(packet_type);
685 uint8_t *packet_out,
size_t packet_size,
size_t *packet_len_out) {
686 if (!ctx || !data || !packet_out || !packet_len_out) {
687 SET_ERRNO(ERROR_INVALID_PARAM,
688 "crypto_create_encrypted_packet: Invalid parameters (ctx=%p, data=%p, packet_out=%p, packet_len_out=%p)",
689 ctx, data, packet_out, packet_len_out);
690 return CRYPTO_ERROR_INVALID_PARAMS;
694 SET_ERRNO(ERROR_CRYPTO,
"crypto_create_encrypted_packet: Crypto context not ready");
695 return CRYPTO_ERROR_KEY_EXCHANGE_INCOMPLETE;
699 if (data_len > CRYPTO_MAX_PLAINTEXT_SIZE) {
700 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_create_encrypted_packet: data_len %zu exceeds max %d", data_len,
701 CRYPTO_MAX_PLAINTEXT_SIZE);
702 return CRYPTO_ERROR_INVALID_PARAMS;
706 if (data_len > SIZE_MAX - ctx->nonce_size - ctx->mac_size) {
707 SET_ERRNO(ERROR_BUFFER,
"crypto_create_encrypted_packet: encrypted_size overflow");
708 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
710 size_t encrypted_size = data_len + ctx->nonce_size + ctx->mac_size;
713 size_t header_size =
sizeof(uint32_t) +
sizeof(uint32_t);
714 if (encrypted_size > SIZE_MAX - header_size) {
715 SET_ERRNO(ERROR_BUFFER,
"crypto_create_encrypted_packet: required_size overflow");
716 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
718 size_t required_size = header_size + encrypted_size;
720 if (packet_size < required_size) {
721 SET_ERRNO(ERROR_BUFFER,
"crypto_create_encrypted_packet: Buffer too small (size=%zu, required=%zu)", packet_size,
723 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
727 size_t ciphertext_len;
728 uint8_t *encrypted_data = packet_out +
sizeof(uint32_t) +
sizeof(uint32_t);
729 crypto_result_t result =
crypto_encrypt(ctx, data, data_len, encrypted_data,
730 packet_size -
sizeof(uint32_t) -
sizeof(uint32_t), &ciphertext_len);
731 if (result != CRYPTO_OK) {
736 uint32_t packet_type = CRYPTO_PACKET_ENCRYPTED_DATA;
737 uint32_t data_length = (uint32_t)ciphertext_len;
739 SAFE_MEMCPY(packet_out,
sizeof(packet_type), &packet_type,
sizeof(packet_type));
740 SAFE_MEMCPY(packet_out +
sizeof(packet_type),
sizeof(data_length), &data_length,
sizeof(data_length));
742 *packet_len_out = required_size;
747 uint8_t *data_out,
size_t data_size,
size_t *data_len_out) {
748 if (!ctx || !packet || !data_out || !data_len_out) {
749 SET_ERRNO(ERROR_INVALID_PARAM,
750 "crypto_process_encrypted_packet: Invalid parameters (ctx=%p, packet=%p, data_out=%p, data_len_out=%p)",
751 ctx, packet, data_out, data_len_out);
752 return CRYPTO_ERROR_INVALID_PARAMS;
756 SET_ERRNO(ERROR_CRYPTO,
"crypto_process_encrypted_packet: Crypto context not ready");
757 return CRYPTO_ERROR_KEY_EXCHANGE_INCOMPLETE;
760 if (packet_len <
sizeof(uint32_t) +
sizeof(uint32_t)) {
761 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_encrypted_packet: Packet too small (size=%zu)", packet_len);
762 return CRYPTO_ERROR_INVALID_PARAMS;
766 uint32_t packet_type;
767 uint32_t data_length;
768 SAFE_MEMCPY(&packet_type,
sizeof(packet_type), packet,
sizeof(packet_type));
769 SAFE_MEMCPY(&data_length,
sizeof(data_length), packet +
sizeof(packet_type),
sizeof(data_length));
771 if (packet_type != CRYPTO_PACKET_ENCRYPTED_DATA) {
772 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_encrypted_packet: Invalid packet type (expected=%u, got=%u)",
773 CRYPTO_PACKET_ENCRYPTED_DATA, packet_type);
774 return CRYPTO_ERROR_INVALID_PARAMS;
777 if (packet_len !=
sizeof(uint32_t) +
sizeof(uint32_t) + data_length) {
778 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_encrypted_packet: Packet length mismatch (expected=%zu, got=%zu)",
779 sizeof(uint32_t) +
sizeof(uint32_t) + data_length, packet_len);
780 return CRYPTO_ERROR_INVALID_PARAMS;
783 const uint8_t *encrypted_data = packet +
sizeof(uint32_t) +
sizeof(uint32_t);
784 return crypto_decrypt(ctx, encrypted_data, data_length, data_out, data_size, data_len_out);
793 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_generate_nonce: NULL nonce buffer");
794 return CRYPTO_ERROR_INVALID_PARAMS;
797 crypto_result_t result = init_libsodium();
798 if (result != CRYPTO_OK) {
802 randombytes_buf(nonce, 32);
808 if (!ctx || !key || !data || !hmac) {
809 return (crypto_result_t)SET_ERRNO(ERROR_INVALID_PARAM,
810 "crypto_compute_hmac: Invalid parameters (ctx=%p, key=%p, data=%p, hmac=%p)", ctx,
814 crypto_result_t result = init_libsodium();
815 if (result != CRYPTO_OK) {
819 crypto_auth_hmacsha256(hmac, data, 32, key);
824 size_t data_len, uint8_t hmac[32]) {
825 if (!ctx || !key || !data || !hmac || data_len == 0) {
826 return (crypto_result_t)SET_ERRNO(
828 "crypto_compute_hmac_ex: Invalid parameters (ctx=%p, key=%p, data=%p, data_len=%zu, hmac=%p)", ctx, key, data,
832 crypto_result_t result = init_libsodium();
833 if (result != CRYPTO_OK) {
837 crypto_auth_hmacsha256(hmac, data, data_len, key);
841bool crypto_verify_hmac(
const uint8_t key[32],
const uint8_t data[32],
const uint8_t expected_hmac[32]) {
842 if (!key || !data || !expected_hmac) {
843 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_verify_hmac: Invalid parameters (key=%p, data=%p, expected_hmac=%p)", key,
844 data, expected_hmac);
848 uint8_t computed_hmac[32];
849 if (crypto_auth_hmacsha256(computed_hmac, data, 32, key) != 0) {
853 return sodium_memcmp(computed_hmac, expected_hmac, 32) == 0;
857 const uint8_t expected_hmac[32]) {
858 if (!key || !data || !expected_hmac || data_len == 0) {
859 SET_ERRNO(ERROR_INVALID_PARAM,
860 "crypto_verify_hmac_ex: Invalid parameters (key=%p, data=%p, data_len=%zu, expected_hmac=%p)", key, data,
861 data_len, expected_hmac);
865 uint8_t computed_hmac[32];
866 if (crypto_auth_hmacsha256(computed_hmac, data, data_len, key) != 0) {
870 return sodium_memcmp(computed_hmac, expected_hmac, 32) == 0;
878 uint8_t hmac_out[32]) {
879 if (!ctx || !nonce || !hmac_out) {
880 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_compute_auth_response: Invalid parameters (ctx=%p, nonce=%p, hmac_out=%p)",
881 ctx, nonce, hmac_out);
882 return CRYPTO_ERROR_INVALID_PARAMS;
887 if (!ctx->key_exchange_complete) {
888 SET_ERRNO(ERROR_CRYPTO,
"Cannot compute auth response - key exchange not complete");
889 return CRYPTO_ERROR_INVALID_PARAMS;
894 uint8_t combined_data[64];
895 memcpy(combined_data, nonce, 32);
896 memcpy(combined_data + 32, ctx->shared_key, 32);
899 const uint8_t *auth_key = ctx->has_password ? ctx->password_key : ctx->shared_key;
904 sodium_memzero(combined_data,
sizeof(combined_data));
910 const uint8_t expected_hmac[32]) {
911 if (!ctx || !nonce || !expected_hmac) {
912 SET_ERRNO(ERROR_INVALID_PARAM,
913 "crypto_verify_auth_response: Invalid parameters (ctx=%p, nonce=%p, expected_hmac=%p)", ctx, nonce,
920 if (!ctx->key_exchange_complete) {
921 SET_ERRNO(ERROR_CRYPTO,
"Cannot verify auth response - key exchange not complete");
927 uint8_t combined_data[64];
928 memcpy(combined_data, nonce, 32);
929 memcpy(combined_data + 32, ctx->shared_key, 32);
932 const uint8_t *auth_key = ctx->has_password ? ctx->password_key : ctx->shared_key;
934 log_debug(
"Verifying auth response: has_password=%d, key_exchange_complete=%d, using_password_key=%d",
935 ctx->has_password, ctx->key_exchange_complete, (auth_key == ctx->password_key));
940 sodium_memzero(combined_data,
sizeof(combined_data));
946 size_t *packet_len_out) {
947 if (!ctx || !ctx->initialized || !packet_out || !packet_len_out) {
950 "crypto_create_auth_challenge: Invalid parameters (ctx=%p, initialized=%d, packet_out=%p, packet_len_out=%p)",
951 ctx, ctx ? ctx->initialized : 0, packet_out, packet_len_out);
952 return CRYPTO_ERROR_INVALID_PARAMS;
955 size_t required_size =
sizeof(uint32_t) + ctx->auth_challenge_size;
956 if (packet_size < required_size) {
957 SET_ERRNO(ERROR_BUFFER,
"crypto_create_auth_challenge: Buffer too small (size=%zu, required=%zu)", packet_size,
959 return CRYPTO_ERROR_BUFFER_TOO_SMALL;
964 if (result != CRYPTO_OK) {
969 uint32_t packet_type = CRYPTO_PACKET_AUTH_CHALLENGE;
970 SAFE_MEMCPY(packet_out,
sizeof(packet_type), &packet_type,
sizeof(packet_type));
971 SAFE_MEMCPY(packet_out +
sizeof(packet_type), ctx->auth_challenge_size, ctx->auth_nonce, ctx->auth_challenge_size);
973 *packet_len_out = required_size;
978 if (!ctx || !ctx->initialized || !packet) {
979 SET_ERRNO(ERROR_INVALID_PARAM,
980 "crypto_process_auth_challenge: Invalid parameters (ctx=%p, initialized=%d, packet=%p)", ctx,
981 ctx ? ctx->initialized : 0, packet);
982 return CRYPTO_ERROR_INVALID_PARAMS;
985 size_t expected_size =
sizeof(uint32_t) + ctx->auth_challenge_size;
986 if (packet_len != expected_size) {
987 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_auth_challenge: Invalid packet size (expected=%zu, got=%zu)",
988 expected_size, packet_len);
989 return CRYPTO_ERROR_INVALID_PARAMS;
993 uint32_t packet_type;
994 SAFE_MEMCPY(&packet_type,
sizeof(packet_type), packet,
sizeof(packet_type));
996 if (packet_type != CRYPTO_PACKET_AUTH_CHALLENGE) {
997 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_auth_challenge: Invalid packet type (expected=%u, got=%u)",
998 CRYPTO_PACKET_AUTH_CHALLENGE, packet_type);
999 return CRYPTO_ERROR_INVALID_PARAMS;
1003 SAFE_MEMCPY(ctx->auth_nonce,
sizeof(ctx->auth_nonce), packet +
sizeof(packet_type), ctx->auth_challenge_size);
1005 log_debug(
"Auth challenge received and processed");
1010 if (!ctx || !ctx->initialized || !packet) {
1011 SET_ERRNO(ERROR_INVALID_PARAM,
1012 "crypto_process_auth_response: Invalid context or packet (ctx=%p, initialized=%d, packet=%p)", ctx,
1013 ctx ? ctx->initialized : 0, packet);
1014 return CRYPTO_ERROR_INVALID_PARAMS;
1017 size_t expected_size =
sizeof(uint32_t) + 32;
1018 if (packet_len != expected_size) {
1019 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_auth_response: Invalid packet size (expected=%zu, got=%zu)",
1020 expected_size, packet_len);
1021 return CRYPTO_ERROR_INVALID_PARAMS;
1025 uint32_t packet_type;
1026 SAFE_MEMCPY(&packet_type,
sizeof(packet_type), packet,
sizeof(packet_type));
1028 if (packet_type != CRYPTO_PACKET_AUTH_RESPONSE) {
1029 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_process_auth_response: Invalid packet type (expected=0x%x, got=0x%x)",
1030 CRYPTO_PACKET_AUTH_RESPONSE, packet_type);
1031 return CRYPTO_ERROR_INVALID_PARAMS;
1034 const uint8_t *received_hmac = packet +
sizeof(packet_type);
1038 SET_ERRNO(ERROR_CRYPTO,
"crypto_process_auth_response: HMAC verification failed");
1039 return CRYPTO_ERROR_INVALID_MAC;
1042 ctx->handshake_complete =
true;
1043 log_debug(
"Authentication successful - handshake complete");
1052 const uint8_t *shared_secret, uint8_t *hmac_out) {
1053 if (!ctx || !password_key || !nonce || !shared_secret || !hmac_out) {
1054 return SET_ERRNO(ERROR_INVALID_PARAM,
1055 "Invalid parameters: ctx=%p, password_key=%p, nonce=%p, shared_secret=%p, hmac_out=%p", ctx,
1056 password_key, nonce, shared_secret, hmac_out);
1061 uint8_t combined_data[64];
1062 memcpy(combined_data, nonce, 32);
1063 memcpy(combined_data + 32, shared_secret, 32);
1068 sodium_memzero(combined_data,
sizeof(combined_data));
1069 return SET_ERRNO(ERROR_CRYPTO,
"Failed to compute password HMAC");
1073 sodium_memzero(combined_data,
sizeof(combined_data));
1075 return ASCIICHAT_OK;
1079 size_t ephemeral_key_size,
const uint8_t *signature) {
1080 if (!peer_public_key || !ephemeral_key || !signature) {
1081 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: peer_public_key=%p, ephemeral_key=%p, signature=%p",
1082 peer_public_key, ephemeral_key, signature);
1085 if (ephemeral_key_size == 0) {
1086 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid ephemeral key size: %zu", ephemeral_key_size);
1090 if (crypto_sign_verify_detached(signature, ephemeral_key, ephemeral_key_size, peer_public_key) != 0) {
1091 return SET_ERRNO(ERROR_CRYPTO,
"Peer signature verification failed");
1094 return ASCIICHAT_OK;
1098 size_t ephemeral_key_size, uint8_t *signature_out) {
1099 if (!private_key || !ephemeral_key || !signature_out) {
1100 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: private_key=%p, ephemeral_key=%p, signature_out=%p",
1101 private_key, ephemeral_key, signature_out);
1104 if (ephemeral_key_size == 0) {
1105 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid ephemeral key size: %zu", ephemeral_key_size);
1106 return ERROR_INVALID_PARAM;
1110 if (private_key->type == KEY_TYPE_ED25519) {
1111 if (crypto_sign_detached(signature_out, NULL, ephemeral_key, ephemeral_key_size, private_key->key.ed25519) != 0) {
1112 return SET_ERRNO(ERROR_CRYPTO,
"Failed to sign ephemeral key");
1115 return SET_ERRNO(ERROR_CRYPTO,
"Unsupported private key type for signing");
1118 return ASCIICHAT_OK;
1122 if (!hmac || !challenge_nonce || !combined_out) {
1123 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: hmac=%p, challenge_nonce=%p, combined_out=%p", hmac,
1124 challenge_nonce, combined_out);
1129 memcpy(combined_out, hmac, 32);
1130 memcpy(combined_out + 32, challenge_nonce, 32);
1134 if (!combined_data || !hmac_out || !challenge_out) {
1135 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: combined_data=%p, hmac_out=%p, challenge_out=%p", combined_data,
1136 hmac_out, challenge_out);
1141 memcpy(hmac_out, combined_data, 32);
1142 memcpy(challenge_out, combined_data + 32, 32);
1150 if (!ctx || !ctx->initialized) {
1155 if (ctx->rekey_in_progress) {
1160 if (!ctx->handshake_complete) {
1165 if (ctx->rekey_packet_count >= ctx->rekey_packet_threshold) {
1166 log_debug(
"Rekey triggered: packet count (%llu) >= threshold (%llu)", (
unsigned long long)ctx->rekey_packet_count,
1167 (
unsigned long long)ctx->rekey_packet_threshold);
1172 time_t now = time(NULL);
1173 time_t elapsed = now - ctx->rekey_last_time;
1174 if (elapsed >= ctx->rekey_time_threshold) {
1175 log_debug(
"Rekey triggered: time elapsed (%ld sec) >= threshold (%ld sec)", (
long)elapsed,
1176 (
long)ctx->rekey_time_threshold);
1184 if (!ctx || !ctx->initialized) {
1185 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_rekey_init: Invalid context");
1186 return CRYPTO_ERROR_INVALID_PARAMS;
1189 if (ctx->rekey_in_progress) {
1190 SET_ERRNO(ERROR_CRYPTO,
"Rekey already in progress");
1191 return CRYPTO_ERROR_REKEY_IN_PROGRESS;
1195 time_t now = time(NULL);
1196 time_t since_last_rekey = now - ctx->rekey_last_time;
1197 time_t min_interval_seconds = (time_t)(REKEY_MIN_INTERVAL / NS_PER_SEC_INT);
1198 if (since_last_rekey < min_interval_seconds) {
1199 char elapsed_str[32], min_str[32];
1202 log_warn(
"Rekey rate limited: %s since last rekey (minimum: %s)", elapsed_str, min_str);
1203 return CRYPTO_ERROR_REKEY_RATE_LIMITED;
1207 if (ctx->rekey_failure_count >= REKEY_MAX_FAILURE_COUNT) {
1208 log_error(
"Too many consecutive rekey failures (%d), giving up", ctx->rekey_failure_count);
1209 return CRYPTO_ERROR_REKEY_FAILED;
1213 if (crypto_box_keypair(ctx->temp_public_key, ctx->temp_private_key) != 0) {
1214 SET_ERRNO(ERROR_CRYPTO,
"Failed to generate rekey ephemeral keypair");
1215 return CRYPTO_ERROR_KEY_GENERATION;
1218 ctx->rekey_in_progress =
true;
1219 ctx->has_temp_key =
true;
1221 log_debug(
"Rekey initiated (packets: %llu, time elapsed: %ld sec, attempt %d)",
1222 (
unsigned long long)ctx->rekey_packet_count, (
long)since_last_rekey, ctx->rekey_failure_count + 1);
1228 if (!ctx || !ctx->initialized || !peer_new_public_key) {
1229 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_rekey_process_request: Invalid parameters");
1230 return CRYPTO_ERROR_INVALID_PARAMS;
1234 if (crypto_box_keypair(ctx->temp_public_key, ctx->temp_private_key) != 0) {
1235 SET_ERRNO(ERROR_CRYPTO,
"Failed to generate rekey ephemeral keypair");
1236 return CRYPTO_ERROR_KEY_GENERATION;
1240 if (crypto_box_beforenm(ctx->temp_shared_key, peer_new_public_key, ctx->temp_private_key) != 0) {
1241 SET_ERRNO(ERROR_CRYPTO,
"Failed to compute rekey shared secret");
1242 secure_memzero(ctx->temp_private_key,
sizeof(ctx->temp_private_key));
1243 secure_memzero(ctx->temp_public_key,
sizeof(ctx->temp_public_key));
1244 return CRYPTO_ERROR_KEY_GENERATION;
1247 ctx->rekey_in_progress =
true;
1248 ctx->has_temp_key =
true;
1250 log_debug(
"Rekey request processed (responder side), new shared secret computed");
1256 if (!ctx || !ctx->initialized || !peer_new_public_key) {
1257 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_rekey_process_response: Invalid parameters");
1258 return CRYPTO_ERROR_INVALID_PARAMS;
1261 if (!ctx->rekey_in_progress || !ctx->has_temp_key) {
1262 SET_ERRNO(ERROR_CRYPTO,
"No rekey in progress");
1263 return CRYPTO_ERROR_REKEY_FAILED;
1267 if (crypto_box_beforenm(ctx->temp_shared_key, peer_new_public_key, ctx->temp_private_key) != 0) {
1268 SET_ERRNO(ERROR_CRYPTO,
"Failed to compute rekey shared secret");
1270 return CRYPTO_ERROR_KEY_GENERATION;
1273 log_debug(
"Rekey response processed (initiator side), new shared secret computed");
1279 if (!ctx || !ctx->initialized) {
1280 SET_ERRNO(ERROR_INVALID_PARAM,
"crypto_rekey_commit: Invalid context");
1281 return CRYPTO_ERROR_INVALID_PARAMS;
1284 if (!ctx->rekey_in_progress || !ctx->has_temp_key) {
1285 SET_ERRNO(ERROR_CRYPTO,
"No rekey in progress to commit");
1286 return CRYPTO_ERROR_REKEY_FAILED;
1290 secure_memzero(ctx->shared_key,
sizeof(ctx->shared_key));
1293 SAFE_MEMCPY(ctx->shared_key,
sizeof(ctx->shared_key), ctx->temp_shared_key,
sizeof(ctx->temp_shared_key));
1296 ctx->nonce_counter = 1;
1299 randombytes_buf(ctx->session_id,
sizeof(ctx->session_id));
1302 ctx->rekey_packet_count = 0;
1303 ctx->rekey_last_time = time(NULL);
1307 secure_memzero(ctx->temp_public_key,
sizeof(ctx->temp_public_key));
1308 secure_memzero(ctx->temp_private_key,
sizeof(ctx->temp_private_key));
1309 secure_memzero(ctx->temp_shared_key,
sizeof(ctx->temp_shared_key));
1310 ctx->has_temp_key =
false;
1311 ctx->rekey_in_progress =
false;
1314 ctx->rekey_failure_count = 0;
1316 log_debug(
"Rekey committed successfully (rekey #%llu, nonce reset to 1, new session_id generated)",
1317 (
unsigned long long)ctx->rekey_count);
1327 log_warn(
"Aborting rekey (attempt %d failed), keeping old encryption key", ctx->rekey_failure_count + 1);
1330 secure_memzero(ctx->temp_public_key,
sizeof(ctx->temp_public_key));
1331 secure_memzero(ctx->temp_private_key,
sizeof(ctx->temp_private_key));
1332 secure_memzero(ctx->temp_shared_key,
sizeof(ctx->temp_shared_key));
1335 ctx->has_temp_key =
false;
1336 ctx->rekey_in_progress =
false;
1339 ctx->rekey_failure_count++;
1349 time_t now = time(NULL);
1350 time_t elapsed = now - ctx->rekey_last_time;
1351 time_t remaining_time = (ctx->rekey_time_threshold > elapsed) ? (ctx->rekey_time_threshold - elapsed) : 0;
1352 uint64_t remaining_packets = (ctx->rekey_packet_threshold > ctx->rekey_packet_count)
1353 ? (ctx->rekey_packet_threshold - ctx->rekey_packet_count)
1357 "Rekey status: %s | "
1358 "Packets: %llu/%llu (%llu remaining) | "
1359 "Time: %ld/%ld sec (%ld sec remaining) | "
1360 "Rekeys: %llu | Failures: %d",
1361 ctx->rekey_in_progress ?
"IN_PROGRESS" :
"IDLE", (
unsigned long long)ctx->rekey_packet_count,
1362 (
unsigned long long)ctx->rekey_packet_threshold, (
unsigned long long)remaining_packets, (
long)elapsed,
1363 (long)ctx->rekey_time_threshold, (
long)remaining_time, (
unsigned long long)ctx->rekey_count,
1364 ctx->rekey_failure_count);
int buffer_size
Size of circular buffer.
crypto_result_t crypto_rekey_commit(crypto_context_t *ctx)
crypto_result_t crypto_create_auth_challenge(const crypto_context_t *ctx, uint8_t *packet_out, size_t packet_size, size_t *packet_len_out)
crypto_result_t crypto_process_public_key_packet(crypto_context_t *ctx, const uint8_t *packet, size_t packet_len)
crypto_result_t crypto_compute_hmac_ex(const crypto_context_t *ctx, const uint8_t key[32], const uint8_t *data, size_t data_len, uint8_t hmac[32])
crypto_result_t crypto_set_peer_public_key(crypto_context_t *ctx, const uint8_t *peer_public_key)
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)
crypto_result_t crypto_compute_auth_response(const crypto_context_t *ctx, const uint8_t nonce[32], uint8_t hmac_out[32])
crypto_result_t crypto_init(crypto_context_t *ctx)
crypto_result_t crypto_generate_nonce(uint8_t nonce[32])
asciichat_error_t crypto_compute_password_hmac(crypto_context_t *ctx, const uint8_t *password_key, const uint8_t *nonce, const uint8_t *shared_secret, uint8_t *hmac_out)
const char * crypto_result_to_string(crypto_result_t result)
asciichat_error_t crypto_verify_peer_signature(const uint8_t *peer_public_key, const uint8_t *ephemeral_key, size_t ephemeral_key_size, const uint8_t *signature)
bool crypto_verify_hmac_ex(const uint8_t key[32], const uint8_t *data, size_t data_len, const uint8_t expected_hmac[32])
bool crypto_verify_password(const crypto_context_t *ctx, const char *password)
crypto_result_t crypto_get_public_key(const crypto_context_t *ctx, uint8_t *public_key_out)
crypto_result_t crypto_create_encrypted_packet(crypto_context_t *ctx, const uint8_t *data, size_t data_len, uint8_t *packet_out, size_t packet_size, size_t *packet_len_out)
void crypto_get_rekey_status(const crypto_context_t *ctx, char *status_buffer, size_t buffer_size)
crypto_result_t crypto_derive_password_encryption_key(const char *password, uint8_t encryption_key[SECRETBOX_KEY_SIZE])
bool crypto_should_rekey(const crypto_context_t *ctx)
crypto_result_t crypto_process_auth_response(crypto_context_t *ctx, const uint8_t *packet, size_t packet_len)
crypto_result_t crypto_generate_keypair(crypto_context_t *ctx)
void crypto_combine_auth_data(const uint8_t *hmac, const uint8_t *challenge_nonce, uint8_t *combined_out)
bool crypto_verify_hmac(const uint8_t key[32], const uint8_t data[32], const uint8_t expected_hmac[32])
crypto_result_t crypto_process_auth_challenge(crypto_context_t *ctx, const uint8_t *packet, size_t packet_len)
void crypto_extract_auth_data(const uint8_t *combined_data, uint8_t *hmac_out, uint8_t *challenge_out)
void crypto_get_status(const crypto_context_t *ctx, char *status_buffer, size_t buffer_size)
void crypto_rekey_abort(crypto_context_t *ctx)
bool crypto_is_ready(const crypto_context_t *ctx)
crypto_result_t crypto_validate_password(const char *password)
crypto_result_t crypto_rekey_process_request(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
asciichat_error_t crypto_sign_ephemeral_key(const private_key_t *private_key, const uint8_t *ephemeral_key, size_t ephemeral_key_size, uint8_t *signature_out)
bool crypto_secure_compare(const uint8_t *lhs, const uint8_t *rhs, size_t len)
crypto_result_t crypto_compute_hmac(crypto_context_t *ctx, const uint8_t key[32], const uint8_t data[32], uint8_t hmac[32])
#define CRYPTO_RESULT_STRING_COUNT
crypto_result_t crypto_derive_password_key(crypto_context_t *ctx, const char *password)
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)
crypto_result_t crypto_init_with_password(crypto_context_t *ctx, const char *password)
crypto_result_t crypto_create_public_key_packet(const crypto_context_t *ctx, uint8_t *packet_out, size_t packet_size, size_t *packet_len_out)
void crypto_destroy(crypto_context_t *ctx)
crypto_result_t crypto_rekey_init(crypto_context_t *ctx)
crypto_result_t crypto_random_bytes(uint8_t *buffer, size_t len)
crypto_result_t crypto_process_encrypted_packet(crypto_context_t *ctx, const uint8_t *packet, size_t packet_len, uint8_t *data_out, size_t data_size, size_t *data_len_out)
bool crypto_verify_auth_response(const crypto_context_t *ctx, const uint8_t nonce[32], const uint8_t expected_hmac[32])
crypto_result_t crypto_rekey_process_response(crypto_context_t *ctx, const uint8_t *peer_new_public_key)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
int format_duration_s(double seconds, char *buffer, size_t buffer_size)