GCC Code Coverage Report


Directory: ./
File: common/src/ccrypt.c
Date: 2026-11-08 20:01:15
Exec Total Coverage
Lines: 188 196 95.9%
Functions: 19 19 100.0%
Branches: 104 114 91.2%

Line Branch Exec Source
1 /*
2 * Common code for working with libgcrypt.
3 * Copyright © 2005-2026, albinoloverats ~ Software Development
4 * email: webmaster@albinoloverats.net
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <inttypes.h>
24 #include <stdbool.h>
25
26 #include <gcrypt.h>
27
28 #include "common.h"
29 #include "non-gnu.h"
30 #include "error.h"
31 #include "ccrypt.h"
32 #include "list.h"
33
34 #define MOSTLY_NEEDED_LIBGCRYPT "1.8.2"
35
36 #define NAME_SHA1 "SHA1"
37 #define NAME_SHA160 "SHA160"
38 #define NAME_TIGER "TIGER"
39 #define NAME_TIGER192 "TIGER192"
40
41 #define NAME_AES "AES"
42 #define NAME_RIJNDAEL "RIJNDAEL"
43 #define NAME_BLOWFISH "BLOWFISH"
44 #define NAME_BLOWFISH128 "BLOWFISH128"
45 #define NAME_TWOFISH "TWOFISH"
46 #define NAME_TWOFISH256 "TWOFISH256"
47
48 static const char *correct_sha1(const char * const restrict);
49 static const char *correct_aes_rijndael(const char * const restrict);
50 static const char *correct_blowfish128(const char * const restrict);
51 static const char *correct_twofish256(const char * const restrict);
52
53 static bool algorithm_is_duplicate(const char * const restrict);
54
55
56 typedef struct
57 {
58 enum gcry_cipher_modes id;
59 const char name[9];
60 }
61 block_mode_s;
62
63 static const block_mode_s MODES[] =
64 {
65 //{ GCRY_CIPHER_MODE_AESWRAP, "AESWRAP" }, // This will require more work in crypt_io
66 { GCRY_CIPHER_MODE_CBC, "CBC" },
67 //{ GCRY_CIPHER_MODE_CCM, "CCM" }, // ditto
68 { GCRY_CIPHER_MODE_CFB, "CFB" },
69 { GCRY_CIPHER_MODE_CFB8, "CFB8" },
70 { GCRY_CIPHER_MODE_CTR, "CTR" },
71 { 14 /*GCRY_CIPHER_MODE_EAX*/, "EAX" }, // available on Arch but nowhere else yet
72 { GCRY_CIPHER_MODE_ECB, "ECB" },
73 { GCRY_CIPHER_MODE_GCM, "GCM" },
74 { 16 /*GCRY_CIPHER_MODE_GCM_SIV*/, "GCM_SIV" }, // not available anywhere but in git source
75 //{ GCRY_CIPHER_MODE_OCB, "OCB" }, // requires more work
76 { GCRY_CIPHER_MODE_OFB, "OFB" },
77 { GCRY_CIPHER_MODE_POLY1305, "POLY1305" },
78 { 15 /*GCRY_CIPHER_MODE_SIV*/, "SIV" }, // ditto
79 { GCRY_CIPHER_MODE_STREAM, "STREAM" },
80 { 13 /*GCRY_CIPHER_MODE_XTS*/, "XTS" }, // not available on Slackware
81 };
82
83
84 54 extern void init_crypto(void)
85 {
86 static bool done = false;
87
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 45 times.
54 if (done)
88 8 return;
89 /*
90 * initialise GNU Crypt library
91 */
92
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!gcry_check_version(NEED_LIBGCRYPT_VERSION))
93 ✗ die(_("libgcrypt is too old (need %s, have %s)"), NEED_LIBGCRYPT_VERSION, gcry_check_version(NULL));
94 2 gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN);
95 2 gcry_control(GCRYCTL_INIT_SECMEM, 10 * MEGABYTE);
96 /*
97 * See libgcrypt source agent/gpg-agent.c:1293 for why this is.
98 * It may cause problems with out-of-memory errors with older
99 * versions, like those on Slaceware and Solaris, but but
100 * Slackware's 1.7.10 works fine otherwise.
101 */
102
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (strverscmp(MOSTLY_NEEDED_LIBGCRYPT, gcry_check_version(NULL)) < 0)
103 2 gcry_control(78/*GCRYCTL_AUTO_EXPAND_SECMEM*/, MEGABYTE);
104 2 gcry_control(GCRYCTL_RESUME_SECMEM_WARN);
105 2 gcry_control(GCRYCTL_INITIALIZATION_FINISHED);
106 2 errno = 0; /* need to reset errno after gcry_check_version() */
107 2 done = true;
108 2 return;
109 }
110
111 2 extern list_t list_of_ciphers(void)
112 {
113 2 init_crypto();
114
115 2 enum gcry_cipher_algos lid[0xff] = { GCRY_CIPHER_NONE };
116 2 int len = 0;
117 2 enum gcry_cipher_algos id = GCRY_CIPHER_NONE;
118
2/2
✓ Branch 0 taken 2040 times.
✓ Branch 1 taken 2 times.
2042 for (unsigned i = 0; i < sizeof lid; i++)
119 {
120
2/2
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 1982 times.
2040 if (gcry_cipher_algo_info(id, GCRYCTL_TEST_ALGO, NULL, NULL) == 0)
121 {
122 58 lid[len] = id;
123 58 len++;
124 }
125 2040 id++;
126 }
127 static list_t *l = NULL;
128
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!l)
129 {
130 1 l = list_init((int (*)(const void *, const void *))strcmp, false, true);
131
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 1 times.
30 for (int i = 0; i < len; i++)
132 {
133 29 const char *n = cipher_name_from_id(lid[i]);
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (!n)
135 ✗ continue;
136 29 list_add(l, n);
137 }
138 }
139 2 return l;
140 }
141
142 2 extern list_t list_of_hashes(void)
143 {
144 2 init_crypto();
145
146 2 enum gcry_md_algos lid[0xff] = { GCRY_MD_NONE };
147 2 int len = 0;
148 2 enum gcry_md_algos id = GCRY_MD_NONE;
149
2/2
✓ Branch 0 taken 2040 times.
✓ Branch 1 taken 2 times.
2042 for (unsigned i = 0; i < sizeof lid; i++)
150 {
151
2/2
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 1964 times.
2040 if (gcry_md_test_algo(id) == 0)
152 {
153 76 lid[len] = id;
154 76 len++;
155 }
156 2040 id++;
157 }
158 static list_t *l = NULL;
159
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!l)
160 {
161 1 l = list_init((int (*)(const void *, const void *))strcmp, false, true);
162
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1 times.
39 for (int i = 0; i < len; i++)
163 {
164 38 const char *n = hash_name_from_id(lid[i]);
165
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 37 times.
38 if (!n)
166 1 continue;
167 37 list_add(l, n);
168 }
169 }
170 2 return l;
171 }
172
173 2 extern list_t list_of_modes(void)
174 {
175 2 init_crypto();
176
177 static list_t *l = NULL;
178
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!l)
179 {
180 1 unsigned m = sizeof MODES / sizeof( block_mode_s );
181 1 l = list_init((int (*)(const void *, const void *))strcmp, false, true);
182
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 for (unsigned i = 0; i < m; i++)
183 {
184 gcry_cipher_hd_t c;
185 gcry_error_t e;
186
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
13 if ((e = gcry_cipher_open(&c, GCRY_CIPHER_AES, MODES[i].id, 0)) == GPG_ERR_NO_ERROR)
187 11 list_add(l, MODES[i].name);
188
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 else if ((e = gcry_cipher_open(&c, GCRY_CIPHER_CHACHA20, MODES[i].id, 0)) == GPG_ERR_NO_ERROR)
189 2 list_add(l, MODES[i].name);
190 else
191 ✗ continue;
192 13 gcry_cipher_close(c);
193 }
194 }
195 2 return l;
196 }
197
198 2 extern list_t list_of_macs(void)
199 {
200 2 init_crypto();
201
202 2 enum gcry_mac_algos lid[0xff] = { GCRY_MAC_NONE };
203 2 int len = 0;
204 2 enum gcry_mac_algos id = GCRY_MAC_NONE;
205
2/2
✓ Branch 0 taken 2040 times.
✓ Branch 1 taken 2 times.
2042 for (unsigned i = 0; i < sizeof lid; i++)
206 {
207
2/2
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 1924 times.
2040 if (gcry_mac_test_algo(id) == 0)
208 {
209 116 lid[len] = id;
210 116 len++;
211 }
212 2040 id++;
213 }
214 static list_t *l = NULL;
215
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!l)
216 {
217 1 l = list_init((int (*)(const void *, const void *))strcmp, false, true);
218
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 1 times.
59 for (int i = 0; i < len; i++)
219 {
220 58 const char *n = gcry_mac_algo_name(lid[i]);
221
2/4
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
58 if (!n || !strcmp("?", n))
222 ✗ continue;
223 58 list_add(l, n);
224 }
225 }
226 2 return l;
227 }
228
229 17 extern enum gcry_cipher_algos cipher_id_from_name(const char * const restrict n)
230 {
231 #ifdef __APPLE__
232 if (!n)
233 return GCRY_CIPHER_NONE;
234 #endif
235 17 int list[0xff] = { 0x00 };
236 2 int len = 0;
237 2 enum gcry_cipher_algos id = GCRY_CIPHER_NONE;
238
2/2
✓ Branch 0 taken 17340 times.
✓ Branch 1 taken 17 times.
17357 for (unsigned i = 0; i < sizeof list; i++)
239 {
240
2/2
✓ Branch 1 taken 493 times.
✓ Branch 2 taken 16847 times.
17340 if (gcry_cipher_algo_info(id, GCRYCTL_TEST_ALGO, NULL, NULL) == 0)
241 {
242 493 list[len] = id;
243 493 len++;
244 }
245 17340 id++;
246 }
247
2/2
✓ Branch 0 taken 161 times.
✓ Branch 1 taken 2 times.
163 for (int i = 0; i < len; i++)
248 {
249 161 const char *x = cipher_name_from_id(list[i]);
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (!x)
251 ✗ continue;
252
2/2
✓ Branch 0 taken 114 times.
✓ Branch 1 taken 47 times.
161 if (!strcasecmp(x, n))
253 1 return list[i];
254 }
255 1 return GCRY_CIPHER_NONE;
256 }
257
258 16 extern enum gcry_md_algos hash_id_from_name(const char * const restrict n)
259 {
260 #ifdef __APPLE__
261 if (!n)
262 return GCRY_MD_NONE;
263 #endif
264 16 int list[0xff] = { 0x00 };
265 2 int len = 0;
266 2 enum gcry_md_algos id = GCRY_MD_NONE;
267
2/2
✓ Branch 0 taken 16320 times.
✓ Branch 1 taken 16 times.
16336 for (unsigned i = 0; i < sizeof list; i++)
268 {
269
2/2
✓ Branch 1 taken 608 times.
✓ Branch 2 taken 15712 times.
16320 if (gcry_md_test_algo(id) == 0)
270 {
271 608 list[len] = id;
272 608 len++;
273 }
274 16320 id++;
275 }
276
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 1 times.
114 for (int i = 0; i < len; i++)
277 {
278 113 const char *x = hash_name_from_id(list[i]);
279
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 97 times.
113 if (!x)
280 16 continue;
281
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 54 times.
97 if (!strcasecmp(x, n))
282 1 return list[i];
283 }
284 1 return GCRY_MD_NONE;
285 }
286
287 16 extern enum gcry_cipher_modes mode_id_from_name(const char * const restrict n)
288 {
289 #ifdef __APPLE__
290 if (!n)
291 return GCRY_CIPHER_MODE_NONE;
292 #endif
293
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1 times.
29 for (unsigned i = 0; i < sizeof MODES / sizeof( block_mode_s ); i++)
294
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 13 times.
28 if (!strcasecmp(n, MODES[i].name))
295 15 return MODES[i].id;
296 1 return GCRY_CIPHER_MODE_NONE;
297 }
298
299 16 extern enum gcry_mac_algos mac_id_from_name(const char * const restrict n)
300 {
301 #ifdef __APPLE__
302 if (!n)
303 return GCRY_MAC_NONE;
304 #endif
305 16 int list[0xff] = { 0x00 };
306 2 int len = 0;
307 2 enum gcry_mac_algos id = GCRY_MAC_NONE;
308
2/2
✓ Branch 0 taken 16320 times.
✓ Branch 1 taken 16 times.
16336 for (unsigned i = 0; i < sizeof list; i++)
309 {
310
2/2
✓ Branch 1 taken 928 times.
✓ Branch 2 taken 15392 times.
16320 if (gcry_mac_test_algo(id) == 0)
311 {
312 928 list[len] = id;
313 928 len++;
314 }
315 16320 id++;
316 }
317
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 2 times.
146 for (int i = 0; i < len; i++)
318 {
319 144 const char *x = mac_name_from_id(list[i]);
320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
144 if (!x)
321 ✗ continue;
322
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 130 times.
144 if (!strcasecmp(x, n))
323 14 return list[i];
324 }
325 1 return GCRY_MAC_NONE;
326 }
327
328 204 extern const char *cipher_name_from_id(enum gcry_cipher_algos c)
329 {
330 204 const char *n = gcry_cipher_algo_name(c);
331
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 138 times.
204 if (!strncasecmp(NAME_AES, n, strlen(NAME_AES)))
332 66 return correct_aes_rijndael(n);
333
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 68 times.
138 else if (!strcasecmp(NAME_BLOWFISH, n))
334 3 return correct_blowfish128(n);
335
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 117 times.
120 else if (!strcasecmp(NAME_TWOFISH, n))
336 3 return correct_twofish256(n);
337 51 return n;
338 }
339
340 166 extern const char *hash_name_from_id(enum gcry_md_algos h)
341 {
342 166 const char *n = gcry_md_algo_name(h);
343
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 79 times.
166 if (algorithm_is_duplicate(n))
344 3 return NULL;
345
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 78 times.
149 else if (!strncasecmp(NAME_SHA1, n, strlen(NAME_SHA1) - 1))
346 29 return correct_sha1(n);
347 50 return n;
348 }
349
350 16 extern const char *mode_name_from_id(enum gcry_cipher_modes m)
351 {
352
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1 times.
29 for (unsigned i = 0; i < sizeof MODES / sizeof( block_mode_s ); i++)
353
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 13 times.
28 if (MODES[i].id == m)
354 15 return MODES[i].name;
355 1 return NULL;
356 }
357
358 196 extern const char *mac_name_from_id(enum gcry_mac_algos m)
359 {
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
144 return gcry_mac_algo_name(m);
361 }
362
363 9 extern bool mode_valid_for_cipher(enum gcry_cipher_algos c, enum gcry_cipher_modes m)
364 {
365 /*
366 * The cipher mode to use must be specified via mode. See Available
367 * cipher modes, for a list of supported cipher modes and the
368 * according constants.
369 *
370 * Note that some modes are incompatible with some algorithms - in
371 * particular, stream mode (GCRY_CIPHER_MODE_STREAM) only works with
372 * stream ciphers.
373 *
374 * Poly1305 AEAD mode (GCRY_CIPHER_MODE_POLY1305) only works with
375 * ChaCha20 stream cipher.
376 *
377 * The block cipher modes (GCRY_CIPHER_MODE_ECB,
378 * GCRY_CIPHER_MODE_CBC,GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_MODE_OFB,
379 * GCRY_CIPHER_MODE_CTR and GCRY_CIPHER_MODE_EAX) will work with any
380 * block cipher algorithm.
381 *
382 * GCM mode (GCRY_CIPHER_MODE_CCM), CCM mode (GCRY_CIPHER_MODE_GCM),
383 * OCB mode (GCRY_CIPHER_MODE_OCB), and XTS mode
384 * (GCRY_CIPHER_MODE_XTS) will only work with block cipher
385 * algorithms which have the block size of 16 bytes.
386 */
387
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 if (m == GCRY_CIPHER_MODE_POLY1305)
388 2 return c == GCRY_CIPHER_CHACHA20;
389
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 switch (c)
390 {
391 1 case GCRY_CIPHER_ARCFOUR:
392 case GCRY_CIPHER_SALSA20:
393 case GCRY_CIPHER_SALSA20R12:
394 case GCRY_CIPHER_CHACHA20:
395 1 return m == GCRY_CIPHER_MODE_STREAM;
396 6 default:
397 6 break;
398 }
399
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
6 switch (m)
400 {
401 2 case GCRY_CIPHER_MODE_CCM:
402 case GCRY_CIPHER_MODE_GCM:
403 case GCRY_CIPHER_MODE_OCB:
404 case 13: // GCRY_CIPHER_MODE_XTS: C'mon Slackware :-/
405 2 return !(gcry_cipher_get_algo_blklen(c) % 16);
406 1 case GCRY_CIPHER_MODE_STREAM:
407 /*
408 * Stream mode, only to be used with stream cipher
409 * algorithms.
410 */
411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 switch (c)
412 {
413 ✗ case GCRY_CIPHER_ARCFOUR:
414 case GCRY_CIPHER_SALSA20:
415 case GCRY_CIPHER_SALSA20R12:
416 case GCRY_CIPHER_CHACHA20:
417 ✗ return true;
418 1 default:
419 1 return false;
420 }
421 break;
422 2 case GCRY_CIPHER_MODE_AESWRAP:
423 /*
424 * This mode is used to implement the AES-Wrap algorithm
425 * according to RFC-3394. It may be used with any 128 bit
426 * block length algorithm, however the specs require one of
427 * the 3 AES algorithms.
428 */
429
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 switch (c)
430 {
431 1 case GCRY_CIPHER_AES128:
432 case GCRY_CIPHER_AES192:
433 case GCRY_CIPHER_AES256:
434 1 return true;
435 1 default:
436 1 return false;
437 }
438 1 default:
439 1 break;
440 }
441 /*
442 * Hopefully whatever's left is a block cipher and a block cipher
443 * mode.
444 */
445 1 return true;
446 }
447
448 29 static const char *correct_sha1(const char * const restrict n)
449 {
450
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 32 times.
71 return strcasecmp(n, NAME_SHA1) ? n : NAME_SHA160;
451 }
452
453 66 static const char *correct_aes_rijndael(const char * const restrict n)
454 {
455
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 19 times.
66 if (!strcasecmp(NAME_AES, n))
456 4 return n; /* use AES (bits/blocks/etc) */
457 /*
458 * use rijndael instead of AES as that’s the actual cipher name
459 */
460 static char x[16];
461 47 snprintf(x, sizeof x, "%s%s", NAME_RIJNDAEL, n + strlen(NAME_AES));
462 47 return (const char *)x;
463 }
464
465 3 static const char *correct_blowfish128(const char * const restrict n)
466 {
467 3 return (void)n , NAME_BLOWFISH128;
468 }
469
470 2 static const char *correct_twofish256(const char * const restrict n)
471 {
472 2 return (void)n , NAME_TWOFISH256;
473 }
474
475 82 static bool algorithm_is_duplicate(const char * const restrict n)
476 {
477
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 14 times.
166 return !strcmp(NAME_TIGER192, n);
478 }
479