ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
client.h File Reference

Go to the source code of this file.

Typedefs

typedef struct crypto_context_t crypto_context_t

Functions

int client_init (void)
int client_opus_encoder_init (int sample_rate, int channels, int bitrate)
int client_opus_decoder_init (int sample_rate, int channels)
int client_opus_encode (const int16_t *pcm_data, int frame_size, uint8_t *opus_data, int max_opus_bytes)
int client_opus_decode (const uint8_t *opus_data, int opus_bytes, int16_t *pcm_data, int frame_size, int decode_fec)
void client_opus_encoder_cleanup (void)
void client_opus_decoder_cleanup (void)
int client_set_server_address (const char *server_host, int server_port)
const uint8_t * client_public_key_bytes (void)
int client_handle_crypto_parameters (const uint8_t *packet, size_t packet_len)
int client_handle_key_exchange_init (const uint8_t *packet, size_t packet_len)
int client_handle_auth_challenge (const uint8_t *packet, size_t packet_len)
int client_handle_handshake_complete (const uint8_t *packet, size_t packet_len)
asciichat_error_t client_handshake_receive (uint32_t type, const void *payload, size_t length)
asciichat_error_t client_acds_handshake_receive (uint32_t type, const void *payload, size_t length)
int client_encrypt_packet (const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *out_len)
int client_decrypt_packet (const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *out_len)
asciichat_error_t client_settings_receive (const void *payload, size_t length)
char * client_settings_values (void)
asciichat_error_t client_validate_frame (const char *text, size_t length, uint32_t width, uint32_t height, uint32_t checksum, uint32_t flags, const void *metadata, size_t metadata_length)
asciichat_error_t client_decode_frame_data (const void *encoded, size_t encoded_length, void *output, size_t output_length)
int client_parse_ssh_private_key (const char *key_path, uint8_t *secret_key_out)
int client_parse_ssh_public_key (const char *key_line, uint8_t *public_key_out)
int client_parse_gpg_private_key (const char *armored_key, uint8_t *secret_key_out)
int client_parse_gpg_public_key (const char *armored_key, uint8_t *public_key_out)
int client_parse_gpg_private_key_binary (const uint8_t *key_data, size_t key_data_len, uint8_t *secret_key_out)
int client_parse_gpg_public_key_binary (const uint8_t *key_data, size_t key_data_len, uint8_t *public_key_out)
int client_get_crypto_error_message (char *output, size_t output_size)
crypto_context_t * client_security_context (void)
int client_configure_crypto_options (const char *password, const uint8_t *identity_secret_key, const uint8_t *expected_server_public_key)
void client_cleanup (void)
int client_generate_keypair (void)
int client_get_connection_state (void)
int client_resolve_crypto_policy (bool secure, int encryption)
asciichat_error_t client_handshake_begin (void)
asciichat_error_t client_acds_handshake_begin (uint8_t mode)

Typedef Documentation

◆ crypto_context_t

typedef struct crypto_context_t crypto_context_t

Browser-facing C interface implemented in client.c.

Definition at line 9 of file src/web/client.h.

Function Documentation

◆ client_acds_handshake_begin()

asciichat_error_t client_acds_handshake_begin ( uint8_t mode)

Definition at line 988 of file src/web/client.c.

988 {
989 g_crypto_handshake_ctx.crypto_ctx.encrypt_data = ACIP_CRYPTO_HAS_ENCRYPT(mode);
990 return acds_handshake_begin(&g_wasm_transport, mode);
991}
asciichat_error_t acds_handshake_begin(acip_transport_t *transport, uint8_t mode)
#define ACIP_CRYPTO_HAS_ENCRYPT(mode)
Check if crypto mode includes payload encryption.
Definition packet.h:1148

References acds_handshake_begin(), and ACIP_CRYPTO_HAS_ENCRYPT.

Referenced by EMSCRIPTEN_BINDINGS().

◆ client_acds_handshake_receive()

asciichat_error_t client_acds_handshake_receive ( uint32_t type,
const void * payload,
size_t length )

Definition at line 992 of file src/web/client.c.

992 {
993 asciichat_error_t result = acds_handshake_receive(&g_crypto_handshake_ctx, &g_wasm_transport, type, payload, length);
994 if (result != ASCIICHAT_OK) {
995 g_connection_state = CONNECTION_STATE_ERROR;
996 return result;
997 }
998 if (g_crypto_handshake_ctx.state == CRYPTO_HANDSHAKE_READY) {
999 g_handshake_complete = true;
1000 g_wasm_transport.crypto_ctx = &g_crypto_handshake_ctx.crypto_ctx;
1001 g_connection_state = CONNECTION_STATE_CONNECTED;
1002 } else {
1003 g_connection_state = CONNECTION_STATE_HANDSHAKE;
1004 }
1005 return ASCIICHAT_OK;
1006}
asciichat_error_t acds_handshake_receive(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t type, const void *payload, size_t length)
@ CRYPTO_HANDSHAKE_READY
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ CONNECTION_STATE_ERROR
@ CONNECTION_STATE_CONNECTED
@ CONNECTION_STATE_HANDSHAKE

References acds_handshake_receive(), ASCIICHAT_OK, CONNECTION_STATE_CONNECTED, CONNECTION_STATE_ERROR, CONNECTION_STATE_HANDSHAKE, and CRYPTO_HANDSHAKE_READY.

Referenced by client_handshake_receive().

◆ client_cleanup()

void client_cleanup ( void )

Definition at line 432 of file src/web/client.c.

