ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
identity.c
Go to the documentation of this file.
1
6#include <ascii-chat/discovery/identity.h>
7#include <ascii-chat/crypto/crypto.h>
8#include <ascii-chat/log/logging.h>
9#include <ascii-chat/platform/abstraction.h>
10#include <ascii-chat/platform/filesystem.h>
11#include <ascii-chat/platform/util.h>
12#include <sodium.h>
13#include <stdio.h>
14#include <string.h>
15#include <errno.h>
16#include <sys/stat.h>
17
18asciichat_error_t acds_identity_generate(uint8_t public_key[32], uint8_t secret_key[64]) {
19 if (!public_key || !secret_key) {
20 return SET_ERRNO(ERROR_INVALID_PARAM, "public_key and secret_key cannot be NULL");
21 }
22
23 // Generate Ed25519 keypair using libsodium
24 if (crypto_sign_keypair(public_key, secret_key) != 0) {
25 return SET_ERRNO(ERROR_CRYPTO, "Failed to generate Ed25519 keypair");
26 }
27
28 log_debug("Generated new Ed25519 identity keypair");
29 return ASCIICHAT_OK;
30}
31
32asciichat_error_t acds_identity_load(const char *path, uint8_t public_key[32], uint8_t secret_key[64]) {
33 if (!path || !public_key || !secret_key) {
34 return SET_ERRNO(ERROR_INVALID_PARAM, "path, public_key, and secret_key cannot be NULL");
35 }
36
37 // Open file for reading
38 FILE *fp = platform_fopen(path, "rb");
39 if (!fp) {
40 if (errno == ENOENT) {
41 return SET_ERRNO(ERROR_CONFIG, "Identity file does not exist: %s", path);
42 }
43 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to open identity file: %s", path);
44 }
45
46 // Read secret key (64 bytes)
47 size_t read = fread(secret_key, 1, 64, fp);
48 if (read != 64) {
49 fclose(fp);
50 return SET_ERRNO(ERROR_CONFIG, "Identity file corrupted (expected 64 bytes, got %zu): %s", read, path);
51 }
52
53 // Extract public key from secret key (last 32 bytes of Ed25519 secret key)
54 memcpy(public_key, secret_key + 32, 32);
55
56 fclose(fp);
57 log_info("Loaded identity from %s", path);
58 return ASCIICHAT_OK;
59}
60
61asciichat_error_t acds_identity_save(const char *path, const uint8_t public_key[32], const uint8_t secret_key[64]) {
62 if (!path || !public_key || !secret_key) {
63 return SET_ERRNO(ERROR_INVALID_PARAM, "path, public_key, and secret_key cannot be NULL");
64 }
65
66 // Extract directory path and create all parent directories
67 char dir_path[PLATFORM_MAX_PATH_LENGTH];
68 SAFE_STRNCPY(dir_path, path, sizeof(dir_path));
69
70 // Find last directory separator
71 char *last_sep = strrchr(dir_path, '/');
72 if (!last_sep) {
73 last_sep = strrchr(dir_path, '\\');
74 }
75
76 if (last_sep) {
77 *last_sep = '\0';
78
79 // Create directory recursively (mkdir -p equivalent)
80 asciichat_error_t result = platform_mkdir_recursive(dir_path, 0700);
81 if (result != ASCIICHAT_OK) {
82 return result;
83 }
84 }
85
86 // Open file for writing (mode 0600 = owner read/write only)
87 int fd = platform_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
88 if (fd < 0) {
89 return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to create identity file: %s", path);
90 }
91
92 // Write secret key (64 bytes)
93 ssize_t written = write(fd, secret_key, 64);
94 close(fd);
95
96 if (written != 64) {
97 return SET_ERRNO(ERROR_CONFIG, "Failed to write identity file (wrote %zd/64 bytes): %s", written, path);
98 }
99
100 log_info("Saved identity to %s", path);
101 return ASCIICHAT_OK;
102}
103
104void acds_identity_fingerprint(const uint8_t public_key[32], char fingerprint[65]) {
105 if (!public_key || !fingerprint) {
106 log_error("acds_identity_fingerprint: NULL parameters");
107 return;
108 }
109
110 // Compute SHA256 hash of public key
111 uint8_t hash[32];
112 crypto_hash_sha256(hash, public_key, 32);
113
114 // Convert to hex string
115 for (int i = 0; i < 32; i++) {
116 safe_snprintf(&fingerprint[i * 2], 3, "%02x", hash[i]);
117 }
118 fingerprint[64] = '\0';
119}
120
121asciichat_error_t acds_identity_default_path(char *path_out, size_t path_size) {
122 if (!path_out || path_size == 0) {
123 return SET_ERRNO(ERROR_INVALID_PARAM, "path_out cannot be NULL and path_size must be > 0");
124 }
125
126 // Get platform-specific config directory (~/.config/ascii-chat/ or %APPDATA%\ascii-chat\‍)
127 char *config_dir = platform_get_config_dir();
128 if (!config_dir) {
129 return SET_ERRNO(ERROR_CONFIG, "Failed to get config directory");
130 }
131
132 // Append "acds_identity" to config directory
133 int written = safe_snprintf(path_out, path_size, "%sacds_identity", config_dir);
134 SAFE_FREE(config_dir);
135
136 if (written < 0 || (size_t)written >= path_size) {
137 return SET_ERRNO(ERROR_CONFIG, "Path buffer too small");
138 }
139
140 return ASCIICHAT_OK;
141}
asciichat_error_t acds_identity_load(const char *path, uint8_t public_key[32], uint8_t secret_key[64])
Definition identity.c:32
asciichat_error_t acds_identity_default_path(char *path_out, size_t path_size)
Definition identity.c:121
asciichat_error_t acds_identity_save(const char *path, const uint8_t public_key[32], const uint8_t secret_key[64])
Definition identity.c:61
void acds_identity_fingerprint(const uint8_t public_key[32], char fingerprint[65])
Definition identity.c:104
asciichat_error_t acds_identity_generate(uint8_t public_key[32], uint8_t secret_key[64])
Definition identity.c:18
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
#define PLATFORM_MAX_PATH_LENGTH
Definition system.c:64
char * platform_get_config_dir(void)
Definition util.c:102
FILE * platform_fopen(const char *filename, const char *mode)
asciichat_error_t platform_mkdir_recursive(const char *path, int mode)
int platform_open(const char *pathname, int flags,...)