2003-01-30 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, 2002, 2003 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 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, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #include "util.h"
30 #include "context.h"
31 #include "ops.h"
32
33 #define SKIP_TOKEN_OR_RETURN(a) do { \
34     while (*(a) && *(a) != ' ') (a)++; \
35     while (*(a) == ' ') (a)++; \
36     if (!*(a)) \
37         return; /* oops */ \
38 } while (0)
39
40 struct sign_result
41 {
42   int okay;
43   GpgmeData xmlinfo;
44 };
45 typedef struct sign_result *SignResult;
46
47
48 static void
49 release_sign_result (void *hook)
50 {
51   SignResult result = (SignResult) hook;
52
53   gpgme_data_release (result->xmlinfo);
54 }
55
56 /* Parse the args and save the information 
57    <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr>
58    in an XML structure.  With args of NULL the xml structure is
59    closed.  */
60 static void
61 append_xml_siginfo (GpgmeData *rdh, char *args)
62 {
63   GpgmeData dh;
64   char helpbuf[100];
65   int i;
66   char *s;
67   unsigned long ul;
68
69   if (!*rdh)
70     {
71       if (gpgme_data_new (rdh))
72         {
73           return; /* fixme: We are ignoring out-of-core */
74         }
75       dh = *rdh;
76       _gpgme_data_append_string (dh, "<GnupgOperationInfo>\n");
77     }
78   else
79     {
80       dh = *rdh;
81       _gpgme_data_append_string (dh, "  </signature>\n");
82     }
83
84   if (!args)
85     {
86       /* Just close the XML containter.  */
87       _gpgme_data_append_string (dh, "</GnupgOperationInfo>\n");
88       return;
89     }
90
91   _gpgme_data_append_string (dh, "  <signature>\n");
92     
93   _gpgme_data_append_string (dh,
94                              *args == 'D' ? "    <detached/>\n" :
95                              *args == 'C' ? "    <cleartext/>\n" :
96                              *args == 'S' ? "    <standard/>\n" : "");
97   SKIP_TOKEN_OR_RETURN (args);
98
99   sprintf (helpbuf, "    <algo>%d</algo>\n", atoi (args));
100   _gpgme_data_append_string (dh, helpbuf);
101   SKIP_TOKEN_OR_RETURN (args);
102
103   i = atoi (args);
104   sprintf (helpbuf, "    <hashalgo>%d</hashalgo>\n", atoi (args));
105   _gpgme_data_append_string (dh, helpbuf);
106   switch (i)
107     {
108     case  1: s = "pgp-md5"; break;
109     case  2: s = "pgp-sha1"; break;
110     case  3: s = "pgp-ripemd160"; break;
111     case  5: s = "pgp-md2"; break;
112     case  6: s = "pgp-tiger192"; break;
113     case  7: s = "pgp-haval-5-160"; break;
114     case  8: s = "pgp-sha256"; break;
115     case  9: s = "pgp-sha384"; break;
116     case 10: s = "pgp-sha512"; break;
117     default: s = "pgp-unknown"; break;
118     }
119   sprintf (helpbuf, "    <micalg>%s</micalg>\n", s);
120   _gpgme_data_append_string (dh,helpbuf);
121   SKIP_TOKEN_OR_RETURN (args);
122     
123   sprintf (helpbuf, "    <sigclass>%.2s</sigclass>\n", args);
124   _gpgme_data_append_string (dh, helpbuf);
125   SKIP_TOKEN_OR_RETURN (args);
126
127   ul = strtoul (args, NULL, 10);
128   sprintf (helpbuf, "    <created>%lu</created>\n", ul);
129   _gpgme_data_append_string (dh, helpbuf);
130   SKIP_TOKEN_OR_RETURN (args);
131
132   /* Count the length of the finperprint.  */
133   for (i = 0; args[i] && args[i] != ' '; i++)
134     ;
135   _gpgme_data_append_string (dh, "    <fpr>");
136   _gpgme_data_append (dh, args, i);
137   _gpgme_data_append_string (dh, "</fpr>\n");
138 }
139
140 GpgmeError
141 _gpgme_sign_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args)
142 {
143   SignResult result;
144   GpgmeError err;
145
146   err = _gpgme_passphrase_status_handler (ctx, code, args);
147   if (err)
148     return err;
149
150   switch (code)
151     {
152     case GPGME_STATUS_EOF:
153       err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &result,
154                                    -1, NULL);
155       if (!err)
156         {
157           if (result && result->okay)
158             {
159               append_xml_siginfo (&result->xmlinfo, NULL);
160               _gpgme_set_op_info (ctx, result->xmlinfo);
161               result->xmlinfo = NULL;
162             }
163           else if (!result || !result->okay)
164             /* FIXME: choose a better error code?  */
165             err = GPGME_No_Data;
166         }
167       break;
168
169     case GPGME_STATUS_SIG_CREATED: 
170       /* FIXME: We have no error return for multiple signatures.  */
171       err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, (void **) &result,
172                                    sizeof (*result), release_sign_result);
173       append_xml_siginfo (&result->xmlinfo, args);
174       result->okay = 1;
175       break;
176
177     default:
178       break;
179     }
180   return err;
181 }
182
183 static GpgmeError
184 _gpgme_op_sign_start (GpgmeCtx ctx, int synchronous,
185                       GpgmeData in, GpgmeData out,
186                       GpgmeSigMode mode)
187 {
188   GpgmeError err = 0;
189
190   if (mode != GPGME_SIG_MODE_NORMAL
191       && mode != GPGME_SIG_MODE_DETACH
192       && mode != GPGME_SIG_MODE_CLEAR)
193     return GPGME_Invalid_Value;
194
195   err = _gpgme_op_reset (ctx, synchronous);
196   if (err)
197     goto leave;
198
199   /* Check the supplied data.  */
200   if (!in)
201     {
202       err = GPGME_No_Data;
203       goto leave;
204     }
205   if (!out)
206     {
207       err = GPGME_Invalid_Value;
208       goto leave;
209     }
210
211   err = _gpgme_passphrase_start (ctx);
212   if (err)
213     goto leave;
214
215   _gpgme_engine_set_status_handler (ctx->engine, _gpgme_sign_status_handler,
216                                     ctx);
217   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
218
219   err = _gpgme_engine_op_sign (ctx->engine, in, out, mode, ctx->use_armor,
220                                ctx->use_textmode, ctx->include_certs,
221                                ctx /* FIXME */);
222
223  leave:
224   if (err)
225     {
226       ctx->pending = 0; 
227       _gpgme_engine_release (ctx->engine);
228       ctx->engine = NULL;
229     }
230   return err;
231 }
232
233 GpgmeError
234 gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out,
235                      GpgmeSigMode mode)
236 {
237   return _gpgme_op_sign_start (ctx, 0, in, out, mode);
238 }
239
240 /**
241  * gpgme_op_sign:
242  * @ctx: The context
243  * @in: Data to be signed
244  * @out: Detached signature
245  * @mode: Signature creation mode
246  * 
247  * Create a detached signature for @in and write it to @out.
248  * The data will be signed using either the default key or the ones
249  * defined through @ctx.
250  * The defined modes for signature create are:
251  * <literal>
252  * GPGME_SIG_MODE_NORMAL (or 0) 
253  * GPGME_SIG_MODE_DETACH
254  * GPGME_SIG_MODE_CLEAR
255  * </literal>
256  * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode()
257  * are ignore for @mode GPGME_SIG_MODE_CLEAR.
258  * 
259  * Return value: 0 on success or an error code.
260  **/
261 GpgmeError
262 gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
263 {
264   GpgmeError err = _gpgme_op_sign_start (ctx, 1, in, out, mode);
265   if (!err)
266     err = _gpgme_wait_one (ctx);
267   return err;
268 }