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