432 {
433 remote_render_settings = (acip_render_settings_t){0};
435
436 // Clean up crypto handshake context
437 crypto_handshake_destroy(&g_crypto_handshake_ctx);
438 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
439
440 g_handshake_complete = false;
441 g_wasm_transport.crypto_ctx = NULL;
442 g_connection_state = CONNECTION_STATE_DISCONNECTED;
443 sodium_memzero(g_crypto_password, sizeof(g_crypto_password));
444 sodium_memzero(g_client_identity_key, sizeof(g_client_identity_key));
445 sodium_memzero(g_expected_server_identity_key, sizeof(g_expected_server_identity_key));
446 g_has_client_identity_key = false;
447 g_has_expected_server_identity_key = false;
448 // Keep module-wide options and buffer domains alive while browser renderers
449 // still reference them. Connection cleanup only resets authentication state.
450}
void errno_destroy(void)
void crypto_handshake_destroy(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
@ CONNECTION_STATE_DISCONNECTED
acip render settings keeps the values that move through the subsystem: caps, width,...

References CONNECTION_STATE_DISCONNECTED, crypto_handshake_destroy(), and errno_destroy().

Referenced by EMSCRIPTEN_BINDINGS(), and ClientSessionBinding::~ClientSessionBinding().

◆ client_configure_crypto_options()

int client_configure_crypto_options ( const char * password,
const uint8_t * identity_secret_key,
const uint8_t * expected_server_public_key )

Configure browser-provided authentication material for the next handshake.

Definition at line 221 of file src/web/client.c.

222 {
223 if (!g_initialized) {
224 SET_ERRNO(ERROR_INVALID_STATE, "Client is not initialized");
225 return -1;
226 }
227
228 sodium_memzero(g_crypto_password, sizeof(g_crypto_password));
229 sodium_memzero(g_client_identity_key, sizeof(g_client_identity_key));
230 sodium_memzero(g_expected_server_identity_key, sizeof(g_expected_server_identity_key));
231 g_has_client_identity_key = false;
232 g_has_expected_server_identity_key = false;
233
234 if (password && password[0] != '\0') {
235 size_t length = strnlen(password, sizeof(g_crypto_password));
236 if (length < 8 || length >= sizeof(g_crypto_password)) {
237 SET_ERRNO(ERROR_INVALID_PARAM, "Client password must contain 8 to %zu bytes", sizeof(g_crypto_password) - 1);
238 return -1;
239 }
240 SAFE_STRNCPY(g_crypto_password, password, sizeof(g_crypto_password) - 1);
241 }
242
243 if (identity_secret_key) {
244 uint8_t derived_public_key[crypto_sign_PUBLICKEYBYTES];
245 if (crypto_sign_ed25519_sk_to_pk(derived_public_key, identity_secret_key) != 0 ||
246 sodium_memcmp(derived_public_key, identity_secret_key + crypto_sign_SEEDBYTES, crypto_sign_PUBLICKEYBYTES) !=
247 0) {
248 sodium_memzero(derived_public_key, sizeof(derived_public_key));
249 SET_ERRNO(ERROR_INVALID_PARAM, "Client identity secret key is invalid");
250 return -1;
251 }
252 memcpy(g_client_identity_key, identity_secret_key, sizeof(g_client_identity_key));
253 sodium_memzero(derived_public_key, sizeof(derived_public_key));
254 g_has_client_identity_key = true;
255 }
256
257 if (expected_server_public_key) {
258 memcpy(g_expected_server_identity_key, expected_server_public_key, sizeof(g_expected_server_identity_key));
259 g_has_expected_server_identity_key = true;
260 }
261 return 0;
262}
#define SET_ERRNO(code,...)
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
unsigned char uint8_t
Definition common.h:60
@ ERROR_INVALID_STATE
@ ERROR_INVALID_PARAM

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, SAFE_STRNCPY, and SET_ERRNO.

◆ client_decode_frame_data()

asciichat_error_t client_decode_frame_data ( const void * encoded,
size_t encoded_length,
void * output,
size_t output_length )

Definition at line 1102 of file src/web/client.c.

1103 {
1104 if (!encoded || !output || !encoded_length || encoded_length > FRAME_RENDER_MAX_BYTES || !output_length ||
1105 output_length > FRAME_RENDER_MAX_BYTES)
1106 return SET_ERRNO(ERROR_INVALID_FRAME, "Invalid browser frame decompression bounds");
1107 return decompress_data_exact(encoded, encoded_length, output, output_length);
1108}
asciichat_error_t decompress_data_exact(const void *input, size_t input_size, void *output, size_t expected_size)
@ ERROR_INVALID_FRAME
#define FRAME_RENDER_MAX_BYTES

References decompress_data_exact(), ERROR_INVALID_FRAME, FRAME_RENDER_MAX_BYTES, and SET_ERRNO.

◆ client_decrypt_packet()

int client_decrypt_packet ( const uint8_t * ciphertext,
size_t ciphertext_len,
uint8_t * plaintext,
size_t plaintext_size,
size_t * out_len )

Decrypt a ciphertext packet

Parameters
ciphertextInput ciphertext data
ciphertext_lenLength of ciphertext
plaintextOutput buffer (allocated by caller, must be at least ciphertext_len)
plaintext_sizeSize of plaintext buffer
out_lenOutput parameter for plaintext length
Returns
0 on success, -1 on error

Definition at line 814 of file src/web/client.c.

815 {
816 if (!g_handshake_complete) {
817 SET_ERRNO(ERROR_INVALID_STATE, "Client handshake is not complete");
818 return -1;
819 }
820
821 if (!ciphertext || !plaintext || !out_len || plaintext_size < ciphertext_len) {
822 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client decryption buffers or lengths");
823 return -1;
824 }
825 *out_len = 0;
826 uint8_t *plain = NULL;
827 size_t length = 0;
828 uint64_t sequence;
829 bool consumed = false;
830 asciichat_error_t result =
832 ciphertext, ciphertext_len, &plain, &length, &sequence, &consumed);
833 if (result == ASCIICHAT_OK && !consumed) {
834 memcpy(plaintext, plain, length);
835 *out_len = length;
836 }
837 buffer_release(plain);
838 return result == ASCIICHAT_OK ? 0 : -1;
839}
#define buffer_release(data)
Definition buffer.h:91
unsigned long long uint64_t
Definition common.h:63
asciichat_error_t acip_security_decrypt(crypto_context_t *crypto, unsigned flow, unsigned delivery, const void *payload, size_t length, uint8_t **output, size_t *written, uint64_t *sequence, bool *consumed)
#define CRYPTO_RECORD_ANY_CHANNEL
Definition record.h:11
@ ACIP_DELIVERY_RELIABLE
Definition traffic.h:12

References ACIP_DELIVERY_RELIABLE, acip_security_decrypt(), ASCIICHAT_OK, buffer_release, CRYPTO_RECORD_ANY_CHANNEL, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.

◆ client_encrypt_packet()

int client_encrypt_packet ( const uint8_t * plaintext,
size_t plaintext_len,
uint8_t * ciphertext,
size_t ciphertext_size,
size_t * out_len )

Encrypt a plaintext packet

Parameters
plaintextInput plaintext data
plaintext_lenLength of plaintext
ciphertextOutput buffer (allocated by caller, must be at least plaintext_len + crypto_aead_overhead)
ciphertext_sizeSize of ciphertext buffer
out_lenOutput parameter for ciphertext length
Returns
0 on success, -1 on error

Definition at line 773 of file src/web/client.c.

774 {
775 if (!g_handshake_complete) {
776 SET_ERRNO(ERROR_INVALID_STATE, "Client handshake is not complete");
777 return -1;
778 }
779
780 const size_t overhead = 32 + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
781 if (!plaintext || !ciphertext || !out_len || plaintext_len > SIZE_MAX - overhead ||
782 ciphertext_size < plaintext_len + overhead) {
783 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client encryption buffers or lengths");
784 return -1;
785 }
786 uint8_t *wire = NULL;
787 size_t length = 0;
788 packet_header_t header;
789 if (plaintext_len < sizeof(header)) {
790 SET_ERRNO(ERROR_INVALID_PARAM, "Client plaintext packet is shorter than its header");
791 return -1;
792 }
793 memcpy(&header, plaintext, sizeof(header));
795 asciichat_error_t result = acip_security_seal(&g_crypto_handshake_ctx.crypto_ctx, channel, ACIP_DELIVERY_RELIABLE,
796 plaintext, plaintext_len, &wire, &length);
797 if (result == ASCIICHAT_OK) {
798 *out_len = length - sizeof(packet_header_t);
799 memcpy(ciphertext, wire + sizeof(packet_header_t), *out_len);
800 }
801 buffer_release(wire);
802 return result == ASCIICHAT_OK ? 0 : -1;
803}
#define NET_TO_HOST_U16(val)
Definition endian.h:111
asciichat_error_t acip_security_seal(crypto_context_t *crypto, acip_traffic_class_t flow, acip_delivery_t delivery, const void *packet, size_t length, uint8_t **output, size_t *written)
#define CRYPTO_NONCE_SIZE
Nonce size (XSalsa20).
Definition sizes.h:79
#define CRYPTO_MAC_SIZE
MAC size (Poly1305).
Definition sizes.h:85
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628
acip_traffic_class_t acip_packet_traffic_class(uint16_t type)
Definition traffic.c:4
acip_traffic_class_t
Definition traffic.h:5

References ACIP_DELIVERY_RELIABLE, acip_packet_traffic_class(), acip_security_seal(), ASCIICHAT_OK, buffer_release, CRYPTO_MAC_SIZE, CRYPTO_NONCE_SIZE, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, NET_TO_HOST_U16, and SET_ERRNO.

