ea3f1ea47e60a7d52d683b7ffaea09666647ad10
[gpgme.git] / src / genkey.c
1 /* genkey.c - Key generation.
2  * Copyright (C) 2000 Werner Koch (dd9jn)
3  * Copyright (C) 2001, 2002, 2003, 2004, 2016 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 Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (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  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27
28 #include "gpgme.h"
29 #include "debug.h"
30 #include "context.h"
31 #include "ops.h"
32 #include "util.h"
33
34 \f
35 typedef struct
36 {
37   struct _gpgme_op_genkey_result result;
38
39   /* The error code from a FAILURE status line or 0.  */
40   gpg_error_t failure_code;
41
42   /* The error code from certain ERROR status lines or 0.  */
43   gpg_error_t error_code;
44
45   /* Flag to indicate that a UID is to be added.  */
46   gpg_error_t uidmode;
47
48   /* The key parameters passed to the crypto engine.  */
49   gpgme_data_t key_parameter;
50 } *op_data_t;
51
52
53 static void
54 release_op_data (void *hook)
55 {
56   op_data_t opd = (op_data_t) hook;
57
58   if (opd->result.fpr)
59     free (opd->result.fpr);
60   if (opd->key_parameter)
61     gpgme_data_release (opd->key_parameter);
62 }
63
64
65 gpgme_genkey_result_t
66 gpgme_op_genkey_result (gpgme_ctx_t ctx)
67 {
68   void *hook;
69   op_data_t opd;
70   gpgme_error_t err;
71
72   TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx);
73
74   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
75   opd = hook;
76   if (err || !opd)
77     {
78       TRACE_SUC0 ("result=(null)");
79       return NULL;
80     }
81
82   TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr,
83               opd->result.primary ? "primary" : "no primary",
84               opd->result.sub ? "sub" : "no sub");
85
86   TRACE_SUC1 ("result=%p", &opd->result);
87   return &opd->result;
88 }
89
90
91 \f
92 /* Parse an error status line.  Return the error location and the
93    error code.  The function may modify ARGS. */
94 static char *
95 parse_error (char *args, gpg_error_t *r_err)
96 {
97   char *where = strchr (args, ' ');
98   char *which;
99
100   if (where)
101     {
102       *where = '\0';
103       which = where + 1;
104
105       where = strchr (which, ' ');
106       if (where)
107         *where = '\0';
108
109       where = args;
110     }
111   else
112     {
113       *r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
114       return NULL;
115     }
116
117   *r_err = atoi (which);
118
119   return where;
120 }
121
122
123 static gpgme_error_t
124 genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
125 {
126   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
127   gpgme_error_t err;
128   void *hook;
129   op_data_t opd;
130   char *loc;
131
132   /* Pipe the status code through the progress status handler.  */
133   err = _gpgme_progress_status_handler (ctx, code, args);
134   if (err)
135     return err;
136
137   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
138   opd = hook;
139   if (err)
140     return err;
141
142   switch (code)
143     {
144     case GPGME_STATUS_KEY_CREATED:
145       if (args && *args)
146         {
147           if (*args == 'B' || *args == 'P')
148             {
149               opd->result.primary = 1;
150               opd->result.uid = 1;
151             }
152           if (*args == 'B' || *args == 'S')
153             opd->result.sub = 1;
154           if (args[1] == ' ')
155             {
156               if (opd->result.fpr)
157                 free (opd->result.fpr);
158               opd->result.fpr = strdup (&args[2]);
159               if (!opd->result.fpr)
160                 return gpg_error_from_syserror ();
161             }
162         }
163       break;
164
165     case GPGME_STATUS_ERROR:
166       loc = parse_error (args, &err);
167       if (!loc)
168         return err;
169       if (!opd->error_code)
170         opd->error_code = err;
171       break;
172
173     case GPGME_STATUS_FAILURE:
174       opd->failure_code = _gpgme_parse_failure (args);
175       break;
176
177     case GPGME_STATUS_EOF:
178       if (opd->error_code)
179         return opd->error_code;
180       else if (!opd->uidmode && !opd->result.primary && !opd->result.sub)
181         return gpg_error (GPG_ERR_GENERAL);
182       else if (opd->failure_code)
183         return opd->failure_code;
184       else if (opd->uidmode == 1)
185         opd->result.uid = 1;  /* We have no status line, thus this hack.  */
186       break;
187
188     case GPGME_STATUS_INQUIRE_MAXLEN:
189       if (ctx->status_cb && !ctx->full_status)
190         {
191           err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args);
192           if (err)
193             return err;
194         }
195       break;
196
197     default:
198       break;
199     }
200   return 0;
201 }
202
203
204 static gpgme_error_t
205 get_key_parameter (const char *parms, gpgme_data_t *key_parameter)
206 {
207   const char *content;
208   const char *attrib;
209   const char *endtag;
210
211   /* Extract the key parameter from the XML structure.  */
212   parms = strstr (parms, "<GnupgKeyParms ");
213   if (!parms)
214     return gpg_error (GPG_ERR_INV_VALUE);
215
216   content = strchr (parms, '>');
217   if (!content)
218     return gpg_error (GPG_ERR_INV_VALUE);
219   content++;
220
221   attrib = strstr (parms, "format=\"internal\"");
222   if (!attrib || attrib >= content)
223     return gpg_error (GPG_ERR_INV_VALUE);
224
225   endtag = strstr (content, "</GnupgKeyParms>");
226   /* FIXME: Check that there are no control statements inside.  */
227   while (content[0] == '\n'
228          || (content[0] == '\r' && content[1] == '\n'))
229     content++;
230
231   return gpgme_data_new_from_mem (key_parameter, content,
232                                   endtag - content, 1);
233 }
234
235
236 static gpgme_error_t
237 genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
238               gpgme_data_t pubkey, gpgme_data_t seckey)
239 {
240   gpgme_error_t err;
241   void *hook;
242   op_data_t opd;
243   err = _gpgme_op_reset (ctx, synchronous);
244   if (err)
245     return err;
246
247   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
248                                sizeof (*opd), release_op_data);
249   opd = hook;
250   if (err)
251     return err;
252
253   err = get_key_parameter (parms, &opd->key_parameter);
254   if (err)
255     return err;
256
257   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
258
259   if (ctx->passphrase_cb)
260     {
261       err = _gpgme_engine_set_command_handler
262         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
263       if (err)
264         return err;
265     }
266
267   return _gpgme_engine_op_genkey (ctx->engine,
268                                   NULL, NULL, 0, 0, NULL, 0,
269                                   opd->key_parameter,
270                                   ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
271                                   pubkey, seckey);
272 }
273
274
275 /* Generate a new keypair and add it to the keyring.  PUBKEY and
276    SECKEY should be null for now.  PARMS specifies what keys should be
277    generated.  */
278 gpgme_error_t
279 gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
280                        gpgme_data_t pubkey, gpgme_data_t seckey)
281 {
282   gpgme_error_t err;
283
284   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx,
285               "pubkey=%p, seckey=%p", pubkey, seckey);
286   TRACE_LOGBUF (parms, strlen (parms));
287
288   if (!ctx)
289     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
290
291   err = genkey_start (ctx, 0, parms, pubkey, seckey);
292   return TRACE_ERR (err);
293 }
294
295
296 /* Generate a new keypair and add it to the keyring.  PUBKEY and
297    SECKEY should be null for now.  PARMS specifies what keys should be
298    generated.  */
299 gpgme_error_t
300 gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
301                  gpgme_data_t seckey)
302 {
303   gpgme_error_t err;
304
305   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx,
306               "pubkey=%p, seckey=%p", pubkey, seckey);
307   TRACE_LOGBUF (parms, strlen (parms));
308
309   if (!ctx)
310     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
311
312   err = genkey_start (ctx, 1, parms, pubkey, seckey);
313   if (!err)
314     err = _gpgme_wait_one (ctx);
315   return TRACE_ERR (err);
316 }
317
318
319 \f
320 static gpgme_error_t
321 createkey_start (gpgme_ctx_t ctx, int synchronous,
322                  const char *userid, const char *algo,
323                  unsigned long reserved, unsigned long expires,
324                  gpgme_key_t anchorkey, unsigned int flags)
325 {
326   gpgme_error_t err;
327   void *hook;
328   op_data_t opd;
329
330   err = _gpgme_op_reset (ctx, synchronous);
331   if (err)
332     return err;
333
334   if (reserved || anchorkey || !userid)
335     return gpg_error (GPG_ERR_INV_ARG);
336
337   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
338                                sizeof (*opd), release_op_data);
339   opd = hook;
340   if (err)
341     return err;
342
343   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
344
345   if (ctx->passphrase_cb)
346     {
347       err = _gpgme_engine_set_command_handler
348         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
349       if (err)
350         return err;
351     }
352
353   return _gpgme_engine_op_genkey (ctx->engine,
354                                   userid, algo, reserved, expires,
355                                   anchorkey, flags,
356                                   NULL,
357                                   ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
358                                   NULL, NULL);
359
360 }
361
362
363 gpgme_error_t
364 gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo,
365                           unsigned long reserved, unsigned long expires,
366                           gpgme_key_t anchorkey, unsigned int flags)
367 {
368   gpgme_error_t err;
369
370   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey_start", ctx,
371               "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
372
373   if (!ctx)
374     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
375
376   err = createkey_start (ctx, 0,
377                          userid, algo, reserved, expires, anchorkey, flags);
378   return TRACE_ERR (err);
379 }
380
381
382 gpgme_error_t
383 gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo,
384                     unsigned long reserved, unsigned long expires,
385                     gpgme_key_t anchorkey, unsigned int flags)
386 {
387   gpgme_error_t err;
388
389   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey", ctx,
390               "userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
391
392   if (!ctx)
393     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
394
395   err = createkey_start (ctx, 1,
396                          userid, algo, reserved, expires, anchorkey, flags);
397   if (!err)
398     err = _gpgme_wait_one (ctx);
399   return TRACE_ERR (err);
400 }
401
402
403 \f
404 static gpgme_error_t
405 createsubkey_start (gpgme_ctx_t ctx, int synchronous,
406                     gpgme_key_t key,
407                     const char *algo,
408                     unsigned long reserved, unsigned long expires,
409                     unsigned int flags)
410 {
411   gpgme_error_t err;
412   void *hook;
413   op_data_t opd;
414
415   if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
416     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
417
418   err = _gpgme_op_reset (ctx, synchronous);
419   if (err)
420     return err;
421
422   if (reserved || !key)
423     return gpg_error (GPG_ERR_INV_ARG);
424
425   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
426                                sizeof (*opd), release_op_data);
427   opd = hook;
428   if (err)
429     return err;
430
431   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
432
433   if (ctx->passphrase_cb)
434     {
435       err = _gpgme_engine_set_command_handler
436         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
437       if (err)
438         return err;
439     }
440
441   return _gpgme_engine_op_genkey (ctx->engine,
442                                   NULL, algo, reserved, expires,
443                                   key, flags,
444                                   NULL,
445                                   ctx->use_armor? GENKEY_EXTRAFLAG_ARMOR:0,
446                                   NULL, NULL);
447
448 }
449
450
451 /* Add a subkey to an existing KEY.  */
452 gpgme_error_t
453 gpgme_op_createsubkey_start (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
454                              unsigned long reserved, unsigned long expires,
455                              unsigned int flags)
456 {
457   gpgme_error_t err;
458
459   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey_start", ctx,
460               "key=%p, algo='%s' flags=0x%x", key, algo, flags);
461
462   if (!ctx)
463     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
464
465   err = createsubkey_start (ctx, 0, key, algo, reserved, expires, flags);
466   return TRACE_ERR (err);
467 }
468
469
470 gpgme_error_t
471 gpgme_op_createsubkey (gpgme_ctx_t ctx, gpgme_key_t key, const char *algo,
472                        unsigned long reserved, unsigned long expires,
473                        unsigned int flags)
474 {
475   gpgme_error_t err;
476
477   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createsubkey", ctx,
478               "key=%p, algo='%s' flags=0x%x", key, algo, flags);
479
480   if (!ctx)
481     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
482
483   err = createsubkey_start (ctx, 1, key, algo, reserved, expires, flags);
484   if (!err)
485     err = _gpgme_wait_one (ctx);
486   return TRACE_ERR (err);
487 }
488
489
490 \f
491 static gpgme_error_t
492 addrevuid_start (gpgme_ctx_t ctx, int synchronous, int revoke,
493                  gpgme_key_t key, const char *userid, unsigned int flags)
494 {
495   gpgme_error_t err;
496   void *hook;
497   op_data_t opd;
498
499   if (ctx->protocol != GPGME_PROTOCOL_OPENPGP)
500     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
501
502   if (!key || !userid)
503     return gpg_error (GPG_ERR_INV_ARG);
504
505   err = _gpgme_op_reset (ctx, synchronous);
506   if (err)
507     return err;
508
509   err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
510                                sizeof (*opd), release_op_data);
511   opd = hook;
512   if (err)
513     return err;
514
515   opd->uidmode = revoke? 2 : 1;
516
517   _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
518
519   if (ctx->passphrase_cb)
520     {
521       err = _gpgme_engine_set_command_handler
522         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
523       if (err)
524         return err;
525     }
526
527   return _gpgme_engine_op_genkey (ctx->engine,
528                                   userid, NULL, 0, 0,
529                                   key, flags,
530                                   NULL,
531                                   revoke? GENKEY_EXTRAFLAG_REVOKE : 0,
532                                   NULL, NULL);
533
534 }
535
536
537 /* Add USERID to an existing KEY.  */
538 gpgme_error_t
539 gpgme_op_adduid_start (gpgme_ctx_t ctx,
540                        gpgme_key_t key, const char *userid, unsigned int flags)
541 {
542   gpgme_error_t err;
543
544   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid_start", ctx,
545               "uid='%s' flags=0x%x", userid, flags);
546
547   if (!ctx)
548     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
549
550   err = addrevuid_start (ctx, 0, 0, key, userid, flags);
551   return TRACE_ERR (err);
552 }
553
554
555 gpgme_error_t
556 gpgme_op_adduid (gpgme_ctx_t ctx,
557                  gpgme_key_t key, const char *userid, unsigned int flags)
558 {
559   gpgme_error_t err;
560
561   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_adduid", ctx,
562               "uid='%s' flags=0x%x", userid, flags);
563
564   if (!ctx)
565     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
566
567   err = addrevuid_start (ctx, 1, 0, key, userid, flags);
568   if (!err)
569     err = _gpgme_wait_one (ctx);
570   return TRACE_ERR (err);
571 }
572
573
574 /* Revoke USERID from KEY.  */
575 gpgme_error_t
576 gpgme_op_revuid_start (gpgme_ctx_t ctx,
577                        gpgme_key_t key, const char *userid, unsigned int flags)
578 {
579   gpgme_error_t err;
580
581   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid_start", ctx,
582               "uid='%s' flags=0x%x", userid, flags);
583
584   if (!ctx)
585     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
586
587   err = addrevuid_start (ctx, 0, 1, key, userid, flags);
588   return TRACE_ERR (err);
589 }
590
591
592 gpgme_error_t
593 gpgme_op_revuid (gpgme_ctx_t ctx,
594                  gpgme_key_t key, const char *userid, unsigned int flags)
595 {
596   gpgme_error_t err;
597
598   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_revuid", ctx,
599               "uid='%s' flags=0x%x", userid, flags);
600
601   if (!ctx)
602     return TRACE_ERR (gpg_error (GPG_ERR_INV_ARG));
603
604   err = addrevuid_start (ctx, 1, 1, key, userid, flags);
605   if (!err)
606     err = _gpgme_wait_one (ctx);
607   return TRACE_ERR (err);
608 }