1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* egg-openssl.c - OpenSSL compatibility functionality
4 Copyright (C) 2007 Stefan Walter
6 The Gnome Keyring Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The Gnome Keyring Library 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 GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the Gnome Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
21 Author: Stef Walter <stef@memberwebs.com>
27 #include "egg-openssl.h"
28 #include "egg-secure-memory.h"
29 #include "egg-symkey.h"
41 * -----BEGIN RSA PRIVATE KEY-----
42 * Proc-Type: 4,ENCRYPTED
43 * DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
45 * 4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
46 * Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
47 * u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
49 * -----END RSA PRIVATE KEY-----
52 #define PEM_SUFF "-----"
54 #define PEM_PREF_BEGIN "-----BEGIN "
55 #define PEM_PREF_BEGIN_L 11
56 #define PEM_PREF_END "-----END "
57 #define PEM_PREF_END_L 9
59 EGG_SECURE_DECLARE (openssl);
62 parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
65 gchar *line, *name, *value;
68 copy = g_strndup (hbeg, hend - hbeg);
69 lines = g_strsplit (copy, "\n", 0);
72 for (l = lines; l && *l; ++l) {
76 /* Look for the break between name: value */
77 value = strchr (line, ':');
82 value = g_strdup (value + 1);
85 name = g_strdup (line);
89 *result = egg_openssl_headers_new ();
90 g_hash_table_replace (*result, name, value);
97 pem_find_begin (const gchar *data,
102 const gchar *pref, *suff;
105 /* Look for a prefix */
106 pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
110 n_data -= (pref - data) + PEM_PREF_BEGIN_L;
111 data = pref + PEM_PREF_BEGIN_L;
113 /* Look for the end of that begin */
114 suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
118 /* Make sure on the same line */
119 if (memchr (pref, '\n', suff - pref))
127 pref += PEM_PREF_BEGIN_L;
128 g_assert (suff > pref);
129 stype = g_alloca (suff - pref + 1);
130 memcpy (stype, pref, suff - pref);
131 stype[suff - pref] = 0;
132 *type = g_quark_from_string (stype);
135 /* The byte after this ---BEGIN--- */
136 return suff + PEM_SUFF_L;
140 pem_find_end (const gchar *data,
149 /* Look for a prefix */
150 pref = g_strstr_len (data, n_data, PEM_PREF_END);
154 n_data -= (pref - data) + PEM_PREF_END_L;
155 data = pref + PEM_PREF_END_L;
157 /* Next comes the type string */
158 stype = g_quark_to_string (type);
159 n_type = strlen (stype);
160 if (strncmp ((gchar*)data, stype, n_type) != 0)
166 /* Next comes the suffix */
167 if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
172 if (isspace (data[0]))
177 /* The beginning of this ---END--- */
182 pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
183 GHashTable **headers)
185 const gchar *x, *hbeg, *hend;
186 const gchar *p, *end;
194 g_assert (n_decoded);
201 /* Try and find a pair of blank lines with only white space between */
202 while (hend == NULL) {
203 x = memchr (p, '\n', end - p);
207 while (isspace (*x)) {
208 /* Found a second line, with only spaces between */
213 /* Found a space between two lines */
229 *n_decoded = (n_data * 3) / 4 + 1;
230 if (egg_secure_check (data))
231 *decoded = egg_secure_alloc (*n_decoded);
233 *decoded = g_malloc0 (*n_decoded);
234 g_return_val_if_fail (*decoded, FALSE);
236 *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
238 egg_secure_free (*decoded);
242 if (headers && hbeg && hend)
243 parse_header_lines (hbeg, hend, headers);
249 egg_openssl_headers_new (void)
251 return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
255 egg_openssl_pem_parse (gconstpointer data, gsize n_data,
256 EggOpensslPemCallback callback, gpointer user_data)
258 const gchar *beg, *end;
259 const gchar *outer_beg, *outer_end;
261 guchar *decoded = NULL;
263 GHashTable *headers = NULL;
266 g_return_val_if_fail (data, 0);
267 g_return_val_if_fail (n_data, 0);
268 g_return_val_if_fail (callback, 0);
272 /* This returns the first character after the PEM BEGIN header */
273 beg = pem_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
279 /* This returns the character position before the PEM END header */
280 end = pem_find_end ((const gchar*)beg, n_data - ((const gchar*)beg - (const gchar *)data),
286 if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
287 g_assert (outer_end > outer_beg);
290 outer_beg, outer_end - outer_beg,
293 egg_secure_free (decoded);
295 g_hash_table_remove_all (headers);
299 /* Try for another block */
301 n_data -= (const gchar*)end - (const gchar*)data;
306 g_hash_table_destroy (headers);
312 append_each_header (gpointer key, gpointer value, gpointer user_data)
314 GString *string = (GString*)user_data;
316 g_string_append (string, (gchar*)key);
317 g_string_append (string, ": ");
318 g_string_append (string, (gchar*)value);
319 g_string_append_c (string, '\n');
323 egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type,
324 GHashTable *headers, gsize *n_result)
329 gsize n_prefix, estimate;
331 g_return_val_if_fail (data || !n_data, NULL);
332 g_return_val_if_fail (type, NULL);
333 g_return_val_if_fail (n_result, NULL);
335 string = g_string_sized_new (4096);
338 g_string_append_len (string, PEM_PREF_BEGIN, PEM_PREF_BEGIN_L);
339 g_string_append (string, g_quark_to_string (type));
340 g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
341 g_string_append_c (string, '\n');
344 if (headers && g_hash_table_size (headers) > 0) {
345 g_hash_table_foreach (headers, append_each_header, string);
346 g_string_append_c (string, '\n');
349 /* Resize string to fit the base64 data. Algorithm from Glib reference */
350 estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
351 n_prefix = string->len;
352 g_string_set_size (string, n_prefix + estimate);
354 /* The actual base64 data, without line breaks */
356 length = g_base64_encode_step (data, n_data, FALSE,
357 string->str + n_prefix, &state, &save);
358 length += g_base64_encode_close (TRUE, string->str + n_prefix + length,
361 g_assert (length <= estimate);
362 g_string_set_size (string, n_prefix + length);
365 * OpenSSL is absolutely certain that it wants its PEM base64
366 * lines to be 64 characters in length. So go through and break
370 for (i = 64; i < length; i += 64) {
371 g_string_insert_c (string, n_prefix + i, '\n');
377 g_string_append_len (string, PEM_PREF_END, PEM_PREF_END_L);
378 g_string_append (string, g_quark_to_string (type));
379 g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
380 g_string_append_c (string, '\n');
382 *n_result = string->len;
383 return (guchar*)g_string_free (string, FALSE);
386 /* ----------------------------------------------------------------------------
390 static const struct {
394 } openssl_algos[] = {
395 { "DES-ECB", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB },
396 { "DES-CFB64", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB },
397 { "DES-CFB", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB },
404 /* DES-EDE-CFB64 DES-EDE-CFB */
409 { "DES-EDE3-ECB", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB },
410 { "DES-EDE3-CFB64", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB },
411 { "DES-EDE3-CFB", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB },
414 { "DES-OFB", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_OFB },
415 { "DES-EDE3-OFB", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_OFB },
416 { "DES-CBC", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC },
417 { "DES-EDE3-CBC", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC },
422 /* RC2-CFB64 RC2-CFB */
424 { "RC4", GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM },
425 { "RC4-40", GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM },
426 { "IDEA-ECB", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_ECB },
427 { "IDEA-CFB64", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_CFB },
428 { "IDEA-OFB", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_OFB },
429 { "IDEA-CBC", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_CBC },
430 { "BF-ECB", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB },
431 { "BF-CBC", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC },
432 { "BF-CFB64", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB },
433 { "BF-CFB", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB },
434 { "BF-OFB", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB },
435 { "CAST5-ECB", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_ECB },
436 { "CAST5-CBC", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC },
437 { "CAST5-CFB64", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB },
438 { "CAST5-CFB", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB },
439 { "CAST5-OFB", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_OFB },
440 /* RC5-32-12-16-CBC */
441 /* RC5-32-12-16-ECB */
442 /* RC5-32-12-16-CFB64 RC5-32-12-16-CFB */
443 /* RC5-32-12-16-OFB */
444 { "AES-128-ECB", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB },
445 { "AES-128-CBC", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC },
448 { "AES-128-CFB128", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB },
449 { "AES-128-CFB", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB },
450 { "AES-128-OFB", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB },
451 { "AES-128-CTR", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR },
452 { "AES-192-ECB", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB },
453 { "AES-192-CBC", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC },
456 { "AES-192-CFB128", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB },
457 { "AES-192-CFB", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB },
458 { "AES-192-OFB", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB },
459 { "AES-192-CTR", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CTR },
460 { "AES-256-ECB", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB },
461 { "AES-256-CBC", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC },
464 { "AES-256-CFB128", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB },
465 { "AES-256-CFB", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB },
466 { "AES-256-OFB", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB },
467 { "AES-256-CTR", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CTR },
468 /* CAMELLIA-128-ECB */
469 /* CAMELLIA-128-CBC */
470 /* CAMELLIA-128-CFB1 */
471 /* CAMELLIA-128-CFB8 */
472 /* CAMELLIA-128-CFB128 CAMELLIA-128-CFB */
473 /* CAMELLIA-128-OFB */
474 /* CAMELLIA-192-ECB */
475 /* CAMELLIA-192-CBC */
476 /* CAMELLIA-192-CFB1 */
477 /* CAMELLIA-192-CFB8 */
478 /* CAMELLIA-192-CFB128 CAMELLIA-192-CFB */
479 /* CAMELLIA-192_OFB */
480 /* CAMELLIA-256-ECB */
481 /* CAMELLIA-256-CBC */
482 /* CAMELLIA-256-CFB1 */
483 /* CAMELLIA-256-CFB8 */
484 /* CAMELLIA-256-CFB128 CAMELLIA-256-CFB */
485 /* CAMELLIA-256-OFB */
488 /* ------------------------------------------------------------------------- */
491 egg_openssl_parse_algo (const char *name, int *mode)
493 static GQuark openssl_quarks[G_N_ELEMENTS(openssl_algos)] = { 0, };
494 static gsize openssl_quarks_inited = 0;
498 if (g_once_init_enter (&openssl_quarks_inited)) {
499 for (i = 0; i < G_N_ELEMENTS(openssl_algos); ++i)
500 openssl_quarks[i] = g_quark_from_static_string (openssl_algos[i].desc);
501 g_once_init_leave (&openssl_quarks_inited, 1);
504 q = g_quark_try_string (name);
506 for (i = 0; i < G_N_ELEMENTS(openssl_algos); ++i) {
507 if (q == openssl_quarks[i]) {
508 *mode = openssl_algos[i].mode;
509 return openssl_algos[i].algo;
518 parse_dekinfo (const gchar *dek, int *algo, int *mode, guchar **iv)
520 gboolean success = FALSE;
521 gchar **parts = NULL;
525 parts = g_strsplit (dek, ",", 2);
526 if (!parts || !parts[0] || !parts[1])
529 /* Parse the algorithm name */
530 *algo = egg_openssl_parse_algo (parts[0], mode);
534 /* Make sure this is usable */
535 gcry = gcry_cipher_test_algo (*algo);
540 ivlen = gcry_cipher_get_algo_blklen (*algo);
542 *iv = egg_hex_decode (parts[1], strlen(parts[1]), &len);
543 if (!*iv || ivlen != len) {
556 egg_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
557 gssize n_password, const guchar *data, gsize n_data,
558 guchar **decrypted, gsize *n_decrypted)
567 if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
570 ivlen = gcry_cipher_get_algo_blklen (algo);
572 /* We assume the iv is at least as long as at 8 byte salt */
573 g_return_val_if_fail (ivlen >= 8, FALSE);
575 /* IV is already set from the DEK info */
576 if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
577 n_password, iv, 8, 1, &key, NULL)) {
582 /* TODO: Use secure memory */
583 gcry = gcry_cipher_open (&ch, algo, mode, 0);
584 g_return_val_if_fail (!gcry, FALSE);
586 gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
587 g_return_val_if_fail (!gcry, FALSE);
588 egg_secure_free (key);
591 gcry = gcry_cipher_setiv (ch, iv, ivlen);
592 g_return_val_if_fail (!gcry, FALSE);
595 /* Allocate output area */
596 *n_decrypted = n_data;
597 *decrypted = egg_secure_alloc (n_data);
599 gcry = gcry_cipher_decrypt (ch, *decrypted, *n_decrypted, (void*)data, n_data);
601 egg_secure_free (*decrypted);
602 g_return_val_if_reached (FALSE);
605 gcry_cipher_close (ch);
611 egg_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
612 gssize n_password, const guchar *data, gsize n_data,
613 guchar **encrypted, gsize *n_encrypted)
615 gsize n_overflow, n_batch, n_padding;
619 guchar *padded = NULL;
624 if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
625 g_return_val_if_reached (FALSE);
627 ivlen = gcry_cipher_get_algo_blklen (algo);
629 /* We assume the iv is at least as long as at 8 byte salt */
630 g_return_val_if_fail (ivlen >= 8, FALSE);
632 /* IV is already set from the DEK info */
633 if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
634 n_password, iv, 8, 1, &key, NULL))
635 g_return_val_if_reached (FALSE);
637 gcry = gcry_cipher_open (&ch, algo, mode, 0);
638 g_return_val_if_fail (!gcry, FALSE);
640 gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
641 g_return_val_if_fail (!gcry, FALSE);
642 egg_secure_free (key);
645 gcry = gcry_cipher_setiv (ch, iv, ivlen);
646 g_return_val_if_fail (!gcry, FALSE);
649 /* Allocate output area */
650 n_overflow = (n_data % ivlen);
651 n_padding = n_overflow ? (ivlen - n_overflow) : 0;
652 n_batch = n_data - n_overflow;
653 *n_encrypted = n_data + n_padding;
654 *encrypted = g_malloc0 (*n_encrypted);
656 g_assert (*n_encrypted % ivlen == 0);
657 g_assert (*n_encrypted >= n_data);
658 g_assert (*n_encrypted == n_batch + n_overflow + n_padding);
660 /* Encrypt everything but the last bit */
661 gcry = gcry_cipher_encrypt (ch, *encrypted, n_batch, (void*)data, n_batch);
664 g_return_val_if_reached (FALSE);
667 /* Encrypt the padded block */
669 padded = egg_secure_alloc (ivlen);
670 memset (padded, 0, ivlen);
671 memcpy (padded, data + n_batch, n_overflow);
672 gcry = gcry_cipher_encrypt (ch, *encrypted + n_batch, ivlen, padded, ivlen);
673 egg_secure_free (padded);
676 g_return_val_if_reached (FALSE);
680 gcry_cipher_close (ch);
685 egg_openssl_get_dekinfo (GHashTable *headers)
690 val = g_hash_table_lookup (headers, "Proc-Type");
691 if (!val || strcmp (val, "4,ENCRYPTED") != 0)
693 val = g_hash_table_lookup (headers, "DEK-Info");
694 g_return_val_if_fail (val, NULL);
699 egg_openssl_prep_dekinfo (GHashTable *headers)
701 gchar *dekinfo, *hex;
706 ivlen = gcry_cipher_get_algo_blklen (GCRY_CIPHER_3DES);
707 g_return_val_if_fail (ivlen, NULL);
708 iv = g_malloc (ivlen);
709 gcry_create_nonce (iv, ivlen);
711 /* And encode it into the string */
712 hex = egg_hex_encode (iv, ivlen);
713 g_return_val_if_fail (hex, NULL);
714 dekinfo = g_strdup_printf ("DES-EDE3-CBC,%s", hex);
717 g_hash_table_insert (headers, g_strdup ("DEK-Info"), (void*)dekinfo);
718 g_hash_table_insert (headers, g_strdup ("Proc-Type"), g_strdup ("4,ENCRYPTED"));