core: New function gpgme_op_create_key.

* src/engine-backend.h (engine_ops): Change prototype of genkey.
* src/engine-gpgsm.c (gpgsm_genkey): Change accordingly.
* src/engine-gpg.c (gpg_genkey): Change it to a dispatcher.
(gpg_createkey_from_param): New for the old functionality.
(gpg_createkey_legacy): New.  Stub for now.
(gpg_createkey): New.
(gpg_addkey): New.  Stub for now.
(gpg_adduid): New.  Stub for now.
* src/engine.c (_gpgme_engine_op_genkey): Add new args.
* src/genkey.c (op_data_t): Add field ERROR_CODE.
(parse_error): New.
(genkey_status_handler): Parse ERROR status line.
(genkey_start): Use NULL/0 for the new args.
(createkey_start): New.
(gpgme_op_createkey_start, gpgme_op_createkey): New.
* src/gpgme.def, src/libgpgme.vers: Add gpgme_op_createkey_start and
gpgme_op_createkey.
* src/gpgme.h.in (_gpgme_op_genkey_result): Add fields PUBKEY and
SECKEY.
(GPGME_CREATE_SIGN): New.
(GPGME_CREATE_ENCR): New.
(GPGME_CREATE_CERT): New.
(GPGME_CREATE_AUTH): New.
(GPGME_CREATE_NOPASSWD): New.
(GPGME_CREATE_SELFSIGNED): New.
(GPGME_CREATE_NOSTORE): New.
(GPGME_CREATE_WANTPUB): New.
(GPGME_CREATE_WANTSEC): New.
(GPGME_CREATE_FORCE): New.

* tests/run-genkey.c: New.
* tests/Makefile.am (noinst_PROGRAMS): Add it.
--

This function uses the new --quick-gen-key API of gpg.  A limited
compatibility mode to use older gpg versions and gpgsm will eventually
be provided.  Not all flags are currently implemented.

 ./run-genkey --unprotected --force test@example.com

Create a new standard key with the given user id.  --force is used to
allow creating more than one key with that user id in the keyring.

 ./run-genkey --unprotected --force \
      test@example.com default default 2145826800

Creates a new standard key with an expiration date of 2037-12-31.

 ./run-genkey --unprotected --force \
      test@example.com future-default default 2145826800

Create a standard key using the fugure default algorithms.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-09-13 18:57:38 +02:00
parent 70a3be27a5
commit 00c501d296
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
13 changed files with 775 additions and 59 deletions

13
NEWS
View File

@ -15,6 +15,9 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_PK_EDDSA NEW.
gpgme_set_ctx_flag NEW.
gpgme_data_set_flag NEW.
gpgme_op_createkey NEW.
gpgme_op_createkey_start NEW.
gpgme_genkey_result_t EXTENDED: New fields pubkey and seckey.
gpgme_signature_t EXTENDED: New field key.
gpgme_key_t EXTENDED: New field fpr.
gpgme_subkey_t EXTENDED: New field keygrip.
@ -31,6 +34,16 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_DATA_TYPE_PGP_SIGNATURE NEW.
GPGME_DATA_ENCODING_MIME NEW.
GPGME_ENCRYPT_SYMMETRIC NEW.
GPGME_CREATE_SIGN NEW.
GPGME_CREATE_ENCR NEW.
GPGME_CREATE_CERT NEW.
GPGME_CREATE_AUTH NEW.
GPGME_CREATE_NOPASSWD NEW.
GPGME_CREATE_SELFSIGNED NEW.
GPGME_CREATE_NOSTORE NEW.
GPGME_CREATE_WANTPUB NEW.
GPGME_CREATE_WANTSEC NEW.
GPGME_CREATE_FORCE NEW.
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]

View File

