From 8f2a053a0ffa0430d01a53b4d491a3f0fff683eb Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 2 Jun 2016 15:54:48 +0200 Subject: [PATCH] gpg: New command --quick-addkey. * g10/keygen.c (DEFAULT_STD_SUBKEYUSE): New. (ask_keysize): Factor code out to ... (get_keysize_range, fixup_keysize): new. (parse_parameter_usage): Factor parsing out to ... (parse_usagestr): new. Allow use of "encr" as alias for "encrypt". (parse_subkey_algostr_usagestr): New. (generate_subkeypair): Add new args. Implement unattended mode. * g10/keyedit.c (keyedit_quick_sign): Factor some code out to ... (find_by_primary_fpr): new. (keyedit_quick_addkey): New. * g10/gpg.c (aQuickAddKey): New. (opts): Add --quick-addkey. (main): Implement. Signed-off-by: Werner Koch --- doc/gpg.texi | 30 ++++ g10/gpg.c | 28 ++++ g10/keyedit.c | 178 +++++++++++++++------ g10/keygen.c | 424 +++++++++++++++++++++++++++++++++++++------------- g10/main.h | 7 +- 5 files changed, 512 insertions(+), 155 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index a09e610c2..9b0f1ba47 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -620,6 +620,35 @@ supplied passphrase is used for the new key and the agent does not ask for it. To create a key without any protection @code{--passphrase ''} may be used. +@item --quick-addkey @code{fpr} [@code{algo} [@code{usage} [@code{expire}]]] +@opindex quick-addkey +Directly add a subkey to the key identified by the fingerprint +@code{fpr}. Without the optional arguments an encryption subkey is +added. If any of the arguments are given a more specific subkey is +added. + +@code{algo} may be any of the supported algorithms or curve names given +in the format as used by key listings. To use the default algorithm +the string ``default'' or ``-'' can be used. Supported algorithms are +``rsa'', ``dsa'', ``elg'', ``ed25519'', ``cv25519'', and other ECC +curves. For example the string ``rsa'' adds an RSA key with the +default key length; a string ``rsa4096'' requests that the key length +is 4096 bits. + +Depending on the given @code{algo} the subkey may either be an +encryption subkey or a signing subkey. If an algorithm is capable of +signing and encryption and such a subkey is desired, a @code{usage} +string must be given. This string is either ``default'' or ``-'' to +keep the default or a comma delimited list of keywords: ``sign'' for a +signing subkey, ``auth'' for an authentication subkey, and ``encr'' +for an encryption subkey (``encrypt'' can be used as alias for +``encr''). The valid combinations depend on the algorithm. + +The @code{expire} argument can be used to specify an expiration date +for the subkey. Several formats are supported; commonly the ISO +YYYY-MM-DD format is used. The values ``never'', ``none'', or ``-'' +can be used for no expiration date. + @item --gen-key @opindex gen-key Generate a new key pair using the current default parameters. This is @@ -636,6 +665,7 @@ There is also a feature which allows you to create keys in batch mode. See the manual section ``Unattended key generation'' on how to use this. + @item --gen-revoke @code{name} @opindex gen-revoke Generate a revocation certificate for the complete key. To only revoke diff --git a/g10/gpg.c b/g10/gpg.c index a88499afb..279533076 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -117,6 +117,7 @@ enum cmd_and_opt_values aQuickSignKey, aQuickLSignKey, aQuickAddUid, + aQuickAddKey, aListConfig, aListGcryptConfig, aGPGConfList, @@ -426,6 +427,7 @@ static ARGPARSE_OPTS opts[] = { N_("quickly generate a new key pair")), ARGPARSE_c (aQuickAddUid, "quick-adduid", N_("quickly add a new user-id")), + ARGPARSE_c (aQuickAddKey, "quick-addkey", "@"), ARGPARSE_c (aFullKeygen, "full-gen-key" , N_("full featured key pair generation")), ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")), @@ -2433,6 +2435,7 @@ main (int argc, char **argv) case aStore: case aQuickKeygen: case aQuickAddUid: + case aQuickAddKey: case aExportOwnerTrust: case aImportOwnerTrust: case aRebuildKeydbCaches: @@ -3775,6 +3778,7 @@ main (int argc, char **argv) case aDeleteSecretAndPublicKeys: case aQuickKeygen: case aQuickAddUid: + case aQuickAddKey: case aFullKeygen: case aKeygen: case aImport: @@ -4148,6 +4152,30 @@ main (int argc, char **argv) } break; + case aQuickAddKey: + { + const char *x_fpr, *x_algo, *x_usage, *x_expire; + + if (argc < 1 || argc > 4) + wrong_args ("--quick-addkey FINGERPRINT [ALGO [USAGE [EXPIRE]]]"); + x_fpr = *argv++; argc--; + x_algo = ""; + x_usage = ""; + x_expire = ""; + if (argc) + { + x_algo = *argv++; argc--; + if (argc) + { + x_usage = *argv++; argc--; + if (argc) + x_expire = *argv++; argc--; + } + } + keyedit_quick_addkey (ctrl, x_fpr, x_algo, x_usage, x_expire); + } + break; + case aFastImport: opt.import_options |= IMPORT_FAST; case aImport: diff --git a/g10/keyedit.c b/g10/keyedit.c index c78f8a3b5..16dbf6280 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,6 +1,6 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. - * Copyright (C) 1998-2015 Werner Koch + * Copyright (C) 1998-2016 Werner Koch * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. @@ -2349,7 +2349,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, break; case cmdADDKEY: - if (!generate_subkeypair (ctrl, keyblock)) + if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL)) { redisplay = 1; modified = 1; @@ -2935,6 +2935,75 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid) } +/* Find a keyblock by fingerprint because only this uniquely + * identifies a key and may thus be used to select a key for + * unattended subkey creation os key signing. */ +static gpg_error_t +find_by_primary_fpr (ctrl_t ctrl, const char *fpr, + kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd) +{ + gpg_error_t err; + kbnode_t keyblock = NULL; + KEYDB_HANDLE kdbhd = NULL; + KEYDB_SEARCH_DESC desc; + byte fprbin[MAX_FINGERPRINT_LEN]; + size_t fprlen; + + *r_keyblock = NULL; + *r_kdbhd = NULL; + + if (classify_user_id (fpr, &desc, 1) + || !(desc.mode == KEYDB_SEARCH_MODE_FPR + || desc.mode == KEYDB_SEARCH_MODE_FPR16 + || desc.mode == KEYDB_SEARCH_MODE_FPR20)) + { + log_error (_("\"%s\" is not a fingerprint\n"), fpr); + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1); + if (err) + { + log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err)); + goto leave; + } + + /* Check that the primary fingerprint has been given. */ + fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen); + if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16 + && !memcmp (fprbin, desc.u.fpr, 16)) + ; + else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR + && !memcmp (fprbin, desc.u.fpr, 16) + && !desc.u.fpr[16] + && !desc.u.fpr[17] + && !desc.u.fpr[18] + && !desc.u.fpr[19]) + ; + else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20 + || desc.mode == KEYDB_SEARCH_MODE_FPR) + && !memcmp (fprbin, desc.u.fpr, 20)) + ; + else + { + log_error (_("\"%s\" is not the primary fingerprint\n"), fpr); + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + *r_keyblock = keyblock; + keyblock = NULL; + *r_kdbhd = kdbhd; + kdbhd = NULL; + err = 0; + + leave: + release_kbnode (keyblock); + keydb_release (kdbhd); + return err; +} + + /* Unattended key signing function. If the key specifified by FPR is available and FPR is the primary fingerprint all user ids of the key are signed using the default signing key. If UIDS is an empty @@ -2949,7 +3018,6 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int modified = 0; - KEYDB_SEARCH_DESC desc; PKT_public_key *pk; kbnode_t node; strlist_t sl; @@ -2963,47 +3031,8 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, /* We require a fingerprint because only this uniquely identifies a key and may thus be used to select a key for unattended key signing. */ - if (classify_user_id (fpr, &desc, 1) - || !(desc.mode == KEYDB_SEARCH_MODE_FPR - || desc.mode == KEYDB_SEARCH_MODE_FPR16 - || desc.mode == KEYDB_SEARCH_MODE_FPR20)) - { - log_error (_("\"%s\" is not a fingerprint\n"), fpr); - goto leave; - } - err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1); - if (err) - { - log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err)); - goto leave; - } - - /* Check that the primary fingerprint has been given. */ - { - byte fprbin[MAX_FINGERPRINT_LEN]; - size_t fprlen; - - fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen); - if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16 - && !memcmp (fprbin, desc.u.fpr, 16)) - ; - else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR - && !memcmp (fprbin, desc.u.fpr, 16) - && !desc.u.fpr[16] - && !desc.u.fpr[17] - && !desc.u.fpr[18] - && !desc.u.fpr[19]) - ; - else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20 - || desc.mode == KEYDB_SEARCH_MODE_FPR) - && !memcmp (fprbin, desc.u.fpr, 20)) - ; - else - { - log_error (_("\"%s\" is not the primary fingerprint\n"), fpr); - goto leave; - } - } + if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) + goto leave; if (fix_keyblock (&keyblock)) modified++; @@ -3129,6 +3158,67 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, } +/* Unattended subkey creation function. + * + */ +void +keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, + const char *usagestr, const char *expirestr) +{ + gpg_error_t err; + kbnode_t keyblock; + KEYDB_HANDLE kdbhd; + int modified = 0; + PKT_public_key *pk; + +#ifdef HAVE_W32_SYSTEM + /* See keyedit_menu for why we need this. */ + check_trustdb_stale (ctrl); +#endif + + /* We require a fingerprint because only this uniquely identifies a + * key and may thus be used to select a key for unattended subkey + * creation. */ + if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) + goto leave; + + if (fix_keyblock (&keyblock)) + modified++; + + pk = keyblock->pkt->pkt.public_key; + if (pk->flags.revoked) + { + if (!opt.verbose) + show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); + log_error ("%s%s", _("Key is revoked."), "\n"); + goto leave; + } + + /* Create the subkey. Noet that the called function already prints + * an error message. */ + if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr)) + modified = 1; + es_fflush (es_stdout); + + /* Store. */ + if (modified) + { + err = keydb_update_keyblock (kdbhd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + } + else + log_info (_("Key not changed so no update needed.\n")); + + leave: + release_kbnode (keyblock); + keydb_release (kdbhd); +} + + static void tty_print_notations (int indent, PKT_signature * sig) diff --git a/g10/keygen.c b/g10/keygen.c index f9cbf21a8..2ef80a755 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,6 +1,6 @@ /* keygen.c - Generate a key pair * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. - * Copyright (C) 2014, 2015 Werner Koch + * Copyright (C) 2014, 2015, 2016 Werner Koch * * This file is part of GnuPG. * @@ -54,6 +54,7 @@ #define DEFAULT_STD_CURVE NULL #define DEFAULT_STD_SUBALGO PUBKEY_ALGO_RSA #define DEFAULT_STD_SUBKEYSIZE 2048 +#define DEFAULT_STD_SUBKEYUSE PUBKEY_USAGE_ENC #define DEFAULT_STD_SUBCURVE NULL /* Flag bits used during key generation. */ @@ -2017,6 +2018,86 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, } +static void +get_keysize_range (int algo, + unsigned int *min, unsigned int *def, unsigned int *max) +{ + *min = 1024; + *def = DEFAULT_STD_KEYSIZE; + *max = 4096; + + /* Deviations from the standard values. */ + switch(algo) + { + case PUBKEY_ALGO_DSA: + *min = opt.expert? 768 : 1024; + *def=2048; + *max=3072; + break; + + case PUBKEY_ALGO_ECDSA: + case PUBKEY_ALGO_ECDH: + *min=256; + *def=256; + *max=521; + break; + + case PUBKEY_ALGO_EDDSA: + *min=255; + *def=255; + *max=441; + break; + } +} + + +/* Return a fixed up keysize depending on ALGO. */ +static unsigned int +fixup_keysize (unsigned int nbits, int algo, int silent) +{ + if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) + { + nbits = ((nbits + 63) / 64) * 64; + if (!silent) + tty_printf (_("rounded up to %u bits\n"), nbits); + } + else if (algo == PUBKEY_ALGO_EDDSA) + { + if (nbits != 255 && nbits != 441) + { + if (nbits < 256) + nbits = 255; + else + nbits = 441; + if (!silent) + tty_printf (_("rounded to %u bits\n"), nbits); + } + } + else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) + { + if (nbits != 256 && nbits != 384 && nbits != 521) + { + if (nbits < 256) + nbits = 256; + else if (nbits < 384) + nbits = 384; + else + nbits = 521; + if (!silent) + tty_printf (_("rounded to %u bits\n"), nbits); + } + } + else if ((nbits % 32)) + { + nbits = ((nbits + 31) / 32) * 32; + if (!silent) + tty_printf (_("rounded up to %u bits\n"), nbits ); + } + + return nbits; +} + + /* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE is not 0, the function asks for the size of the encryption subkey. */ @@ -2024,12 +2105,12 @@ static unsigned ask_keysize (int algo, unsigned int primary_keysize) { unsigned int nbits; - unsigned int min = 1024; - unsigned int def = DEFAULT_STD_KEYSIZE; - unsigned int max = 4096; + unsigned int min, def, max; int for_subkey = !!primary_keysize; int autocomp = 0; + get_keysize_range (algo, &min, &def, &max); + if (primary_keysize && !opt.expert) { /* Deduce the subkey size from the primary key size. */ @@ -2044,29 +2125,6 @@ ask_keysize (int algo, unsigned int primary_keysize) goto leave; } - /* Deviations from the standard values. */ - switch(algo) - { - case PUBKEY_ALGO_DSA: - min = opt.expert? 768 : 1024; - def=2048; - max=3072; - break; - - case PUBKEY_ALGO_ECDSA: - case PUBKEY_ALGO_ECDH: - min=256; - def=256; - max=521; - break; - - case PUBKEY_ALGO_EDDSA: - min=255; - def=255; - max=441; - break; - } - tty_printf(_("%s keys may be between %u and %u bits long.\n"), openpgp_pk_algo_name (algo), min, max); @@ -2095,45 +2153,7 @@ ask_keysize (int algo, unsigned int primary_keysize) tty_printf (_("Requested keysize is %u bits\n"), nbits); leave: - if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) - { - nbits = ((nbits + 63) / 64) * 64; - if (!autocomp) - tty_printf (_("rounded up to %u bits\n"), nbits); - } - else if (algo == PUBKEY_ALGO_EDDSA) - { - if (nbits != 255 && nbits != 441) - { - if (nbits < 256) - nbits = 255; - else - nbits = 441; - if (!autocomp) - tty_printf (_("rounded to %u bits\n"), nbits); - } - } - else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) - { - if (nbits != 256 && nbits != 384 && nbits != 521) - { - if (nbits < 256) - nbits = 256; - else if (nbits < 384) - nbits = 384; - else - nbits = 521; - if (!autocomp) - tty_printf (_("rounded to %u bits\n"), nbits); - } - } - else if ((nbits % 32)) - { - nbits = ((nbits + 31) / 32) * 32; - if (!autocomp) - tty_printf (_("rounded up to %u bits\n"), nbits ); - } - + nbits = fixup_keysize (nbits, algo, autocomp); return nbits; } @@ -2885,6 +2905,50 @@ get_parameter_algo( struct para_data_s *para, enum para_name key, return i; } + +/* Parse a usage string. The usage keywords "auth", "sign", "encr" + * may be elimited by space, tab, or comma. On error -1 is returned + * instead of the usage flags/ */ +static int +parse_usagestr (const char *usagestr) +{ + gpg_error_t err; + char **tokens = NULL; + const char *s; + int i; + unsigned int use = 0; + + tokens = strtokenize (usagestr, " \t,"); + if (!tokens) + { + err = gpg_error_from_syserror (); + log_error ("strtokenize failed: %s\n", gpg_strerror (err)); + return -1; + } + + for (i=0; (s = tokens[i]); i++) + { + if (!*s) + ; + else if (!ascii_strcasecmp (s, "sign")) + use |= PUBKEY_USAGE_SIG; + else if (!ascii_strcasecmp (s, "encrypt") + || !ascii_strcasecmp (s, "encr")) + use |= PUBKEY_USAGE_ENC; + else if (!ascii_strcasecmp (s, "auth")) + use |= PUBKEY_USAGE_AUTH; + else + { + xfree (tokens); + return -1; /* error */ + } + } + + xfree (tokens); + return use; +} + + /* * Parse the usage parameter and set the keyflags. Returns -1 on * error, 0 for no usage given or 1 for usage available. @@ -2893,33 +2957,24 @@ static int parse_parameter_usage (const char *fname, struct para_data_s *para, enum para_name key) { - struct para_data_s *r = get_parameter( para, key ); - char *p, *pn; - unsigned int use; + struct para_data_s *r = get_parameter( para, key ); + int i; - if( !r ) - return 0; /* none (this is an optional parameter)*/ + if (!r) + return 0; /* none (this is an optional parameter)*/ - use = 0; - pn = r->u.value; - while ( (p = strsep (&pn, " \t,")) ) { - if ( !*p) - ; - else if ( !ascii_strcasecmp (p, "sign") ) - use |= PUBKEY_USAGE_SIG; - else if ( !ascii_strcasecmp (p, "encrypt") ) - use |= PUBKEY_USAGE_ENC; - else if ( !ascii_strcasecmp (p, "auth") ) - use |= PUBKEY_USAGE_AUTH; - else { - log_error("%s:%d: invalid usage list\n", fname, r->lnr ); - return -1; /* error */ - } + i = parse_usagestr (r->u.value); + if (i == -1) + { + log_error ("%s:%d: invalid usage list\n", fname, r->lnr ); + return -1; /* error */ } - r->u.usage = use; - return 1; + + r->u.usage = i; + return 1; } + static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) @@ -4260,12 +4315,119 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, } -/* Add a new subkey to an existing key. Returns 0 if a new key has - been generated and put into the keyblocks. */ gpg_error_t -generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) +parse_subkey_algostr_usagestr (ctrl_t ctrl, const char *algostr, + const char *usagestr, + int *r_algo, unsigned int *r_usage, + unsigned int *r_nbits, char **r_curve) +{ + int algo; + unsigned int use, nbits; + int wantuse; + unsigned int min, def, max; + const char *curve = NULL; + int eccalgo = 0; + + *r_curve = NULL; + + nbits = 0; + /* Parse the algo string. */ + if (!algostr || !*algostr + || !strcmp (algostr, "default") || !strcmp (algostr, "-")) + { + algo = DEFAULT_STD_SUBALGO; + use = DEFAULT_STD_SUBKEYUSE; + } + else if (*algostr == '&' && strlen (algostr) == 41) + { + /* Take algo from existing key. */ + algo = check_keygrip (ctrl, algostr+1); + /* FIXME: We need the curve name as well. */ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else if (!strncmp (algostr, "rsa", 3)) + { + algo = PUBKEY_ALGO_RSA; + use = DEFAULT_STD_SUBKEYUSE; + if (algostr[3]) + nbits = atoi (algostr + 3); + } + else if (!strncmp (algostr, "elg", 3)) + { + algo = PUBKEY_ALGO_ELGAMAL_E; + use = PUBKEY_USAGE_ENC; + if (algostr[3]) + nbits = atoi (algostr + 3); + } + else if (!strncmp (algostr, "dsa", 3)) + { + algo = PUBKEY_ALGO_DSA; + use = PUBKEY_USAGE_SIG; + if (algostr[3]) + nbits = atoi (algostr + 3); + } + else if ((curve = openpgp_is_curve_supported (algostr, &algo))) + { + if (!algo) + { + algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm. */ + eccalgo = 1; /* Remember - we may need to fix it up. */ + } + + if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) + use = PUBKEY_USAGE_SIG; + else + use = PUBKEY_USAGE_ENC; + } + else + return gpg_error (GPG_ERR_INV_CURVE); + + /* Parse the usage string. */ + if (!usagestr || !*usagestr + || !strcmp (usagestr, "default") || !strcmp (usagestr, "-")) + ; /* Keep default usage */ + else if ((wantuse = parse_usagestr (usagestr)) != -1) + { + use = wantuse; + if (eccalgo && !(use & PUBKEY_USAGE_ENC)) + algo = PUBKEY_ALGO_ECDSA; /* Switch from ECDH to ECDSA. */ + } + else + return gpg_error (GPG_ERR_INV_VALUE); + + /* Make sure the keysize is in the allowed range. */ + get_keysize_range (algo, &min, &def, &max); + if (!nbits) + nbits = def; + else if (nbits < min) + nbits = min; + else if (nbits > max) + nbits = max; + + nbits = fixup_keysize (nbits, algo, 1); + + if (curve) + { + *r_curve = xtrystrdup (curve); + if (!*r_curve) + return gpg_error_from_syserror (); + } + *r_algo = algo; + *r_usage = use; + *r_nbits = nbits; + return 0; +} + + +/* Add a new subkey to an existing key. Returns 0 if a new key has + been generated and put into the keyblocks. If any of ALGOSTR, + USAGESTR, or EXPIRESTR is NULL interactive mode is used. */ +gpg_error_t +generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, + const char *usagestr, const char *expirestr) { gpg_error_t err = 0; + int interactive; kbnode_t node; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; @@ -4278,6 +4440,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) char *hexgrip = NULL; char *serialno = NULL; + interactive = (!algostr || !usagestr || !expirestr); + /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) @@ -4317,32 +4481,72 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) goto leave; if (agent_get_keyinfo (NULL, hexgrip, &serialno)) { - tty_printf (_("Secret parts of primary key are not available.\n")); + if (interactive) + tty_printf (_("Secret parts of primary key are not available.\n")); + else + log_info ( _("Secret parts of primary key are not available.\n")); + err = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } if (serialno) - tty_printf (_("Secret parts of primary key are stored on-card.\n")); + { + if (interactive) + tty_printf (_("Secret parts of primary key are stored on-card.\n")); + else + log_info ( _("Secret parts of primary key are stored on-card.\n")); + } xfree (hexgrip); hexgrip = NULL; - algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip); - log_assert (algo); - - if (hexgrip) - nbits = 0; - else if (algo == PUBKEY_ALGO_ECDSA - || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH) - curve = ask_curve (&algo, NULL); - else - nbits = ask_keysize (algo, 0); - - expire = ask_expire_interval (0, NULL); - if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", - _("Really create? (y/N) "))) + if (interactive) { - err = gpg_error (GPG_ERR_CANCELED); - goto leave; + algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip); + log_assert (algo); + + if (hexgrip) + nbits = 0; + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + curve = ask_curve (&algo, NULL); + else + nbits = ask_keysize (algo, 0); + + expire = ask_expire_interval (0, NULL); + if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", + _("Really create? (y/N) "))) + { + err = gpg_error (GPG_ERR_CANCELED); + goto leave; + } + } + else /* Unattended mode. */ + { + err = parse_subkey_algostr_usagestr (ctrl, algostr, usagestr, + &algo, &use, &nbits, &curve); + if (err) + goto leave; + + if (!expirestr || !*expirestr || !strcmp (expirestr, "none") + || !strcmp (expirestr, "never") || !strcmp (expirestr, "-")) + expire = 0; + else + expire = parse_expire_string (expirestr); + if (expire == (u32)-1 ) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + + /* Check that usage is possible. */ + if ( ((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) + && !pubkey_get_nsig (algo)) + || ((use & PUBKEY_USAGE_ENC) + && !pubkey_get_nenc (algo))) + { + err = gpg_error (GPG_ERR_WRONG_KEY_USAGE); + goto leave; + } } if (hexgrip) diff --git a/g10/main.h b/g10/main.h index 5b5947e48..0ca4d3947 100644 --- a/g10/main.h +++ b/g10/main.h @@ -287,6 +287,8 @@ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, void keyedit_passwd (ctrl_t ctrl, const char *username); void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid); +void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, + const char *usagestr, const char *expirestr); void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local); void show_basic_key_info (KBNODE keyblock); @@ -311,7 +313,10 @@ int keygen_add_revkey(PKT_signature *sig, void *opaque); gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); -gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock); +gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, + const char *algostr, + const char *usagestr, + const char *expirestr); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, int keyno, const char *serialno);