25 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key=%p", key);
26 return ERROR_INVALID_PARAM;
30 if (key->type == KEY_TYPE_UNKNOWN) {
31 SET_ERRNO(ERROR_CRYPTO_KEY,
"Key type is unknown");
32 return ERROR_CRYPTO_KEY;
35 if (key->type != KEY_TYPE_ED25519 && key->type != KEY_TYPE_X25519 && key->type != KEY_TYPE_GPG) {
36 SET_ERRNO(ERROR_CRYPTO_KEY,
"Unsupported key type: %d", key->type);
37 return ERROR_CRYPTO_KEY;
42 for (
int i = 0; i < 32; i++) {
43 if (key->key[i] != 0) {
50 SET_ERRNO(ERROR_CRYPTO_KEY,
"Key data is all zeros");
51 return ERROR_CRYPTO_KEY;
55 if (strlen(key->comment) >= MAX_COMMENT_LEN) {
56 SET_ERRNO(ERROR_CRYPTO_KEY,
"Key comment too long: %zu (maximum %d)", strlen(key->comment), MAX_COMMENT_LEN - 1);
57 return ERROR_CRYPTO_KEY;
61 if (key->comment[0] !=
'\0' && !
utf8_is_valid(key->comment)) {
62 SET_ERRNO(ERROR_CRYPTO_KEY,
"Key comment contains invalid UTF-8 sequence");
63 return ERROR_CRYPTO_KEY;
71 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key=%p", key);
72 return ERROR_INVALID_PARAM;
76 if (key->type == KEY_TYPE_UNKNOWN) {
77 SET_ERRNO(ERROR_CRYPTO_KEY,
"Private key type is unknown");
78 return ERROR_CRYPTO_KEY;
81 if (key->type != KEY_TYPE_ED25519 && key->type != KEY_TYPE_X25519) {
82 SET_ERRNO(ERROR_CRYPTO_KEY,
"Unsupported private key type: %d", key->type);
83 return ERROR_CRYPTO_KEY;
88 size_t key_size = (key->type == KEY_TYPE_ED25519) ? 64 : 32;
90 for (
size_t i = 0; i < key_size; i++) {
91 if (key->key.ed25519[i] != 0) {
98 SET_ERRNO(ERROR_CRYPTO_KEY,
"Private key data is all zeros");
99 return ERROR_CRYPTO_KEY;
103 if (strlen(key->key_comment) >= MAX_COMMENT_LEN) {
104 SET_ERRNO(ERROR_CRYPTO_KEY,
"Private key comment too long: %zu (maximum %d)", strlen(key->key_comment),
105 MAX_COMMENT_LEN - 1);
106 return ERROR_CRYPTO_KEY;
110 if (key->key_comment[0] !=
'\0' && !
utf8_is_valid(key->key_comment)) {
111 SET_ERRNO(ERROR_CRYPTO_KEY,
"Private key comment contains invalid UTF-8 sequence");
112 return ERROR_CRYPTO_KEY;
156 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key_text=%p", key_text);
157 return ERROR_INVALID_PARAM;
161 if (strncmp(key_text,
"ssh-ed25519 ", 12) != 0) {
162 SET_ERRNO(ERROR_CRYPTO_KEY,
"SSH key does not start with 'ssh-ed25519 '");
163 return ERROR_CRYPTO_KEY;
167 const char *base64_start = key_text + 12;
168 while (*base64_start ==
' ' || *base64_start ==
'\t') {
172 if (*base64_start ==
'\0' || *base64_start ==
'\n' || *base64_start ==
'\r') {
173 SET_ERRNO(ERROR_CRYPTO_KEY,
"SSH key has no base64 data");
174 return ERROR_CRYPTO_KEY;
186 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key_text=%p", key_text);
187 return ERROR_INVALID_PARAM;
191 if (strncmp(key_text,
"-----BEGIN PGP", 14) != 0) {
192 SET_ERRNO(ERROR_CRYPTO_KEY,
"GPG key does not start with armor header");
193 return ERROR_CRYPTO_KEY;
197 if (strstr(key_text,
"-----END PGP") == NULL) {
198 SET_ERRNO(ERROR_CRYPTO_KEY,
"GPG key does not contain armor footer");
199 return ERROR_CRYPTO_KEY;
204 bool has_version_header = (strstr(key_text,
"Version:") != NULL);
205 bool has_charset_header = (strstr(key_text,
"Charset:") != NULL || strstr(key_text,
"Version:") != NULL);
207 if (!has_version_header && !has_charset_header) {
209 log_warn(
"GPG armor missing version/charset headers - key might be incomplete");
213 const char *start_pos = strstr(key_text,
"\n\n");
214 const char *end_pos = strstr(key_text,
"\n-----END");
215 if (start_pos && end_pos && start_pos < end_pos) {
217 ptrdiff_t diff = end_pos - start_pos - 2;
219 SET_ERRNO(ERROR_CRYPTO_KEY,
"GPG key has empty or malformed body content");
220 return ERROR_CRYPTO_KEY;
229 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key_hex=%p", key_hex);
230 return ERROR_INVALID_PARAM;
234 size_t hex_len = strlen(key_hex);
236 SET_ERRNO(ERROR_CRYPTO_KEY,
"X25519 key has invalid length: %zu (expected 64)", hex_len);
237 return ERROR_CRYPTO_KEY;
241 for (
size_t i = 0; i < hex_len; i++) {
242 if (!isxdigit(key_hex[i])) {
243 SET_ERRNO(ERROR_CRYPTO_KEY,
"X25519 key contains invalid hex character at position %zu: '%c'", i, key_hex[i]);
244 return ERROR_CRYPTO_KEY;
306 if (!key || !has_weak_patterns) {
307 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key=%p, has_weak_patterns=%p", key, has_weak_patterns);
308 return ERROR_INVALID_PARAM;
311 *has_weak_patterns =
false;
314 bool is_sequential =
true;
315 for (
int i = 1; i < 32; i++) {
316 if (key->key[i] != key->key[i - 1] + 1) {
317 is_sequential =
false;
323 *has_weak_patterns =
true;
329 const uint8_t weak_bytes[] = {0x00, 0xFF, 0xAA, 0x55, 0x11, 0x22, 0x33, 0x44,
330 0x66, 0x77, 0x88, 0x99, 0xBB, 0xCC, 0xDD, 0xEE};
331 for (
size_t pattern_idx = 0; pattern_idx <
sizeof(weak_bytes); pattern_idx++) {
332 uint8_t pattern_byte = weak_bytes[pattern_idx];
333 bool is_repeated =
true;
334 for (
size_t j = 0; j < 32; j++) {
336 if (key->key[j] != pattern_byte) {
342 *has_weak_patterns =
true;
349 bool byte_appeared[256] = {0};
350 int unique_values = 0;
351 for (
size_t i = 0; i < 32; i++) {
352 if (!byte_appeared[key->key[i]]) {
353 byte_appeared[key->key[i]] =
true;
359 if (unique_values <= 2) {
360 bool is_alternating =
true;
361 for (
size_t i = 1; i < 32; i++) {
365 if (key->key[i] == key->key[i - 1] || (i >= 2 && key->key[i] != key->key[i - 2])) {
366 is_alternating =
false;
370 if (is_alternating) {
371 *has_weak_patterns =
true;
378 uint32_t *words = (uint32_t *)key->key;
380 bool all_words_same =
true;
381 for (
size_t i = 1; i < 8; i++) {
382 if (words[i] != words[0]) {
383 all_words_same =
false;
387 if (all_words_same) {
388 *has_weak_patterns =
true;
399asciichat_error_t
compare_public_keys(
const public_key_t *key1,
const public_key_t *key2,
bool *are_equal) {
400 if (!key1 || !key2 || !are_equal) {
401 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key1=%p, key2=%p, are_equal=%p", key1, key2, are_equal);
402 return ERROR_INVALID_PARAM;
408 if (key1->type != key2->type) {
413 if (sodium_memcmp(key1->key, key2->key, 32) == 0) {
422 if (!key || !fingerprint || !matches) {
423 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key=%p, fingerprint=%p, matches=%p", key, fingerprint, matches);
424 return ERROR_INVALID_PARAM;
430 uint8_t key_fingerprint[32];
432 if (fingerprint_result != ASCIICHAT_OK) {
433 return fingerprint_result;
437 size_t compare_len = (fingerprint_len < 32) ? fingerprint_len : 32;
438 if (sodium_memcmp(key_fingerprint, fingerprint, compare_len) == 0) {
446 if (!key || !fingerprint_out) {
447 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: key=%p, fingerprint_out=%p", key, fingerprint_out);
448 return ERROR_INVALID_PARAM;
451 if (fingerprint_size < 32) {
452 SET_ERRNO(ERROR_INVALID_PARAM,
"Fingerprint buffer too small: %zu (minimum 32)", fingerprint_size);
453 return ERROR_INVALID_PARAM;
457 if (crypto_hash_sha256(fingerprint_out, key->key, 32) != 0) {
458 SET_ERRNO(ERROR_CRYPTO,
"Failed to generate key fingerprint");