ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
keys_validation.c File Reference

Go to the source code of this file.

Functions

asciichat_error_t validate_public_key (const public_key_t *key)
 
asciichat_error_t validate_private_key (const private_key_t *key)
 
asciichat_error_t check_key_expiry (const public_key_t *key, bool *is_expired)
 
asciichat_error_t validate_key_security (const char *key_path)
 
asciichat_error_t validate_ssh_key_format (const char *key_text)
 
asciichat_error_t validate_gpg_key_format (const char *key_text)
 
asciichat_error_t validate_x25519_key_format (const char *key_hex)
 
asciichat_error_t check_key_strength (const public_key_t *key, bool *is_weak)
 
asciichat_error_t check_key_patterns (const public_key_t *key, bool *has_weak_patterns)
 
asciichat_error_t compare_public_keys (const public_key_t *key1, const public_key_t *key2, bool *are_equal)
 
asciichat_error_t check_key_fingerprint (const public_key_t *key, const uint8_t *fingerprint, size_t fingerprint_len, bool *matches)
 
asciichat_error_t generate_key_fingerprint (const public_key_t *key, uint8_t *fingerprint_out, size_t fingerprint_size)
 

Function Documentation

◆ check_key_expiry()

asciichat_error_t check_key_expiry ( const public_key_t *  key,
bool is_expired 
)

Definition at line 118 of file keys_validation.c.

118 {
119 if (!key || !is_expired) {
120 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key=%p, is_expired=%p", key, is_expired);
121 return ERROR_INVALID_PARAM;
122 }
123
124 // For now, we don't implement key expiry checking
125 // This would require parsing key creation dates and expiration times
126 *is_expired = false;
127
128 return ASCIICHAT_OK;
129}

◆ check_key_fingerprint()

asciichat_error_t check_key_fingerprint ( const public_key_t *  key,
const uint8_t *  fingerprint,
size_t  fingerprint_len,
bool matches 
)

Definition at line 420 of file keys_validation.c.

421 {
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;
425 }
426
427 *matches = false;
428
429 // Generate fingerprint for the key
430 uint8_t key_fingerprint[32];
431 asciichat_error_t fingerprint_result = generate_key_fingerprint(key, key_fingerprint, 32);
432 if (fingerprint_result != ASCIICHAT_OK) {
433 return fingerprint_result;
434 }
435
436 // Compare fingerprints
437 size_t compare_len = (fingerprint_len < 32) ? fingerprint_len : 32;
438 if (sodium_memcmp(key_fingerprint, fingerprint, compare_len) == 0) {
439 *matches = true;
440 }
441
442 return ASCIICHAT_OK;
443}
asciichat_error_t generate_key_fingerprint(const public_key_t *key, uint8_t *fingerprint_out, size_t fingerprint_size)

References generate_key_fingerprint().

◆ check_key_patterns()

asciichat_error_t check_key_patterns ( const public_key_t *  key,
bool has_weak_patterns 
)

Definition at line 305 of file keys_validation.c.

305 {
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;
309 }
310
311 *has_weak_patterns = false;
312
313 // Check for sequential patterns
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;
318 break;
319 }
320 }
321
322 if (is_sequential) {
323 *has_weak_patterns = true;
324 return ASCIICHAT_OK;
325 }
326
327 // Check for repeated byte patterns (e.g., all 0xAA, all 0x55)
328 // Common weak patterns to check
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++) {
335 // CORRECT: Compare against pattern_byte, not key->key[0]
336 if (key->key[j] != pattern_byte) {
337 is_repeated = false;
338 break;
339 }
340 }
341 if (is_repeated) {
342 *has_weak_patterns = true;
343 return ASCIICHAT_OK;
344 }
345 }
346
347 // Check for alternating bit patterns (e.g., 0xAA 0xBB 0xAA 0xBB or 0x55 0xAA 0x55 0xAA)
348 // A proper alternating pattern has at most 2 unique byte values that truly alternate
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;
354 unique_values++;
355 }
356 }
357
358 // Only consider it weak if there are 2 or fewer unique values AND they properly alternate
359 if (unique_values <= 2) {
360 bool is_alternating = true;
361 for (size_t i = 1; i < 32; i++) {
362 // In a true alternating pattern:
363 // 1. Adjacent bytes must be different
364 // 2. Pattern must repeat every 2 bytes (byte[i] == byte[i-2])
365 if (key->key[i] == key->key[i - 1] || (i >= 2 && key->key[i] != key->key[i - 2])) {
366 is_alternating = false;
367 break;
368 }
369 }
370 if (is_alternating) {
371 *has_weak_patterns = true;
372 return ASCIICHAT_OK;
373 }
374 }
375
376 // Check for common cryptographic initialization patterns
377 // OpenSSL weak keys, old Debian SSH keys, etc.
378 uint32_t *words = (uint32_t *)key->key;
379 // Check if all 32-bit words are identical
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;
384 break;
385 }
386 }
387 if (all_words_same) {
388 *has_weak_patterns = true;
389 return ASCIICHAT_OK;
390 }
391
392 return ASCIICHAT_OK;
393}

