* decrypt.c (gpgsm_decrypt): Allow multiple recipients.
[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 mode;
40   int blklen;
41   GCRY_CIPHER_HD hd;
42   char iv[16];
43   size_t ivlen;
44   int any_data;  /* dod we push anything through the filter at all? */
45   unsigned char lastblock[16];  /* to strip the padding we have to
46                                    keep this one */
47   char helpblock[16];  /* needed because there is no block buffering in
48                           libgcrypt (yet) */
49   int  helpblocklen;
50 };
51
52
53
54 /* decrypt the session key and fill in the parm structure.  The
55    algo and the IV is expected to be already in PARM. */
56 static int 
57 prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val,
58                     struct decrypt_filter_parm_s *parm)
59 {
60   char *seskey = NULL;
61   size_t n, seskeylen;
62   int rc;
63
64   rc = gpgsm_agent_pkdecrypt (hexkeygrip, enc_val,
65                               &seskey, &seskeylen);
66   if (rc)
67     {
68       log_error ("error decrypting session key: %s\n", gnupg_strerror (rc));
69       goto leave;
70     }
71
72   if (DBG_CRYPTO)
73     log_printhex ("pkcs1 encoded session key:", seskey, seskeylen);
74
75   n=0;
76   if (n + 7 > seskeylen )
77     {
78       rc = seterr (Invalid_Session_Key);
79       goto leave; 
80     }
81
82   /* FIXME: Actually the leading zero is required but due to the way
83      we encode the output in libgcrypt as an MPI we are not able to
84      encode that leading zero.  However, when using a Smartcard we are
85      doing it the rightway and therefore we have skip the zero.  This
86      should be fixed in gpg-agent of course. */
87   if (!seskey[n])
88     n++;
89
90   if (seskey[n] != 2 )  /* wrong block type version */
91     { 
92       rc = seterr (Invalid_Session_Key);
93       goto leave; 
94     }
95
96   for (n++; n < seskeylen && seskey[n]; n++) /* skip the random bytes */
97     ;
98   n++; /* and the zero byte */
99   if (n >= seskeylen )
100     { 
101       rc = seterr (Invalid_Session_Key);
102       goto leave; 
103     }
104   
105   if (DBG_CRYPTO)
106     log_printhex ("session key:", seskey+n, seskeylen-n);
107
108   parm->hd = gcry_cipher_open (parm->algo, parm->mode, 0);
109   if (!parm->hd)
110     {
111       rc = gcry_errno ();
112       log_error ("error creating decryptor: %s\n", gcry_strerror (rc));
113       rc = map_gcry_err (rc);
114       goto leave;
115     }
116                         
117   rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
118   if (rc == GCRYERR_WEAK_KEY)
119     {
120       log_info (_("WARNING: message was encrypted with "
121                   "a weak key in the symmetric cipher.\n"));
122       rc = 0;
123     }
124   if (rc)
125     {
126       log_error("key setup failed: %s\n", gcry_strerror(rc) );
127       rc = map_gcry_err (rc);
128       goto leave;
129     }
130
131   gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
132
133  leave:
134   xfree (seskey);
135   return rc;
136 }
137
138
139 /* This function is called by the KSBA writer just before the actual
140    write is done.  The function must take INLEN bytes from INBUF,
141    decrypt it and store it inoutbuf which has a maximum size of
142    maxoutlen.  The valid bytes in outbuf should be return in outlen.
143    Due to different buffer sizes or different length of input and
144    output, it may happen that fewer bytes are process or fewer bytes
145    are written. */
146 static KsbaError  
147 decrypt_filter (void *arg,
148                 const void *inbuf, size_t inlen, size_t *inused,
149                 void *outbuf, size_t maxoutlen, size_t *outlen)
150 {
151   struct decrypt_filter_parm_s *parm = arg;
152   int blklen = parm->blklen;
153   size_t orig_inlen = inlen;
154
155   /* fixme: Should we issue an error when we have not seen one full block? */
156   if (!inlen)
157     return KSBA_Bug;
158
159   if (maxoutlen < 2*parm->blklen)
160     return KSBA_Bug;
161   /* make some space becuase we will later need an extra block at the end */
162   maxoutlen -= blklen;
163
164   if (parm->helpblocklen)
165     {
166       int i, j;
167
168       for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
169         parm->helpblock[i] = ((const char*)inbuf)[j];
170       inlen -= j;
171       if (blklen > maxoutlen)
172         return KSBA_Bug;
173       if (i < blklen)
174         {
175           parm->helpblocklen = i;
176           *outlen = 0;
177         }
178       else
179         {
180           parm->helpblocklen = 0;
181           if (parm->any_data)
182             {
183               memcpy (outbuf, parm->lastblock, blklen);
184               *outlen =blklen;
185             }
186           else
187             *outlen = 0;
188           gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
189                                parm->helpblock, blklen);
190           parm->any_data = 1;
191         }
192       *inused = orig_inlen - inlen;
193       return 0;
194     }
195
196
197   if (inlen > maxoutlen)
198     inlen = maxoutlen;
199   if (inlen % blklen)
200     { /* store the remainder away */
201       parm->helpblocklen = inlen%blklen;
202       inlen = inlen/blklen*blklen;
203       memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
204     }
205
206   *inused = inlen + parm->helpblocklen;
207   if (inlen)
208     {
209       assert (inlen >= blklen);
210       if (parm->any_data)
211         {
212           gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
213                                inbuf, inlen);
214           memcpy (outbuf, parm->lastblock, blklen);
215           memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
216           *outlen = inlen;
217         }
218       else
219         {
220           gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
221           memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
222           *outlen = inlen - blklen;
223           parm->any_data = 1;
224         }
225     }
226   else
227     *outlen = 0;
228   return 0;
229 }
230
231
232 \f
233 /* Perform a decrypt operation.  */
234 int
235 gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp)
236 {
237   int rc;
238   KsbaError err;
239   Base64Context b64reader = NULL;
240   Base64Context b64writer = NULL;
241   KsbaReader reader;
242   KsbaWriter writer;
243   KsbaCMS cms = NULL;
244   KsbaStopReason stopreason;
245   KEYDB_HANDLE kh;
246   int recp;
247   FILE *in_fp = NULL;
248   struct decrypt_filter_parm_s dfparm;
249
250   memset (&dfparm, 0, sizeof dfparm);
251
252   kh = keydb_new (0);
253   if (!kh)
254     {
255       log_error (_("failed to allocated keyDB handle\n"));
256       rc = GNUPG_General_Error;
257       goto leave;
258     }
259
260
261   in_fp = fdopen ( dup (in_fd), "rb");
262   if (!in_fp)
263     {
264       log_error ("fdopen() failed: %s\n", strerror (errno));
265       rc = seterr (IO_Error);
266       goto leave;
267     }
268
269   rc = gpgsm_create_reader (&b64reader, ctrl, in_fp, &reader);
270   if (rc)
271     {
272       log_error ("can't create reader: %s\n", gnupg_strerror (rc));
273       goto leave;
274     }
275
276   rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
277   if (rc)
278     {
279       log_error ("can't create writer: %s\n", gnupg_strerror (rc));
280       goto leave;
281     }
282
283   cms = ksba_cms_new ();
284   if (!cms)
285     {
286       rc = seterr (Out_Of_Core);
287       goto leave;
288     }
289
290   err = ksba_cms_set_reader_writer (cms, reader, writer);
291   if (err)
292     {
293       log_debug ("ksba_cms_set_reader_writer failed: %s\n",
294                  ksba_strerror (err));
295       rc = map_ksba_err (err);
296       goto leave;
297     }
298
299   /* parser loop */
300   do 
301     {
302       err = ksba_cms_parse (cms, &stopreason);
303       if (err)
304         {
305           log_debug ("ksba_cms_parse failed: %s\n", ksba_strerror (err));
306           rc = map_ksba_err (err);
307           goto leave;
308         }
309
310       if (stopreason == KSBA_SR_BEGIN_DATA
311           || stopreason == KSBA_SR_DETACHED_DATA)
312         {
313           int algo, mode;
314           const char *algoid;
315           int any_key = 0;
316           
317           algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
318           algo = gcry_cipher_map_name (algoid);
319           mode = gcry_cipher_mode_from_oid (algoid);
320           if (!algo || !mode)
321             {
322               log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
323               rc = GNUPG_Unsupported_Algorithm;
324               goto leave;
325             }
326           dfparm.algo = algo;
327           dfparm.mode = mode;
328           dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
329           if (dfparm.blklen > sizeof (dfparm.helpblock))
330             return GNUPG_Bug;
331
332           rc = ksba_cms_get_content_enc_iv (cms,
333                                             dfparm.iv,
334                                             sizeof (dfparm.iv),
335                                             &dfparm.ivlen);
336           if (rc)
337             {
338               log_error ("error getting IV: %s\n", ksba_strerror (err));
339               rc = map_ksba_err (err);
340               goto leave;
341             }
342           
343           for (recp=0; !any_key; recp++)
344             {
345               char *issuer;
346               KsbaSexp serial;
347               KsbaSexp enc_val;
348               char *hexkeygrip = NULL;
349
350               err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
351               if (err == -1 && recp)
352                 break; /* no more recipients */
353               if (err)
354                 log_error ("recp %d - error getting info: %s\n",
355                            recp, ksba_strerror (err));
356               else
357                 {
358                   KsbaCert cert = NULL;
359
360                   log_debug ("recp %d - issuer: `%s'\n",
361                              recp, issuer? issuer:"[NONE]");
362                   log_debug ("recp %d - serial: ", recp);
363                   gpgsm_dump_serial (serial);
364                   log_printf ("\n");
365
366                   keydb_search_reset (kh);
367                   rc = keydb_search_issuer_sn (kh, issuer, serial);
368                   if (rc)
369                     {
370                       log_error ("failed to find the certificate: %s\n",
371                                  gnupg_strerror(rc));
372                       goto oops;
373                     }
374
375                   rc = keydb_get_cert (kh, &cert);
376                   if (rc)
377                     {
378                       log_error ("failed to get cert: %s\n", gnupg_strerror (rc));
379                       goto oops;     
380                     }
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               if (!hexkeygrip)
391                 ;
392               else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
393                 log_error ("recp %d - error getting encrypted session key\n",
394                            recp);
395               else
396                 {
397                   rc = prepare_decryption (hexkeygrip, enc_val, &dfparm);
398                   xfree (enc_val);
399                   if (rc)
400                     {
401                       log_debug ("decrypting session key failed: %s\n",
402                                  gnupg_strerror (rc));
403                     }
404                   else
405                     { /* setup the bulk decrypter */
406                       any_key = 1;
407                       ksba_writer_set_filter (writer,
408                                               decrypt_filter,
409                                               &dfparm);
410                     }
411                 }
412             }
413           if (!any_key)
414             {
415               rc = GNUPG_No_Secret_Key;
416               goto leave;
417             }
418         }
419       else if (stopreason == KSBA_SR_END_DATA)
420         {
421           ksba_writer_set_filter (writer, NULL, NULL);
422           if (dfparm.any_data)
423             { /* write the last block with padding removed */
424               int i, npadding = dfparm.lastblock[dfparm.blklen-1];
425               if (!npadding || npadding > dfparm.blklen)
426                 {
427                   log_error ("invalid padding with value %d\n", npadding);
428                   rc = seterr (Invalid_Data);
429                   goto leave;
430                 }
431               rc = ksba_writer_write (writer,
432                                       dfparm.lastblock, 
433                                       dfparm.blklen - npadding);
434               if (rc)
435                 {
436                   rc = map_ksba_err (rc);
437                   goto leave;
438                 }
439               for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
440                 {
441                   if (dfparm.lastblock[i] != npadding)
442                     {
443                       log_error ("inconsistent padding\n");
444                       rc = seterr (Invalid_Data);
445                       goto leave;
446                     }
447                 }
448             }
449         }
450
451     }
452   while (stopreason != KSBA_SR_READY);   
453
454   rc = gpgsm_finish_writer (b64writer);
455   if (rc) 
456     {
457       log_error ("write failed: %s\n", gnupg_strerror (rc));
458       goto leave;
459     }
460   gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
461
462
463  leave:
464   if (rc)
465     gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
466   ksba_cms_release (cms);
467   gpgsm_destroy_reader (b64reader);
468   gpgsm_destroy_writer (b64writer);
469   keydb_release (kh); 
470   if (in_fp)
471     fclose (in_fp);
472   if (dfparm.hd)
473     gcry_cipher_close (dfparm.hd); 
474   return rc;
475 }
476
477