4fd92c616706d2c627e0bbd0f186fa27cbf34733
[gpgme.git] / src / decrypt.c
1 /* decrypt.c - Decrypt function.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include "debug.h"
30 #include "gpgme.h"
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34
35
36 \f
37 typedef struct
38 {
39   struct _gpgme_op_decrypt_result result;
40
41   int okay;
42   int failed;
43
44   /* A pointer to the next pointer of the last recipient in the list.
45      This makes appending new invalid signers painless while
46      preserving the order.  */
47   gpgme_recipient_t *last_recipient_p;
48 } *op_data_t;
49
50
51 static void
52 release_op_data (void *hook)
53 {
54   op_data_t opd = (op_data_t) hook;
55   gpgme_recipient_t recipient = opd->result.recipients;
56
57   if (opd->result.unsupported_algorithm)
58     free (opd->result.unsupported_algorithm);
59
60   if (opd->result.file_name)
61     free (opd->result.file_name);
62
63   while (recipient)
64     {
65       gpgme_recipient_t next = recipient->next;
66       free (recipient);
67       recipient = next;
68     }
69 }
70
71
72 gpgme_decrypt_result_t
73 gpgme_op_decrypt_result (gpgme_ctx_t ctx)
74 {
75   void *hook;
76   op_data_t opd;
77   gpgme_error_t err;
78
79   TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx);
80
81   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
82   opd = hook;
83   if (err || !opd)
84     {
85       TRACE_SUC0 ("result=(null)");
86       return NULL;
87     }
88
89   if (_gpgme_debug_trace ())
90     {
91       gpgme_recipient_t rcp;
92
93       if (opd->result.unsupported_algorithm)
94         {
95           TRACE_LOG1 ("result: unsupported_algorithm: %s",
96                       opd->result.unsupported_algorithm);
97         }
98       if (opd->result.wrong_key_usage)
99         {
100           TRACE_LOG ("result: wrong key usage");
101         }
102       rcp = opd->result.recipients;
103       while (rcp)
104         {
105           TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, "
106                       "status=%s", rcp->keyid, rcp->pubkey_algo,
107                       gpg_strerror (rcp->status));
108           rcp = rcp->next;
109         }
110       if (opd->result.file_name)
111         {
112           TRACE_LOG1 ("result: original file name: %s", opd->result.file_name);
113         }
114     }
115
116   TRACE_SUC1 ("result=%p", &opd->result);
117   return &opd->result;
118 }
119
120 \f
121 static gpgme_error_t
122 parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol)
123 {
124   gpgme_recipient_t rec;
125   char *tail;
126   int i;
127
128   rec = malloc (sizeof (*rec));
129   if (!rec)
130     return gpg_error_from_syserror ();
131
132   rec->next = NULL;
133   rec->keyid = rec->_keyid;
134   rec->status = 0;
135
136   for (i = 0; i < sizeof (rec->_keyid) - 1; i++)
137     {
138       if (args[i] == '\0' || args[i] == ' ')
139         break;
140
141       rec->_keyid[i] = args[i];
142     }
143   rec->_keyid[i] = '\0';
144
145   args = &args[i];
146   if (*args != '\0' && *args != ' ')
147     {
148       free (rec);
149       return trace_gpg_error (GPG_ERR_INV_ENGINE);
150     }
151
152   while (*args == ' ')
153     args++;
154
155   if (*args)
156     {
157       gpg_err_set_errno (0);
158       rec->pubkey_algo = _gpgme_map_pk_algo (strtol (args, &tail, 0), protocol);
159       if (errno || args == tail || *tail != ' ')
160         {
161           /* The crypto backend does not behave.  */
162           free (rec);
163           return trace_gpg_error (GPG_ERR_INV_ENGINE);
164         }
165     }
166
167   /* FIXME: The key length is always 0 right now, so no need to parse
168      it.  */
169
170   *recp = rec;
171   return 0;
172 }
173
174
175 gpgme_error_t
176 _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
177                                char *args)
178 {
179   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
180   gpgme_error_t err;
181   void *hook;
182   op_data_t opd;
183
184   err = _gpgme_passphrase_status_handler (priv, code, args);
185   if (err)
186     return err;
187
188   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
189   opd = hook;
190   if (err)
191     return err;
192
193   switch (code)
194     {
195     case GPGME_STATUS_EOF:
196       /* FIXME: These error values should probably be attributed to
197          the underlying crypto engine (as error source).  */
198       if (opd->failed)
199         return gpg_error (GPG_ERR_DECRYPT_FAILED);
200       else if (!opd->okay)
201         return gpg_error (GPG_ERR_NO_DATA);
202       break;
203
204     case GPGME_STATUS_DECRYPTION_INFO:
205       /* Fixme: Provide a way to return the used symmetric algorithm. */
206       break;
207
208     case GPGME_STATUS_DECRYPTION_OKAY:
209       opd->okay = 1;
210       break;
211
212     case GPGME_STATUS_DECRYPTION_FAILED:
213       opd->failed = 1;
214       break;
215
216     case GPGME_STATUS_ERROR:
217       /* Note that this is an informational status code which should
218          not lead to an error return unless it is something not
219          related to the backend.  */
220       {
221         const char d_alg[] = "decrypt.algorithm";
222         const char k_alg[] = "decrypt.keyusage";
223
224         if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
225           {
226             args += sizeof (d_alg) - 1;
227             while (*args == ' ')
228               args++;
229
230             if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
231               {
232                 char *end;
233
234                 while (*args && *args != ' ')
235                   args++;
236                 while (*args == ' ')
237                   args++;
238
239                 end = strchr (args, ' ');
240                 if (end)
241                   *end = '\0';
242
243                 if (!(*args == '?' && *(args + 1) == '\0'))
244                   {
245                     opd->result.unsupported_algorithm = strdup (args);
246                     if (!opd->result.unsupported_algorithm)
247                       return gpg_error_from_syserror ();
248                   }
249               }
250           }
251         else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
252           {
253             args += sizeof (k_alg) - 1;
254             while (*args == ' ')
255               args++;
256
257             if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
258               opd->result.wrong_key_usage = 1;
259           }
260       }
261       break;
262
263     case GPGME_STATUS_ENC_TO:
264       err = parse_enc_to (args, opd->last_recipient_p, ctx->protocol);
265       if (err)
266         return err;
267
268       opd->last_recipient_p = &(*opd->last_recipient_p)->next;
269       break;
270
271     case GPGME_STATUS_NO_SECKEY:
272       {
273         gpgme_recipient_t rec = opd->result.recipients;
274
275         while (rec)
276           {
277             if (!strcmp (rec->keyid, args))
278               {
279                 rec->status = gpg_error (GPG_ERR_NO_SECKEY);
280                 break;
281               }
282             rec = rec->next;
283           }
284         /* FIXME: Is this ok?  */
285         if (!rec)
286           return trace_gpg_error (GPG_ERR_INV_ENGINE);
287       }
288       break;
289
290     case GPGME_STATUS_PLAINTEXT:
291       err = _gpgme_parse_plaintext (args, &opd->result.file_name);
292       if (err)
293         return err;
294       break;
295
296     case GPGME_STATUS_INQUIRE_MAXLEN:
297       if (ctx->status_cb)
298         {
299           err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
300           if (err)
301             return err;
302         }
303       break;
304
305     default:
306       break;
307     }
308
309   return 0;
310 }
311
312
313 static gpgme_error_t
314 decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
315 {
316   gpgme_error_t err;
317
318   err = _gpgme_progress_status_handler (priv, code, args);
319   if (!err)
320     err = _gpgme_decrypt_status_handler (priv, code, args);
321   return err;
322 }
323
324
325 gpgme_error_t
326 _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
327 {
328   gpgme_error_t err;
329   void *hook;
330   op_data_t opd;
331
332   err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
333                                sizeof (*opd), release_op_data);
334   opd = hook;
335   if (err)
336     return err;
337
338   opd->last_recipient_p = &opd->result.recipients;
339   return 0;
340 }
341
342
343 static gpgme_error_t
344 decrypt_start (gpgme_ctx_t ctx, int synchronous,
345                       gpgme_data_t cipher, gpgme_data_t plain)
346 {
347   gpgme_error_t err;
348
349   err = _gpgme_op_reset (ctx, synchronous);
350   if (err)
351     return err;
352
353   err = _gpgme_op_decrypt_init_result (ctx);
354   if (err)
355     return err;
356
357   if (!cipher)
358     return gpg_error (GPG_ERR_NO_DATA);
359   if (!plain)
360     return gpg_error (GPG_ERR_INV_VALUE);
361
362   if (err)
363     return err;
364
365   if (ctx->passphrase_cb)
366     {
367       err = _gpgme_engine_set_command_handler
368         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
369       if (err)
370         return err;
371     }
372
373   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
374
375   return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
376 }
377
378
379 gpgme_error_t
380 gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
381                         gpgme_data_t plain)
382 {
383   gpgme_error_t err;
384
385   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
386               "cipher=%p, plain=%p", cipher, plain);
387
388   if (!ctx)
389     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
390
391   err = decrypt_start (ctx, 0, cipher, plain);
392   return TRACE_ERR (err);
393 }
394
395
396 /* Decrypt ciphertext CIPHER within CTX and store the resulting
397    plaintext in PLAIN.  */
398 gpgme_error_t
399 gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
400 {
401   gpgme_error_t err;
402
403   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx,
404               "cipher=%p, plain=%p", cipher, plain);
405
406   if (!ctx)
407     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
408
409   err = decrypt_start (ctx, 1, cipher, plain);
410   if (!err)
411     err = _gpgme_wait_one (ctx);
412   return TRACE_ERR (err);
413 }