Merge branch 'gnome-3-2'
[platform/upstream/gcr.git] / egg / egg-openssl.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* egg-openssl.c - OpenSSL compatibility functionality
3
4    Copyright (C) 2007 Stefan Walter
5
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.
10
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.
15
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.
20
21    Author: Stef Walter <stef@memberwebs.com>
22 */
23
24 #include "config.h"
25
26 #include "egg-hex.h"
27 #include "egg-openssl.h"
28 #include "egg-secure-memory.h"
29 #include "egg-symkey.h"
30
31 #include <gcrypt.h>
32
33 #include <glib.h>
34
35 #include <ctype.h>
36 #include <string.h>
37
38 /*
39  * PEM looks like:
40  *
41  *      -----BEGIN RSA PRIVATE KEY-----
42  *      Proc-Type: 4,ENCRYPTED
43  *      DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
44  *
45  *      4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
46  *      Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
47  *      u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
48  *      ....
49  *      -----END RSA PRIVATE KEY-----
50  */
51
52 #define PEM_SUFF          "-----"
53 #define PEM_SUFF_L        5
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
58
59 EGG_SECURE_DECLARE (openssl);
60
61 static void
62 parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
63 {
64         gchar **lines, **l;
65         gchar *line, *name, *value;
66         gchar *copy;
67
68         copy = g_strndup (hbeg, hend - hbeg);
69         lines = g_strsplit (copy, "\n", 0);
70         g_free (copy);
71
72         for (l = lines; l && *l; ++l) {
73                 line = *l;
74                 g_strstrip (line);
75
76                 /* Look for the break between name: value */
77                 value = strchr (line, ':');
78                 if (value == NULL)
79                         continue;
80
81                 *value = 0;
82                 value = g_strdup (value + 1);
83                 g_strstrip (value);
84
85                 name = g_strdup (line);
86                 g_strstrip (name);
87
88                 if (!*result)
89                         *result = egg_openssl_headers_new ();
90                 g_hash_table_replace (*result, name, value);
91         }
92
93         g_strfreev (lines);
94 }
95
96 static const gchar*
97 pem_find_begin (const gchar *data,
98                 gsize n_data,
99                 GQuark *type,
100                 const gchar **outer)
101 {
102         const gchar *pref, *suff;
103         gchar *stype;
104
105         /* Look for a prefix */
106         pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
107         if (!pref)
108                 return NULL;
109
110         n_data -= (pref - data) + PEM_PREF_BEGIN_L;
111         data = pref + PEM_PREF_BEGIN_L;
112
113         /* Look for the end of that begin */
114         suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
115         if (!suff)
116                 return NULL;
117
118         /* Make sure on the same line */
119         if (memchr (pref, '\n', suff - pref))
120                 return NULL;
121
122         if (outer)
123                 *outer = pref;
124
125         if (type) {
126                 *type = 0;
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);
133         }
134
135         /* The byte after this ---BEGIN--- */
136         return suff + PEM_SUFF_L;
137 }
138
139 static const gchar*
140 pem_find_end (const gchar *data,
141               gsize n_data,
142               GQuark type,
143               const gchar **outer)
144 {
145         const gchar *stype;
146         const gchar *pref;
147         gsize n_type;
148
149         /* Look for a prefix */
150         pref = g_strstr_len (data, n_data, PEM_PREF_END);
151         if (!pref)
152                 return NULL;
153
154         n_data -= (pref - data) + PEM_PREF_END_L;
155         data = pref + PEM_PREF_END_L;
156
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)
161                 return NULL;
162
163         n_data -= n_type;
164         data += n_type;
165
166         /* Next comes the suffix */
167         if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
168                 return NULL;
169
170         if (outer != NULL) {
171                 data += PEM_SUFF_L;
172                 if (isspace (data[0]))
173                         data++;
174                 *outer = data;
175         }
176
177         /* The beginning of this ---END--- */
178         return pref;
179 }
180
181 static gboolean
182 pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
183                  GHashTable **headers)
184 {
185         const gchar *x, *hbeg, *hend;
186         const gchar *p, *end;
187         gint state = 0;
188         guint save = 0;
189
190         g_assert (data);
191         g_assert (n_data);
192
193         g_assert (decoded);
194         g_assert (n_decoded);
195
196         p = data;
197         end = p + n_data;
198
199         hbeg = hend = NULL;
200
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);
204                 if (!x)
205                         break;
206                 ++x;
207                 while (isspace (*x)) {
208                         /* Found a second line, with only spaces between */
209                         if (*x == '\n') {
210                                 hbeg = data;
211                                 hend = x;
212                                 break;
213                         /* Found a space between two lines */
214                         } else {
215                                 ++x;
216                         }
217                 }
218
219                 /* Try next line */
220                 p = x;
221         }
222
223         /* Headers found? */
224         if (hbeg && hend) {
225                 data = hend;
226                 n_data = end - data;
227         }
228
229         *n_decoded = (n_data * 3) / 4 + 1;
230         if (egg_secure_check (data))
231                 *decoded = egg_secure_alloc (*n_decoded);
232         else
233                 *decoded = g_malloc0 (*n_decoded);
234         g_return_val_if_fail (*decoded, FALSE);
235
236         *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
237         if (!*n_decoded) {
238                 egg_secure_free (*decoded);
239                 return FALSE;
240         }
241
242         if (headers && hbeg && hend)
243                 parse_header_lines (hbeg, hend, headers);
244
245         return TRUE;
246 }
247
248 GHashTable*
249 egg_openssl_headers_new (void)
250 {
251         return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
252 }
253
254 guint
255 egg_openssl_pem_parse (gconstpointer data, gsize n_data,
256                        EggOpensslPemCallback callback, gpointer user_data)
257 {
258         const gchar *beg, *end;
259         const gchar *outer_beg, *outer_end;
260         guint nfound = 0;
261         guchar *decoded = NULL;
262         gsize n_decoded = 0;
263         GHashTable *headers = NULL;
264         GQuark type;
265
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);
269
270         while (n_data > 0) {
271
272                 /* This returns the first character after the PEM BEGIN header */
273                 beg = pem_find_begin ((const gchar*)data, n_data, &type, &outer_beg);
274                 if (!beg)
275                         break;
276
277                 g_assert (type);
278
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),
281                                     type, &outer_end);
282                 if (!end)
283                         break;
284
285                 if (beg != end) {
286                         if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
287                                 g_assert (outer_end > outer_beg);
288                                 (callback) (type,
289                                             decoded, n_decoded,
290                                             outer_beg, outer_end - outer_beg,
291                                             headers, user_data);
292                                 ++nfound;
293                                 egg_secure_free (decoded);
294                                 if (headers)
295                                         g_hash_table_remove_all (headers);
296                         }
297                 }
298
299                 /* Try for another block */
300                 end += PEM_SUFF_L;
301                 n_data -= (const gchar*)end - (const gchar*)data;
302                 data = end;
303         }
304
305         if (headers)
306                 g_hash_table_destroy (headers);
307
308         return nfound;
309 }
310
311 static void
312 append_each_header (gpointer key, gpointer value, gpointer user_data)
313 {
314         GString *string = (GString*)user_data;
315
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');
320 }
321
322 guchar*
323 egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type,
324                     GHashTable *headers, gsize *n_result)
325 {
326         GString *string;
327         gint state, save;
328         gsize i, length;
329         gsize n_prefix, estimate;
330
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);
334
335         string = g_string_sized_new (4096);
336
337         /* The prefix */
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');
342
343         /* The headers */
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');
347         }
348
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);
353
354         /* The actual base64 data, without line breaks */
355         state = save = 0;
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,
359                                          &state, &save);
360
361         g_assert (length <= estimate);
362         g_string_set_size (string, n_prefix + length);
363
364         /*
365          * OpenSSL is absolutely certain that it wants its PEM base64
366          * lines to be 64 characters in length. So go through and break
367          * those lines up.
368          */
369
370         for (i = 64; i < length; i += 64) {
371                 g_string_insert_c (string, n_prefix + i, '\n');
372                 ++length;
373                 ++i;
374         }
375
376         /* The suffix */
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');
381
382         *n_result = string->len;
383         return (guchar*)g_string_free (string, FALSE);
384 }
385
386 /* ----------------------------------------------------------------------------
387  * DEFINITIONS
388  */
389
390 static const struct {
391         const gchar *desc;
392         int algo;
393         int mode;
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 },
398         /* DES-CFB1 */
399         /* DES-CFB8 */
400         /* DESX-CBC */
401         /* DES-EDE */
402         /* DES-EDE-CBC */
403         /* DES-EDE-ECB */
404         /* DES-EDE-CFB64 DES-EDE-CFB */
405         /* DES-EDE-CFB1 */
406         /* DES-EDE-CFB8 */
407         /* DES-EDE-OFB */
408         /* DES-EDE3 */
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 },
412         /* DES-EDE3-CFB1 */
413         /* DES-EDE3-CFB8 */
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 },
418         /* RC2-ECB */
419         /* RC2-CBC */
420         /* RC2-40-CBC */
421         /* RC2-64-CBC */
422         /* RC2-CFB64    RC2-CFB */
423         /* RC2-OFB */
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 },
446         /* AES-128-CFB1 */
447         /* AES-128-CFB8 */
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 },
454         /* AES-192-CFB1 */
455         /* AES-192-CFB8 */
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 },
462         /* AES-256-CFB1 */
463         /* AES-256-CFB8 */
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 */
486 };
487
488 /* ------------------------------------------------------------------------- */
489
490 int
491 egg_openssl_parse_algo (const char *name, int *mode)
492 {
493         static GQuark openssl_quarks[G_N_ELEMENTS(openssl_algos)] = { 0, };
494         static gsize openssl_quarks_inited = 0;
495         GQuark q;
496         int i;
497
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);
502         }
503
504         q = g_quark_try_string (name);
505         if (q) {
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;
510                         }
511                 }
512         }
513
514         return 0;
515 }
516
517 static gboolean
518 parse_dekinfo (const gchar *dek, int *algo, int *mode, guchar **iv)
519 {
520         gboolean success = FALSE;
521         gchar **parts = NULL;
522         gcry_error_t gcry;
523         gsize ivlen, len;
524
525         parts = g_strsplit (dek, ",", 2);
526         if (!parts || !parts[0] || !parts[1])
527                 goto done;
528
529         /* Parse the algorithm name */
530         *algo = egg_openssl_parse_algo (parts[0], mode);
531         if (!*algo)
532                 goto done;
533
534         /* Make sure this is usable */
535         gcry = gcry_cipher_test_algo (*algo);
536         if (gcry)
537                 goto done;
538
539         /* Parse the IV */
540         ivlen = gcry_cipher_get_algo_blklen (*algo);
541
542         *iv = egg_hex_decode (parts[1], strlen(parts[1]), &len);
543         if (!*iv || ivlen != len) {
544                 g_free (*iv);
545                 goto done;
546         }
547
548         success = TRUE;
549
550 done:
551         g_strfreev (parts);
552         return success;
553 }
554
555 gboolean
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)
559 {
560         gcry_cipher_hd_t ch;
561         guchar *key = NULL;
562         guchar *iv = NULL;
563         int gcry, ivlen;
564         int algo = 0;
565         int mode = 0;
566
567         if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
568                 return FALSE;
569
570         ivlen = gcry_cipher_get_algo_blklen (algo);
571
572         /* We assume the iv is at least as long as at 8 byte salt */
573         g_return_val_if_fail (ivlen >= 8, FALSE);
574
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)) {
578                 g_free (iv);
579                 return FALSE;
580         }
581
582         /* TODO: Use secure memory */
583         gcry = gcry_cipher_open (&ch, algo, mode, 0);
584         g_return_val_if_fail (!gcry, FALSE);
585
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);
589
590         /* 16 = 128 bits */
591         gcry = gcry_cipher_setiv (ch, iv, ivlen);
592         g_return_val_if_fail (!gcry, FALSE);
593         g_free (iv);
594
595         /* Allocate output area */
596         *n_decrypted = n_data;
597         *decrypted = egg_secure_alloc (n_data);
598
599         gcry = gcry_cipher_decrypt (ch, *decrypted, *n_decrypted, (void*)data, n_data);
600         if (gcry) {
601                 egg_secure_free (*decrypted);
602                 g_return_val_if_reached (FALSE);
603         }
604
605         gcry_cipher_close (ch);
606
607         return TRUE;
608 }
609
610 gboolean
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)
614 {
615         gsize n_overflow, n_batch, n_padding;
616         gcry_cipher_hd_t ch;
617         guchar *key = NULL;
618         guchar *iv = NULL;
619         guchar *padded = NULL;
620         int gcry, ivlen;
621         int algo = 0;
622         int mode = 0;
623
624         if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
625                 g_return_val_if_reached (FALSE);
626
627         ivlen = gcry_cipher_get_algo_blklen (algo);
628
629         /* We assume the iv is at least as long as at 8 byte salt */
630         g_return_val_if_fail (ivlen >= 8, FALSE);
631
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);
636
637         gcry = gcry_cipher_open (&ch, algo, mode, 0);
638         g_return_val_if_fail (!gcry, FALSE);
639
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);
643
644         /* 16 = 128 bits */
645         gcry = gcry_cipher_setiv (ch, iv, ivlen);
646         g_return_val_if_fail (!gcry, FALSE);
647         g_free (iv);
648
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);
655
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);
659
660         /* Encrypt everything but the last bit */
661         gcry = gcry_cipher_encrypt (ch, *encrypted, n_batch, (void*)data, n_batch);
662         if (gcry) {
663                 g_free (*encrypted);
664                 g_return_val_if_reached (FALSE);
665         }
666
667         /* Encrypt the padded block */
668         if (n_overflow) {
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);
674                 if (gcry) {
675                         g_free (*encrypted);
676                         g_return_val_if_reached (FALSE);
677                 }
678         }
679
680         gcry_cipher_close (ch);
681         return TRUE;
682 }
683
684 const gchar*
685 egg_openssl_get_dekinfo (GHashTable *headers)
686 {
687         const gchar *val;
688         if (!headers)
689                 return NULL;
690         val = g_hash_table_lookup (headers, "Proc-Type");
691         if (!val || strcmp (val, "4,ENCRYPTED") != 0)
692                 return NULL;
693         val = g_hash_table_lookup (headers, "DEK-Info");
694         g_return_val_if_fail (val, NULL);
695         return val;
696 }
697
698 const gchar*
699 egg_openssl_prep_dekinfo (GHashTable *headers)
700 {
701         gchar *dekinfo, *hex;
702         gsize ivlen;
703         guchar *iv;
704
705         /* Create the iv */
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);
710
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);
715         g_free (hex);
716
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"));
719
720         return dekinfo;
721 }