@ -82,7 +82,11 @@ struct engine_ops
gpgme_error_t (*export_ext) (void *engine, const char *pattern[],
gpgme_export_mode_t mode, gpgme_data_t keydata,
int use_armor);
gpgme_error_t (*genkey) (void *engine, gpgme_data_t help_data, int use_armor,
gpgme_error_t (*genkey) (void *engine,
const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t key, unsigned int flags,
gpgme_data_t help_data, int use_armor,
gpgme_data_t pubkey, gpgme_data_t seckey);
gpgme_error_t (*import) (void *engine, gpgme_data_t keydata,
gpgme_key_t *keyarray);

View File

@ -1964,22 +1964,11 @@ gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
static gpgme_error_t
gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
gpgme_data_t pubkey, gpgme_data_t seckey)
gpg_createkey_from_param (engine_gpg_t gpg,
gpgme_data_t help_data, int use_armor)
{
engine_gpg_t gpg = engine;
gpgme_error_t err;
if (!gpg)
return gpg_error (GPG_ERR_INV_VALUE);
/* We need a special mechanism to get the fd of a pipe here, so that
we can use this for the %pubring and %secring parameters. We
don't have this yet, so we implement only the adding to the
standard keyrings. */
if (pubkey || seckey)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
err = add_arg (gpg, "--gen-key");
if (!err && use_armor)
err = add_arg (gpg, "--armor");
@ -1987,9 +1976,156 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
err = add_arg (gpg, "--");
if (!err)
err = add_data (gpg, help_data, -1, 0);
if (!err)
err = start (gpg);
return err;
}
/* This is used for gpg versions which do not support the quick-genkey
* command to emulate the gpgme_op_createkey API. */
static gpgme_error_t
gpg_createkey_legacy (engine_gpg_t gpg,
const char *userid, const char *algo,
unsigned long expires,
unsigned int flags,
int use_armor)
{
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpgme_error_t
gpg_createkey (engine_gpg_t gpg,
const char *userid, const char *algo,
unsigned long expires,
unsigned int flags,
int use_armor)
{
gpgme_error_t err;
err = add_arg (gpg, "--quick-gen-key");
if (!err && use_armor)
err = add_arg (gpg, "--armor");
if (!err && (flags & GPGME_CREATE_NOPASSWD))
{
err = add_arg (gpg, "--passphrase");
if (!err)
err = add_arg (gpg, "");
}
if (!err && (flags & GPGME_CREATE_FORCE))
err = add_arg (gpg, "--yes");
if (!err)
err = add_arg (gpg, "--");
if (!err)
err = add_arg (gpg, userid);
/* This condition is only required to allow the use of gpg < 2.1.16 */
if (algo
|| (flags & (GPGME_CREATE_SIGN | GPGME_CREATE_ENCR
| GPGME_CREATE_CERT | GPGME_CREATE_AUTH))
|| expires)
{
if (!err)
err = add_arg (gpg, algo? algo : "default");
if (!err)
{
char tmpbuf[5*4+1];
snprintf (tmpbuf, sizeof tmpbuf, "%s%s%s%s",
(flags & GPGME_CREATE_SIGN)? " sign":"",
(flags & GPGME_CREATE_ENCR)? " encr":"",
(flags & GPGME_CREATE_CERT)? " cert":"",
(flags & GPGME_CREATE_AUTH)? " auth":"");
err = add_arg (gpg, *tmpbuf? tmpbuf : "default");
}
if (!err && expires)
{
char tmpbuf[8+20];
snprintf (tmpbuf, sizeof tmpbuf, "seconds=%lu", expires);
err = add_arg (gpg, tmpbuf);
}
}
if (!err)
err = start (gpg);
return err;
}
static gpgme_error_t
gpg_addkey (engine_gpg_t gpg,
const char *algo,
unsigned long expires,
gpgme_key_t key,
unsigned int flags,
int use_armor)
{
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpgme_error_t
gpg_adduid (engine_gpg_t gpg,
const char *userid,
unsigned int flags,
int use_armor)
{
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpgme_error_t
gpg_genkey (void *engine,
const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t key, unsigned int flags,
gpgme_data_t help_data, int use_armor,
gpgme_data_t pubkey, gpgme_data_t seckey)
{
engine_gpg_t gpg = engine;
gpgme_error_t err;
(void)reserved;
if (!gpg)
return gpg_error (GPG_ERR_INV_VALUE);
/* If HELP_DATA is given the use of the old interface
* (gpgme_op_genkey) has been requested. The other modes are:
*
* USERID && !KEY - Create a new keyblock.
* !USERID && KEY - Add a new subkey to KEY (gpg >= 2.1.14)
* USERID && KEY && !ALGO - Add a new user id to KEY (gpg >= 2.1.14).
*
*/
if (help_data)
{
/* We need a special mechanism to get the fd of a pipe here, so
that we can use this for the %pubring and %secring
parameters. We don't have this yet, so we implement only the
adding to the standard keyrings. */
if (pubkey || seckey)
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
else
err = gpg_createkey_from_param (gpg, help_data, use_armor);
}
else if (userid && !key)
{
if (!have_gpg_version (gpg, "2.1.13"))
err = gpg_createkey_legacy (gpg, userid, algo, expires, flags,
use_armor);
else
err = gpg_createkey (gpg, userid, algo, expires, flags, use_armor);
}
else if (!have_gpg_version (gpg, "2.1.13"))
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
else if (!userid && key)
err = gpg_addkey (gpg, algo, expires, key, flags, use_armor);
else if (userid && key && !algo)
err = gpg_adduid (gpg, userid, flags, use_armor);
else
err = gpg_error (GPG_ERR_INV_VALUE);
return err;
}

View File

@ -1433,13 +1433,24 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
static gpgme_error_t
gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
gpgsm_genkey (void *engine,
const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t key, unsigned int flags,
gpgme_data_t help_data, int use_armor,
gpgme_data_t pubkey, gpgme_data_t seckey)
{
engine_gpgsm_t gpgsm = engine;
gpgme_error_t err;
if (!gpgsm || !pubkey || seckey)
(void)reserved;
if (!gpgsm)
return gpg_error (GPG_ERR_INV_VALUE);
if (help_data)
{
if (!pubkey || seckey)
return gpg_error (GPG_ERR_INV_VALUE);
gpgsm->input_cb.data = help_data;
@ -1458,6 +1469,15 @@ gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
return err;
}
(void)userid;
(void)expires;
(void)key;
(void)flags;
/* The new interface has not yet been implemented, */
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpgme_error_t
gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)

View File

@ -773,7 +773,11 @@ _gpgme_engine_op_export_ext (engine_t engine, const char *pattern[],
gpgme_error_t
_gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
_gpgme_engine_op_genkey (engine_t engine,
const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t key, unsigned int flags,
gpgme_data_t help_data,
int use_armor, gpgme_data_t pubkey,
gpgme_data_t seckey)
{
@ -783,7 +787,9 @@ _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
if (!engine->ops->genkey)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
return (*engine->ops->genkey) (engine->engine, help_data, use_armor,
return (*engine->ops->genkey) (engine->engine,
userid, algo, reserved, expires, key, flags,
help_data, use_armor,
pubkey, seckey);
}

View File

@ -108,6 +108,10 @@ gpgme_error_t _gpgme_engine_op_export_ext (engine_t engine,
gpgme_data_t keydata,
int use_armor);
gpgme_error_t _gpgme_engine_op_genkey (engine_t engine,
const char *userid, const char *algo,
unsigned long reserved,
unsigned long expires,
gpgme_key_t key, unsigned int flags,
gpgme_data_t help_data,
int use_armor, gpgme_data_t pubkey,
gpgme_data_t seckey);

View File

@ -34,7 +34,7 @@
/* Local operation data. */
typedef struct
{
gpg_error_t err; /* Error encountred during the export. */
gpg_error_t err; /* Error encountered during the export. */
} *op_data_t;

View File

@ -1,23 +1,22 @@
/* 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. */
* Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002, 2003, 2004, 2016 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, see <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include <config.h>
@ -40,6 +39,9 @@ typedef struct
/* The error code from a FAILURE status line or 0. */
gpg_error_t failure_code;
/* The error code from certain ERROR status lines or 0. */
gpg_error_t error_code;
/* The key parameters passed to the crypto engine. */
gpgme_data_t key_parameter;
} *op_data_t;
@ -82,7 +84,39 @@ gpgme_op_genkey_result (gpgme_ctx_t ctx)
return &opd->result;
}
/* Parse an error status line. Return the error location and the
error code. The function may modify ARGS. */
static char *
parse_error (char *args, gpg_error_t *r_err)
{
char *where = strchr (args, ' ');
char *which;
if (where)
{
*where = '\0';
which = where + 1;
where = strchr (which, ' ');
if (where)
*where = '\0';
where = args;
}
else
{
*r_err = trace_gpg_error (GPG_ERR_INV_ENGINE);
return NULL;
}
*r_err = atoi (which);
return where;
}
static gpgme_error_t
genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
{
@ -90,6 +124,7 @@ genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
gpgme_error_t err;
void *hook;
op_data_t opd;
char *loc;
/* Pipe the status code through the progress status handler. */
err = _gpgme_progress_status_handler (ctx, code, args);
@ -121,13 +156,22 @@ genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
}
break;
case GPGME_STATUS_ERROR:
loc = parse_error (args, &err);
if (!loc)
return err;
if (!opd->error_code)
opd->error_code = err;
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)
if (opd->error_code)
return opd->error_code;
else if (!opd->result.primary && !opd->result.sub)
return gpg_error (GPG_ERR_GENERAL);
else if (opd->failure_code)
return opd->failure_code;
@ -212,7 +256,9 @@ genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
return err;
}
return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter,
return _gpgme_engine_op_genkey (ctx->engine,
NULL, NULL, 0, 0, NULL, 0,
opd->key_parameter,
ctx->use_armor, pubkey, seckey);
}
@ -259,3 +305,85 @@ gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
err = _gpgme_wait_one (ctx);
return TRACE_ERR (err);
}
static gpgme_error_t
createkey_start (gpgme_ctx_t ctx, int synchronous,
const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t anchorkey, unsigned int flags)
{
gpgme_error_t err;
void *hook;
op_data_t opd;
err = _gpgme_op_reset (ctx, synchronous);
if (err)
return err;
if (reserved || anchorkey || !userid)
return gpg_error (GPG_ERR_INV_VALUE);
err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
sizeof (*opd), release_op_data);
opd = hook;
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,
userid, algo, reserved, expires,
anchorkey, flags,
NULL, ctx->use_armor, NULL, NULL);
}
gpgme_error_t
gpgme_op_createkey_start (gpgme_ctx_t ctx, const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t anchorkey, unsigned int flags)
{
gpgme_error_t err;
TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey_start", ctx,
"userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
err = createkey_start (ctx, 0,
userid, algo, reserved, expires, anchorkey, flags);
return TRACE_ERR (err);
}
gpgme_error_t
gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo,
unsigned long reserved, unsigned long expires,
gpgme_key_t anchorkey, unsigned int flags)
{
gpgme_error_t err;
TRACE_BEG3 (DEBUG_CTX, "gpgme_op_createkey", ctx,
"userid='%s', algo='%s' flags=0x%x", userid, algo, flags);
if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
err = createkey_start (ctx, 1,
userid, algo, reserved, expires, anchorkey, flags);
if (!err)
err = _gpgme_wait_one (ctx);
return TRACE_ERR (err);
}

