[egg] Complete implementation of openssl sytle PEM writing
[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 static void
60 parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
61 {
62         gchar **lines, **l;
63         gchar *line, *name, *value;
64         gchar *copy;
65
66         copy = g_strndup (hbeg, hend - hbeg);
67         lines = g_strsplit (copy, "\n", 0);
68         g_free (copy);
69
70         for (l = lines; l && *l; ++l) {
71                 line = *l;
72                 g_strstrip (line);
73
74                 /* Look for the break between name: value */
75                 value = strchr (line, ':');
76                 if (value == NULL)
77                         continue;
78
79                 *value = 0;
80                 value = g_strdup (value + 1);
81                 g_strstrip (value);
82
83                 name = g_strdup (line);
84                 g_strstrip (name);
85
86                 if (!*result)
87                         *result = egg_openssl_headers_new ();
88                 g_hash_table_replace (*result, name, value);
89         }
90
91         g_strfreev (lines);
92 }
93
94 static const gchar*
95 pem_find_begin (const gchar *data, gsize n_data, GQuark *type)
96 {
97         const gchar *pref, *suff;
98         gchar *stype;
99
100         /* Look for a prefix */
101         pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
102         if (!pref)
103                 return NULL;
104
105         n_data -= (pref - data) + PEM_PREF_BEGIN_L;
106         data = pref + PEM_PREF_BEGIN_L;
107
108         /* Look for the end of that begin */
109         suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
110         if (!suff)
111                 return NULL;
112
113         /* Make sure on the same line */
114         if (memchr (pref, '\n', suff - pref))
115                 return NULL;
116
117         if (type) {
118                 *type = 0;
119                 pref += PEM_PREF_BEGIN_L;
120                 g_assert (suff > pref);
121                 stype = g_alloca (suff - pref + 1);
122                 memcpy (stype, pref, suff - pref);
123                 stype[suff - pref] = 0;
124                 *type = g_quark_from_string (stype);
125         }
126
127         /* The byte after this ---BEGIN--- */
128         return suff + PEM_SUFF_L;
129 }
130
131 static const gchar*
132 pem_find_end (const gchar *data, gsize n_data, GQuark type)
133 {
134         const gchar *stype;
135         const gchar *pref;
136         gsize n_type;
137
138         /* Look for a prefix */
139         pref = g_strstr_len (data, n_data, PEM_PREF_END);
140         if (!pref)
141                 return NULL;
142
143         n_data -= (pref - data) + PEM_PREF_END_L;
144         data = pref + PEM_PREF_END_L;
145
146         /* Next comes the type string */
147         stype = g_quark_to_string (type);
148         n_type = strlen (stype);
149         if (strncmp ((gchar*)data, stype, n_type) != 0)
150                 return NULL;
151
152         n_data -= n_type;
153         data += n_type;
154
155         /* Next comes the suffix */
156         if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
157                 return NULL;
158
159         /* The beginning of this ---END--- */
160         return pref;
161 }
162
163 static gboolean
164 pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
165                  GHashTable **headers)
166 {
167         const gchar *x, *hbeg, *hend;
168         const gchar *p, *end;
169         gint state = 0;
170         guint save = 0;
171
172         g_assert (data);
173         g_assert (n_data);
174
175         g_assert (decoded);
176         g_assert (n_decoded);
177
178         p = data;
179         end = p + n_data;
180
181         hbeg = hend = NULL;
182
183         /* Try and find a pair of blank lines with only white space between */
184         while (hend == NULL) {
185                 x = memchr (p, '\n', end - p);
186                 if (!x)
187                         break;
188                 ++x;
189                 while (isspace (*x)) {
190                         /* Found a second line, with only spaces between */
191                         if (*x == '\n') {
192                                 hbeg = data;
193                                 hend = x;
194                                 break;
195                         /* Found a space between two lines */
196                         } else {
197                                 ++x;
198                         }
199                 }
200
201                 /* Try next line */
202                 p = x;
203         }
204
205         /* Headers found? */
206         if (hbeg && hend) {
207                 data = hend;
208                 n_data = end - data;
209         }
210
211         *n_decoded = (n_data * 3) / 4 + 1;
212         if (egg_secure_check (data))
213                 *decoded = egg_secure_alloc (*n_decoded);
214         else
215                 *decoded = g_malloc0 (*n_decoded);
216         g_return_val_if_fail (*decoded, FALSE);
217
218         *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
219         if (!*n_decoded) {
220                 egg_secure_free (*decoded);
221                 return FALSE;
222         }
223
224         if (headers && hbeg && hend)
225                 parse_header_lines (hbeg, hend, headers);
226
227         return TRUE;
228 }
229
230 GHashTable*
231 egg_openssl_headers_new (void)
232 {
233         return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
234 }
235
236 guint
237 egg_openssl_pem_parse (const guchar *data, gsize n_data,
238                        EggOpensslPemCallback callback, gpointer user_data)
239 {
240         const gchar *beg, *end;
241         guint nfound = 0;
242         guchar *decoded = NULL;
243         gsize n_decoded = 0;
244         GHashTable *headers = NULL;
245         GQuark type;
246
247         g_return_val_if_fail (data, 0);
248         g_return_val_if_fail (n_data, 0);
249         g_return_val_if_fail (callback, 0);
250
251         while (n_data > 0) {
252
253                 /* This returns the first character after the PEM BEGIN header */
254                 beg = pem_find_begin ((const gchar*)data, n_data, &type);
255                 if (!beg)
256                         break;
257
258                 g_assert (type);
259
260                 /* This returns the character position before the PEM END header */
261                 end = pem_find_end ((const gchar*)beg, n_data - ((const guchar*)beg - data), type);
262                 if (!end)
263                         break;
264
265                 if (beg != end) {
266                         if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
267                                 (callback) (type, decoded, n_decoded, headers, user_data);
268                                 ++nfound;
269                                 egg_secure_free (decoded);
270                                 if (headers)
271                                         g_hash_table_remove_all (headers);
272                         }
273                 }
274
275                 /* Try for another block */
276                 end += PEM_SUFF_L;
277                 n_data -= (const guchar*)end - data;
278                 data = (const guchar*)end;
279         }
280
281         if (headers)
282                 g_hash_table_destroy (headers);
283
284         return nfound;
285 }
286
287 static void
288 append_each_header (gpointer key, gpointer value, gpointer user_data)
289 {
290         GString *string = (GString*)user_data;
291
292         g_string_append (string, (gchar*)key);
293         g_string_append (string, ": ");
294         g_string_append (string, (gchar*)value);
295         g_string_append_c (string, '\n');
296 }
297
298 guchar*
299 egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type,
300                     GHashTable *headers, gsize *n_result)
301 {
302         GString *string;
303         gint state, save;
304         gsize i, length;
305         gsize n_prefix, estimate;
306
307         g_return_val_if_fail (data || !n_data, NULL);
308         g_return_val_if_fail (type, NULL);
309         g_return_val_if_fail (n_result, NULL);
310
311         string = g_string_sized_new (4096);
312
313         /* The prefix */
314         g_string_append_len (string, PEM_PREF_BEGIN, PEM_PREF_BEGIN_L);
315         g_string_append (string, g_quark_to_string (type));
316         g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
317         g_string_append_c (string, '\n');
318
319         /* The headers */
320         if (headers && g_hash_table_size (headers) > 0) {
321                 g_hash_table_foreach (headers, append_each_header, string);
322                 g_string_append_c (string, '\n');
323         }
324
325         /* Resize string to fit the base64 data. Algorithm from Glib reference */
326         estimate = n_data * 4 / 3 + n_data * 4 / (3 * 65) + 7;
327         n_prefix = string->len;
328         g_string_set_size (string, n_prefix + estimate);
329
330         /* The actual base64 data, without line breaks */
331         state = save = 0;
332         length = g_base64_encode_step (data, n_data, FALSE,
333                                        string->str + n_prefix, &state, &save);
334         length += g_base64_encode_close (TRUE, string->str + n_prefix + length,
335                                          &state, &save);
336
337         g_assert (length <= estimate);
338         g_string_set_size (string, n_prefix + length);
339
340         /*
341          * OpenSSL is absolutely certain that it wants its PEM base64
342          * lines to be 64 characters in length. So go through and break
343          * those lines up.
344          */
345
346         for (i = 64; i < length; i += 64) {
347                 g_string_insert_c (string, n_prefix + i, '\n');
348                 ++length;
349                 ++i;
350         }
351
352         /* The suffix */
353         g_string_append_len (string, PEM_PREF_END, PEM_PREF_END_L);
354         g_string_append (string, g_quark_to_string (type));
355         g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
356         g_string_append_c (string, '\n');
357
358         *n_result = string->len;
359         return (guchar*)g_string_free (string, FALSE);
360 }
361
362 /* ----------------------------------------------------------------------------
363  * DEFINITIONS
364  */
365
366 static const struct {
367         const gchar *desc;
368         int algo;
369         int mode;
370 } openssl_algos[] = {
371         { "DES-ECB", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB },
372         { "DES-CFB64", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB },
373         { "DES-CFB", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB },
374         /* DES-CFB1 */
375         /* DES-CFB8 */
376         /* DESX-CBC */
377         /* DES-EDE */
378         /* DES-EDE-CBC */
379         /* DES-EDE-ECB */
380         /* DES-EDE-CFB64 DES-EDE-CFB */
381         /* DES-EDE-CFB1 */
382         /* DES-EDE-CFB8 */
383         /* DES-EDE-OFB */
384         /* DES-EDE3 */
385         { "DES-EDE3-ECB", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB },
386         { "DES-EDE3-CFB64", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB },
387         { "DES-EDE3-CFB", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB },
388         /* DES-EDE3-CFB1 */
389         /* DES-EDE3-CFB8 */
390         { "DES-OFB", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_OFB },
391         { "DES-EDE3-OFB", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_OFB },
392         { "DES-CBC", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC },
393         { "DES-EDE3-CBC", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC },
394         /* RC2-ECB */
395         /* RC2-CBC */
396         /* RC2-40-CBC */
397         /* RC2-64-CBC */
398         /* RC2-CFB64    RC2-CFB */
399         /* RC2-OFB */
400         { "RC4", GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM },
401         { "RC4-40", GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM },
402         { "IDEA-ECB", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_ECB },
403         { "IDEA-CFB64", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_CFB },
404         { "IDEA-OFB", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_OFB },
405         { "IDEA-CBC", GCRY_CIPHER_IDEA, GCRY_CIPHER_MODE_CBC },
406         { "BF-ECB", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB },
407         { "BF-CBC", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC },
408         { "BF-CFB64", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB },
409         { "BF-CFB", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB },
410         { "BF-OFB", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB },
411         { "CAST5-ECB", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_ECB },
412         { "CAST5-CBC", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC },
413         { "CAST5-CFB64", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB },
414         { "CAST5-CFB", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB },
415         { "CAST5-OFB", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_OFB },
416         /* RC5-32-12-16-CBC */
417         /* RC5-32-12-16-ECB */
418         /* RC5-32-12-16-CFB64  RC5-32-12-16-CFB */
419         /* RC5-32-12-16-OFB */
420         { "AES-128-ECB", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB },
421         { "AES-128-CBC", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC },
422         /* AES-128-CFB1 */
423         /* AES-128-CFB8 */
424         { "AES-128-CFB128", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB },
425         { "AES-128-CFB", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB },
426         { "AES-128-OFB", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB },
427         { "AES-128-CTR", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR },
428         { "AES-192-ECB", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB },
429         { "AES-192-CBC", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC },
430         /* AES-192-CFB1 */
431         /* AES-192-CFB8 */
432         { "AES-192-CFB128", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB },
433         { "AES-192-CFB", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB },
434         { "AES-192-OFB", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB },
435         { "AES-192-CTR", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CTR },
436         { "AES-256-ECB", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB },
437         { "AES-256-CBC", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC },
438         /* AES-256-CFB1 */
439         /* AES-256-CFB8 */
440         { "AES-256-CFB128", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB },
441         { "AES-256-CFB", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB },
442         { "AES-256-OFB", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB },
443         { "AES-256-CTR", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CTR },
444         /* CAMELLIA-128-ECB */
445         /* CAMELLIA-128-CBC */
446         /* CAMELLIA-128-CFB1 */
447         /* CAMELLIA-128-CFB8 */
448         /* CAMELLIA-128-CFB128   CAMELLIA-128-CFB */
449         /* CAMELLIA-128-OFB */
450         /* CAMELLIA-192-ECB */
451         /* CAMELLIA-192-CBC */
452         /* CAMELLIA-192-CFB1 */
453         /* CAMELLIA-192-CFB8 */
454         /* CAMELLIA-192-CFB128   CAMELLIA-192-CFB */
455         /* CAMELLIA-192_OFB */
456         /* CAMELLIA-256-ECB */
457         /* CAMELLIA-256-CBC */
458         /* CAMELLIA-256-CFB1 */
459         /* CAMELLIA-256-CFB8 */
460         /* CAMELLIA-256-CFB128   CAMELLIA-256-CFB */
461         /* CAMELLIA-256-OFB */
462 };
463
464 /* ------------------------------------------------------------------------- */
465
466 int
467 egg_openssl_parse_algo (const char *name, int *mode)
468 {
469         static GQuark openssl_quarks[G_N_ELEMENTS(openssl_algos)] = { 0, };
470         static gsize openssl_quarks_inited = 0;
471         GQuark q;
472         int i;
473
474         if (g_once_init_enter (&openssl_quarks_inited)) {
475                 for (i = 0; i < G_N_ELEMENTS(openssl_algos); ++i)
476                         openssl_quarks[i] = g_quark_from_static_string (openssl_algos[i].desc);
477                 g_once_init_leave (&openssl_quarks_inited, 1);
478         }
479
480         q = g_quark_try_string (name);
481         if (q) {
482                 for (i = 0; i < G_N_ELEMENTS(openssl_algos); ++i) {
483                         if (q == openssl_quarks[i]) {
484                                 *mode = openssl_algos[i].mode;
485                                 return openssl_algos[i].algo;
486                         }
487                 }
488         }
489
490         return 0;
491 }
492
493 static gboolean
494 parse_dekinfo (const gchar *dek, int *algo, int *mode, guchar **iv)
495 {
496         gboolean success = FALSE;
497         gchar **parts = NULL;
498         gcry_error_t gcry;
499         gsize ivlen, len;
500
501         parts = g_strsplit (dek, ",", 2);
502         if (!parts || !parts[0] || !parts[1])
503                 goto done;
504
505         /* Parse the algorithm name */
506         *algo = egg_openssl_parse_algo (parts[0], mode);
507         if (!*algo)
508                 goto done;
509
510         /* Make sure this is usable */
511         gcry = gcry_cipher_test_algo (*algo);
512         if (gcry)
513                 goto done;
514
515         /* Parse the IV */
516         ivlen = gcry_cipher_get_algo_blklen (*algo);
517
518         *iv = egg_hex_decode (parts[1], strlen(parts[1]), &len);
519         if (!*iv || ivlen != len) {
520                 g_free (*iv);
521                 goto done;
522         }
523
524         success = TRUE;
525
526 done:
527         g_strfreev (parts);
528         return success;
529 }
530
531 gboolean
532 egg_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
533                            gssize n_password, const guchar *data, gsize n_data,
534                            guchar **decrypted, gsize *n_decrypted)
535 {
536         gcry_cipher_hd_t ch;
537         guchar *key = NULL;
538         guchar *iv = NULL;
539         int gcry, ivlen;
540         int algo = 0;
541         int mode = 0;
542
543         if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
544                 return FALSE;
545
546         ivlen = gcry_cipher_get_algo_blklen (algo);
547
548         /* We assume the iv is at least as long as at 8 byte salt */
549         g_return_val_if_fail (ivlen >= 8, FALSE);
550
551         /* IV is already set from the DEK info */
552         if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
553                                                 n_password, iv, 8, 1, &key, NULL)) {
554                 g_free (iv);
555                 return FALSE;
556         }
557
558         /* TODO: Use secure memory */
559         gcry = gcry_cipher_open (&ch, algo, mode, 0);
560         g_return_val_if_fail (!gcry, FALSE);
561
562         gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
563         g_return_val_if_fail (!gcry, FALSE);
564         egg_secure_free (key);
565
566         /* 16 = 128 bits */
567         gcry = gcry_cipher_setiv (ch, iv, ivlen);
568         g_return_val_if_fail (!gcry, FALSE);
569         g_free (iv);
570
571         /* Allocate output area */
572         *n_decrypted = n_data;
573         *decrypted = egg_secure_alloc (n_data);
574
575         gcry = gcry_cipher_decrypt (ch, *decrypted, *n_decrypted, (void*)data, n_data);
576         if (gcry) {
577                 egg_secure_free (*decrypted);
578                 g_return_val_if_reached (FALSE);
579         }
580
581         gcry_cipher_close (ch);
582
583         return TRUE;
584 }
585
586 gboolean
587 egg_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
588                                 gssize n_password, const guchar *data, gsize n_data,
589                                 guchar **encrypted, gsize *n_encrypted)
590 {
591         gsize n_overflow, n_batch, n_padding;
592         gcry_cipher_hd_t ch;
593         guchar *key = NULL;
594         guchar *iv = NULL;
595         guchar *padded = NULL;
596         int gcry, ivlen;
597         int algo = 0;
598         int mode = 0;
599
600         if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
601                 g_return_val_if_reached (FALSE);
602
603         ivlen = gcry_cipher_get_algo_blklen (algo);
604
605         /* We assume the iv is at least as long as at 8 byte salt */
606         g_return_val_if_fail (ivlen >= 8, FALSE);
607
608         /* IV is already set from the DEK info */
609         if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
610                                                 n_password, iv, 8, 1, &key, NULL))
611                 g_return_val_if_reached (FALSE);
612
613         gcry = gcry_cipher_open (&ch, algo, mode, 0);
614         g_return_val_if_fail (!gcry, FALSE);
615
616         gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
617         g_return_val_if_fail (!gcry, FALSE);
618         egg_secure_free (key);
619
620         /* 16 = 128 bits */
621         gcry = gcry_cipher_setiv (ch, iv, ivlen);
622         g_return_val_if_fail (!gcry, FALSE);
623         g_free (iv);
624
625         /* Allocate output area */
626         n_overflow = (n_data % ivlen);
627         n_padding = n_overflow ? (ivlen - n_overflow) : 0;
628         n_batch = n_data - n_overflow;
629         *n_encrypted = n_data + n_padding;
630         *encrypted = g_malloc0 (*n_encrypted);
631
632         g_assert (*n_encrypted % ivlen == 0);
633         g_assert (*n_encrypted >= n_data);
634         g_assert (*n_encrypted == n_batch + n_overflow + n_padding);
635
636         /* Encrypt everything but the last bit */
637         gcry = gcry_cipher_encrypt (ch, *encrypted, n_batch, (void*)data, n_batch);
638         if (gcry) {
639                 g_free (*encrypted);
640                 g_return_val_if_reached (FALSE);
641         }
642
643         /* Encrypt the padded block */
644         if (n_overflow) {
645                 padded = egg_secure_alloc (ivlen);
646                 memset (padded, 0, ivlen);
647                 memcpy (padded, data + n_batch, n_overflow);
648                 gcry = gcry_cipher_encrypt (ch, *encrypted + n_batch, ivlen, padded, ivlen);
649                 egg_secure_free (padded);
650                 if (gcry) {
651                         g_free (*encrypted);
652                         g_return_val_if_reached (FALSE);
653                 }
654         }
655
656         gcry_cipher_close (ch);
657         return TRUE;
658 }
659
660 const gchar*
661 egg_openssl_get_dekinfo (GHashTable *headers)
662 {
663         const gchar *val;
664         if (!headers)
665                 return NULL;
666         val = g_hash_table_lookup (headers, "Proc-Type");
667         if (!val || strcmp (val, "4,ENCRYPTED") != 0)
668                 return NULL;
669         val = g_hash_table_lookup (headers, "DEK-Info");
670         g_return_val_if_fail (val, NULL);
671         return val;
672 }
673
674 const gchar*
675 egg_openssl_prep_dekinfo (GHashTable *headers)
676 {
677         gchar *dekinfo, *hex;
678         gsize ivlen;
679         guchar *iv;
680
681         /* Create the iv */
682         ivlen = gcry_cipher_get_algo_blklen (GCRY_CIPHER_3DES);
683         g_return_val_if_fail (ivlen, NULL);
684         iv = g_malloc (ivlen);
685         gcry_create_nonce (iv, ivlen);
686
687         /* And encode it into the string */
688         hex = egg_hex_encode (iv, ivlen);
689         g_return_val_if_fail (hex, NULL);
690         dekinfo = g_strdup_printf ("DES-EDE3-CBC,%s", hex);
691         g_free (hex);
692
693         g_hash_table_insert (headers, g_strdup ("DEK-Info"), (void*)dekinfo);
694         g_hash_table_insert (headers, g_strdup ("Proc-Type"), g_strdup ("4,ENCRYPTED"));
695
696         return dekinfo;
697 }