◆ check_key_strength()

asciichat_error_t check_key_strength ( const public_key_t *  key,
bool is_weak 
)

Definition at line 255 of file keys_validation.c.

255 {
256 if (!key || !is_weak) {
257 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key=%p, is_weak=%p", key, is_weak);
258 return ERROR_INVALID_PARAM;
259 }
260
261 *is_weak = false;
262
263 // Check for all zeros (already checked in validate_public_key)
264 // Check for all ones
265 bool is_ones = true;
266 for (int i = 0; i < 32; i++) {
267 if (key->key[i] != 0xFF) {
268 is_ones = false;
269 break;
270 }
271 }
272
273 if (is_ones) {
274 *is_weak = true;
275 return ASCIICHAT_OK;
276 }
277
278 // Check for low entropy: Count unique bytes
279 // A properly random 32-byte key should have ~24-32 unique values
280 // Low entropy keys will have very few unique bytes
281 bool byte_seen[256] = {0};
282 int unique_bytes = 0;
283 for (int i = 0; i < 32; i++) {
284 uint8_t byte_val = key->key[i];
285 if (!byte_seen[byte_val]) {
286 byte_seen[byte_val] = true;
287 unique_bytes++;
288 }
289 }
290
291 // If less than 8 unique bytes in 32 bytes, entropy is very low
292 // This indicates non-random key generation (e.g., PRNG seed not properly initialized)
293 if (unique_bytes < 8) {
294 *is_weak = true;
295 return ASCIICHAT_OK;
296 }
297
298 // Additional weak key pattern detection:
299 // - Check for repeated patterns (handled in check_key_patterns)
300 // - Check for known weak sequences (see check_key_patterns)
301
302 return ASCIICHAT_OK;
303}

◆ compare_public_keys()

asciichat_error_t compare_public_keys ( const public_key_t *  key1,
const public_key_t *  key2,
bool are_equal 
)

Definition at line 399 of file keys_validation.c.

399 {
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;
403 }
404
405 *are_equal = false;
406
407 // Compare key types
408 if (key1->type != key2->type) {
409 return ASCIICHAT_OK;
410 }
411
412 // Compare key data using constant-time comparison
413 if (sodium_memcmp(key1->key, key2->key, 32) == 0) {
414 *are_equal = true;
415 }
416
417 return ASCIICHAT_OK;
418}

◆ generate_key_fingerprint()

asciichat_error_t generate_key_fingerprint ( const public_key_t *  key,
uint8_t *  fingerprint_out,
size_t  fingerprint_size 
)

Definition at line 445 of file keys_validation.c.

445 {
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;
449 }
450
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;
454 }
455
456 // Generate SHA-256 fingerprint of the key
457 if (crypto_hash_sha256(fingerprint_out, key->key, 32) != 0) {
458 SET_ERRNO(ERROR_CRYPTO, "Failed to generate key fingerprint");
459 return ERROR_CRYPTO;
460 }
461
462 return ASCIICHAT_OK;
463}

Referenced by check_key_fingerprint().

◆ validate_gpg_key_format()

asciichat_error_t validate_gpg_key_format ( const char *  key_text)

Definition at line 184 of file keys_validation.c.

184 {
185 if (!key_text) {
186 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key_text=%p", key_text);
187 return ERROR_INVALID_PARAM;
188 }
189
190 // Check for GPG armor header
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;
194 }
195
196 // Check for GPG armor footer
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;
200 }
201
202 // Additional GPG key validation
203 // Validate armor format (should have valid armor headers)
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);
206
207 if (!has_version_header && !has_charset_header) {
208 // Missing common armor headers - might be incomplete
209 log_warn("GPG armor missing version/charset headers - key might be incomplete");
210 }
211
212 // Validate base64 content (keys must have non-empty body between headers and footer)
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) {
216 // -2 for \n\n at start_pos; check for underflow before casting to size_t
217 ptrdiff_t diff = end_pos - start_pos - 2;
218 if (diff <= 0) {
219 SET_ERRNO(ERROR_CRYPTO_KEY, "GPG key has empty or malformed body content");
220 return ERROR_CRYPTO_KEY;
221 }
222 }
223
224 return ASCIICHAT_OK;
225}

◆ validate_key_security()

asciichat_error_t validate_key_security ( const char *  key_path)

Definition at line 131 of file keys_validation.c.

