7c0a64c66d62188d2c23fe8102eb09c74e9a06cb
[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   _gpgme_passphrase_status_handler (ctx, code, args);
142
143   if (ctx->out_of_core)
144     return;
145   if (!ctx->result.sign)
146     {
147       ctx->result.sign = xtrycalloc (1, sizeof *ctx->result.sign);
148       if (!ctx->result.sign)
149         {
150           ctx->out_of_core = 1;
151           return;
152         }
153     }
154
155   switch (code)
156     {
157     case STATUS_EOF:
158       if (ctx->result.sign->okay)
159         {
160           append_xml_siginfo (&ctx->result.sign->xmlinfo, NULL);
161           _gpgme_set_op_info (ctx, ctx->result.sign->xmlinfo);
162           ctx->result.sign->xmlinfo = NULL;
163         }
164       break;
165
166     case STATUS_SIG_CREATED: 
167       /* FIXME: We have no error return for multiple signatures.  */
168       append_xml_siginfo (&ctx->result.sign->xmlinfo, args);
169       ctx->result.sign->okay =1;
170       break;
171
172     default:
173       break;
174     }
175 }
176
177 GpgmeError
178 gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out,
179                      GpgmeSigMode mode)
180 {
181   GpgmeError err = 0;
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_engine_release (ctx->engine);
196   ctx->engine = NULL;
197   err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
198                            : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
199   if (err)
200     goto leave;
201
202   /* Check the supplied data.  */
203   if (gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE)
204     {
205       err = mk_error (No_Data);
206       goto leave;
207     }
208   _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT);
209   if (!out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE)
210     {
211       err = mk_error (Invalid_Value);
212       goto leave;
213     }
214   _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN);
215
216   err = _gpgme_passphrase_start (ctx);
217   if (err)
218     goto leave;
219
220   _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx);
221   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
222
223   _gpgme_engine_op_sign (ctx->engine, in, out, mode, ctx->use_armor,
224                          ctx->use_textmode, ctx /* FIXME */);
225
226   /* And kick off the process.  */
227   err = _gpgme_engine_start (ctx->engine, ctx);
228   
229  leave:
230   if (err)
231     {
232       ctx->pending = 0; 
233       _gpgme_engine_release (ctx->engine);
234       ctx->engine = NULL;
235     }
236   return err;
237 }
238
239 /**
240  * gpgme_op_sign:
241  * @ctx: The context
242  * @in: Data to be signed
243  * @out: Detached signature
244  * @mode: Signature creation mode
245  * 
246  * Create a detached signature for @in and write it to @out.
247  * The data will be signed using either the default key or the ones
248  * defined through @ctx.
249  * The defined modes for signature create are:
250  * <literal>
251  * GPGME_SIG_MODE_NORMAL (or 0) 
252  * GPGME_SIG_MODE_DETACH
253  * GPGME_SIG_MODE_CLEAR
254  * </literal>
255  * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode()
256  * are ignore for @mode GPGME_SIG_MODE_CLEAR.
257  * 
258  * Return value: 0 on success or an error code.
259  **/
260 GpgmeError
261 gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
262 {
263   GpgmeError err = gpgme_op_sign_start (ctx, in, out, mode);
264   if (!err)
265     {
266       gpgme_wait (ctx, 1);
267       if (!ctx->result.sign)
268         err = mk_error (General_Error);
269       else if (ctx->out_of_core)
270         err = mk_error (Out_Of_Core);
271       else
272         {
273           err = _gpgme_passphrase_result (ctx);
274           if (! err)
275             {
276               if (!ctx->result.sign->okay)
277                 err = mk_error (No_Data); /* Hmmm: choose a better error? */
278             }
279         }
280     }
281   return err;
282 }