2001-11-21 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
181   fail_on_pending_request (ctx);
182   ctx->pending = 1;
183
184   _gpgme_release_result (ctx);
185   ctx->out_of_core = 0;
186
187   if (mode != GPGME_SIG_MODE_NORMAL
188       && mode != GPGME_SIG_MODE_DETACH
189       && mode != GPGME_SIG_MODE_CLEAR)
190     return mk_error (Invalid_Value);
191         
192   /* Create a process object.  */
193   _gpgme_engine_release (ctx->engine);
194   ctx->engine = NULL;
195   err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
196                            : GPGME_PROTOCOL_OpenPGP, &ctx->engine);
197   if (err)
198     goto leave;
199
200   /* Check the supplied data.  */
201   if (gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE)
202     {
203       err = mk_error (No_Data);
204       goto leave;
205     }
206   _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT);
207   if (!out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE)
208     {
209       err = mk_error (Invalid_Value);
210       goto leave;
211     }
212   _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN);
213
214   err = _gpgme_passphrase_start (ctx);
215   if (err)
216     goto leave;
217
218   _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler, ctx);
219   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
220
221   _gpgme_engine_op_sign (ctx->engine, in, out, mode, ctx->use_armor,
222                          ctx->use_textmode, ctx /* FIXME */);
223
224   /* And kick off the process.  */
225   err = _gpgme_engine_start (ctx->engine, ctx);
226   
227  leave:
228   if (err)
229     {
230       ctx->pending = 0; 
231       _gpgme_engine_release (ctx->engine);
232       ctx->engine = NULL;
233     }
234   return err;
235 }
236
237 /**
238  * gpgme_op_sign:
239  * @ctx: The context
240  * @in: Data to be signed
241  * @out: Detached signature
242  * @mode: Signature creation mode
243  * 
244  * Create a detached signature for @in and write it to @out.
245  * The data will be signed using either the default key or the ones
246  * defined through @ctx.
247  * The defined modes for signature create are:
248  * <literal>
249  * GPGME_SIG_MODE_NORMAL (or 0) 
250  * GPGME_SIG_MODE_DETACH
251  * GPGME_SIG_MODE_CLEAR
252  * </literal>
253  * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode()
254  * are ignore for @mode GPGME_SIG_MODE_CLEAR.
255  * 
256  * Return value: 0 on success or an error code.
257  **/
258 GpgmeError
259 gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
260 {
261   GpgmeError err = gpgme_op_sign_start (ctx, in, out, mode);
262   if (!err)
263     {
264       gpgme_wait (ctx, 1);
265       if (!ctx->result.sign)
266         err = mk_error (General_Error);
267       else if (ctx->out_of_core)
268         err = mk_error (Out_Of_Core);
269       else
270         {
271           err = _gpgme_passphrase_result (ctx);
272           if (! err)
273             {
274               if (!ctx->result.sign->okay)
275                 err = mk_error (No_Data); /* Hmmm: choose a better error? */
276             }
277         }
278       ctx->pending = 0;
279     }
280   return err;
281 }