View File

@ -229,5 +229,8 @@ EXPORTS
gpgme_data_set_flag @171
gpgme_op_createkey_start @172
gpgme_op_createkey @173
; END

View File

@ -1815,6 +1815,18 @@ gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx,
* Key generation.
*/
/* Flags for the key creation functions. */
#define GPGME_CREATE_SIGN (1 << 0) /* Allow usage: signing. */
#define GPGME_CREATE_ENCR (1 << 1) /* Allow usage: encryption. */
#define GPGME_CREATE_CERT (1 << 2) /* Allow usage: certification. */
#define GPGME_CREATE_AUTH (1 << 3) /* Allow usage: authentication. */
#define GPGME_CREATE_NOPASSWD (1 << 7) /* Create w/o passphrase. */
#define GPGME_CREATE_SELFSIGNED (1 << 8) /* Create self-signed cert. */
#define GPGME_CREATE_NOSTORE (1 << 9) /* Do not store the key. */
#define GPGME_CREATE_WANTPUB (1 << 10) /* Return the public key. */
#define GPGME_CREATE_WANTSEC (1 << 11) /* Return the secret key. */
#define GPGME_CREATE_FORCE (1 << 12) /* Force creation. */
struct _gpgme_op_genkey_result
{
/* A primary key was generated. */
@ -1828,6 +1840,14 @@ struct _gpgme_op_genkey_result
/* The fingerprint of the generated key. */
char *fpr;
/* A memory data object with the created public key. Only set when
* GPGME_CREATE_WANTPUB has been used. */
gpgme_data_t pubkey;
/* A memory data object with the created secret key. Only set when
* GPGME_CREATE_WANTSEC has been used. */
gpgme_data_t seckey;
};
typedef struct _gpgme_op_genkey_result *gpgme_genkey_result_t;
@ -1839,7 +1859,39 @@ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms,
gpgme_data_t pubkey, gpgme_data_t seckey);
/* Retrieve a pointer to the result of the genkey operation. */
/* Generate a key pair using the modern interface. */
gpgme_error_t gpgme_op_createkey_start (gpgme_ctx_t ctx,
const char *userid,
const char *algo,
unsigned long reserved,
unsigned long expires,
gpgme_key_t certkey,
unsigned int flags);
gpgme_error_t gpgme_op_createkey (gpgme_ctx_t ctx,
const char *userid,
const char *algo,
unsigned long reserved,
unsigned long expires,
gpgme_key_t certkey,
unsigned int flags);
/* Add a new subkey to KEY. */
gpgme_error_t gpgme_op_createsubkey_start (gpgme_ctx_t ctx,
gpgme_key_t key,
const char *algo,
unsigned long reserved,
unsigned long expires,
unsigned int flags);
gpgme_error_t gpgme_op_createsubkey (gpgme_ctx_t ctx,
gpgme_key_t key,
const char *algo,
unsigned long reserved,
unsigned long expires,
unsigned int flags);
/* Retrieve a pointer to the result of a genkey, createkey, or
* createsubkey operation. */
gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx);
@ -2177,7 +2229,7 @@ typedef struct gpgme_conf_arg
} *gpgme_conf_arg_t;
/* The flags of a configuration option. See the gpg-conf
/* The flags of a configuration option. See the gpgconf
documentation for details. */
#define GPGME_CONF_GROUP (1 << 0)
#define GPGME_CONF_OPTIONAL (1 << 1)

