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