382ec0975c95f99129d3e05617c0bcdbfb1d6b02
[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           
316           algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
317           algo = gcry_cipher_map_name (algoid);
318           mode = gcry_cipher_mode_from_oid (algoid);
319           if (!algo || !mode)
320             {
321               log_error ("unsupported algorithm `%s'\n", algoid? algoid:"?");
322               rc = GNUPG_Unsupported_Algorithm;
323               goto leave;
324             }
325           dfparm.algo = algo;
326           dfparm.mode = mode;
327           dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
328           if (dfparm.blklen > sizeof (dfparm.helpblock))
329             return GNUPG_Bug;
330
331           rc = ksba_cms_get_content_enc_iv (cms,
332                                             dfparm.iv,
333                                             sizeof (dfparm.iv),
334                                             &dfparm.ivlen);
335           if (rc)
336             {
337               log_error ("error getting IV: %s\n", ksba_strerror (err));
338               rc = map_ksba_err (err);
339               goto leave;
340             }
341           
342           for (recp=0; recp < 1; recp++)
343             {
344               char *issuer;
345               KsbaSexp serial;
346               KsbaSexp enc_val;
347               char *hexkeygrip = NULL;
348
349               err = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
350               if (err)
351                 log_error ("recp %d - error getting info: %s\n",
352                            recp, ksba_strerror (err));
353               else
354                 {
355                   KsbaCert cert = NULL;
356
357                   log_debug ("recp %d - issuer: `%s'\n",
358                              recp, issuer? issuer:"[NONE]");
359                   log_debug ("recp %d - serial: ", recp);
360                   gpgsm_dump_serial (serial);
361                   log_printf ("\n");
362
363                   keydb_search_reset (kh);
364                   rc = keydb_search_issuer_sn (kh, issuer, serial);
365                   if (rc)
366                     {
367                       log_debug ("failed to find the certificate: %s\n",
368                                  gnupg_strerror(rc));
369                       goto oops;
370                     }
371
372                   rc = keydb_get_cert (kh, &cert);
373                   if (rc)
374                     {
375                       log_debug ("failed to get cert: %s\n", gnupg_strerror (rc));
376                       goto oops;                    }
377
378                   hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
379
380                 oops:
381                   xfree (issuer);
382                   xfree (serial);
383                   ksba_cert_release (cert);
384                 }
385
386               enc_val = ksba_cms_get_enc_val (cms, recp);
387               if (!enc_val)
388                 log_error ("recp %d - error getting encrypted session key\n",
389                            recp);
390               else
391                 {
392                   rc = prepare_decryption (hexkeygrip, enc_val,
393                                            &dfparm);
394                   xfree (enc_val);
395                   if (rc)
396                     {
397                       /* fixme: as soon as we support multiple recipients, we 
398                          should just set a flag and try the next recipient */
399                       log_error ("decrypting session key failed: %s\n",
400                                  gnupg_strerror (rc));
401                       goto leave;
402                     }
403                   else
404                     { /* setup the bulk decrypter */
405                       ksba_writer_set_filter (writer,
406                                               decrypt_filter,
407                                               &dfparm);
408                     }
409                 }
410             }
411         }
412       else if (stopreason == KSBA_SR_END_DATA)
413         {
414           ksba_writer_set_filter (writer, NULL, NULL);
415           if (dfparm.any_data)
416             { /* write the last block with padding removed */
417               int i, npadding = dfparm.lastblock[dfparm.blklen-1];
418               if (!npadding || npadding > dfparm.blklen)
419                 {
420                   log_error ("invalid padding with value %d\n", npadding);
421                   rc = seterr (Invalid_Data);
422                   goto leave;
423                 }
424               rc = ksba_writer_write (writer,
425                                       dfparm.lastblock, 
426                                       dfparm.blklen - npadding);
427               if (rc)
428                 {
429                   rc = map_ksba_err (rc);
430                   goto leave;
431                 }
432               for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
433                 {
434                   if (dfparm.lastblock[i] != npadding)
435                     {
436                       log_error ("inconsistent padding\n");
437                       rc = seterr (Invalid_Data);
438                       goto leave;
439                     }
440                 }
441             }
442         }
443
444     }
445   while (stopreason != KSBA_SR_READY);   
446
447   rc = gpgsm_finish_writer (b64writer);
448   if (rc) 
449     {
450       log_error ("write failed: %s\n", gnupg_strerror (rc));
451       goto leave;
452     }
453   gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
454
455
456  leave:
457   if (rc)
458     gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
459   ksba_cms_release (cms);
460   gpgsm_destroy_reader (b64reader);
461   gpgsm_destroy_writer (b64writer);
462   keydb_release (kh); 
463   if (in_fp)
464     fclose (in_fp);
465   if (dfparm.hd)
466     gcry_cipher_close (dfparm.hd); 
467   return rc;
468 }
469
470