◆ client_generate_keypair()

int client_generate_keypair ( void )

Generate client keypair for handshake

Returns
0 on success, -1 on error

Definition at line 460 of file src/web/client.c.

460 {
461
462 if (!g_initialized) {
463 SET_ERRNO(ERROR_INVALID_STATE, "Client is not initialized");
464 return -1;
465 }
466
467 // A replacement handshake must not retain the previous connection's sequence state.
468 // Reset handshake context if previously used (reconnection / React Strict Mode remount)
469 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
470 crypto_handshake_destroy(&g_crypto_handshake_ctx);
471 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
472 g_handshake_complete = false;
473 }
474
475 // Initialize crypto handshake context
476 asciichat_error_t result = crypto_handshake_init("wasm-client", &g_crypto_handshake_ctx, false /* is_server */);
477 if (result != ASCIICHAT_OK) {
478 return -1;
479 }
480 if (apply_client_crypto_options() != 0) {
481 crypto_handshake_destroy(&g_crypto_handshake_ctx);
482 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
483 return -1;
484 }
485
486 g_connection_state = CONNECTION_STATE_DISCONNECTED;
487 return 0;
488}
asciichat_error_t crypto_handshake_init(const char *name, crypto_handshake_context_t *ctx, bool is_server)
Initialize named crypto handshake context.
@ CRYPTO_HANDSHAKE_DISABLED

References ASCIICHAT_OK, CONNECTION_STATE_DISCONNECTED, crypto_handshake_destroy(), CRYPTO_HANDSHAKE_DISABLED, crypto_handshake_init(), ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by EMSCRIPTEN_BINDINGS().

◆ client_get_connection_state()

int client_get_connection_state ( void )

Get current connection state

Returns
Connection state enum value

Definition at line 849 of file src/web/client.c.

849 {
850 return (int)g_connection_state;
851}

Referenced by EMSCRIPTEN_BINDINGS().

◆ client_get_crypto_error_message()

int client_get_crypto_error_message ( char * output,
size_t output_size )

Copy the most recent C crypto error into a caller-provided buffer.

Definition at line 378 of file src/web/client.c.

378 {
379 if (!output || output_size == 0) {
380 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid crypto error output buffer");
381 return -1;
382 }
383 asciichat_error_context_t context = {0};
384 if (!HAS_ERRNO(&context) || !context.context_message[0])
385 return -1;
386 safe_snprintf(output, output_size, "%s", context.context_message);
387 return 0;
388}
#define HAS_ERRNO(context)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.

References asciichat_error_context_t::context_message, ERROR_INVALID_PARAM, HAS_ERRNO, safe_snprintf(), and SET_ERRNO.

◆ client_handle_auth_challenge()

int client_handle_auth_challenge ( const uint8_t * packet,
size_t packet_len )

Handle CRYPTO_AUTH_CHALLENGE packet from server

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 666 of file src/web/client.c.

666 {
667
668 if (!packet || packet_len == 0) {
669 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client authentication challenge packet");
670 return -1;
671 }
672
673 // Extract packet type and payload
674 if (packet_len < sizeof(packet_header_t)) {
675 SET_ERRNO(ERROR_INVALID_PARAM, "Client authentication challenge is shorter than its header");
676 return -1;
677 }
678
679 const packet_header_t *header = (const packet_header_t *)packet;
680 packet_type_t packet_type = ntohs(header->type);
681 const uint8_t *payload_src = packet + sizeof(packet_header_t);
682 size_t payload_len = packet_len - sizeof(packet_header_t);
683
684 // Allocate payload copy from buffer domain (crypto function takes ownership and frees it)
685 uint8_t *payload = NULL;
686 if (payload_len > 0) {
687 payload = buffer_alloc(NULL, payload_len);
688 if (!payload) {
689 g_connection_state = CONNECTION_STATE_ERROR;
690 return -1;
691 }
692 memcpy(payload, payload_src, payload_len);
693 }
694
695 // Process auth challenge
696 asciichat_error_t result = crypto_handshake_client_auth_response(&g_crypto_handshake_ctx, &g_wasm_transport,
697 packet_type, payload, payload_len);
698
699 if (result != ASCIICHAT_OK) {
700 g_connection_state = CONNECTION_STATE_ERROR;
701 return -1;
702 }
703
704 return 0;
705}
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:301
asciichat_error_t crypto_handshake_client_auth_response(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Client: Process auth challenge and send response.

References ASCIICHAT_OK, buffer_alloc(), CONNECTION_STATE_ERROR, crypto_handshake_client_auth_response(), ERROR_INVALID_PARAM, and SET_ERRNO.

◆ client_handle_crypto_parameters()

int client_handle_crypto_parameters ( const uint8_t * packet,
size_t packet_len )

Handle CRYPTO_PARAMETERS packet from server Sets up the key size negotiation before KEY_EXCHANGE_INIT

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 600 of file src/web/client.c.

600 {
601
602 if (!packet || packet_len == 0) {
603 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client crypto parameters packet");
604 return -1;
605 }
606
607 // Discovery sends CRYPTO_PARAMETERS before KEY_EXCHANGE_INIT. Initialize
608 // here so the negotiated values survive the subsequent key exchange packet.
609 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_INIT) {
610 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
611 crypto_handshake_destroy(&g_crypto_handshake_ctx);
612 }
613 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
614 if (crypto_handshake_init("wasm-client", &g_crypto_handshake_ctx, false) != ASCIICHAT_OK) {
615 return -1;
616 }
617 if (apply_client_crypto_options() != 0)
618 return -1;
619 }
620
621 // Extract packet type and payload
622 if (packet_len < sizeof(packet_header_t)) {
623 SET_ERRNO(ERROR_INVALID_PARAM, "Client crypto parameters packet is shorter than its header");
624 return -1;
625 }
626
627 const packet_header_t *header = (const packet_header_t *)packet;
628 packet_type_t packet_type = ntohs(header->type);
629 const uint8_t *payload_src = packet + sizeof(packet_header_t);
630 size_t payload_len = packet_len - sizeof(packet_header_t);
631
632 // Allocate payload copy from buffer domain
633 uint8_t *payload = NULL;
634 if (payload_len > 0) {
635 payload = buffer_alloc(NULL, payload_len);
636 if (!payload) {
637 g_connection_state = CONNECTION_STATE_ERROR;
638 return -1;
639 }
640 memcpy(payload, payload_src, payload_len);
641 }
642
643 // Process crypto parameters
644 asciichat_error_t result =
645 crypto_handshake_set_parameters(&g_crypto_handshake_ctx, (const crypto_parameters_packet_t *)payload);
646
647 // Free payload
648 if (payload) {
649 buffer_release(payload);
650 }
651
652 if (result != ASCIICHAT_OK) {
653 g_connection_state = CONNECTION_STATE_ERROR;
654 return -1;
655 }
656
657 return 0;
658}
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.
@ CRYPTO_HANDSHAKE_INIT
Crypto parameters packet structure (Packet Type 15).
Definition packet.h:1037

References ASCIICHAT_OK, buffer_alloc(), buffer_release, CONNECTION_STATE_ERROR, crypto_handshake_destroy(), CRYPTO_HANDSHAKE_DISABLED, CRYPTO_HANDSHAKE_INIT, crypto_handshake_init(), crypto_handshake_set_parameters(), ERROR_INVALID_PARAM, and SET_ERRNO.

◆ client_handle_handshake_complete()

int client_handle_handshake_complete ( const uint8_t * packet,
size_t packet_len )

