/* genkey.c - Key generation. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include "gpgme.h" #include "debug.h" #include "context.h" #include "ops.h" #include "util.h" typedef struct { struct _gpgme_op_genkey_result result; /* The error code from a FAILURE status line or 0. */ gpg_error_t failure_code; /* The key parameters passed to the crypto engine. */ gpgme_data_t key_parameter; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; if (opd->result.fpr) free (opd->result.fpr); if (opd->key_parameter) gpgme_data_release (opd->key_parameter); } gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx); err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC0 ("result=(null)"); return NULL; } TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr, opd->result.primary ? "primary" : "no primary", opd->result.sub ? "sub" : "no sub"); TRACE_SUC1 ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t genkey_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; /* Pipe the status code through the progress status handler. */ err = _gpgme_progress_status_handler (ctx, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_KEY_CREATED: if (args && *args) { if (*args == 'B' || *args == 'P') opd->result.primary = 1; if (*args == 'B' || *args == 'S') opd->result.sub = 1; if (args[1] == ' ') { if (opd->result.fpr) free (opd->result.fpr); opd->result.fpr = strdup (&args[2]); if (!opd->result.fpr) return gpg_error_from_syserror (); } } break; case GPGME_STATUS_FAILURE: opd->failure_code = _gpgme_parse_failure (args); break; case GPGME_STATUS_EOF: /* FIXME: Should return some more useful error value. */ if (!opd->result.primary && !opd->result.sub) return gpg_error (GPG_ERR_GENERAL); else if (opd->failure_code) return opd->failure_code; break; case GPGME_STATUS_INQUIRE_MAXLEN: if (ctx->status_cb) { err = ctx->status_cb (ctx->status_cb_value, "INQUIRE_MAXLEN", args); if (err) return err; } break; default: break; } return 0; } static gpgme_error_t get_key_parameter (const char *parms, gpgme_data_t *key_parameter) { const char *content; const char *attrib; const char *endtag; /* Extract the key parameter from the XML structure. */ parms = strstr (parms, "'); if (!content) return gpg_error (GPG_ERR_INV_VALUE); content++; attrib = strstr (parms, "format=\"internal\""); if (!attrib || attrib >= content) return gpg_error (GPG_ERR_INV_VALUE); endtag = strstr (content, ""); /* FIXME: Check that there are no control statements inside. */ while (content[0] == '\n' || (content[0] == '\r' && content[1] == '\n')) content++; return gpgme_data_new_from_mem (key_parameter, content, endtag - content, 1); } static gpgme_error_t genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; err = get_key_parameter (parms, &opd->key_parameter); if (err) return err; _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx); if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); if (err) return err; } return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter, ctx->use_armor, pubkey, seckey); } /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx, "pubkey=%p, seckey=%p", pubkey, seckey); TRACE_LOGBUF (parms, strlen (parms)); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = genkey_start (ctx, 0, parms, pubkey, seckey); return TRACE_ERR (err); } /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey) { gpgme_error_t err; TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx, "pubkey=%p, seckey=%p", pubkey, seckey); TRACE_LOGBUF (parms, strlen (parms)); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = genkey_start (ctx, 1, parms, pubkey, seckey); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); }