131 {
132 if (!key_path) {
133 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key_path=%p", key_path);
134 return ERROR_INVALID_PARAM;
135 }
136
137 // Check file permissions
138 asciichat_error_t perm_result = platform_validate_key_file_permissions(key_path);
139 if (perm_result != ASCIICHAT_OK) {
140 return perm_result;
141 }
142
143 // Additional security checks have been implemented in check_key_strength()
144 // and check_key_patterns() functions. File permissions check is the critical one.
145 // Known weak keys database would require periodic updates and is beyond scope.
146
147 return ASCIICHAT_OK;
148}

◆ validate_private_key()

asciichat_error_t validate_private_key ( const private_key_t *  key)

Definition at line 69 of file keys_validation.c.

69 {
70 if (!key) {
71 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key=%p", key);
72 return ERROR_INVALID_PARAM;
73 }
74
75 // Check key type
76 if (key->type == KEY_TYPE_UNKNOWN) {
77 SET_ERRNO(ERROR_CRYPTO_KEY, "Private key type is unknown");
78 return ERROR_CRYPTO_KEY;
79 }
80
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;
84 }
85
86 // Check key data is not all zeros
87 bool is_zero = true;
88 size_t key_size = (key->type == KEY_TYPE_ED25519) ? 64 : 32;
89
90 for (size_t i = 0; i < key_size; i++) {
91 if (key->key.ed25519[i] != 0) {
92 is_zero = false;
93 break;
94 }
95 }
96
97 if (is_zero) {
98 SET_ERRNO(ERROR_CRYPTO_KEY, "Private key data is all zeros");
99 return ERROR_CRYPTO_KEY;
100 }
101
102 // Check comment length
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;
107 }
108
109 // Validate comment is valid UTF-8 (user-provided text in key files)
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;
113 }
114
115 return ASCIICHAT_OK;
116}
bool utf8_is_valid(const char *str)
Definition utf8.c:158

References utf8_is_valid().

◆ validate_public_key()

asciichat_error_t validate_public_key ( const public_key_t *  key)

Definition at line 23 of file keys_validation.c.

23 {
24 if (!key) {
25 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key=%p", key);
26 return ERROR_INVALID_PARAM;
27 }
28
29 // Check key type
30 if (key->type == KEY_TYPE_UNKNOWN) {
31 SET_ERRNO(ERROR_CRYPTO_KEY, "Key type is unknown");
32 return ERROR_CRYPTO_KEY;
33 }
34
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;
38 }
39
40 // Check key data is not all zeros
41 bool is_zero = true;
42 for (int i = 0; i < 32; i++) {
43 if (key->key[i] != 0) {
44 is_zero = false;
45 break;
46 }
47 }
48
49 if (is_zero) {
50 SET_ERRNO(ERROR_CRYPTO_KEY, "Key data is all zeros");
51 return ERROR_CRYPTO_KEY;
52 }
53
54 // Check comment length
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;
58 }
59
60 // Validate comment is valid UTF-8 (user-provided text in key files)
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;
64 }
65
66 return ASCIICHAT_OK;
67}

References utf8_is_valid().

◆ validate_ssh_key_format()

asciichat_error_t validate_ssh_key_format ( const char *  key_text)

Definition at line 154 of file keys_validation.c.

154 {
155 if (!key_text) {
156 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key_text=%p", key_text);
157 return ERROR_INVALID_PARAM;
158 }
159
160 // Check for SSH key format
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;
164 }
165
166 // Check for base64 data
167 const char *base64_start = key_text + 12;
168 while (*base64_start == ' ' || *base64_start == '\t') {
169 base64_start++;
170 }
171
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;
175 }
176
177 // Additional comprehensive SSH key validation (base64, blob structure, etc.)
178 // would require full OpenSSH format parsing. Basic format check above is sufficient
179 // to catch malformed keys. Full validation happens during key loading.
180
181 return ASCIICHAT_OK;
182}

◆ validate_x25519_key_format()

asciichat_error_t validate_x25519_key_format ( const char *  key_hex)

Definition at line 227 of file keys_validation.c.

227 {
228 if (!key_hex) {
229 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: key_hex=%p", key_hex);
230 return ERROR_INVALID_PARAM;
231 }
232
233 // Check hex string length (32 bytes = 64 hex characters)
234 size_t hex_len = strlen(key_hex);
235 if (hex_len != 64) {
236 SET_ERRNO(ERROR_CRYPTO_KEY, "X25519 key has invalid length: %zu (expected 64)", hex_len);
237 return ERROR_CRYPTO_KEY;
238 }
239
240 // Check that all characters are valid hex
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;
245 }
246 }
247
248 return ASCIICHAT_OK;
249}