View File

@ -101,6 +101,10 @@ GPGME_1.1 {
gpgme_pubkey_algo_string;
gpgme_set_ctx_flag;
gpgme_data_set_flag;
gpgme_op_createkey_start;
gpgme_op_createkey;
};
@ -230,8 +234,6 @@ GPGME_1.0 {
gpgme_err_code_from_syserror;
gpgme_err_set_errno;
gpgme_data_set_flag;
local:
*;

View File

@ -32,7 +32,7 @@ LDADD = ../src/libgpgme.la @GPG_ERROR_LIBS@
noinst_HEADERS = run-support.h
noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
run-verify run-encrypt run-identify run-decrypt
run-verify run-encrypt run-identify run-decrypt run-genkey
if RUN_GPG_TESTS

348
tests/run-genkey.c Normal file
View File

@ -0,0 +1,348 @@
/* run-genkey.c - Test tool to perform key generation
* Copyright (C) 2016 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, see <http://www.gnu.org/licenses/>.
*/
/* We need to include config.h so that we know whether we are building
with large file system (LFS) support. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <gpgme.h>
#define PGM "run-genkey"
#include "run-support.h"
static int verbose;
/* Tokenize STRING using the set of delimiters in DELIM. Leading
* spaces and tabs are removed from all tokens. The caller must free
* the result.
*
* Returns: A malloced and NULL delimited array with the tokens. On
* memory error NULL is returned and ERRNO is set.
*/
static char **
strtokenize (const char *string, const char *delim)
{
const char *s;
size_t fields;
size_t bytes, n;
char *buffer;
char *p, *px, *pend;
char **result;
/* Count the number of fields. */
for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim))
fields++;
fields++; /* Add one for the terminating NULL. */
/* Allocate an array for all fields, a terminating NULL, and space
for a copy of the string. */
bytes = fields * sizeof *result;
if (bytes / sizeof *result != fields)
{
gpg_err_set_errno (ENOMEM);
return NULL;
}
n = strlen (string) + 1;
bytes += n;
if (bytes < n)
{
gpg_err_set_errno (ENOMEM);
return NULL;
}
result = malloc (bytes);
if (!result)
return NULL;
buffer = (char*)(result + fields);
/* Copy and parse the string. */
strcpy (buffer, string);
for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1)
{
*pend = 0;
while (*p == ' ' || *p == '\t')
p++;
for (px = pend - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
*px = 0;
result[n++] = p;
}
while (*p == ' ' || *p == '\t')
p++;
for (px = p + strlen (p) - 1; px >= p && (*px == ' ' || *px == '\t'); px--)
*px = 0;
result[n++] = p;
result[n] = NULL;
assert ((char*)(result + n + 1) == buffer);
return result;
}
static gpg_error_t
status_cb (void *opaque, const char *keyword, const char *value)
{
(void)opaque;
fprintf (stderr, "status_cb: %s %s\n", nonnull(keyword), nonnull(value));
return 0;
}
static void
progress_cb (void *opaque, const char *what, int type, int current, int total)
{
(void)opaque;
(void)type;
if (total)
fprintf (stderr, "progress for '%s' %u%% (%d of %d)\n",
nonnull (what),
(unsigned)(((double)current / total) * 100), current, total);
else
fprintf (stderr, "progress for '%s' %d\n", nonnull(what), current);
fflush (stderr);
}
static unsigned long
parse_expire_string (const char *string)
{
unsigned long seconds;
if (!string || !*string || !strcmp (string, "none")
|| !strcmp (string, "never") || !strcmp (string, "-"))
seconds = 0;
else if (strspn (string, "01234567890") == strlen (string))
seconds = strtoul (string, NULL, 10);
else
{
fprintf (stderr, PGM ": invalid value '%s'\n", string);
exit (1);
}
return seconds;
}
/* Parse a usage string and return flags for gpgme_op_createkey. */
static unsigned int
parse_usage_string (const char *string)
{
gpg_error_t err;
char **tokens = NULL;
const char *s;
int i;
unsigned int flags = 0;
tokens = strtokenize (string, " \t,");
if (!tokens)
{
err = gpg_error_from_syserror ();
fprintf (stderr, PGM": strtokenize failed: %s\n", gpg_strerror (err));
exit (1);
}
for (i=0; (s = tokens[i]); i++)
{
if (!*s)
;
else if (!strcmp (s, "default"))
;
else if (!strcmp (s, "sign"))
flags |= GPGME_CREATE_SIGN;
else if (!strcmp (s, "encr"))
flags |= GPGME_CREATE_ENCR;
else if (!strcmp (s, "cert"))
flags |= GPGME_CREATE_CERT;
else if (!strcmp (s, "auth"))
flags |= GPGME_CREATE_AUTH;
else
{
free (tokens);
fprintf (stderr, PGM": invalid value '%s': %s\n",
string, "bad usage");
exit (1);
}
}
free (tokens);
return flags;
}
static int
show_usage (int ex)
{
fputs ("usage: " PGM " [options] USERID [ALGO [USAGE [EXPIRESECONDS]]]\n\n"
"Options:\n"
" --verbose run in verbose mode\n"
" --status print status lines from the backend\n"
" --progress print progress info\n"
" --openpgp use the OpenPGP protocol (default)\n"
" --cms use the CMS protocol\n"
" --loopback use a loopback pinentry\n"
" --unprotected do not use a passphrase\n"
" --force do not check for a duplicated user id\n"
, stderr);
exit (ex);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
gpgme_error_t err;
gpgme_ctx_t ctx;
gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
int print_status = 0;
int print_progress = 0;
int use_loopback = 0;
const char *userid;
const char *algo = NULL;
unsigned int flags = 0;
unsigned long expire = 0;
gpgme_genkey_result_t result;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
show_usage (0);
else if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--status"))
{
print_status = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--progress"))
{
print_progress = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--openpgp"))
{
protocol = GPGME_PROTOCOL_OpenPGP;
argc--; argv++;
}
else if (!strcmp (*argv, "--cms"))
{
protocol = GPGME_PROTOCOL_CMS;
argc--; argv++;
}
else if (!strcmp (*argv, "--loopback"))
{
use_loopback = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--unprotected"))
{
flags |= GPGME_CREATE_NOPASSWD;
argc--; argv++;
}
else if (!strcmp (*argv, "--force"))
{
flags |= GPGME_CREATE_FORCE;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
show_usage (1);
}
if (!argc || argc > 4)
show_usage (1);
userid = argv[0];
if (argc > 1)
algo = argv[1];
if (argc > 2)
flags |= parse_usage_string (argv[2]);
if (argc > 3)
expire = parse_expire_string (argv[3]);
init_gpgme (protocol);
err = gpgme_new (&ctx);
fail_if_err (err);
gpgme_set_protocol (ctx, protocol);
gpgme_set_armor (ctx, 1);
if (print_status)
{
gpgme_set_status_cb (ctx, status_cb, NULL);
gpgme_set_ctx_flag (ctx, "full-status", "1");
}
if (print_progress)
gpgme_set_progress_cb (ctx, progress_cb, NULL);
if (use_loopback)
{
gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK);
gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL);
}
err = gpgme_op_createkey (ctx, userid, algo, 0, expire, NULL, flags);
if (err)
{
fprintf (stderr, PGM ": gpgme_op_createkey failed: %s\n",
gpg_strerror (err));
exit (1);
}
result = gpgme_op_genkey_result (ctx);
if (!result)
{
fprintf (stderr, PGM": gpgme_op_genkey_result returned NULL\n");
exit (1);
}
printf ("Generated key: %s (%s)\n",
result->fpr ? result->fpr : "none",
result->primary ? (result->sub ? "primary, sub" : "primary")
/**/ : (result->sub ? "sub" : "none"));
if (result->fpr && strlen (result->fpr) < 40)
fprintf (stderr, PGM": generated key has unexpected fingerprint\n");
if (!result->primary)
fprintf (stderr, PGM": primary key was not generated\n");
if (!result->sub)
fprintf (stderr, PGM": sub key was not generated\n");
gpgme_release (ctx);
return 0;
}