Handle CRYPTO_HANDSHAKE_COMPLETE packet from server

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 713 of file src/web/client.c.

713 {
714
715 if (!packet || packet_len == 0) {
716 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client handshake completion packet");
717 return -1;
718 }
719
720 // Extract packet type and payload
721 if (packet_len < sizeof(packet_header_t)) {
722 SET_ERRNO(ERROR_INVALID_PARAM, "Client handshake completion is shorter than its header");
723 return -1;
724 }
725
726 const packet_header_t *header = (const packet_header_t *)packet;
727 packet_type_t packet_type = ntohs(header->type);
728 const uint8_t *payload_src = packet + sizeof(packet_header_t);
729 size_t payload_len = packet_len - sizeof(packet_header_t);
730
731 // Allocate payload copy from buffer domain (crypto function takes ownership and frees it)
732 uint8_t *payload = NULL;
733 if (payload_len > 0) {
734 payload = buffer_alloc(NULL, payload_len);
735 if (!payload) {
736 g_connection_state = CONNECTION_STATE_ERROR;
737 return -1;
738 }
739 memcpy(payload, payload_src, payload_len);
740 }
741
742 // Complete handshake (takes ownership of payload and will free it)
743 asciichat_error_t result =
744 crypto_handshake_client_complete(&g_crypto_handshake_ctx, &g_wasm_transport, packet_type, payload, payload_len);
745
746 if (result != ASCIICHAT_OK) {
747 g_connection_state = CONNECTION_STATE_ERROR;
748 return -1;
749 }
750
751 g_handshake_complete = true;
752 g_connection_state = CONNECTION_STATE_CONNECTED;
753
754 // Update transport with the crypto context so ACIP can encrypt/decrypt packets
755 g_wasm_transport.crypto_ctx = &g_crypto_handshake_ctx.crypto_ctx;
756
757 return 0;
758}
asciichat_error_t crypto_handshake_client_complete(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Client: Wait for handshake complete confirmation.

References ASCIICHAT_OK, buffer_alloc(), CONNECTION_STATE_CONNECTED, CONNECTION_STATE_ERROR, crypto_handshake_client_complete(), ERROR_INVALID_PARAM, and SET_ERRNO.

◆ client_handle_key_exchange_init()

int client_handle_key_exchange_init ( const uint8_t * packet,
size_t packet_len )

Handle CRYPTO_KEY_EXCHANGE_INIT packet from server This is the first step of the crypto handshake

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 529 of file src/web/client.c.

529 {
530
531 // Safety check: if handshake context is not in INIT state, reinitialize it
532 // This handles cases where previous handshakes weren't properly cleaned up
533 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_INIT) {
534
535 // Destroy and reset
536 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
537 crypto_handshake_destroy(&g_crypto_handshake_ctx);
538 }
539 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
540
541 // Reinitialize to INIT state
542 asciichat_error_t init_result = crypto_handshake_init("wasm-client", &g_crypto_handshake_ctx, false);
543 if (init_result != ASCIICHAT_OK) {
544 return -1;
545 }
546 if (apply_client_crypto_options() != 0)
547 return -1;
548 }
549
550 if (!packet || packet_len == 0) {
551 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client handshake packet");
552 return -1;
553 }
554
555 // Extract packet type and payload
556 if (packet_len < sizeof(packet_header_t)) {
557 SET_ERRNO(ERROR_INVALID_PARAM, "Client handshake packet is shorter than its header");
558 return -1;
559 }
560
561 const packet_header_t *header = (const packet_header_t *)packet;
562 packet_type_t packet_type = ntohs(header->type);
563 const uint8_t *payload_src = packet + sizeof(packet_header_t);
564 size_t payload_len = packet_len - sizeof(packet_header_t);
565
566 // Allocate payload copy from buffer domain (crypto function takes ownership and frees it)
567 // The raw packet pointer from JS cannot be passed directly because the crypto
568 // handshake function calls buffer_release() on the payload when done.
569 uint8_t *payload = NULL;
570 if (payload_len > 0) {
571 payload = buffer_alloc(NULL, payload_len);
572 if (!payload) {
573 g_connection_state = CONNECTION_STATE_ERROR;
574 return -1;
575 }
576 memcpy(payload, payload_src, payload_len);
577 }
578
579 // Process key exchange using transport-abstracted handshake
580
581 asciichat_error_t result = crypto_handshake_client_key_exchange(&g_crypto_handshake_ctx, &g_wasm_transport,
582 packet_type, payload, payload_len);
583
584 if (result != ASCIICHAT_OK) {
585 g_connection_state = CONNECTION_STATE_ERROR;
586 return -1;
587 }
588
589 g_connection_state = CONNECTION_STATE_HANDSHAKE;
590 return 0;
591}
asciichat_error_t crypto_handshake_client_key_exchange(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Client: Process server's public key and send our public key.

References ASCIICHAT_OK, buffer_alloc(), CONNECTION_STATE_ERROR, CONNECTION_STATE_HANDSHAKE, crypto_handshake_client_key_exchange(), crypto_handshake_destroy(), CRYPTO_HANDSHAKE_DISABLED, CRYPTO_HANDSHAKE_INIT, crypto_handshake_init(), ERROR_INVALID_PARAM, and SET_ERRNO.

◆ client_handshake_begin()

asciichat_error_t client_handshake_begin ( void )

Definition at line 1025 of file src/web/client.c.

1025 {
1026 g_connection_state = CONNECTION_STATE_CONNECTING;
1027 g_version_received = false;
1028 g_crypto_handshake_ctx.crypto_ctx.encrypt_data = ACIP_CRYPTO_HAS_ENCRYPT(g_crypto_mode);
1029 return acds_handshake_begin(&g_wasm_transport, g_crypto_mode);
1030}
@ CONNECTION_STATE_CONNECTING

References acds_handshake_begin(), ACIP_CRYPTO_HAS_ENCRYPT, and CONNECTION_STATE_CONNECTING.

Referenced by EMSCRIPTEN_BINDINGS().

◆ client_handshake_receive()

asciichat_error_t client_handshake_receive ( uint32_t type,
const void * payload,
size_t length )

Definition at line 1032 of file src/web/client.c.

1032 {
1033 if (!g_version_received && type != PACKET_TYPE_PROTOCOL_VERSION)
1034 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Expected server protocol version first");
1035 if (type == PACKET_TYPE_PROTOCOL_VERSION) {
1036 if (g_version_received)
1037 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Duplicate server protocol version");
1038 if (!payload || length != sizeof(protocol_version_packet_t))
1039 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid server protocol version");
1040 const protocol_version_packet_t *version = payload;
1042 version->supports_encryption != g_crypto_mode)
1043 return SET_ERRNO(ERROR_CRYPTO, "Server rejected requested security policy");
1044 g_version_received = true;
1045 if (g_crypto_mode == ACIP_CRYPTO_NONE) {
1046 g_connection_state = CONNECTION_STATE_CONNECTED;
1047 return ASCIICHAT_OK;
1048 }
1056 caps.requires_verification = g_has_expected_server_identity_key;
1057 asciichat_error_t result =
1058 packet_send_via_transport(&g_wasm_transport, PACKET_TYPE_CRYPTO_CAPABILITIES, &caps, sizeof(caps), 0);
1059 if (result != ASCIICHAT_OK || !g_has_expected_server_identity_key)
1060 return result;
1061 return packet_send_via_transport(&g_wasm_transport, PACKET_TYPE_CRYPTO_CLIENT_HELLO, g_expected_server_identity_key,
1062 sizeof(g_expected_server_identity_key), 0);
1063 }
1064 if (type == PACKET_TYPE_CRYPTO_PARAMETERS) {
1065 if (!payload || length != sizeof(crypto_parameters_packet_t) ||
1066 (((const crypto_parameters_packet_t *)payload)->selected_cipher != CIPHER_ALGO_NONE) !=
1067 ACIP_CRYPTO_HAS_ENCRYPT(g_crypto_mode))
1068 return SET_ERRNO(ERROR_CRYPTO, "Server changed requested payload encryption");
1069 }
1070 return client_acds_handshake_receive(type, payload, length);
1071}
#define HOST_TO_NET_U16(val)
Definition endian.h:96
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_CRYPTO
Definition error_codes.h:93
#define AUTH_ALGO_ED25519
Ed25519 authentication (Edwards-curve signatures).
Definition packet.h:1121
#define ACIP_CRYPTO_NONE
No encryption, no authentication (plaintext only).
Definition packet.h:1138
#define ACIP_CRYPTO_HAS_AUTH(mode)
Check if crypto mode includes authentication.
Definition packet.h:1155
#define KEX_ALGO_X25519
X25519 key exchange (Curve25519).
Definition packet.h:1120
#define AUTH_ALGO_NONE
No authentication (plaintext mode).
Definition packet.h:1122
#define CIPHER_ALGO_NONE
No encryption (plaintext mode).
Definition packet.h:1124
#define CIPHER_ALGO_XSALSA20_POLY1305
XSalsa20-Poly1305 authenticated encryption.
Definition packet.h:1123
@ PACKET_TYPE_CRYPTO_CLIENT_HELLO
Client -> Server: Expected server key fingerprint for multi-key selection (UNENCRYPTED).
Definition packet.h:314
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:303
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED).
Definition packet.h:324
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED).
Definition packet.h:322
#define PROTOCOL_VERSION_MAJOR
Major protocol version number.
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)
Definition send.c:190
asciichat_error_t client_acds_handshake_receive(uint32_t type, const void *payload, size_t length)
Crypto capabilities packet structure (Packet Type 14).
Definition packet.h:1008
uint8_t preferred_auth
Preferred authentication algorithm (AUTH_ALGO_*).
Definition packet.h:1020
uint16_t supported_auth_algorithms
Supported authentication algorithms bitmask (AUTH_ALGO_*).
Definition packet.h:1012
uint16_t supported_kex_algorithms
Supported key exchange algorithms bitmask (KEX_ALGO_*).
Definition packet.h:1010
uint8_t preferred_cipher
Preferred cipher algorithm (CIPHER_ALGO_*).
Definition packet.h:1022
uint16_t supported_cipher_algorithms
Supported cipher algorithms bitmask (CIPHER_ALGO_*).
Definition packet.h:1014
uint8_t preferred_kex
Preferred key exchange algorithm (KEX_ALGO_*).
Definition packet.h:1018
uint8_t requires_verification
Server verification requirement flag (1=required, 0=optional).
Definition packet.h:1016
Protocol version negotiation packet structure (Packet Type 1).
Definition packet.h:860
uint16_t protocol_version
Major protocol version (must match for compatibility).
Definition packet.h:862
uint8_t supports_encryption
Encryption & authentication mode bitmask (ACIP_CRYPTO_* constants: NONE, ENCRYPT, AUTH,...
Definition packet.h:868

References ACIP_CRYPTO_HAS_AUTH, ACIP_CRYPTO_HAS_ENCRYPT, ACIP_CRYPTO_NONE, ASCIICHAT_OK, AUTH_ALGO_ED25519, AUTH_ALGO_NONE, CIPHER_ALGO_NONE, CIPHER_ALGO_XSALSA20_POLY1305, client_acds_handshake_receive(), CONNECTION_STATE_CONNECTED, ERROR_CRYPTO, ERROR_NETWORK_PROTOCOL, HOST_TO_NET_U16, KEX_ALGO_X25519, NET_TO_HOST_U16, packet_send_via_transport(), PACKET_TYPE_CRYPTO_CAPABILITIES, PACKET_TYPE_CRYPTO_CLIENT_HELLO, PACKET_TYPE_CRYPTO_PARAMETERS, PACKET_TYPE_PROTOCOL_VERSION, crypto_capabilities_packet_t::preferred_auth, crypto_capabilities_packet_t::preferred_cipher, crypto_capabilities_packet_t::preferred_kex, protocol_version_packet_t::protocol_version, PROTOCOL_VERSION_MAJOR, crypto_capabilities_packet_t::requires_verification, SET_ERRNO, crypto_capabilities_packet_t::supported_auth_algorithms, crypto_capabilities_packet_t::supported_cipher_algorithms, crypto_capabilities_packet_t::supported_kex_algorithms, and protocol_version_packet_t::supports_encryption.

◆ client_init()

int client_init ( void )

Initialize client mode with native defaults.

Returns
0 on success, -1 on error

Definition at line 398 of file src/web/client.c.

398 {
399 // The module, renderers, and allocator outlive individual connections.
400 if (g_initialized)
401 return 0;
402 char *argv[] = {"client", NULL};
403 asciichat_error_t err = options_init(1, argv);
404 if (err != ASCIICHAT_OK)
405 return -1;
406
407 // Initialize platform layer
408 err = platform_init();
409 if (err != ASCIICHAT_OK) {
410 return -1;
411 }
412
413 // Honor the requested verbosity and let INFO use stdout (console.log).
414 log_init(NULL, GET_OPTION(log_level), false, false);
415 // Use debug-style format in WASM so browser console shows full log headers
417 log_info("WASM client initialized via logging system");
418
419 if (err != ASCIICHAT_OK) {
420 return -1;
421 }
422
423 // Initialize ANSI color code generation
425
426 g_initialized = true;
427 g_connection_state = CONNECTION_STATE_DISCONNECTED;
428
429 return 0;
430}
void ansi_fast_init(void)
Definition ansi.c:227
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
Initialize the logging system.
Definition log.c:569
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
asciichat_error_t log_set_format(const char *format_str, bool console_only)
Set a custom log format string.
Definition log.c:790
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
asciichat_error_t options_init(int argc, char **argv)
Initialize options by parsing command-line arguments with unified mode detection.
Definition options.c:2417
#define OPT_LOG_TEMPLATE_DEFAULT_DEBUG
Default log format string - debug mode (verbose with thread name, file relative path,...
Definition options.h:378
asciichat_error_t platform_init(void)
Initialize platform-specific subsystems.

References ansi_fast_init(), ASCIICHAT_OK, CONNECTION_STATE_DISCONNECTED, GET_OPTION, log_info, log_init(), log_set_format(), OPT_LOG_TEMPLATE_DEFAULT_DEBUG, options_init(), and platform_init().

◆ client_opus_decode()

int client_opus_decode ( const uint8_t * opus_data,
int opus_bytes,
int16_t * pcm_data,
int frame_size,
int decode_fec )

Decode Opus audio to PCM

Parameters
opus_dataInput Opus data
opus_bytesLength of Opus data
pcm_dataOutput buffer for PCM samples (Int16)
frame_sizeMaximum number of samples per channel
decode_fecWhether to use FEC
Returns
Number of samples decoded per channel, or -1 on error

Definition at line 948 of file src/web/client.c.

948 {
949 if (!g_opus_decoder) {
950 SET_ERRNO(ERROR_INVALID_STATE, "Opus decoder is not initialized");
951 return -1;
952 }
953
954 if ((!opus_data && opus_bytes > 0) || opus_bytes < 0 || !pcm_data || frame_size <= 0) {
955 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid Opus decoder buffers or lengths");
956 return -1;
957 }
958
959 int decoded_samples = opus_decode(g_opus_decoder, opus_data, opus_bytes, pcm_data, frame_size, decode_fec);
960 if (decoded_samples < 0) {
961 SET_ERRNO(ERROR_INVALID_PARAM, "Opus decoding failed: %s", opus_strerror(decoded_samples));
962 return -1;
963 }
964
965 return decoded_samples;
966}

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by OpusCodecBinding::decode().

◆ client_opus_decoder_cleanup()

void client_opus_decoder_cleanup ( void )

Cleanup Opus decoder

Definition at line 981 of file src/web/client.c.

981 {
982 if (g_opus_decoder) {
983 opus_decoder_destroy(g_opus_decoder);
984 g_opus_decoder = NULL;
985 }
986}

Referenced by OpusCodecBinding::~OpusCodecBinding().

◆ client_opus_decoder_init()

int client_opus_decoder_init ( int sample_rate,
int channels )

Initialize Opus decoder

Parameters
sample_rateSample rate (8000, 16000, 24000, or 48000)
channelsNumber of channels (1=mono, 2=stereo)
Returns
0 on success, -1 on error

Definition at line 895 of file src/web/client.c.

895 {
896 if (g_opus_decoder) {
897 opus_decoder_destroy(g_opus_decoder);
898 g_opus_decoder = NULL;
899 }
900
901 int error;
902 g_opus_decoder = opus_decoder_create(sample_rate, channels, &error);
903 if (error != OPUS_OK || !g_opus_decoder) {
904 SET_ERRNO(ERROR_INVALID_PARAM, "Cannot initialize Opus decoder: %s", opus_strerror(error));
905 return -1;
906 }
907
908 return 0;
909}

References ERROR_INVALID_PARAM, and SET_ERRNO.

Referenced by OpusCodecBinding::OpusCodecBinding().

◆ client_opus_encode()

int client_opus_encode ( const int16_t * pcm_data,
int frame_size,
uint8_t * opus_data,
int max_opus_bytes )

Encode PCM audio to Opus

Parameters
pcm_dataInput PCM data (Int16 samples)
frame_sizeNumber of samples per channel
opus_dataOutput buffer for Opus data
max_opus_bytesMaximum size of output buffer
Returns
Number of bytes encoded, or -1 on error

Definition at line 919 of file src/web/client.c.

919 {
920 if (!g_opus_encoder) {
921 SET_ERRNO(ERROR_INVALID_STATE, "Opus encoder is not initialized");
922 return -1;
923 }
924
925 if (!pcm_data || !opus_data || frame_size <= 0 || max_opus_bytes <= 0) {
926 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid Opus encoder buffers or lengths");
927 return -1;
928 }
929
930 int encoded_bytes = opus_encode(g_opus_encoder, pcm_data, frame_size, opus_data, max_opus_bytes);
931 if (encoded_bytes < 0) {
932 SET_ERRNO(ERROR_INVALID_PARAM, "Opus encoding failed: %s", opus_strerror(encoded_bytes));
933 return -1;
934 }
935
936 return encoded_bytes;
937}

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by OpusCodecBinding::encode().

◆ client_opus_encoder_cleanup()

void client_opus_encoder_cleanup ( void )

Cleanup Opus encoder

Definition at line 971 of file src/web/client.c.

971 {
972 if (g_opus_encoder) {
973 opus_encoder_destroy(g_opus_encoder);
974 g_opus_encoder = NULL;
975 }
976}

Referenced by OpusCodecBinding::OpusCodecBinding(), and OpusCodecBinding::~OpusCodecBinding().

◆ client_opus_encoder_init()

int client_opus_encoder_init ( int sample_rate,
int channels,
int bitrate )

Initialize Opus encoder

Parameters
sample_rateSample rate (8000, 16000, 24000, or 48000)
channelsNumber of channels (1=mono, 2=stereo)
bitrateTarget bitrate in bits/sec
Returns
0 on success, -1 on error

Definition at line 864 of file src/web/client.c.

864 {
865 if (g_opus_encoder) {
866 opus_encoder_destroy(g_opus_encoder);
867 g_opus_encoder = NULL;
868 }
869
870 int error;
871 g_opus_encoder = opus_encoder_create(sample_rate, channels,
872 bitrate >= 128000 ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error);
873 if (error != OPUS_OK || !g_opus_encoder) {
874 SET_ERRNO(ERROR_INVALID_PARAM, "Cannot initialize Opus encoder: %s", opus_strerror(error));
875 return -1;
876 }
877
878 // Set bitrate
879 if (opus_encoder_ctl(g_opus_encoder, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) {
880 opus_encoder_destroy(g_opus_encoder);
881 g_opus_encoder = NULL;
882 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid Opus encoder bitrate: %d", bitrate);
883 return -1;
884 }
885
886 return 0;
887}
@ OPUS_APPLICATION_AUDIO
General audio (optimized for music).
Definition opus.h:86
@ OPUS_APPLICATION_VOIP
Voice over IP (optimized for speech).
Definition opus.h:85

References ERROR_INVALID_PARAM, OPUS_APPLICATION_AUDIO, OPUS_APPLICATION_VOIP, and SET_ERRNO.

Referenced by OpusCodecBinding::OpusCodecBinding().

◆ client_parse_gpg_private_key()

int client_parse_gpg_private_key ( const char * armored_key,
uint8_t * secret_key_out )

Parse an unencrypted armored OpenPGP Ed25519 private key through the shared C parser.

Definition at line 316 of file src/web/client.c.

316 {
317 if (!armored_key || !secret_key_out) {
318 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid OpenPGP private key input or output");
319 return -1;
320 }
321 uint8_t public_key[crypto_sign_PUBLICKEYBYTES] = {0};
322 uint8_t seed[crypto_sign_SEEDBYTES] = {0};
323 uint8_t derived_public_key[crypto_sign_PUBLICKEYBYTES] = {0};
324 uint8_t secret_key[crypto_sign_SECRETKEYBYTES] = {0};
325 int result = -1;
326
327 if (openpgp_parse_armored_seckey(armored_key, public_key, seed) != ASCIICHAT_OK)
328 goto cleanup;
329 if (crypto_sign_seed_keypair(derived_public_key, secret_key, seed) != 0 ||
330 sodium_memcmp(derived_public_key, public_key, sizeof(public_key)) != 0) {
331 SET_ERRNO(ERROR_CRYPTO_KEY, "OpenPGP private key does not match its Ed25519 public key");
332 goto cleanup;
333 }
334
335 memcpy(secret_key_out, secret_key, sizeof(secret_key));
336 result = 0;
337
338cleanup:
339 sodium_memzero(public_key, sizeof(public_key));
340 sodium_memzero(seed, sizeof(seed));
341 sodium_memzero(derived_public_key, sizeof(derived_public_key));
342 sodium_memzero(secret_key, sizeof(secret_key));
343 return result;
344}
@ ERROR_CRYPTO_KEY
Definition error_codes.h:94
asciichat_error_t openpgp_parse_armored_seckey(const char *armored_text, uint8_t ed25519_pk[ED25519_PUBLIC_KEY_SIZE], uint8_t ed25519_sk[ED25519_SEED_SIZE])
Definition openpgp.c:756

References ASCIICHAT_OK, ERROR_CRYPTO_KEY, ERROR_INVALID_PARAM, openpgp_parse_armored_seckey(), and SET_ERRNO.

◆ client_parse_gpg_private_key_binary()

int client_parse_gpg_private_key_binary ( const uint8_t * key_data,
size_t key_data_len,
uint8_t * secret_key_out )

Parse an unencrypted binary OpenPGP Ed25519 secret-key packet stream.

Definition at line 347 of file src/web/client.c.

347 {
348 if (!key_data || key_data_len == 0 || !secret_key_out) {
349 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid binary OpenPGP private key input or output");
350 return -1;
351 }
352 uint8_t public_key[crypto_sign_PUBLICKEYBYTES] = {0};
353 uint8_t seed[crypto_sign_SEEDBYTES] = {0};
354 uint8_t derived_public_key[crypto_sign_PUBLICKEYBYTES] = {0};
355 uint8_t secret_key[crypto_sign_SECRETKEYBYTES] = {0};
356 int result = -1;
357
358 if (openpgp_parse_binary_seckey(key_data, key_data_len, public_key, seed) != ASCIICHAT_OK)
359 goto cleanup;
360 if (crypto_sign_seed_keypair(derived_public_key, secret_key, seed) != 0 ||
361 sodium_memcmp(derived_public_key, public_key, sizeof(public_key)) != 0) {
362 SET_ERRNO(ERROR_CRYPTO_KEY, "OpenPGP private key does not match its Ed25519 public key");
363 goto cleanup;
364 }
365
366 memcpy(secret_key_out, secret_key, sizeof(secret_key));
367 result = 0;
368
369cleanup:
370 sodium_memzero(public_key, sizeof(public_key));
371 sodium_memzero(seed, sizeof(seed));
372 sodium_memzero(derived_public_key, sizeof(derived_public_key));
373 sodium_memzero(secret_key, sizeof(secret_key));
374 return result;
375}
asciichat_error_t openpgp_parse_binary_seckey(const uint8_t *data, size_t data_len, uint8_t ed25519_pk[ED25519_PUBLIC_KEY_SIZE], uint8_t ed25519_sk[ED25519_SEED_SIZE])
Definition openpgp.c:722

References ASCIICHAT_OK, ERROR_CRYPTO_KEY, ERROR_INVALID_PARAM, openpgp_parse_binary_seckey(), and SET_ERRNO.

◆ client_parse_gpg_public_key()

int client_parse_gpg_public_key ( const char * armored_key,
uint8_t * public_key_out )

Parse an armored OpenPGP Ed25519 public key through the shared C parser.

Definition at line 298 of file src/web/client.c.

298 {
299 if (!armored_key || !public_key_out) {
300 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid OpenPGP public key input or output");
301 return -1;
302 }
303 return openpgp_parse_armored_pubkey(armored_key, public_key_out) == ASCIICHAT_OK ? 0 : -1;
304}
asciichat_error_t openpgp_parse_armored_pubkey(const char *armored_text, uint8_t ed25519_pk[ED25519_PUBLIC_KEY_SIZE])
Definition openpgp.c:317

References ASCIICHAT_OK, ERROR_INVALID_PARAM, openpgp_parse_armored_pubkey(), and SET_ERRNO.

◆ client_parse_gpg_public_key_binary()

int client_parse_gpg_public_key_binary ( const uint8_t * key_data,
size_t key_data_len,
uint8_t * public_key_out )

Parse a binary OpenPGP Ed25519 public-key packet stream.

Definition at line 307 of file src/web/client.c.

307 {
308 if (!key_data || key_data_len == 0 || !public_key_out) {
309 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid binary OpenPGP public key input or output");
310 return -1;
311 }
312 return openpgp_parse_binary_pubkey(key_data, key_data_len, public_key_out) == ASCIICHAT_OK ? 0 : -1;
313}
asciichat_error_t openpgp_parse_binary_pubkey(const uint8_t *data, size_t data_len, uint8_t ed25519_pk[ED25519_PUBLIC_KEY_SIZE])
Definition openpgp.c:289

References ASCIICHAT_OK, ERROR_INVALID_PARAM, openpgp_parse_binary_pubkey(), and SET_ERRNO.

◆ client_parse_ssh_private_key()

int client_parse_ssh_private_key ( const char * key_path,
uint8_t * secret_key_out )

Parse an OpenSSH Ed25519 private-key file through the shared SSH parser.

Definition at line 265 of file src/web/client.c.

265 {
266 if (!key_path || !secret_key_out) {
267 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SSH private key input or output");
268 return -1;
269 }
270#ifdef EMSCRIPTEN_BUILD
271 static const char wasm_key_prefix[] = "/tmp/ascii-chat-key-";
272 if (strncmp(key_path, wasm_key_prefix, sizeof(wasm_key_prefix) - 1) != 0) {
273 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid browser SSH private key path");
274 return -1;
275 }
276#endif
277 private_key_t key = {0};
278 asciichat_error_t result = parse_ssh_private_key(key_path, &key);
279 if (result != ASCIICHAT_OK || key.type != KEY_TYPE_ED25519 || key.use_ssh_agent || key.use_gpg_agent) {
280 sodium_memzero(&key, sizeof(key));
281 return -1;
282 }
283 memcpy(secret_key_out, key.key.ed25519, crypto_sign_SECRETKEYBYTES);
284 sodium_memzero(&key, sizeof(key));
285 return 0;
286}
uint8_t ed25519[ED25519_PRIVATE_KEY_SIZE]
Definition key_types.h:100
key_type_t type
Definition key_types.h:98
bool use_gpg_agent
Definition key_types.h:104
bool use_ssh_agent
Definition key_types.h:103
asciichat_error_t parse_ssh_private_key(const char *key_path, private_key_t *key_out)
Parse SSH Ed25519 private key from file.
Definition ssh_keys.c:984
union private_key_t::@040247167361101132173265304166142033301213115260 key
@ KEY_TYPE_ED25519
Definition key_types.h:54
Private identity-key material used for server authentication and signing.
Definition key_types.h:97

References ASCIICHAT_OK, private_key_t::ed25519, ERROR_INVALID_PARAM, private_key_t::key, KEY_TYPE_ED25519, parse_ssh_private_key(), SET_ERRNO, private_key_t::type, private_key_t::use_gpg_agent, and private_key_t::use_ssh_agent.

◆ client_parse_ssh_public_key()

int client_parse_ssh_public_key ( const char * key_line,
uint8_t * public_key_out )

Parse an OpenSSH Ed25519 public-key line through the shared SSH parser.

Definition at line 289 of file src/web/client.c.

289 {
290 if (!key_line || !public_key_out) {
291 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid SSH public key input or output");
292 return -1;
293 }
294 return parse_ssh_ed25519_line(key_line, public_key_out) == ASCIICHAT_OK ? 0 : -1;
295}
asciichat_error_t parse_ssh_ed25519_line(const char *line, uint8_t ed25519_pk[ED25519_PUBLIC_KEY_SIZE])
Definition ssh_keys.c:172

References ASCIICHAT_OK, ERROR_INVALID_PARAM, parse_ssh_ed25519_line(), and SET_ERRNO.

◆ client_public_key_bytes()

const uint8_t * client_public_key_bytes ( void )

Definition at line 514 of file src/web/client.c.

514 {
515 if (g_crypto_handshake_ctx.state == CRYPTO_HANDSHAKE_DISABLED) {
516 SET_ERRNO(ERROR_INVALID_STATE, "Client handshake is not initialized");
517 return NULL;
518 }
519 return g_crypto_handshake_ctx.crypto_ctx.public_key;
520}

References CRYPTO_HANDSHAKE_DISABLED, ERROR_INVALID_STATE, and SET_ERRNO.

◆ client_resolve_crypto_policy()

int client_resolve_crypto_policy ( bool secure,
int encryption )

Resolve browser inputs through the same policy as native and mobile clients.

Definition at line 1009 of file src/web/client.c.

1009 {
1010 crypto_policy_t policy = {
1011 .transport_secure = secure,
1012 .encrypt = encryption == 1,
1013 .no_encrypt = encryption == 0,
1014 .has_password = g_crypto_password[0] != '\0',
1015 .has_identity = g_has_client_identity_key,
1016 .has_verification_key = g_has_expected_server_identity_key,
1017 };
1018 uint8_t mode;
1019 if (crypto_policy_resolve(&policy, &mode) != ASCIICHAT_OK)
1020 return -1;
1021 g_crypto_mode = mode;
1022 return mode;
1023}
asciichat_error_t crypto_policy_resolve(const crypto_policy_t *policy, uint8_t *mode)
Definition policy.c:4
The useful part of crypto policy is in transport_secure, encrypt, no_encrypt, no_auth: together they ...
Definition policy.h:12

References ASCIICHAT_OK, and crypto_policy_resolve().

Referenced by EMSCRIPTEN_BINDINGS().

◆ client_security_context()

crypto_context_t * client_security_context ( void )

Definition at line 1110 of file src/web/client.c.

1110 {
1111 return g_handshake_complete ? &g_crypto_handshake_ctx.crypto_ctx : NULL;
1112}

◆ client_set_server_address()

int client_set_server_address ( const char * server_host,
int server_port )

Set server address for known_hosts verification

Parameters
server_hostServer hostname or IP address
server_portServer port number
Returns
0 on success, -1 on error

Definition at line 496 of file src/web/client.c.

496 {
497 if (!g_initialized) {
498 SET_ERRNO(ERROR_INVALID_STATE, "Client is not initialized");
499 return -1;
500 }
501
502 if (!server_host || !server_host[0] || server_port <= 0 || server_port > 65535) {
503 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client server address or port");
504 return -1;
505 }
506
507 // Set server IP and port in handshake context
508 SAFE_STRNCPY(g_crypto_handshake_ctx.server_ip, server_host, sizeof(g_crypto_handshake_ctx.server_ip));
509 g_crypto_handshake_ctx.server_port = server_port;
510
511 return 0;
512}

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, SAFE_STRNCPY, and SET_ERRNO.

◆ client_settings_receive()

asciichat_error_t client_settings_receive ( const void * payload,
size_t length )

Effective remote state is separate from this participant's saved preferences.

Definition at line 21 of file src/web/client.c.

21 {
23 const char *json;
24 size_t json_length;
25 asciichat_error_t result = acip_settings_decode(payload, length, &header, &json, &json_length);
26 if (result != ASCIICHAT_OK)
27 return result;
28 options_t *current = SAFE_MALLOC(sizeof(*current), options_t *),
29 *candidate = SAFE_MALLOC(sizeof(*candidate), options_t *);
30 // Browser capability packets and the shared renderer always support UTF-8.
31 remote_render_settings.caps.utf8_support = true;
32 acip_render_settings_options(&remote_render_settings, current);
33 result = options_prepare_json(current, json, json_length, candidate, true);
34 if (result == ASCIICHAT_OK)
35 result = acip_render_settings_prepare(&remote_render_settings, candidate);
36 if (result == ASCIICHAT_OK)
37 remote_render_settings.revision = header.revision;
38 SAFE_FREE(current);
39 SAFE_FREE(candidate);
40 return result;
41}
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
asciichat_error_t acip_render_settings_prepare(acip_render_settings_t *candidate, const options_t *values)
asciichat_error_t acip_settings_decode(const void *payload, size_t length, acip_settings_header_t *header, const char **json, size_t *json_length)
void acip_render_settings_options(const acip_render_settings_t *state, options_t *values)
asciichat_error_t options_prepare_json(const options_t *current, const char *json, size_t length, options_t *candidate, bool remote_only)
Definition rcu.c:987
acip settings header keeps the values that move through the subsystem: schema, request,...

References acip_render_settings_options(), acip_render_settings_prepare(), acip_settings_decode(), ASCIICHAT_OK, options_prepare_json(), SAFE_FREE, and SAFE_MALLOC.

◆ client_settings_values()

char * client_settings_values ( void )

Definition at line 42 of file src/web/client.c.

42 {
43 options_t *values = SAFE_MALLOC(sizeof(*values), options_t *);
44 acip_render_settings_options(&remote_render_settings, values);
45 char *json = options_values_json(values, true);
46 SAFE_FREE(values);
47 return json;
48}
char * options_values_json(const options_t *values, bool remote_only)
Definition rcu.c:1013

References acip_render_settings_options(), options_values_json(), SAFE_FREE, and SAFE_MALLOC.

◆ client_validate_frame()

asciichat_error_t client_validate_frame ( const char * text,
size_t length,
uint32_t width,
uint32_t height,
uint32_t checksum,
uint32_t flags,
const void * metadata,
size_t metadata_length )

Definition at line 1075 of file src/web/client.c.

1077 {
1079 return SET_ERRNO(ERROR_INVALID_FRAME, "Unknown browser frame flags");
1080 acip_render_settings_t settings;
1081 uint32_t effects = 0;
1082 bool contract = (flags & FRAME_FLAG_RENDER_CONTRACT) != 0;
1083 if (contract) {
1084 size_t consumed;
1085 asciichat_error_t decoded = frame_render_contract_decode(metadata, metadata_length, &settings, &effects, &consumed);
1086 if (decoded != ASCIICHAT_OK)
1087 return decoded;
1088 if (settings.revision && settings.revision == remote_render_settings.revision &&
1089 remote_render_settings.palette[0] && remote_render_settings.caps.utf8_support &&
1090 strcmp(settings.palette, remote_render_settings.palette) != 0)
1091 return SET_ERRNO(ERROR_INVALID_FRAME, "Browser frame palette contradicts acknowledged revision");
1092 if (consumed != metadata_length)
1093 return SET_ERRNO(ERROR_INVALID_FRAME, "Trailing browser frame contract bytes");
1094 } else if (metadata_length) {
1095 return SET_ERRNO(ERROR_INVALID_FRAME, "Unexpected browser frame contract");
1096 }
1097 if (!text || !length || length > FRAME_RENDER_MAX_BYTES || asciichat_crc32(text, length) != checksum)
1098 return SET_ERRNO(ERROR_INVALID_FRAME, "Browser frame length/checksum mismatch");
1099 return session_frame_validate_contract(text, length, width, height, contract ? &settings : NULL, effects);
1100}
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
asciichat_error_t frame_render_contract_decode(const void *wire, size_t available, acip_render_settings_t *settings, uint32_t *effects, size_t *length)
asciichat_error_t session_frame_validate_contract(const char *text, size_t length, uint32_t width, uint32_t height, const acip_render_settings_t *settings, uint32_t effects)
unsigned int uint32_t
Definition common.h:62
#define FRAME_FLAG_RENDER_CONTRACT
Explicit versioned render contract precedes encoded ASCII text.
#define FRAME_FLAG_IS_COMPRESSED
Frame data is compressed.
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:147

References asciichat_crc32, ASCIICHAT_OK, ERROR_INVALID_FRAME, FRAME_FLAG_IS_COMPRESSED, FRAME_FLAG_RENDER_CONTRACT, frame_render_contract_decode(), FRAME_RENDER_MAX_BYTES, height, acip_render_settings_t::palette, acip_render_settings_t::revision, session_frame_validate_contract(), SET_ERRNO, and width.