c077c55e223ae73ae713f78a9c3d663eb3cd3a52
[gpgme.git] / gpgme / sign.c
1 /* sign.c -  signing functions
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001, 2002 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 void
139 _gpgme_sign_status_handler (GpgmeCtx ctx, GpgmeStatusCode 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 GPGME_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 GPGME_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 static GpgmeError
172 _gpgme_op_sign_start (GpgmeCtx ctx, int synchronous,
173                       GpgmeData in, GpgmeData out,
174                       GpgmeSigMode mode)
175 {
176   GpgmeError err = 0;
177
178   if (mode != GPGME_SIG_MODE_NORMAL
179       && mode != GPGME_SIG_MODE_DETACH
180       && mode != GPGME_SIG_MODE_CLEAR)
181     return mk_error (Invalid_Value);
182
183   err = _gpgme_op_reset (ctx, synchronous);
184   if (err)
185     goto leave;
186
187   /* Check the supplied data.  */
188   if (gpgme_data_get_type (in) == GPGME_DATA_TYPE_NONE)
189     {
190       err = mk_error (No_Data);
191       goto leave;
192     }
193   _gpgme_data_set_mode (in, GPGME_DATA_MODE_OUT);
194   if (!out || gpgme_data_get_type (out) != GPGME_DATA_TYPE_NONE)
195     {
196       err = mk_error (Invalid_Value);
197       goto leave;
198     }
199   _gpgme_data_set_mode (out, GPGME_DATA_MODE_IN);
200
201   err = _gpgme_passphrase_start (ctx);
202   if (err)
203     goto leave;
204
205   _gpgme_engine_set_status_handler (ctx->engine, _gpgme_sign_status_handler,
206                                     ctx);
207   _gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
208
209   _gpgme_engine_op_sign (ctx->engine, in, out, mode, ctx->use_armor,
210                          ctx->use_textmode, ctx->include_certs,
211                          ctx /* FIXME */);
212
213   /* And kick off the process.  */
214   err = _gpgme_engine_start (ctx->engine, ctx);
215   
216  leave:
217   if (err)
218     {
219       ctx->pending = 0; 
220       _gpgme_engine_release (ctx->engine);
221       ctx->engine = NULL;
222     }
223   return err;
224 }
225
226 GpgmeError
227 gpgme_op_sign_start (GpgmeCtx ctx, GpgmeData in, GpgmeData out,
228                      GpgmeSigMode mode)
229 {
230   return _gpgme_op_sign_start (ctx, 0, in, out, mode);
231 }
232
233 /**
234  * gpgme_op_sign:
235  * @ctx: The context
236  * @in: Data to be signed
237  * @out: Detached signature
238  * @mode: Signature creation mode
239  * 
240  * Create a detached signature for @in and write it to @out.
241  * The data will be signed using either the default key or the ones
242  * defined through @ctx.
243  * The defined modes for signature create are:
244  * <literal>
245  * GPGME_SIG_MODE_NORMAL (or 0) 
246  * GPGME_SIG_MODE_DETACH
247  * GPGME_SIG_MODE_CLEAR
248  * </literal>
249  * Note that the settings done by gpgme_set_armor() and gpgme_set_textmode()
250  * are ignore for @mode GPGME_SIG_MODE_CLEAR.
251  * 
252  * Return value: 0 on success or an error code.
253  **/
254 GpgmeError
255 gpgme_op_sign (GpgmeCtx ctx, GpgmeData in, GpgmeData out, GpgmeSigMode mode)
256 {
257   GpgmeError err = _gpgme_op_sign_start (ctx, 1, in, out, mode);
258   if (!err)
259     err = _gpgme_wait_one (ctx);
260   return err;
261 }