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