Decryption does now work
[gnupg.git] / sm / decrypt.c
1 /* decrypt.c - Decrypt a message
2  *      Copyright (C) 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29
30 #include <gcrypt.h>
31 #include <ksba.h>
32
33 #include "gpgsm.h"
34 #include "keydb.h"
35 #include "i18n.h"
36
37 struct decrypt_filter_parm_s {
38   int algo;
39   int blklen;
40   GCRY_CIPHER_HD hd;
41   char iv[16];
42   size_t ivlen;
43   int any_data;  /* dod we push anything through the filter at all? */
44   unsigned char lastblock[16];  /* to strip the padding we have to
45                                    keep this one */
46   char helpblock[16];  /* needed because there is no block bufferin in
47                           libgcrypt (yet) */
48   int  helpblocklen;
49 };
50
51
52 static void
53 print_integer (unsigned char *p)
54 {
55   unsigned long len;
56
57   if (!p)
58     log_printf ("none");
59   else
60     {
61       len = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
62       for (p+=4; len; len--, p++)
63         log_printf ("%02X", *p);
64     }
65 }
66
67 /* decrypt the session key and fill in the parm structure.  The
68    algo and the IV is expected to be already in PARM. */
69 static int 
70 prepare_decryption (const char *hexkeygrip, const char *enc_val,
71                     struct decrypt_filter_parm_s *parm)
72 {
73   char *seskey = NULL;
74   size_t n, seskeylen;
75   int rc;
76
77   rc = gpgsm_agent_pkdecrypt (hexkeygrip, enc_val, strlen (enc_val),
78                               &seskey, &seskeylen);
79   if (rc)
80     {
81       log_error ("error decrypting session key: %s\n", gnupg_strerror (rc));
82       goto leave;
83     }
84
85   if (DBG_CRYPTO)
86     log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
87
88   n=0;
89   if (n + 7 > seskeylen )
90     {
91       rc = seterr (Invalid_Session_Key);
92       goto leave; 
93     }
94
95   if (seskey[n] != 2 )  /* wrong block type version */
96     { 
97       rc = seterr (Invalid_Session_Key);
98       goto leave; 
99     }
100
101   for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */
102     ;
103   n++; /* and the zero byte */
104   if (n >= seskeylen )
105     { 
106       rc = seterr (Invalid_Session_Key);
107       goto leave; 
108     }
109   
110   if (DBG_CRYPTO)
111     log_printhex ("session key:", seskey+n, seskeylen-n);
112
113   parm->hd = gcry_cipher_open (parm->algo, GCRY_CIPHER_MODE_CBC, 0);
114   if (!parm->hd)
115     {
116       rc = gcry_errno ();
117       log_error ("error creating decryptor: %s\n", gcry_strerror (rc));
118       rc = map_gcry_err (rc);
119       goto leave;
120     }
121                         
122   rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
123   if (rc == GCRYERR_WEAK_KEY)
124     {
125       log_info (_("WARNING: message was encrypted with "
126                   "a weak key in the symmetric cipher.\n"));
127       rc = 0;
128     }
129   if (rc)
130     {
131       log_error("key setup failed: %s\n", gcry_strerror(rc) );
132       rc = map_gcry_err (rc);
133       goto leave;
134     }
135
136   gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
137
138  leave:
139   xfree (seskey);
140   return rc;
141 }
142
143
144 /* This function is called by the KSab writer just before the actual
145    write is done.  The function must take INLEN bytes from INBUF,
146    decrypt it and store it inoutbuf which has a maximum size of
147    maxoutlen.  The valid bytes in outbuf should be return in outlen.
148    Due to different buffer sizes or different length of input and
149    output, it may happen that fewer bytes are process or fewer bytes
150    are written. */
151 static KsbaError  
152 decrypt_filter (void *arg,
153                 const void *inbuf, size_t inlen, size_t *inused,
154                 void *outbuf, size_t maxoutlen, size_t *outlen)
155 {
156   struct decrypt_filter_parm_s *parm = arg;
157   int blklen = parm->blklen;
158   size_t orig_inlen = inlen;
159
160   /* fixme: Should we issue an error when we have not seen one full block? */
161   if (!inlen)
162     return KSBA_Bug;
163
164   if (maxoutlen < 2*parm->blklen)
165     return KSBA_Bug;
166   /* make some space becuase we will later need an extra block at the end */
167   maxoutlen -= blklen;
168
169   if (parm->helpblocklen)
170     {
171       int i, j;
172
173       for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
174         parm->helpblock[i] = ((const char*)inbuf)[j];
175       inlen -= j;
176       if (blklen > maxoutlen)
177         return KSBA_Bug;
178       if (i < blklen)
179         {
180           parm->helpblocklen = i;
181           *outlen = 0;
182         }
183       else
184         {
185           parm->helpblocklen = 0;
186           if (parm->any_data)
187             {
188               memcpy (outbuf, parm->lastblock, blklen);
189               *outlen =blklen;
190             }
191           else
192             *outlen = 0;
193           gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
194                                parm->helpblock, blklen);
195           parm->any_data = 1;
196         }
197       *inused = orig_inlen - inlen;
198       return 0;
199     }
200
201
202   if (inlen > maxoutlen)
203     inlen = maxoutlen;
204   if (inlen % blklen)
205     { /* store the remainder away */
206       parm->helpblocklen = inlen%blklen;
207       inlen = inlen/blklen*blklen;
208       memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
209     }
210
211   *inused = inlen + parm->helpblocklen;
212   if (inlen)
213     {
214       assert (inlen >= blklen);
215       if (parm->any_data)
216         {
217           gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
218                                inbuf, inlen);
219           memcpy (outbuf, parm->lastblock, blklen);
220           memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
221           *outlen = inlen;
222         }
223       else
224         {
225           gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
226           memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
227           *outlen = inlen - blklen;
228           parm->any_data = 1;
229         }
230     }
231   else
232     *outlen = 0;
233   return 0;
234 }
235
236
237 \f
238 /* Perform a decrypt operation.  */
239 int
240 gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
241 {
242   int rc;
243   KsbaError err;
244   Base64Context b64reader = NULL;
245   Base64Context b64writer = NULL;
246   KsbaReader reader;
247   KsbaWriter writer;
248   KsbaCMS cms = NULL;
249   KsbaStopReason stopreason;
250   KEYDB_HANDLE kh;
251   int recp;
252   FILE *in_fp = NULL;
253   struct decrypt_filter_parm_s dfparm;
254
255   memset (&dfparm, 0, sizeof dfparm);
256
257   kh = keydb_new (0);
258   if (!kh)
259     {
260       log_error (_("failed to allocated keyDB handle\n"));
261       rc = GNUPG_General_Error;
262       goto leave;
263     }
264
265
266   in_fp = fdopen ( dup (in_fd), "rb");
267   if (!in_fp)
268     {
269       log_error ("fdopen() failed: %s\n", strerror (errno));
270       rc = seterr (IO_Error);
271       goto leave;
272     }
273
274   rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, &reader);
275   if (rc)
276     {
277       log_error ("can't create reader: %s\n", gnupg_strerror (rc));
278       goto leave;
279     }
280
281   rc = gpgsm_create_writer (&b64reader, ctrl, out_fp, &writer);
282   if (rc)
283     {
284       log_error ("can't create writer: %s\n", gnupg_strerror (rc));
285       goto leave;
286     }
287
288   cms = ksba_cms_new ();
289   if (!cms)
290     {
291       rc = seterr (Out_Of_Core);
292       goto leave;
293     }
294
295   err = ksba_cms_set_reader_writer (cms, reader, writer);
296   if (err)
297     {
298       log_debug ("ksba_cms_set_reader_writer failed: %s\n",
299                  ksba_strerror (err));
300       rc = map_ksba_err (err);
301       goto leave;
302     }
303
304   /* parser loop */
305   do 
306     {
307       err = ksba_cms_parse (cms, &stopreason);
308       if (err)
309         {
310           log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
311           rc = map_ksba_err (err);
312           goto leave;
313         }
314       log_debug ("ksba_cms_parse - stop reason %d\n", stopreason);
315
316       if (stopreason == KSBA_SR_BEGIN_DATA
317           || stopreason == KSBA_SR_DETACHED_DATA)
318         {
319           int algo;
320           const char *algoid;
321           
322           algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
323           algo = gcry_cipher_map_name (algoid);
324           if (!algo)
325             {
326               log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
327               rc = GNUPG_Unsupported_Algorithm;
328               goto leave;
329             }
330           dfparm.algo = algo;
331           dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
332           if (dfparm.blklen > sizeof (dfparm.helpblock))
333             return GNUPG_Bug;
334
335           rc = ksba_cms_get_content_enc_iv (cms,
336                                             dfparm.iv,
337                                             sizeof (dfparm.iv),
338                                             &dfparm.ivlen);
339           if (rc)
340             {
341               log_error ("error getting IV: %s\n", ksba_strerror (err));
342               rc = map_ksba_err (err);
343               goto leave;
344             }
345           
346           for (recp=0; recp < 1; recp++)
347             {
348               char *issuer;
349               unsigned char *serial;
350               char *enc_val;
351               char *hexkeygrip = NULL;
352
353               err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
354               if (err)
355                 log_error ("recp %d - error getting info: %s\n",
356                            recp, ksba_strerror (err));
357               else
358                 {
359                   KsbaCert cert = NULL;
360
361                   log_debug ("recp %d - issuer: `%s'\n",
362                              recp, issuer? issuer:"[NONE]");
363                   log_debug ("recp %d - serial: ", recp);
364                   print_integer (serial);
365                   log_printf ("\n");
366
367                   keydb_search_reset (kh);
368                   rc = keydb_search_issuer_sn (kh, issuer, serial);
369                   if (rc)
370                     {
371                       log_debug ("failed to find the certificate: %s\n",
372                                  gnupg_strerror(rc));
373                       goto oops;
374                     }
375
376                   rc = keydb_get_cert (kh, &cert);
377                   if (rc)
378                     {
379                       log_debug ("failed to get cert: %s\n", gnupg_strerror (rc));
380                       goto oops;                    }
381
382                   hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
383
384                 oops:
385                   xfree (issuer);
386                   xfree (serial);
387                   ksba_cert_release (cert);
388                 }
389
390               enc_val = ksba_cms_get_enc_val (cms, recp);
391               if (!enc_val)
392                 log_error ("recp %d - error getting encrypted session key\n",
393                            recp);
394               else
395                 {
396                   log_debug ("recp %d - enc-val: `%s'\n",
397                              recp, enc_val);
398                   rc = prepare_decryption (hexkeygrip, enc_val,
399                                            &dfparm);
400                   xfree (enc_val);
401                   if (rc)
402                     log_error ("decrypting session key failed: %s\n",
403                                gnupg_strerror (rc));
404                   else
405                     { /* setup the bulk decrypter */
406                       ksba_writer_set_filter (writer,
407                                               decrypt_filter,
408                                               &dfparm);
409                     }
410                 }
411             }
412         }
413       else if (stopreason == KSBA_SR_END_DATA)
414         {
415           ksba_writer_set_filter (writer, NULL, NULL);
416           if (dfparm.any_data)
417             { /* write the last block with padding removed */
418               int i, npadding = dfparm.lastblock[dfparm.blklen-1];
419               if (!npadding || npadding > dfparm.blklen)
420                 {
421                   log_error ("invalid padding with value %d\n", npadding);
422                   rc = seterr (Invalid_Data);
423                   goto leave;
424                 }
425               rc = ksba_writer_write (writer,
426                                       dfparm.lastblock, 
427                                       dfparm.blklen - npadding);
428               if (rc)
429                 {
430                   rc = map_ksba_err (rc);
431                   goto leave;
432                 }
433               for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
434                 {
435                   if (dfparm.lastblock[i] != npadding)
436                     {
437                       log_error ("inconsistent padding\n");
438                       rc = seterr (Invalid_Data);
439                       goto leave;
440                     }
441                 }
442             }
443         }
444
445     }
446   while (stopreason != KSBA_SR_READY);   
447
448  leave:
449   ksba_cms_release (cms);
450   gpgsm_destroy_reader (b64reader);
451   gpgsm_destroy_writer (b64writer);
452   keydb_release (kh); 
453   if (in_fp)
454     fclose (in_fp);
455   if (dfparm.hd)
456     gcry_cipher_close (dfparm.hd); 
457   return rc;
458 }
459
460