2001-11-16 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / sign.c
1 /* sign.c -  signing functions
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include "util.h"
29 #include "context.h"
30 #include "ops.h"
31
32 #define SKIP_TOKEN_OR_RETURN(a) do { \
33     while (*(a) && *(a) != ' ') (a)++; \
34     while (*(a) == ' ') (a)++; \
35     if (!*(a)) \
36         return; /* oops */ \
37 } while (0)
38
39 struct sign_result_s
40 {
41   int okay;
42   GpgmeData xmlinfo;
43 };
44
45 void
46 _gpgme_release_sign_result (SignResult result)
47 {
48   if (!result)
49     return;
50   gpgme_data_release (result->xmlinfo);
51   xfree (result);
52 }
53
54 /* Parse the args and save the information 
55  * <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr>
56  * in an XML structure.  With args of NULL the xml structure is closed.
57  */
58 static void
59 append_xml_siginfo (GpgmeData *rdh, char *args)
60 {
61   GpgmeData dh;
62   char helpbuf[100];
63   int i;
64   char *s;
65   unsigned long ul;
66
67   if (!*rdh)
68     {
69       if (gpgme_data_new (rdh))
70         {
71           return; /* fixme: We are ignoring out-of-core */
72         }
73       dh = *rdh;
74       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
75     }
76   else
77     {
78       dh = *rdh;
79       _gpgme_data_append_string (dh, "  </signature>\n");
80     }
81
82   if (!args)
83     {
84       /* Just close the XML containter.  */
85       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
86       return;
87     }
88
89   _gpgme_data_append_string (dh, "  <signature>\n");
90     
91   _gpgme_data_append_string (dh,
92                              *args == 'D' ? "    <detached/>\n" :
93                              *args == 'C' ? "    <cleartext/>\n" :
94                              *args == 'S' ? "    <standard/>\n" : "");
95   SKIP_TOKEN_OR_RETURN (args);
96
97   sprintf (helpbuf, "    <algo>%d</algo>\n", atoi (args));
98   _gpgme_data_append_string (dh, helpbuf);
99   SKIP_TOKEN_OR_RETURN (args);
100
101   i = atoi (args);
102   sprintf (helpbuf, "    <hashalgo>%d</hashalgo>\n", atoi (args));
103   _gpgme_data_append_string (dh, helpbuf);
104   switch (i)
105     {
106     case  1: s = "pgp-md5"; break;
107     case  2: s = "pgp-sha1"; break;
108     case  3: s = "pgp-ripemd160"; break;
109     case  5: s = "pgp-md2"; break;
110     case  6: s = "pgp-tiger192"; break;
111     case  7: s = "pgp-haval-5-160"; break;
112     case  8: s = "pgp-sha256"; break;
113     case  9: s = "pgp-sha384"; break;
114     case 10: s = "pgp-sha512"; break;
115     default: s = "pgp-unknown"; break;
116     }
117   sprintf (helpbuf, "    <micalg>%s</micalg>\n", s);
118   _gpgme_data_append_string (dh,helpbuf);
119   SKIP_TOKEN_OR_RETURN (args);
120     
121   sprintf (helpbuf, "    <sigclass>%.2s</sigclass>\n", args);
122   _gpgme_data_append_string (dh, helpbuf);
123   SKIP_TOKEN_OR_RETURN (args);
124
125   ul = strtoul (args, NULL, 10);
126   sprintf (helpbuf, "    <created>%lu</created>\n", ul);
127   _gpgme_data_append_string (dh, helpbuf);
128   SKIP_TOKEN_OR_RETURN (args);
129
130   /* Count the length of the finperprint.  */
131   for (i = 0; args[i] && args[i] != ' '; i++)
132     ;
133   _gpgme_data_append_string (dh, "    <fpr>");
134   _gpgme_data_append (dh, args, i);
135   _gpgme_data_append_string (dh, "</fpr>\n");
136 }
137
138 static void
139 sign_status_handler (GpgmeCtx ctx, GpgStatusCode code, char *args)
140 {
141   if (ctx->out_of_core)
142     return;
143   if (!ctx->result.sign)
144     {
145       ctx->result.sign = xtrycalloc (1, sizeof *ctx->result.sign);
146       if (!ctx->result.sign)
147         {
148           ctx->out_of_core = 1;
149           return;
150         }
151     }
152
153   switch (code)
154     {
155     case STATUS_EOF:
156       if (ctx->result.sign->okay)
157         {
158           append_xml_siginfo (&ctx->result.sign->xmlinfo, NULL);
159           _gpgme_set_op_info (ctx, ctx->result.sign->xmlinfo);
160           ctx->result.sign->xmlinfo = NULL;
161         }
162       break;
163
164     case STATUS_SIG_CREATED: 
165       /* FIXME: We have no error return for multiple signatures.  */
166       append_xml_siginfo (&ctx->result.sign->xmlinfo, args);
167       ctx->result.sign->okay =1;
168       break;
169
170     default:
171       break;
172     }
173 }
174
175 GpgmeError
176 gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out,
177                      GpgmeSigMode mode)
178 {
179   GpgmeError err = 0;
180   int i;
181   GpgmeKey key;
182
183   fail_on_pending_request (ctx);
184   ctx->pending = 1;
185
186   _gpgme_release_result (ctx);
187   ctx->out_of_core = 0;
188
189   if (mode != GPGME_SIG_MODE_NORMAL
190       && mode != GPGME_SIG_MODE_DETACH
191       && mode != GPGME_SIG_MODE_CLEAR)
192     return mk_error (Invalid_Value);
193         
194   /* Create a process object.  */
195   _gpgme_gpg_release (ctx->gpg);
196   ctx->gpg = NULL;
197   err = _gpgme_gpg_new (&ctx->gpg);
198   if (err)
199     goto leave;
200
201   _gpgme_gpg_set_status_handler (ctx->gpg, sign_status_handler, ctx);
202
203   err = _gpgme_passphrase_start (ctx);
204   if (err)
205     goto leave;
206
207   /* Build the commandline.  */
208   if (mode == GPGME_SIG_MODE_CLEAR)
209     _gpgme_gpg_add_arg (ctx->gpg, "--clearsign");
210   else
211     {
212       _gpgme_gpg_add_arg (ctx->gpg, "--sign");
213       if (mode == GPGME_SIG_MODE_DETACH)
214         _gpgme_gpg_add_arg (ctx->gpg, "--detach");
215       if (ctx->use_armor)
216         _gpgme_gpg_add_arg (ctx->gpg, "--armor");
217       if (ctx->use_textmode)
218         _gpgme_gpg_add_arg (ctx->gpg, "--textmode");
219     }
220   for (i = 0; i < ctx->verbosity; i++)
221     _gpgme_gpg_add_arg (ctx->gpg, "--verbose");
222   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
223     {
224       const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID,
225                                                  NULL, 0);
226         if (s)
227           {
228             _gpgme_gpg_add_arg (ctx->gpg, "-u");
229             _gpgme_gpg_add_arg (ctx->gpg, s);
230           }
231         gpgme_key_unref (key);
232     }
233     
234   /* Check the supplied data.  */
235   if (gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE)
236     {
237       err = mk_error (No_Data);
238       goto leave;
239     }
240   _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT);
241   if (!out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE)
242     {
243       err = mk_error (Invalid_Value);
244       goto leave;
245     }
246   _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN);
247
248   /* Tell the gpg object about the data.  */
249   _gpgme_gpg_add_data (ctx->gpg, in, 0);
250   _gpgme_gpg_add_data (ctx->gpg, out, 1);
251
252   /* And kick off the process.  */
253   err = _gpgme_gpg_spawn (ctx->gpg, ctx);
254   
255  leave:
256   if (err)
257     {
258       ctx->pending = 0; 
259       _gpgme_gpg_release (ctx->gpg);
260       ctx->gpg = NULL;
261     }
262   return err;
263 }
264
265 /**
266  * gpgme_op_sign:
267  * @ctx: The context
268  * @in: Data to be signed
269  * @out: Detached signature
270  * @mode: Signature creation mode
271  * 
272  * Create a detached signature for @in and write it to @out.
273  * The data will be signed using either the default key or the ones
274  * defined through @ctx.
275  * The defined modes for signature create are:
276  * <literal>
277  * GPGME_SIG_MODE_NORMAL (or 0) 
278  * GPGME_SIG_MODE_DETACH
279  * GPGME_SIG_MODE_CLEAR
280  * </literal>
281  * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode()
282  * are ignore for @mode GPGME_SIG_MODE_CLEAR.
283  * 
284  * Return value: 0 on success or an error code.
285  **/
286 GpgmeError
287 gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
288 {
289   GpgmeError err = gpgme_op_sign_start (ctx, in, out, mode);
290   if (!err)
291     {
292       gpgme_wait (ctx, 1);
293       if (!ctx->result.sign)
294         err = mk_error (General_Error);
295       else if (ctx->out_of_core)
296         err = mk_error (Out_Of_Core);
297       else
298         {
299           err = _gpgme_passphrase_result (ctx);
300           if (! err)
301             {
302               if (!ctx->result.sign->okay)
303                 err = mk_error (No_Data); /* Hmmm: choose a better error? */
304             }
305         }
306       ctx->pending = 0;
307     }
308   return err;
309 }