From eee068778cb28ecf3c14e1bf843a95547d72c42d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:14:06 +0200 Subject: Adding upstream version 2.2.40. Signed-off-by: Daniel Baumann --- g10/keygen.c | 5766 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5766 insertions(+) create mode 100644 g10/keygen.c (limited to 'g10/keygen.c') diff --git a/g10/keygen.c b/g10/keygen.c new file mode 100644 index 0000000..80d65c4 --- /dev/null +++ b/g10/keygen.c @@ -0,0 +1,5766 @@ +/* keygen.c - Generate a key pair + * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. + * Copyright (C) 2014, 2015, 2016 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpg.h" +#include "../common/util.h" +#include "main.h" +#include "packet.h" +#include "../common/ttyio.h" +#include "options.h" +#include "keydb.h" +#include "trustdb.h" +#include "../common/status.h" +#include "../common/i18n.h" +#include "keyserver-internal.h" +#include "call-agent.h" +#include "pkglue.h" +#include "../common/shareddefs.h" +#include "../common/host2net.h" +#include "../common/mbox-util.h" + + +/* The default algorithms. You should also check that the value + is inside the bounds enforced by ask_keysize and gen_xxx. See also + get_keysize_range which encodes the allowed ranges. */ +#define DEFAULT_STD_KEY_PARAM "rsa3072/cert,sign+rsa3072/encr" +#define FUTURE_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr" + +/* When generating keys using the streamlined key generation dialog, + use this as a default expiration interval. */ +const char *default_expiration_interval = "2y"; + +/* Flag bits used during key generation. */ +#define KEYGEN_FLAG_NO_PROTECTION 1 +#define KEYGEN_FLAG_TRANSIENT_KEY 2 + +/* Maximum number of supported algorithm preferences. */ +#define MAX_PREFS 30 + +enum para_name { + pKEYTYPE, + pKEYLENGTH, + pKEYCURVE, + pKEYUSAGE, + pSUBKEYTYPE, + pSUBKEYLENGTH, + pSUBKEYCURVE, + pSUBKEYUSAGE, + pAUTHKEYTYPE, + pNAMEREAL, + pNAMEEMAIL, + pNAMECOMMENT, + pPREFERENCES, + pREVOKER, + pUSERID, + pCREATIONDATE, + pKEYCREATIONDATE, /* Same in seconds since epoch. */ + pEXPIREDATE, + pKEYEXPIRE, /* in n seconds */ + pSUBKEYEXPIRE, /* in n seconds */ + pPASSPHRASE, + pSERIALNO, + pCARDBACKUPKEY, + pHANDLE, + pKEYSERVER, + pKEYGRIP, + pSUBKEYGRIP, +}; + +struct para_data_s { + struct para_data_s *next; + int lnr; + enum para_name key; + union { + u32 expire; + u32 creation; + unsigned int usage; + struct revocation_key revkey; + char value[1]; + } u; +}; + +struct output_control_s +{ + int lnr; + int dryrun; + unsigned int keygen_flags; + int use_files; + struct { + char *fname; + char *newfname; + IOBUF stream; + armor_filter_context_t *afx; + } pub; +}; + + +struct opaque_data_usage_and_pk { + unsigned int usage; + PKT_public_key *pk; +}; + + +static int prefs_initialized = 0; +static byte sym_prefs[MAX_PREFS]; +static int nsym_prefs; +static byte hash_prefs[MAX_PREFS]; +static int nhash_prefs; +static byte zip_prefs[MAX_PREFS]; +static int nzip_prefs; +static int mdc_available,ks_modify; + +static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, + const char *algostr, const char *usagestr, + const char *expirestr, + int *r_algo, unsigned int *r_usage, + u32 *r_expire, unsigned int *r_nbits, + const char **r_curve, + char **r_keygrip); +static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, + struct output_control_s *outctrl, int card ); +static int write_keyblock (iobuf_t out, kbnode_t node); +static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, + kbnode_t pub_root, u32 *timestamp, + u32 expireval); +static unsigned int get_keysize_range (int algo, + unsigned int *min, unsigned int *max); + + + +/* Return the algo string for a default new key. */ +const char * +get_default_pubkey_algo (void) +{ + if (opt.def_new_key_algo) + { + if (*opt.def_new_key_algo && !strchr (opt.def_new_key_algo, ':')) + return opt.def_new_key_algo; + /* To avoid checking that option every time we delay that until + * here. The only thing we really need to make sure is that + * there is no colon in the string so that the --gpgconf-list + * command won't mess up its output. */ + log_info (_("invalid value for option '%s'\n"), "--default-new-key-algo"); + } + return DEFAULT_STD_KEY_PARAM; +} + + +static void +print_status_key_created (int letter, PKT_public_key *pk, const char *handle) +{ + byte array[MAX_FINGERPRINT_LEN], *s; + char *buf, *p; + size_t i, n; + + if (!handle) + handle = ""; + + buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); + + p = buf; + if (letter || pk) + { + *p++ = letter; + if (pk) + { + *p++ = ' '; + fingerprint_from_pk (pk, array, &n); + s = array; + /* Fixme: Use bin2hex */ + for (i=0; i < n ; i++, s++, p += 2) + snprintf (p, 3, "%02X", *s); + } + } + if (*handle) + { + *p++ = ' '; + for (i=0; handle[i] && i < 100; i++) + *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; + } + *p = 0; + write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, + buf); + xfree (buf); +} + +static void +print_status_key_not_created (const char *handle) +{ + print_status_key_created (0, NULL, handle); +} + + + +static gpg_error_t +write_uid (kbnode_t root, const char *s) +{ + PACKET *pkt = xmalloc_clear (sizeof *pkt); + size_t n = strlen (s); + + if (n > MAX_UID_PACKET_LENGTH - 10) + return gpg_error (GPG_ERR_INV_USER_ID); + + pkt->pkttype = PKT_USER_ID; + pkt->pkt.user_id = xmalloc_clear (sizeof *pkt->pkt.user_id + n); + pkt->pkt.user_id->len = n; + pkt->pkt.user_id->ref = 1; + strcpy (pkt->pkt.user_id->name, s); + add_kbnode (root, new_kbnode (pkt)); + return 0; +} + +static void +do_add_key_flags (PKT_signature *sig, unsigned int use) +{ + byte buf[1]; + + buf[0] = 0; + + /* The spec says that all primary keys MUST be able to certify. */ + if(sig->sig_class!=0x18) + buf[0] |= 0x01; + + if (use & PUBKEY_USAGE_SIG) + buf[0] |= 0x02; + if (use & PUBKEY_USAGE_ENC) + buf[0] |= 0x04 | 0x08; + if (use & PUBKEY_USAGE_AUTH) + buf[0] |= 0x20; + + build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); +} + + +int +keygen_add_key_expire (PKT_signature *sig, void *opaque) +{ + PKT_public_key *pk = opaque; + byte buf[8]; + u32 u; + + if (pk->expiredate) + { + if (pk->expiredate > pk->timestamp) + u = pk->expiredate - pk->timestamp; + else + u = 1; + + buf[0] = (u >> 24) & 0xff; + buf[1] = (u >> 16) & 0xff; + buf[2] = (u >> 8) & 0xff; + buf[3] = u & 0xff; + build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); + } + else + { + /* Make sure we don't leave a key expiration subpacket lying + around */ + delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE); + } + + return 0; +} + + +/* Add the key usage (i.e. key flags) in SIG from the public keys + * pubkey_usage field. OPAQUE has the public key. */ +int +keygen_add_key_flags (PKT_signature *sig, void *opaque) +{ + PKT_public_key *pk = opaque; + + do_add_key_flags (sig, pk->pubkey_usage); + return 0; +} + + +static int +keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) +{ + struct opaque_data_usage_and_pk *oduap = opaque; + + do_add_key_flags (sig, oduap->usage); + return keygen_add_key_expire (sig, oduap->pk); +} + + +static int +set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) +{ + int i; + + for (i=0; i < *nbuf; i++ ) + if (buf[i] == val) + { + log_info (_("preference '%s' duplicated\n"), item); + return -1; + } + + if (*nbuf >= MAX_PREFS) + { + if(type==1) + log_info(_("too many cipher preferences\n")); + else if(type==2) + log_info(_("too many digest preferences\n")); + else if(type==3) + log_info(_("too many compression preferences\n")); + else + BUG(); + + return -1; + } + + buf[(*nbuf)++] = val; + return 0; +} + +/* + * Parse the supplied string and use it to set the standard + * preferences. The string may be in a form like the one printed by + * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual + * cipher/hash/compress names. Use NULL to set the default + * preferences. Returns: 0 = okay + */ +int +keygen_set_std_prefs (const char *string,int personal) +{ + byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; + int nsym=0, nhash=0, nzip=0, val, rc=0; + int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ + char dummy_string[20*4+1]; /* Enough for 20 items. */ + + if (!string || !ascii_strcasecmp (string, "default")) + { + if (opt.def_preference_list) + string=opt.def_preference_list; + else + { + int any_compress = 0; + dummy_string[0]='\0'; + + /* The rationale why we use the order AES256,192,128 is + for compatibility reasons with PGP. If gpg would + define AES128 first, we would get the somewhat + confusing situation: + + gpg -r pgpkey -r gpgkey ---gives--> AES256 + gpg -r gpgkey -r pgpkey ---gives--> AES + + Note that by using --personal-cipher-preferences it is + possible to prefer AES128. + */ + + /* Make sure we do not add more than 15 items here, as we + could overflow the size of dummy_string. We currently + have at most 12. */ + if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES256) ) + strcat(dummy_string,"S9 "); + if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES192) ) + strcat(dummy_string,"S8 "); + if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES) ) + strcat(dummy_string,"S7 "); + strcat(dummy_string,"S2 "); /* 3DES */ + + if (personal) + { + /* The default internal hash algo order is: + * SHA-256, SHA-384, SHA-512, SHA-224, SHA-1. + */ + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) + strcat (dummy_string, "H8 "); + + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) + strcat (dummy_string, "H9 "); + + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) + strcat (dummy_string, "H10 "); + } + else + { + /* The default advertised hash algo order is: + * SHA-512, SHA-384, SHA-256, SHA-224, SHA-1. + */ + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) + strcat (dummy_string, "H10 "); + + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) + strcat (dummy_string, "H9 "); + + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) + strcat (dummy_string, "H8 "); + } + + if (!openpgp_md_test_algo (DIGEST_ALGO_SHA224)) + strcat (dummy_string, "H11 "); + + strcat (dummy_string, "H2 "); /* SHA-1 */ + + if(!check_compress_algo(COMPRESS_ALGO_ZLIB)) + { + strcat(dummy_string,"Z2 "); + any_compress = 1; + } + + if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) + { + strcat(dummy_string,"Z3 "); + any_compress = 1; + } + + if(!check_compress_algo(COMPRESS_ALGO_ZIP)) + { + strcat(dummy_string,"Z1 "); + any_compress = 1; + } + + /* In case we have no compress algo at all, declare that + we prefer no compresssion. */ + if (!any_compress) + strcat(dummy_string,"Z0 "); + + /* Remove the trailing space. */ + if (*dummy_string && dummy_string[strlen (dummy_string)-1] == ' ') + dummy_string[strlen (dummy_string)-1] = 0; + + string=dummy_string; + } + } + else if (!ascii_strcasecmp (string, "none")) + string = ""; + + if(strlen(string)) + { + char *prefstringbuf; + char *tok, *prefstring; + + /* We need a writable string. */ + prefstring = prefstringbuf = xstrdup (string); + + while((tok=strsep(&prefstring," ,"))) + { + if((val=string_to_cipher_algo (tok))) + { + if(set_one_pref(val,1,tok,sym,&nsym)) + rc=-1; + } + else if((val=string_to_digest_algo (tok))) + { + if(set_one_pref(val,2,tok,hash,&nhash)) + rc=-1; + } + else if((val=string_to_compress_algo(tok))>-1) + { + if(set_one_pref(val,3,tok,zip,&nzip)) + rc=-1; + } + else if (ascii_strcasecmp(tok,"mdc")==0) + mdc=1; + else if (ascii_strcasecmp(tok,"no-mdc")==0) + mdc=0; + else if (ascii_strcasecmp(tok,"ks-modify")==0) + modify=1; + else if (ascii_strcasecmp(tok,"no-ks-modify")==0) + modify=0; + else + { + log_info (_("invalid item '%s' in preference string\n"),tok); + rc=-1; + } + } + + xfree (prefstringbuf); + } + + if(!rc) + { + if(personal) + { + if(personal==PREFTYPE_SYM) + { + xfree(opt.personal_cipher_prefs); + + if(nsym==0) + opt.personal_cipher_prefs=NULL; + else + { + int i; + + opt.personal_cipher_prefs= + xmalloc(sizeof(prefitem_t *)*(nsym+1)); + + for (i=0; iref=1; + + uid->prefs=xmalloc((sizeof(prefitem_t *)* + (nsym_prefs+nhash_prefs+nzip_prefs+1))); + + for(i=0;iprefs[j].type=PREFTYPE_SYM; + uid->prefs[j].value=sym_prefs[i]; + } + + for(i=0;iprefs[j].type=PREFTYPE_HASH; + uid->prefs[j].value=hash_prefs[i]; + } + + for(i=0;iprefs[j].type=PREFTYPE_ZIP; + uid->prefs[j].value=zip_prefs[i]; + } + + uid->prefs[j].type=PREFTYPE_NONE; + uid->prefs[j].value=0; + + uid->flags.mdc=mdc_available; + uid->flags.ks_modify=ks_modify; + + return uid; +} + +static void +add_feature_mdc (PKT_signature *sig,int enabled) +{ + const byte *s; + size_t n; + int i; + char *buf; + + s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); + /* Already set or cleared */ + if (s && n && + ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01)))) + return; + + if (!s || !n) { /* create a new one */ + n = 1; + buf = xmalloc_clear (n); + } + else { + buf = xmalloc (n); + memcpy (buf, s, n); + } + + if(enabled) + buf[0] |= 0x01; /* MDC feature */ + else + buf[0] &= ~0x01; + + /* Are there any bits set? */ + for(i=0;ihashed, SIGSUBPKT_FEATURES); + else + build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); + + xfree (buf); +} + +static void +add_keyserver_modify (PKT_signature *sig,int enabled) +{ + const byte *s; + size_t n; + int i; + char *buf; + + /* The keyserver modify flag is a negative flag (i.e. no-modify) */ + enabled=!enabled; + + s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n ); + /* Already set or cleared */ + if (s && n && + ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80)))) + return; + + if (!s || !n) { /* create a new one */ + n = 1; + buf = xmalloc_clear (n); + } + else { + buf = xmalloc (n); + memcpy (buf, s, n); + } + + if(enabled) + buf[0] |= 0x80; /* no-modify flag */ + else + buf[0] &= ~0x80; + + /* Are there any bits set? */ + for(i=0;ihashed, SIGSUBPKT_KS_FLAGS); + else + build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n); + + xfree (buf); +} + + +int +keygen_upd_std_prefs (PKT_signature *sig, void *opaque) +{ + (void)opaque; + + if (!prefs_initialized) + keygen_set_std_prefs (NULL, 0); + + if (nsym_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); + else + { + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); + delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); + } + + if (nhash_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); + else + { + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); + delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH); + } + + if (nzip_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); + else + { + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); + delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); + } + + /* Make sure that the MDC feature flag is set if needed. */ + add_feature_mdc (sig,mdc_available); + add_keyserver_modify (sig,ks_modify); + keygen_add_keyserver_url(sig,NULL); + + return 0; +} + + +/**************** + * Add preference to the self signature packet. + * This is only called for packets with version > 3. + */ +int +keygen_add_std_prefs (PKT_signature *sig, void *opaque) +{ + PKT_public_key *pk = opaque; + + do_add_key_flags (sig, pk->pubkey_usage); + keygen_add_key_expire (sig, opaque ); + keygen_upd_std_prefs (sig, opaque); + keygen_add_keyserver_url (sig,NULL); + + return 0; +} + +int +keygen_add_keyserver_url(PKT_signature *sig, void *opaque) +{ + const char *url=opaque; + + if(!url) + url=opt.def_keyserver_url; + + if(url) + build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); + else + delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); + + return 0; +} + +int +keygen_add_notations(PKT_signature *sig,void *opaque) +{ + struct notation *notation; + + /* We always start clean */ + delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); + delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); + sig->flags.notation=0; + + for(notation=opaque;notation;notation=notation->next) + if(!notation->flags.ignore) + { + unsigned char *buf; + unsigned int n1,n2; + + n1=strlen(notation->name); + if(notation->altvalue) + n2=strlen(notation->altvalue); + else if(notation->bdat) + n2=notation->blen; + else + n2=strlen(notation->value); + + buf = xmalloc( 8 + n1 + n2 ); + + /* human readable or not */ + buf[0] = notation->bdat?0:0x80; + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy(buf+8, notation->name, n1 ); + if(notation->altvalue) + memcpy(buf+8+n1, notation->altvalue, n2 ); + else if(notation->bdat) + memcpy(buf+8+n1, notation->bdat, n2 ); + else + memcpy(buf+8+n1, notation->value, n2 ); + build_sig_subpkt( sig, SIGSUBPKT_NOTATION | + (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), + buf, 8+n1+n2 ); + xfree(buf); + } + + return 0; +} + +int +keygen_add_revkey (PKT_signature *sig, void *opaque) +{ + struct revocation_key *revkey = opaque; + byte buf[2+MAX_FINGERPRINT_LEN]; + + buf[0] = revkey->class; + buf[1] = revkey->algid; + memcpy (&buf[2], revkey->fpr, MAX_FINGERPRINT_LEN); + + build_sig_subpkt (sig, SIGSUBPKT_REV_KEY, buf, 2+MAX_FINGERPRINT_LEN); + + /* All sigs with revocation keys set are nonrevocable. */ + sig->flags.revocable = 0; + buf[0] = 0; + build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); + + parse_revkeys (sig); + + return 0; +} + + + +/* Create a back-signature. If TIMESTAMP is not NULL, use it for the + signature creation time. */ +gpg_error_t +make_backsig (ctrl_t ctrl, 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 err; + PKT_signature *backsig; + + cache_public_key (sub_pk); + + err = make_keysig_packet (ctrl, &backsig, pk, NULL, sub_pk, sub_psk, 0x19, + 0, timestamp, 0, NULL, NULL, cache_nonce); + if (err) + log_error ("make_keysig_packet failed for backsig: %s\n", + gpg_strerror (err)); + else + { + /* Get it into a binary packed form. */ + IOBUF backsig_out = iobuf_temp(); + PACKET backsig_pkt; + + init_packet (&backsig_pkt); + backsig_pkt.pkttype = PKT_SIGNATURE; + backsig_pkt.pkt.signature = backsig; + err = build_packet (backsig_out, &backsig_pkt); + free_packet (&backsig_pkt, NULL); + if (err) + log_error ("build_packet failed for backsig: %s\n", gpg_strerror (err)); + else + { + size_t pktlen = 0; + byte *buf = iobuf_get_temp_buffer (backsig_out); + + /* Remove the packet header. */ + if(buf[0]&0x40) + { + if (buf[1] < 192) + { + pktlen = buf[1]; + buf += 2; + } + else if(buf[1] < 224) + { + pktlen = (buf[1]-192)*256; + pktlen += buf[2]+192; + buf += 3; + } + else if (buf[1] == 255) + { + pktlen = buf32_to_size_t (buf+2); + buf += 6; + } + else + BUG (); + } + else + { + int mark = 1; + + switch (buf[0]&3) + { + case 3: + BUG (); + break; + + case 2: + pktlen = (size_t)buf[mark++] << 24; + pktlen |= buf[mark++] << 16; + /* fall through */ + case 1: + pktlen |= buf[mark++] << 8; + /* fall through */ + case 0: + pktlen |= buf[mark++]; + } + + buf += mark; + } + + /* Now make the binary blob into a subpacket. */ + build_sig_subpkt (sig, SIGSUBPKT_SIGNATURE, buf, pktlen); + + iobuf_close (backsig_out); + } + } + + return err; +} + + +/* Write a direct key signature to the first key in ROOT using the key + PSK. REVKEY is describes the direct key signature and TIMESTAMP is + the timestamp to set on the signature. */ +static gpg_error_t +write_direct_sig (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, + struct revocation_key *revkey, u32 timestamp, + const char *cache_nonce) +{ + gpg_error_t err; + PACKET *pkt; + PKT_signature *sig; + KBNODE node; + PKT_public_key *pk; + + if (opt.verbose) + log_info (_("writing direct signature\n")); + + /* Get the pk packet from the pub_tree. */ + node = find_kbnode (root, PKT_PUBLIC_KEY); + if (!node) + BUG (); + pk = node->pkt->pkt.public_key; + + /* We have to cache the key, so that the verification of the + signature creation is able to retrieve the public key. */ + cache_public_key (pk); + + /* Make the signature. */ + err = make_keysig_packet (ctrl, &sig, pk, NULL,NULL, psk, 0x1F, + 0, timestamp, 0, + keygen_add_revkey, revkey, cache_nonce); + if (err) + { + log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err) ); + return err; + } + + pkt = xmalloc_clear (sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode (root, new_kbnode (pkt)); + return err; +} + + + +/* Write a self-signature to the first user id in ROOT using the key + PSK. USE and TIMESTAMP give the extra data we need for the + signature. */ +static gpg_error_t +write_selfsigs (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, + unsigned int use, u32 timestamp, const char *cache_nonce) +{ + gpg_error_t err; + PACKET *pkt; + PKT_signature *sig; + PKT_user_id *uid; + KBNODE node; + PKT_public_key *pk; + + if (opt.verbose) + log_info (_("writing self signature\n")); + + /* Get the uid packet from the list. */ + node = find_kbnode (root, PKT_USER_ID); + if (!node) + BUG(); /* No user id packet in tree. */ + uid = node->pkt->pkt.user_id; + + /* Get the pk packet from the pub_tree. */ + node = find_kbnode (root, PKT_PUBLIC_KEY); + if (!node) + BUG(); + pk = node->pkt->pkt.public_key; + + /* The usage has not yet been set - do it now. */ + pk->pubkey_usage = use; + + /* We have to cache the key, so that the verification of the + signature creation is able to retrieve the public key. */ + cache_public_key (pk); + + /* Make the signature. */ + err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, psk, 0x13, + 0, timestamp, 0, + keygen_add_std_prefs, pk, cache_nonce); + if (err) + { + log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err)); + return err; + } + + pkt = xmalloc_clear (sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode (root, new_kbnode (pkt)); + + return err; +} + + +/* Write the key binding signature. If TIMESTAMP is not NULL use the + signature creation time. PRI_PSK is the key use for signing. + SUB_PSK is a key used to create a back-signature; that one is only + used if USE has the PUBKEY_USAGE_SIG capability. */ +static int +write_keybinding (ctrl_t ctrl, kbnode_t root, + PKT_public_key *pri_psk, PKT_public_key *sub_psk, + unsigned int use, u32 timestamp, const char *cache_nonce) +{ + gpg_error_t err; + PACKET *pkt; + PKT_signature *sig; + KBNODE node; + PKT_public_key *pri_pk, *sub_pk; + struct opaque_data_usage_and_pk oduap; + + if (opt.verbose) + log_info(_("writing key binding signature\n")); + + /* Get the primary pk packet from the tree. */ + node = find_kbnode (root, PKT_PUBLIC_KEY); + if (!node) + BUG(); + pri_pk = node->pkt->pkt.public_key; + + /* We have to cache the key, so that the verification of the + * signature creation is able to retrieve the public key. */ + cache_public_key (pri_pk); + + /* Find the last subkey. */ + sub_pk = NULL; + for (node = root; node; node = node->next ) + { + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + sub_pk = node->pkt->pkt.public_key; + } + if (!sub_pk) + BUG(); + + /* Make the signature. */ + oduap.usage = use; + oduap.pk = sub_pk; + err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, + 0, timestamp, 0, + keygen_add_key_flags_and_expire, &oduap, + cache_nonce); + if (err) + { + log_error ("make_keysig_packeto failed: %s\n", gpg_strerror (err)); + return err; + } + + /* Make a backsig. */ + if (use & PUBKEY_USAGE_SIG) + { + err = make_backsig (ctrl, + sig, pri_pk, sub_pk, sub_psk, timestamp, cache_nonce); + if (err) + return err; + } + + pkt = xmalloc_clear ( sizeof *pkt ); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode (root, new_kbnode (pkt) ); + return err; +} + + +static gpg_error_t +ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) +{ + gpg_error_t err; + gcry_sexp_t list, l2; + char *curve = NULL; + int i; + const char *oidstr; + unsigned int nbits; + + array[0] = NULL; + array[1] = NULL; + array[2] = NULL; + + list = gcry_sexp_find_token (sexp, "public-key", 0); + if (!list) + return gpg_error (GPG_ERR_INV_OBJ); + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + return gpg_error (GPG_ERR_NO_OBJ); + + l2 = gcry_sexp_find_token (list, "curve", 0); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + curve = gcry_sexp_nth_string (l2, 1); + if (!curve) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + gcry_sexp_release (l2); + oidstr = openpgp_curve_to_oid (curve, &nbits, NULL); + if (!oidstr) + { + /* That can't happen because we used one of the curves + gpg_curve_to_oid knows about. */ + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + err = openpgp_oid_from_str (oidstr, &array[0]); + if (err) + goto leave; + + l2 = gcry_sexp_find_token (list, "q", 0); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + if (!array[1]) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + gcry_sexp_release (list); + + if (algo == PUBKEY_ALGO_ECDH) + { + array[2] = pk_ecdh_default_params (nbits); + if (!array[2]) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + leave: + xfree (curve); + if (err) + { + for (i=0; i < 3; i++) + { + gcry_mpi_release (array[i]); + array[i] = NULL; + } + } + return err; +} + + +/* Extract key parameters from SEXP and store them in ARRAY. ELEMS is + a string where each character denotes a parameter name. TOPNAME is + the name of the top element above the elements. */ +static int +key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, + const char *topname, const char *elems) +{ + gcry_sexp_t list, l2; + const char *s; + int i, idx; + int rc = 0; + + list = gcry_sexp_find_token (sexp, topname, 0); + if (!list) + return gpg_error (GPG_ERR_INV_OBJ); + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + return gpg_error (GPG_ERR_NO_OBJ); + + for (idx=0,s=elems; *s; s++, idx++) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + rc = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ + goto leave; + } + array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + if (!array[idx]) + { + rc = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ + goto leave; + } + } + gcry_sexp_release (list); + + leave: + if (rc) + { + for (i=0; itimestamp = timestamp; + pk->version = 4; + if (expireval) + pk->expiredate = pk->timestamp + expireval; + pk->pubkey_algo = algo; + + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH ) + err = ecckey_from_sexp (pk->pkey, s_key, algo); + else + err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); + if (err) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); + gcry_sexp_release (s_key); + free_public_key (pk); + return err; + } + gcry_sexp_release (s_key); + + pkt = xtrycalloc (1, sizeof *pkt); + if (!pkt) + { + err = gpg_error_from_syserror (); + free_public_key (pk); + return err; + } + + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + add_kbnode (pub_root, new_kbnode (pkt)); + + return 0; +} + + +/* Common code for the key generation function gen_xxx. */ +static int +common_gen (const char *keyparms, int algo, const char *algoelem, + kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr) +{ + int err; + PACKET *pkt; + PKT_public_key *pk; + gcry_sexp_t s_key; + + err = agent_genkey (NULL, cache_nonce_addr, passwd_nonce_addr, keyparms, + !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), + passphrase, timestamp, + &s_key); + if (err) + { + log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); + return err; + } + + pk = xtrycalloc (1, sizeof *pk); + if (!pk) + { + err = gpg_error_from_syserror (); + gcry_sexp_release (s_key); + return err; + } + + pk->timestamp = timestamp; + pk->version = 4; + if (expireval) + pk->expiredate = pk->timestamp + expireval; + pk->pubkey_algo = algo; + + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH ) + err = ecckey_from_sexp (pk->pkey, s_key, algo); + else + err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); + if (err) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); + gcry_sexp_release (s_key); + free_public_key (pk); + return err; + } + gcry_sexp_release (s_key); + + pkt = xtrycalloc (1, sizeof *pkt); + if (!pkt) + { + err = gpg_error_from_syserror (); + free_public_key (pk); + return err; + } + + pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; + pkt->pkt.public_key = pk; + add_kbnode (pub_root, new_kbnode (pkt)); + + return 0; +} + + +/* + * Generate an Elgamal key. + */ +static int +gen_elg (int algo, unsigned int nbits, KBNODE pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr) +{ + int err; + char *keyparms; + char nbitsstr[35]; + + log_assert (is_ELGAMAL (algo)); + + if (nbits < 1024) + { + nbits = 2048; + log_info (_("keysize invalid; using %u bits\n"), nbits ); + } + else if (nbits > 4096) + { + nbits = 4096; + log_info (_("keysize invalid; using %u bits\n"), nbits ); + } + + if ((nbits % 32)) + { + nbits = ((nbits + 31) / 32) * 32; + log_info (_("keysize rounded up to %u bits\n"), nbits ); + } + + /* Note that we use transient-key only if no-protection has also + been enabled. */ + snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); + keyparms = xtryasprintf ("(genkey(%s(nbits %zu:%s)%s))", + algo == GCRY_PK_ELG_E ? "openpgp-elg" : + algo == GCRY_PK_ELG ? "elg" : "x-oops" , + strlen (nbitsstr), nbitsstr, + ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + "(transient-key)" : "" ); + if (!keyparms) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms, algo, "pgy", + pub_root, timestamp, expireval, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + xfree (keyparms); + } + + return err; +} + + +/* + * Generate an DSA key + */ +static gpg_error_t +gen_dsa (unsigned int nbits, KBNODE pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr) +{ + int err; + unsigned int qbits; + char *keyparms; + char nbitsstr[35]; + char qbitsstr[35]; + + if (nbits < 768) + { + nbits = 2048; + log_info(_("keysize invalid; using %u bits\n"), nbits ); + } + else if ( nbits > 3072 ) + { + nbits = 3072; + log_info(_("keysize invalid; using %u bits\n"), nbits ); + } + + if( (nbits % 64) ) + { + nbits = ((nbits + 63) / 64) * 64; + log_info(_("keysize rounded up to %u bits\n"), nbits ); + } + + /* To comply with FIPS rules we round up to the next value unless in + expert mode. */ + if (!opt.expert && nbits > 1024 && (nbits % 1024)) + { + nbits = ((nbits + 1023) / 1024) * 1024; + log_info(_("keysize rounded up to %u bits\n"), nbits ); + } + + /* + Figure out a q size based on the key size. FIPS 180-3 says: + + L = 1024, N = 160 + L = 2048, N = 224 + L = 2048, N = 256 + L = 3072, N = 256 + + 2048/256 is an odd pair since there is also a 2048/224 and + 3072/256. Matching sizes is not a very exact science. + + We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 + but less than 2048, and 160 for 1024 (DSA1). + */ + + if (nbits > 2047) + qbits = 256; + else if ( nbits > 1024) + qbits = 224; + else + qbits = 160; + + if (qbits != 160 ) + log_info (_("WARNING: some OpenPGP programs can't" + " handle a DSA key with this digest size\n")); + + snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); + snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); + keyparms = xtryasprintf ("(genkey(dsa(nbits %zu:%s)(qbits %zu:%s)%s))", + strlen (nbitsstr), nbitsstr, + strlen (qbitsstr), qbitsstr, + ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + "(transient-key)" : "" ); + if (!keyparms) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", + pub_root, timestamp, expireval, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + xfree (keyparms); + } + + return err; +} + + + +/* + * Generate an ECC key + */ +static gpg_error_t +gen_ecc (int algo, const char *curve, kbnode_t pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr) +{ + gpg_error_t err; + char *keyparms; + + log_assert (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH); + + if (!curve || !*curve) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + + /* Map the displayed short forms of some curves to their canonical + * names. */ + if (!ascii_strcasecmp (curve, "cv25519")) + curve = "Curve25519"; + else if (!ascii_strcasecmp (curve, "ed25519")) + curve = "Ed25519"; + + /* Note that we use the "comp" flag with EdDSA to request the use of + a 0x40 compression prefix octet. */ + if (algo == PUBKEY_ALGO_EDDSA) + keyparms = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))", + strlen (curve), curve, + (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + else if (algo == PUBKEY_ALGO_ECDH && !strcmp (curve, "Curve25519")) + keyparms = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", + strlen (curve), curve, + (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + else + keyparms = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", + strlen (curve), curve, + (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + + if (!keyparms) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms, algo, "", + pub_root, timestamp, expireval, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + xfree (keyparms); + } + + return err; +} + + +/* + * Generate an RSA key. + */ +static int +gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr) +{ + int err; + char *keyparms; + char nbitsstr[35]; + const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096); + + log_assert (is_RSA(algo)); + + if (!nbits) + nbits = get_keysize_range (algo, NULL, NULL); + + if (nbits < 1024) + { + nbits = 3072; + log_info (_("keysize invalid; using %u bits\n"), nbits ); + } + else if (nbits > maxsize) + { + nbits = maxsize; + log_info (_("keysize invalid; using %u bits\n"), nbits ); + } + + if ((nbits % 32)) + { + nbits = ((nbits + 31) / 32) * 32; + log_info (_("keysize rounded up to %u bits\n"), nbits ); + } + + snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); + keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", + strlen (nbitsstr), nbitsstr, + ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + "(transient-key)" : "" ); + if (!keyparms) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms, algo, "ne", + pub_root, timestamp, expireval, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + xfree (keyparms); + } + + return err; +} + + +/**************** + * check valid days: + * return 0 on error or the multiplier + */ +static int +check_valid_days( const char *s ) +{ + if( !digitp(s) ) + return 0; + for( s++; *s; s++) + if( !digitp(s) ) + break; + if( !*s ) + return 1; + if( s[1] ) + return 0; /* e.g. "2323wc" */ + if( *s == 'd' || *s == 'D' ) + return 1; + if( *s == 'w' || *s == 'W' ) + return 7; + if( *s == 'm' || *s == 'M' ) + return 30; + if( *s == 'y' || *s == 'Y' ) + return 365; + return 0; +} + + +static void +print_key_flags(int flags) +{ + if(flags&PUBKEY_USAGE_SIG) + tty_printf("%s ",_("Sign")); + + if(flags&PUBKEY_USAGE_CERT) + tty_printf("%s ",_("Certify")); + + if(flags&PUBKEY_USAGE_ENC) + tty_printf("%s ",_("Encrypt")); + + if(flags&PUBKEY_USAGE_AUTH) + tty_printf("%s ",_("Authenticate")); +} + + +/* Ask for the key flags and return them. CURRENT gives the current + * usage which should normally be given as 0. MASK gives the allowed + * flags. */ +unsigned int +ask_key_flags_with_mask (int algo, int subkey, unsigned int current, + unsigned int mask) +{ + /* TRANSLATORS: Please use only plain ASCII characters for the + * translation. If this is not possible use single digits. The + * string needs to 8 bytes long. Here is a description of the + * functions: + * + * s = Toggle signing capability + * e = Toggle encryption capability + * a = Toggle authentication capability + * q = Finish + */ + const char *togglers = _("SsEeAaQq"); + char *answer = NULL; + const char *s; + unsigned int possible; + + if ( strlen(togglers) != 8 ) + { + tty_printf ("NOTE: Bad translation at %s:%d. " + "Please report.\n", __FILE__, __LINE__); + togglers = "11223300"; + } + + /* Mask the possible usage flags. This is for example used for a + * card based key. */ + possible = (openpgp_pk_algo_usage (algo) & mask); + + /* However, only primary keys may certify. */ + if (subkey) + possible &= ~PUBKEY_USAGE_CERT; + + /* Preload the current set with the possible set, without + * authentication if CURRENT is 0. If CURRENT is non-zero we mask + * with all possible usages. */ + if (current) + current &= possible; + else + current = (possible&~PUBKEY_USAGE_AUTH); + + for (;;) + { + tty_printf("\n"); + tty_printf(_("Possible actions for a %s key: "), + (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA) + ? "ECDSA/EdDSA" : openpgp_pk_algo_name (algo)); + print_key_flags(possible); + tty_printf("\n"); + tty_printf(_("Current allowed actions: ")); + print_key_flags(current); + tty_printf("\n\n"); + + if(possible&PUBKEY_USAGE_SIG) + tty_printf(_(" (%c) Toggle the sign capability\n"), + togglers[0]); + if(possible&PUBKEY_USAGE_ENC) + tty_printf(_(" (%c) Toggle the encrypt capability\n"), + togglers[2]); + if(possible&PUBKEY_USAGE_AUTH) + tty_printf(_(" (%c) Toggle the authenticate capability\n"), + togglers[4]); + + tty_printf(_(" (%c) Finished\n"),togglers[6]); + tty_printf("\n"); + + xfree(answer); + answer = cpr_get("keygen.flags",_("Your selection? ")); + cpr_kill_prompt(); + + if (*answer == '=') + { + /* Hack to allow direct entry of the capabilities. */ + current = 0; + for (s=answer+1; *s; s++) + { + if ((*s == 's' || *s == 'S') && (possible&PUBKEY_USAGE_SIG)) + current |= PUBKEY_USAGE_SIG; + else if ((*s == 'e' || *s == 'E') && (possible&PUBKEY_USAGE_ENC)) + current |= PUBKEY_USAGE_ENC; + else if ((*s == 'a' || *s == 'A') && (possible&PUBKEY_USAGE_AUTH)) + current |= PUBKEY_USAGE_AUTH; + else if (!subkey && *s == 'c') + { + /* Accept 'c' for the primary key because USAGE_CERT + will be set anyway. This is for folks who + want to experiment with a cert-only primary key. */ + current |= PUBKEY_USAGE_CERT; + } + } + break; + } + else if (strlen(answer)>1) + tty_printf(_("Invalid selection.\n")); + else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) + break; + else if((*answer==togglers[0] || *answer==togglers[1]) + && possible&PUBKEY_USAGE_SIG) + { + if(current&PUBKEY_USAGE_SIG) + current&=~PUBKEY_USAGE_SIG; + else + current|=PUBKEY_USAGE_SIG; + } + else if((*answer==togglers[2] || *answer==togglers[3]) + && possible&PUBKEY_USAGE_ENC) + { + if(current&PUBKEY_USAGE_ENC) + current&=~PUBKEY_USAGE_ENC; + else + current|=PUBKEY_USAGE_ENC; + } + else if((*answer==togglers[4] || *answer==togglers[5]) + && possible&PUBKEY_USAGE_AUTH) + { + if(current&PUBKEY_USAGE_AUTH) + current&=~PUBKEY_USAGE_AUTH; + else + current|=PUBKEY_USAGE_AUTH; + } + else + tty_printf(_("Invalid selection.\n")); + } + + xfree(answer); + + return current; +} + + +unsigned int +ask_key_flags (int algo, int subkey, unsigned int current) +{ + return ask_key_flags_with_mask (algo, subkey, current, ~0); +} + + +/* Check whether we have a key for the key with HEXGRIP. Returns 0 if + there is no such key or the OpenPGP algo number for the key. */ +static int +check_keygrip (ctrl_t ctrl, const char *hexgrip) +{ + gpg_error_t err; + unsigned char *public; + size_t publiclen; + int algo; + + if (hexgrip[0] == '&') + hexgrip++; + + err = agent_readkey (ctrl, 0, hexgrip, &public); + if (err) + return 0; + publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); + + algo = get_pk_algo_from_canon_sexp (public, publiclen); + xfree (public); + + return map_pk_gcry_to_openpgp (algo); +} + + + +/* Ask for an algorithm. The function returns the algorithm id to + * create. If ADDMODE is false the function won't show an option to + * create the primary and subkey combined and won't set R_USAGE + * either. If a combined algorithm has been selected, the subkey + * algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the + * user has the choice to enter the keygrip of an existing key. That + * keygrip is then stored at this address. The caller needs to free + * it. */ +static int +ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, + char **r_keygrip) +{ + gpg_error_t err; + char *keygrip = NULL; + char *answer = NULL; + int algo; + int dummy_algo; + char *p; + + if (!r_subkey_algo) + r_subkey_algo = &dummy_algo; + + tty_printf (_("Please select what kind of key you want:\n")); + +#if GPG_USE_RSA + if (!addmode) + tty_printf (_(" (%d) RSA and RSA (default)\n"), 1 ); +#endif + + if (!addmode && opt.compliance != CO_DE_VS) + tty_printf (_(" (%d) DSA and Elgamal\n"), 2 ); + + if (opt.compliance != CO_DE_VS) + tty_printf (_(" (%d) DSA (sign only)\n"), 3 ); +#if GPG_USE_RSA + tty_printf (_(" (%d) RSA (sign only)\n"), 4 ); +#endif + + if (addmode) + { + if (opt.compliance != CO_DE_VS) + tty_printf (_(" (%d) Elgamal (encrypt only)\n"), 5 ); +#if GPG_USE_RSA + tty_printf (_(" (%d) RSA (encrypt only)\n"), 6 ); +#endif + } + if (opt.expert) + { + if (opt.compliance != CO_DE_VS) + tty_printf (_(" (%d) DSA (set your own capabilities)\n"), 7 ); +#if GPG_USE_RSA + tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); +#endif + } + +#if GPG_USE_ECDSA || GPG_USE_ECDH || GPG_USE_EDDSA + if (opt.expert && !addmode) + tty_printf (_(" (%d) ECC and ECC\n"), 9 ); + if (opt.expert) + tty_printf (_(" (%d) ECC (sign only)\n"), 10 ); + if (opt.expert) + tty_printf (_(" (%d) ECC (set your own capabilities)\n"), 11 ); + if (opt.expert && addmode) + tty_printf (_(" (%d) ECC (encrypt only)\n"), 12 ); +#endif + + if (opt.expert && r_keygrip) + tty_printf (_(" (%d) Existing key\n"), 13 ); + if (r_keygrip) + tty_printf (_(" (%d) Existing key from card\n"), 14 ); + + for (;;) + { + *r_usage = 0; + *r_subkey_algo = 0; + xfree (answer); + answer = cpr_get ("keygen.algo", _("Your selection? ")); + cpr_kill_prompt (); + algo = *answer? atoi (answer) : 1; + + if (opt.compliance == CO_DE_VS + && (algo == 2 || algo == 3 || algo == 5 || algo == 7)) + { + tty_printf (_("Invalid selection.\n")); + } + else if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode) + { + algo = PUBKEY_ALGO_RSA; + *r_subkey_algo = PUBKEY_ALGO_RSA; + break; + } + else if ((algo == 2 || !strcmp (answer, "dsa+elg")) && !addmode) + { + algo = PUBKEY_ALGO_DSA; + *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E; + break; + } + else if (algo == 3 || !strcmp (answer, "dsa")) + { + algo = PUBKEY_ALGO_DSA; + *r_usage = PUBKEY_USAGE_SIG; + break; + } + else if (algo == 4 || !strcmp (answer, "rsa/s")) + { + algo = PUBKEY_ALGO_RSA; + *r_usage = PUBKEY_USAGE_SIG; + break; + } + else if ((algo == 5 || !strcmp (answer, "elg")) && addmode) + { + algo = PUBKEY_ALGO_ELGAMAL_E; + *r_usage = PUBKEY_USAGE_ENC; + break; + } + else if ((algo == 6 || !strcmp (answer, "rsa/e")) && addmode) + { + algo = PUBKEY_ALGO_RSA; + *r_usage = PUBKEY_USAGE_ENC; + break; + } + else if ((algo == 7 || !strcmp (answer, "dsa/*")) && opt.expert) + { + algo = PUBKEY_ALGO_DSA; + *r_usage = ask_key_flags (algo, addmode, 0); + break; + } + else if ((algo == 8 || !strcmp (answer, "rsa/*")) && opt.expert) + { + algo = PUBKEY_ALGO_RSA; + *r_usage = ask_key_flags (algo, addmode, 0); + break; + } + else if ((algo == 9 || !strcmp (answer, "ecc+ecc")) + && opt.expert && !addmode) + { + algo = PUBKEY_ALGO_ECDSA; + *r_subkey_algo = PUBKEY_ALGO_ECDH; + break; + } + else if ((algo == 10 || !strcmp (answer, "ecc/s")) && opt.expert) + { + algo = PUBKEY_ALGO_ECDSA; + *r_usage = PUBKEY_USAGE_SIG; + break; + } + else if ((algo == 11 || !strcmp (answer, "ecc/*")) && opt.expert) + { + algo = PUBKEY_ALGO_ECDSA; + *r_usage = ask_key_flags (algo, addmode, 0); + break; + } + else if ((algo == 12 || !strcmp (answer, "ecc/e")) + && opt.expert && addmode) + { + algo = PUBKEY_ALGO_ECDH; + *r_usage = PUBKEY_USAGE_ENC; + break; + } + else if ((algo == 13 || !strcmp (answer, "keygrip")) + && opt.expert && r_keygrip) + { + for (;;) + { + xfree (answer); + answer = cpr_get ("keygen.keygrip", _("Enter the keygrip: ")); + cpr_kill_prompt (); + trim_spaces (answer); + if (!*answer) + { + xfree (answer); + answer = NULL; + continue; + } + + if (strlen (answer) != 40 && + !(answer[0] == '&' && strlen (answer+1) == 40)) + tty_printf + (_("Not a valid keygrip (expecting 40 hex digits)\n")); + else if (!(algo = check_keygrip (ctrl, answer)) ) + tty_printf (_("No key with this keygrip\n")); + else + break; /* Okay. */ + } + xfree (keygrip); + keygrip = answer; + answer = NULL; + *r_usage = ask_key_flags (algo, addmode, 0); + break; + } + else if ((algo == 14 || !strcmp (answer, "cardkey")) && r_keygrip) + { + char *serialno; + strlist_t keypairlist, sl; + int count, selection; + + err = agent_scd_serialno (&serialno, NULL); + if (err) + { + tty_printf (_("error reading the card: %s\n"), + gpg_strerror (err)); + goto ask_again; + } + tty_printf (_("Serial number of the card: %s\n"), serialno); + xfree (serialno); + + err = agent_scd_keypairinfo (ctrl, &keypairlist); + if (err) + { + tty_printf (_("error reading the card: %s\n"), + gpg_strerror (err)); + goto ask_again; + } + + do + { + tty_printf (_("Available keys:\n")); + for (count=1,sl=keypairlist; sl; sl = sl->next, count++) + { + gcry_sexp_t s_pkey; + char *algostr = NULL; + enum gcry_pk_algos algoid = 0; + const char *keyref; + int any = 0; + + keyref = strchr (sl->d, ' '); + if (keyref) + { + keyref++; + if (!agent_scd_readkey (keyref, &s_pkey)) + { + algostr = pubkey_algo_string (s_pkey, &algoid); + gcry_sexp_release (s_pkey); + } + } + /* We use the flags also encode the algo for use + * below. We need to tweak the algo in case + * GCRY_PK_ECC is returned becuase pubkey_algo_string + * is not aware of the OpenPGP algo mapping. + * FIXME: This is an ugly hack. */ + sl->flags &= 0xff; + if (algoid == GCRY_PK_ECC + && algostr && !strncmp (algostr, "nistp", 5) + && !(sl->flags & GCRY_PK_USAGE_ENCR)) + sl->flags |= (PUBKEY_ALGO_ECDSA << 8); + else if (algoid == GCRY_PK_ECC + && algostr && !strncmp (algostr, "brainpool", 9) + && !(sl->flags & GCRY_PK_USAGE_ENCR)) + sl->flags |= (PUBKEY_ALGO_ECDSA << 8); + else if (algoid == GCRY_PK_ECC + && algostr && !strcmp (algostr, "ed25519") + && !(sl->flags & GCRY_PK_USAGE_ENCR)) + sl->flags = (PUBKEY_ALGO_EDDSA << 8); + else + sl->flags |= (map_pk_gcry_to_openpgp (algoid) << 8); + + tty_printf (" (%d) %s %s", count, sl->d, algostr); + if ((sl->flags & GCRY_PK_USAGE_CERT)) + { + tty_printf ("%scert", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_SIGN)) + { + tty_printf ("%ssign", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_AUTH)) + { + tty_printf ("%sauth", any?",":" ("); + any = 1; + } + if ((sl->flags & GCRY_PK_USAGE_ENCR)) + { + tty_printf ("%sencr", any?",":" ("); + any = 1; + } + tty_printf ("%s\n", any?")":""); + xfree (algostr); + } + + xfree (answer); + answer = cpr_get ("keygen.cardkey", _("Your selection? ")); + cpr_kill_prompt (); + trim_spaces (answer); + selection = atoi (answer); + } + while (!(selection > 0 && selection < count)); + + for (count=1,sl=keypairlist; sl; sl = sl->next, count++) + if (count == selection) + break; + if (!sl) + { + /* Just in case COUNT is zero (no keys). */ + free_strlist (keypairlist); + goto ask_again; + } + + xfree (keygrip); + keygrip = xstrdup (sl->d); + if ((p = strchr (keygrip, ' '))) + *p = 0; + algo = (sl->flags >>8); + if (opt.expert) + *r_usage = ask_key_flags_with_mask (algo, addmode, + (sl->flags & 0xff), + (sl->flags & 0xff)); + else + { + *r_usage = (sl->flags & 0xff); + if (addmode) + *r_usage &= ~GCRY_PK_USAGE_CERT; + } + free_strlist (keypairlist); + break; + } + else + tty_printf (_("Invalid selection.\n")); + + ask_again: + ; + } + + xfree(answer); + if (r_keygrip) + *r_keygrip = keygrip; + return algo; +} + + +static unsigned int +get_keysize_range (int algo, unsigned int *min, unsigned int *max) +{ + unsigned int def; + unsigned int dummy1, dummy2; + + if (!min) + min = &dummy1; + if (!max) + max = &dummy2; + + switch(algo) + { + case PUBKEY_ALGO_DSA: + *min = opt.expert? 768 : 1024; + *max=3072; + def=2048; + break; + + case PUBKEY_ALGO_ECDSA: + case PUBKEY_ALGO_ECDH: + *min=256; + *max=521; + def=256; + break; + + case PUBKEY_ALGO_EDDSA: + *min=255; + *max=441; + def=255; + break; + + default: + *min = opt.compliance == CO_DE_VS ? 2048: 1024; + *max = 4096; + def = 3072; + break; + } + + return def; +} + + +/* 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. */ +static unsigned +ask_keysize (int algo, unsigned int primary_keysize) +{ + unsigned int nbits; + unsigned int min, def, max; + int for_subkey = !!primary_keysize; + int autocomp = 0; + + def = get_keysize_range (algo, &min, &max); + + if (primary_keysize && !opt.expert) + { + /* Deduce the subkey size from the primary key size. */ + if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072) + nbits = 3072; /* For performance reasons we don't support more + than 3072 bit DSA. However we won't see this + case anyway because DSA can't be used as an + encryption subkey ;-). */ + else + nbits = primary_keysize; + autocomp = 1; + goto leave; + } + + tty_printf(_("%s keys may be between %u and %u bits long.\n"), + openpgp_pk_algo_name (algo), min, max); + + for (;;) + { + char *prompt, *answer; + + if (for_subkey) + prompt = xasprintf (_("What keysize do you want " + "for the subkey? (%u) "), def); + else + prompt = xasprintf (_("What keysize do you want? (%u) "), def); + answer = cpr_get ("keygen.size", prompt); + cpr_kill_prompt (); + nbits = *answer? atoi (answer): def; + xfree(prompt); + xfree(answer); + + if(nbitsmax) + tty_printf(_("%s keysizes must be in the range %u-%u\n"), + openpgp_pk_algo_name (algo), min, max); + else + break; + } + + tty_printf (_("Requested keysize is %u bits\n"), nbits); + + leave: + nbits = fixup_keysize (nbits, algo, autocomp); + return nbits; +} + + +/* Ask for the curve. ALGO is the selected algorithm which this + function may adjust. Returns a const string of the name of the + curve. */ +const char * +ask_curve (int *algo, int *subkey_algo, const char *current) +{ + /* NB: We always use a complete algo list so that we have stable + numbers in the menu regardless on how Gpg was configured. */ + struct { + const char *name; + const char* eddsa_curve; /* Corresponding EdDSA curve. */ + const char *pretty_name; + unsigned int supported : 1; /* Supported by gpg. */ + unsigned int de_vs : 1; /* Allowed in CO_DE_VS. */ + unsigned int expert_only : 1; /* Only with --expert */ + unsigned int available : 1; /* Available in Libycrypt (runtime checked) */ + } curves[] = { +#if GPG_USE_ECDSA || GPG_USE_ECDH +# define MY_USE_ECDSADH 1 +#else +# define MY_USE_ECDSADH 0 +#endif + { "Curve25519", "Ed25519", "Curve 25519", !!GPG_USE_EDDSA, 0, 0, 0 }, + { "Curve448", "Ed448", "Curve 448", 0/*reserved*/ , 0, 1, 0 }, + { "NIST P-256", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, + { "NIST P-384", NULL, NULL, MY_USE_ECDSADH, 0, 0, 0 }, + { "NIST P-521", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, + { "brainpoolP256r1", NULL, "Brainpool P-256", MY_USE_ECDSADH, 1, 1, 0 }, + { "brainpoolP384r1", NULL, "Brainpool P-384", MY_USE_ECDSADH, 1, 1, 0 }, + { "brainpoolP512r1", NULL, "Brainpool P-512", MY_USE_ECDSADH, 1, 1, 0 }, + { "secp256k1", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, + }; +#undef MY_USE_ECDSADH + int idx; + char *answer; + const char *result = NULL; + gcry_sexp_t keyparms; + + tty_printf (_("Please select which elliptic curve you want:\n")); + + keyparms = NULL; + for (idx=0; idx < DIM(curves); idx++) + { + int rc; + + curves[idx].available = 0; + if (!curves[idx].supported) + continue; + + if (opt.compliance==CO_DE_VS) + { + if (!curves[idx].de_vs) + continue; /* Not allowed. */ + } + else if (!opt.expert && curves[idx].expert_only) + continue; + + /* We need to switch from the ECDH name of the curve to the + EDDSA name of the curve if we want a signing key. */ + gcry_sexp_release (keyparms); + rc = gcry_sexp_build (&keyparms, NULL, + "(public-key(ecc(curve %s)))", + curves[idx].eddsa_curve? curves[idx].eddsa_curve + /**/ : curves[idx].name); + if (rc) + continue; + if (!gcry_pk_get_curve (keyparms, 0, NULL)) + continue; + if (subkey_algo && curves[idx].eddsa_curve) + { + /* Both Curve 25519 (or 448) keys are to be created. Check that + Libgcrypt also supports the real Curve25519 (or 448). */ + gcry_sexp_release (keyparms); + rc = gcry_sexp_build (&keyparms, NULL, + "(public-key(ecc(curve %s)))", + curves[idx].name); + if (rc) + continue; + if (!gcry_pk_get_curve (keyparms, 0, NULL)) + continue; + } + + curves[idx].available = 1; + tty_printf (" (%d) %s\n", idx + 1, + curves[idx].pretty_name? + curves[idx].pretty_name:curves[idx].name); + } + gcry_sexp_release (keyparms); + + + for (;;) + { + answer = cpr_get ("keygen.curve", _("Your selection? ")); + cpr_kill_prompt (); + idx = *answer? atoi (answer) : 1; + if (!*answer && current) + { + xfree(answer); + return NULL; + } + else if (*answer && !idx) + { + /* See whether the user entered the name of the curve. */ + for (idx=0; idx < DIM(curves); idx++) + { + if (!opt.expert && curves[idx].expert_only) + continue; + if (!stricmp (curves[idx].name, answer) + || (curves[idx].pretty_name + && !stricmp (curves[idx].pretty_name, answer))) + break; + } + if (idx == DIM(curves)) + idx = -1; + } + else + idx--; + xfree(answer); + answer = NULL; + if (idx < 0 || idx >= DIM (curves) || !curves[idx].available) + tty_printf (_("Invalid selection.\n")); + else + { + /* If the user selected a signing algorithm and Curve25519 + we need to set the algo to EdDSA and update the curve name. + If switching away from EdDSA, we need to set the algo back + to ECDSA. */ + if (*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) + { + if (curves[idx].eddsa_curve) + { + if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA) + *subkey_algo = PUBKEY_ALGO_EDDSA; + *algo = PUBKEY_ALGO_EDDSA; + result = curves[idx].eddsa_curve; + } + else + { + if (subkey_algo && *subkey_algo == PUBKEY_ALGO_EDDSA) + *subkey_algo = PUBKEY_ALGO_ECDSA; + *algo = PUBKEY_ALGO_ECDSA; + result = curves[idx].name; + } + } + else + result = curves[idx].name; + break; + } + } + + if (!result) + result = curves[0].name; + + return result; +} + + +/**************** + * Parse an expire string and return its value in seconds. + * Returns (u32)-1 on error. + * This isn't perfect since scan_isodatestr returns unix time, and + * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. + * Because of this, we only permit setting expirations up to 2106, but + * OpenPGP could theoretically allow up to 2242. I think we'll all + * just cope for the next few years until we get a 64-bit time_t or + * similar. + */ +u32 +parse_expire_string( const char *string ) +{ + int mult; + u32 seconds; + u32 abs_date = 0; + u32 curtime = make_timestamp (); + time_t tt; + + if (!string || !*string || !strcmp (string, "none") + || !strcmp (string, "never") || !strcmp (string, "-")) + seconds = 0; + else if (!strncmp (string, "seconds=", 8)) + seconds = atoi (string+8); + else if ((abs_date = scan_isodatestr(string)) + && (abs_date+86400/2) > curtime) + seconds = (abs_date+86400/2) - curtime; + else if ((tt = isotime2epoch (string)) != (time_t)(-1)) + seconds = (u32)tt - curtime; + else if ((mult = check_valid_days (string))) + seconds = atoi (string) * 86400L * mult; + else + seconds = (u32)(-1); + + return seconds; +} + +/* Parse a Creation-Date string which is either "1986-04-26" or + "19860426T042640". Returns 0 on error. */ +static u32 +parse_creation_string (const char *string) +{ + u32 seconds; + + if (!*string) + seconds = 0; + else if ( !strncmp (string, "seconds=", 8) ) + seconds = atoi (string+8); + else if ( !(seconds = scan_isodatestr (string))) + { + time_t tmp = isotime2epoch (string); + seconds = (tmp == (time_t)(-1))? 0 : tmp; + } + return seconds; +} + + +/* object == 0 for a key, and 1 for a sig */ +u32 +ask_expire_interval(int object,const char *def_expire) +{ + u32 interval; + char *answer; + + switch(object) + { + case 0: + if(def_expire) + BUG(); + tty_printf(_("Please specify how long the key should be valid.\n" + " 0 = key does not expire\n" + " = key expires in n days\n" + " w = key expires in n weeks\n" + " m = key expires in n months\n" + " y = key expires in n years\n")); + break; + + case 1: + if(!def_expire) + BUG(); + tty_printf(_("Please specify how long the signature should be valid.\n" + " 0 = signature does not expire\n" + " = signature expires in n days\n" + " w = signature expires in n weeks\n" + " m = signature expires in n months\n" + " y = signature expires in n years\n")); + break; + + default: + BUG(); + } + + /* Note: The elgamal subkey for DSA has no expiration date because + * it must be signed with the DSA key and this one has the expiration + * date */ + + answer = NULL; + for(;;) + { + u32 curtime; + + xfree(answer); + if(object==0) + answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); + else + { + char *prompt; + + prompt = xasprintf (_("Signature is valid for? (%s) "), def_expire); + answer = cpr_get("siggen.valid",prompt); + xfree(prompt); + + if(*answer=='\0') + answer=xstrdup(def_expire); + } + cpr_kill_prompt(); + trim_spaces(answer); + curtime = make_timestamp (); + interval = parse_expire_string( answer ); + if( interval == (u32)-1 ) + { + tty_printf(_("invalid value\n")); + continue; + } + + if( !interval ) + { + tty_printf((object==0) + ? _("Key does not expire at all\n") + : _("Signature does not expire at all\n")); + } + else + { + tty_printf(object==0 + ? _("Key expires at %s\n") + : _("Signature expires at %s\n"), + asctimestamp((ulong)(curtime + interval) ) ); +#if SIZEOF_TIME_T <= 4 && !defined (HAVE_UNSIGNED_TIME_T) + if ( (time_t)((ulong)(curtime+interval)) < 0 ) + tty_printf (_("Your system can't display dates beyond 2038.\n" + "However, it will be correctly handled up to" + " 2106.\n")); + else +#endif /*SIZEOF_TIME_T*/ + if ( (time_t)((unsigned long)(curtime+interval)) < curtime ) + { + tty_printf (_("invalid value\n")); + continue; + } + } + + if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", + _("Is this correct? (y/N) ")) ) + break; + } + + xfree(answer); + return interval; +} + +u32 +ask_expiredate() +{ + u32 x = ask_expire_interval(0,NULL); + return x? make_timestamp() + x : 0; +} + + + +static PKT_user_id * +uid_from_string (const char *string) +{ + size_t n; + PKT_user_id *uid; + + n = strlen (string); + uid = xmalloc_clear (sizeof *uid + n); + uid->len = n; + strcpy (uid->name, string); + uid->ref = 1; + return uid; +} + + +/* Return true if the user id UID already exists in the keyblock. */ +static int +uid_already_in_keyblock (kbnode_t keyblock, const char *uid) +{ + PKT_user_id *uidpkt = uid_from_string (uid); + kbnode_t node; + int result = 0; + + for (node=keyblock; node && !result; node=node->next) + if (!is_deleted_kbnode (node) + && node->pkt->pkttype == PKT_USER_ID + && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id)) + result = 1; + free_user_id (uidpkt); + return result; +} + + +/* Ask for a user ID. With a MODE of 1 an extra help prompt is + printed for use during a new key creation. If KEYBLOCK is not NULL + the function prevents the creation of an already existing user + ID. IF FULL is not set some prompts are not shown. */ +static char * +ask_user_id (int mode, int full, KBNODE keyblock) +{ + char *answer; + char *aname, *acomment, *amail, *uid; + + if ( !mode ) + { + /* TRANSLATORS: This is the new string telling the user what + gpg is now going to do (i.e. ask for the parts of the user + ID). Note that if you do not translate this string, a + different string will be used, which might still have + a correct translation. */ + const char *s1 = + N_("\n" + "GnuPG needs to construct a user ID to identify your key.\n" + "\n"); + const char *s2 = _(s1); + + if (!strcmp (s1, s2)) + { + /* There is no translation for the string thus we to use + the old info text. gettext has no way to tell whether + a translation is actually available, thus we need to + to compare again. */ + /* TRANSLATORS: This string is in general not anymore used + but you should keep your existing translation. In case + the new string is not translated this old string will + be used. */ + const char *s3 = N_("\n" +"You need a user ID to identify your key; " + "the software constructs the user ID\n" +"from the Real Name, Comment and Email Address in this form:\n" +" \"Heinrich Heine (Der Dichter) \"\n\n"); + const char *s4 = _(s3); + if (strcmp (s3, s4)) + s2 = s3; /* A translation exists - use it. */ + } + tty_printf ("%s", s2) ; + } + uid = aname = acomment = amail = NULL; + for(;;) { + char *p; + int fail=0; + + if( !aname ) { + for(;;) { + xfree(aname); + aname = cpr_get("keygen.name",_("Real name: ")); + trim_spaces(aname); + cpr_kill_prompt(); + + if( opt.allow_freeform_uid ) + break; + + if( strpbrk( aname, "<>" ) ) + { + tty_printf(_("Invalid character in name\n")); + tty_printf(_("The characters '%s' and '%s' may not " + "appear in name\n"), "<", ">"); + } + else if( digitp(aname) ) + tty_printf(_("Name may not start with a digit\n")); + else if (*aname && strlen (aname) < 5) + { + tty_printf(_("Name must be at least 5 characters long\n")); + /* However, we allow an empty name. */ + } + else + break; + } + } + if( !amail ) { + for(;;) { + xfree(amail); + amail = cpr_get("keygen.email",_("Email address: ")); + trim_spaces(amail); + cpr_kill_prompt(); + if( !*amail || opt.allow_freeform_uid ) + break; /* no email address is okay */ + else if ( !is_valid_mailbox (amail) ) + tty_printf(_("Not a valid email address\n")); + else + break; + } + } + if (!acomment) { + if (full) { + for(;;) { + xfree(acomment); + acomment = cpr_get("keygen.comment",_("Comment: ")); + trim_spaces(acomment); + cpr_kill_prompt(); + if( !*acomment ) + break; /* no comment is okay */ + else if( strpbrk( acomment, "()" ) ) + tty_printf(_("Invalid character in comment\n")); + else + break; + } + } + else { + xfree (acomment); + acomment = xstrdup (""); + } + } + + + xfree(uid); + uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); + if (!*aname && *amail && !*acomment && !random_is_faked ()) + { /* Empty name and comment but with mail address. Use + simplified form with only the non-angle-bracketed mail + address. */ + p = stpcpy (p, amail); + } + else + { + p = stpcpy (p, aname ); + if (*acomment) + p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); + if (*amail) + p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); + } + + /* Append a warning if the RNG is switched into fake mode. */ + if ( random_is_faked () ) + strcpy(p, " (insecure!)" ); + + /* print a note in case that UTF8 mapping has to be done */ + for(p=uid; *p; p++ ) { + if( *p & 0x80 ) { + tty_printf(_("You are using the '%s' character set.\n"), + get_native_charset() ); + break; + } + } + + tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); + + if( !*amail && !opt.allow_freeform_uid + && (strchr( aname, '@' ) || strchr( acomment, '@'))) { + fail = 1; + tty_printf(_("Please don't put the email address " + "into the real name or the comment\n") ); + } + + if (!fail && keyblock) + { + if (uid_already_in_keyblock (keyblock, uid)) + { + tty_printf (_("Such a user ID already exists on this key!\n")); + fail = 1; + } + } + + for(;;) { + /* TRANSLATORS: These are the allowed answers in + lower and uppercase. Below you will find the matching + string which should be translated accordingly and the + letter changed to match the one in the answer string. + + n = Change name + c = Change comment + e = Change email + o = Okay (ready, continue) + q = Quit + */ + const char *ansstr = _("NnCcEeOoQq"); + + if( strlen(ansstr) != 10 ) + BUG(); + if( cpr_enabled() ) { + answer = xstrdup (ansstr + (fail?8:6)); + answer[1] = 0; + } + else if (full) { + answer = cpr_get("keygen.userid.cmd", fail? + _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : + _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); + cpr_kill_prompt(); + } + else { + answer = cpr_get("keygen.userid.cmd", fail? + _("Change (N)ame, (E)mail, or (Q)uit? ") : + _("Change (N)ame, (E)mail, or (O)kay/(Q)uit? ")); + cpr_kill_prompt(); + } + if( strlen(answer) > 1 ) + ; + else if( *answer == ansstr[0] || *answer == ansstr[1] ) { + xfree(aname); aname = NULL; + break; + } + else if( *answer == ansstr[2] || *answer == ansstr[3] ) { + xfree(acomment); acomment = NULL; + break; + } + else if( *answer == ansstr[4] || *answer == ansstr[5] ) { + xfree(amail); amail = NULL; + break; + } + else if( *answer == ansstr[6] || *answer == ansstr[7] ) { + if( fail ) { + tty_printf(_("Please correct the error first\n")); + } + else { + xfree(aname); aname = NULL; + xfree(acomment); acomment = NULL; + xfree(amail); amail = NULL; + break; + } + } + else if( *answer == ansstr[8] || *answer == ansstr[9] ) { + xfree(aname); aname = NULL; + xfree(acomment); acomment = NULL; + xfree(amail); amail = NULL; + xfree(uid); uid = NULL; + break; + } + xfree(answer); + } + xfree(answer); + if (!amail && !acomment) + break; + xfree(uid); uid = NULL; + } + if( uid ) { + char *p = native_to_utf8( uid ); + xfree( uid ); + uid = p; + } + return uid; +} + + +/* Basic key generation. Here we divert to the actual generation + routines based on the requested algorithm. */ +static int +do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root, + u32 timestamp, u32 expiredate, int is_subkey, + int keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr) +{ + gpg_error_t err; + + /* Fixme: The entropy collecting message should be moved to a + libgcrypt progress handler. */ + if (!opt.batch) + tty_printf (_( +"We need to generate a lot of random bytes. It is a good idea to perform\n" +"some other action (type on the keyboard, move the mouse, utilize the\n" +"disks) during the prime generation; this gives the random number\n" +"generator a better chance to gain enough entropy.\n") ); + + if (algo == PUBKEY_ALGO_ELGAMAL_E) + err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + else if (algo == PUBKEY_ALGO_DSA) + err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + else if (algo == PUBKEY_ALGO_RSA) + err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr); + else + BUG(); + + return err; +} + + +/* Generate a new user id packet or return NULL if canceled. If + KEYBLOCK is not NULL the function prevents the creation of an + already existing user ID. If UIDSTR is not NULL the user is not + asked but UIDSTR is used to create the user id packet; if the user + id already exists NULL is returned. UIDSTR is expected to be utf-8 + encoded and should have already been checked for a valid length + etc. */ +PKT_user_id * +generate_user_id (KBNODE keyblock, const char *uidstr) +{ + PKT_user_id *uid; + char *p; + + if (uidstr) + { + if (uid_already_in_keyblock (keyblock, uidstr)) + return NULL; /* Already exists. */ + uid = uid_from_string (uidstr); + } + else + { + p = ask_user_id (1, 1, keyblock); + if (!p) + return NULL; /* Canceled. */ + uid = uid_from_string (p); + xfree (p); + } + return uid; +} + + +/* Helper for parse_key_parameter_string for one part of the + * specification string; i.e. ALGO/FLAGS. If STRING is NULL or empty + * success is returned. On error an error code is returned. Note + * that STRING may be modified by this function. NULL may be passed + * for any parameter. FOR_SUBKEY shall be true if this is used as a + * subkey. If CLEAR_CERT is set a default CERT usage will be cleared; + * this is useful if for example the default algorithm is used for a + * subkey. */ +static gpg_error_t +parse_key_parameter_part (ctrl_t ctrl, + char *string, int for_subkey, int clear_cert, + int *r_algo, unsigned int *r_size, + unsigned int *r_keyuse, + char const **r_curve, + char **r_keygrip) +{ + gpg_error_t err; + char *flags; + int algo; + char *endp; + const char *curve = NULL; + int ecdh_or_ecdsa = 0; + unsigned int size; + int keyuse; + int i; + const char *s; + int from_card = 0; + char *keygrip = NULL; + + if (!string || !*string) + return 0; /* Success. */ + + flags = strchr (string, '/'); + if (flags) + *flags++ = 0; + + algo = 0; + if (!ascii_strcasecmp (string, "card")) + from_card = 1; + else if (strlen (string) >= 3 && (digitp (string+3) || !string[3])) + { + if (!ascii_memcasecmp (string, "rsa", 3)) + algo = PUBKEY_ALGO_RSA; + else if (!ascii_memcasecmp (string, "dsa", 3)) + algo = PUBKEY_ALGO_DSA; + else if (!ascii_memcasecmp (string, "elg", 3)) + algo = PUBKEY_ALGO_ELGAMAL_E; + } + + if (from_card) + ; /* We need the flags before we can figure out the key to use. */ + else if (algo) + { + if (!string[3]) + size = get_keysize_range (algo, NULL, NULL); + else + { + size = strtoul (string+3, &endp, 10); + if (size < 512 || size > 16384 || *endp) + return gpg_error (GPG_ERR_INV_VALUE); + } + } + else if ((curve = openpgp_is_curve_supported (string, &algo, &size))) + { + if (!algo) + { + algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm. */ + ecdh_or_ecdsa = 1; /* We may need to switch the algo. */ + } + } + else + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + + /* Parse the flags. */ + keyuse = 0; + if (flags) + { + char **tokens = NULL; + + tokens = strtokenize (flags, ","); + if (!tokens) + return gpg_error_from_syserror (); + + for (i=0; (s = tokens[i]); i++) + { + if (!*s) + ; + else if (!ascii_strcasecmp (s, "sign")) + keyuse |= PUBKEY_USAGE_SIG; + else if (!ascii_strcasecmp (s, "encrypt") + || !ascii_strcasecmp (s, "encr")) + keyuse |= PUBKEY_USAGE_ENC; + else if (!ascii_strcasecmp (s, "auth")) + keyuse |= PUBKEY_USAGE_AUTH; + else if (!ascii_strcasecmp (s, "cert")) + keyuse |= PUBKEY_USAGE_CERT; + else if (!ascii_strcasecmp (s, "ecdsa") && !from_card) + { + if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) + algo = PUBKEY_ALGO_ECDSA; + else + { + xfree (tokens); + return gpg_error (GPG_ERR_INV_FLAG); + } + ecdh_or_ecdsa = 0; + } + else if (!ascii_strcasecmp (s, "ecdh") && !from_card) + { + if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) + algo = PUBKEY_ALGO_ECDH; + else + { + xfree (tokens); + return gpg_error (GPG_ERR_INV_FLAG); + } + ecdh_or_ecdsa = 0; + } + else if (!ascii_strcasecmp (s, "eddsa") && !from_card) + { + /* Not required but we allow it for consistency. */ + if (algo == PUBKEY_ALGO_EDDSA) + ; + else + { + xfree (tokens); + return gpg_error (GPG_ERR_INV_FLAG); + } + } + else + { + xfree (tokens); + return gpg_error (GPG_ERR_UNKNOWN_FLAG); + } + } + + xfree (tokens); + } + + /* If not yet decided switch between ecdh and ecdsa unless we want + * to read the algo from the current card. */ + if (from_card) + { + strlist_t keypairlist, sl; + char *reqkeyref; + + if (!keyuse) + keyuse = (for_subkey? PUBKEY_USAGE_ENC + /* */ : (PUBKEY_USAGE_CERT|PUBKEY_USAGE_SIG)); + + /* Access the card to make sure we have one and to show the S/N. */ + { + char *serialno; + + err = agent_scd_serialno (&serialno, NULL); + if (err) + { + log_error (_("error reading the card: %s\n"), gpg_strerror (err)); + return err; + } + if (!opt.quiet) + log_info (_("Serial number of the card: %s\n"), serialno); + xfree (serialno); + } + + err = agent_scd_keypairinfo (ctrl, &keypairlist); + if (err) + { + log_error (_("error reading the card: %s\n"), gpg_strerror (err)); + return err; + } + agent_scd_getattr_one ((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT)) + ? "$SIGNKEYID":"$ENCRKEYID", &reqkeyref); + + algo = 0; /* Should already be the case. */ + for (sl=keypairlist; sl && !algo; sl = sl->next) + { + gcry_sexp_t s_pkey; + char *algostr = NULL; + enum gcry_pk_algos algoid = 0; + const char *keyref; + + if (!reqkeyref) + continue; /* Card does not provide the info (skip all). */ + + keyref = strchr (sl->d, ' '); + if (!keyref) + continue; /* Ooops. */ + keyref++; + if (strcmp (reqkeyref, keyref)) + continue; /* This is not the requested keyref. */ + + if ((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT)) + && (sl->flags & (GCRY_PK_USAGE_SIGN|GCRY_PK_USAGE_CERT))) + ; /* Okay */ + else if ((keyuse & PUBKEY_USAGE_ENC) + && (sl->flags & GCRY_PK_USAGE_ENCR)) + ; /* Okay */ + else + continue; /* Not usable for us. */ + + if (agent_scd_readkey (keyref, &s_pkey)) + continue; /* Could not read the key. */ + + algostr = pubkey_algo_string (s_pkey, &algoid); + gcry_sexp_release (s_pkey); + + + /* Map to OpenPGP algo number. + * We need to tweak the algo in case GCRY_PK_ECC is returned + * because pubkey_algo_string is not aware of the OpenPGP + * algo mapping. FIXME: This is an ugly hack. */ + if (algoid == GCRY_PK_ECC + && algostr && !strncmp (algostr, "nistp", 5) + && !(sl->flags & GCRY_PK_USAGE_ENCR)) + algo = PUBKEY_ALGO_ECDSA; + else if (algoid == GCRY_PK_ECC + && algostr && !strcmp (algostr, "ed25519") + && !(sl->flags & GCRY_PK_USAGE_ENCR)) + algo = PUBKEY_ALGO_EDDSA; + else + algo = map_pk_gcry_to_openpgp (algoid); + + xfree (algostr); + xfree (keygrip); + keygrip = xtrystrdup (sl->d); + if (!keygrip) + { + err = gpg_error_from_syserror (); + xfree (reqkeyref); + free_strlist (keypairlist); + return err; + } + if ((endp = strchr (keygrip, ' '))) + *endp = 0; + } + + xfree (reqkeyref); + free_strlist (keypairlist); + if (!algo || !keygrip) + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + log_error ("no usable key on the card: %s\n", gpg_strerror (err)); + xfree (keygrip); + return err; + } + } + else if (ecdh_or_ecdsa && keyuse) + algo = (keyuse & PUBKEY_USAGE_ENC)? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; + else if (ecdh_or_ecdsa) + algo = for_subkey? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; + + /* Set or fix key usage. */ + if (!keyuse) + { + if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_DSA) + keyuse = PUBKEY_USAGE_SIG; + else if (algo == PUBKEY_ALGO_RSA) + keyuse = for_subkey? PUBKEY_USAGE_ENC : PUBKEY_USAGE_SIG; + else + keyuse = PUBKEY_USAGE_ENC; + } + else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_DSA) + { + keyuse &= ~PUBKEY_USAGE_ENC; /* Forbid encryption. */ + } + else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ELGAMAL_E) + { + keyuse = PUBKEY_USAGE_ENC; /* Allow only encryption. */ + } + + /* Make sure a primary key can certify. */ + if (!for_subkey) + keyuse |= PUBKEY_USAGE_CERT; + + /* But if requested remove th cert usage. */ + if (clear_cert) + keyuse &= ~PUBKEY_USAGE_CERT; + + /* Check that usage is actually possible. */ + if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) + && !pubkey_get_nsig (algo)) + || ((keyuse & PUBKEY_USAGE_ENC) + && !pubkey_get_nenc (algo)) + || (for_subkey && (keyuse & PUBKEY_USAGE_CERT))) + { + xfree (keygrip); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + + /* Return values. */ + if (r_algo) + *r_algo = algo; + if (r_size) + { + unsigned int min, def, max; + + /* Make sure the keysize is in the allowed range. */ + def = get_keysize_range (algo, &min, &max); + if (!size) + size = def; + else if (size < min) + size = min; + else if (size > max) + size = max; + + *r_size = fixup_keysize (size, algo, 1); + } + + if (r_keyuse) + *r_keyuse = keyuse; + if (r_curve) + *r_curve = curve; + + if (r_keygrip) + *r_keygrip = keygrip; + else + xfree (keygrip); + + return 0; +} + +/* Parse and return the standard key generation parameter. + * The string is expected to be in this format: + * + * ALGO[/FLAGS][+SUBALGO[/FLAGS]] + * + * Here ALGO is a string in the same format as printed by the + * keylisting. For example: + * + * rsa3072 := RSA with 3072 bit. + * dsa2048 := DSA with 2048 bit. + * elg2048 := Elgamal with 2048 bit. + * ed25519 := EDDSA using curve Ed25519. + * cv25519 := ECDH using curve Curve25519. + * nistp256:= ECDSA or ECDH using curve NIST P-256 + * + * All strings with an unknown prefix are considered an elliptic + * curve. Curves which have no implicit algorithm require that FLAGS + * is given to select whether ECDSA or ECDH is used; this can either + * be done using an algorithm keyword or usage keywords. + * + * FLAGS is a comma delimited string of keywords: + * + * cert := Allow usage Certify + * sign := Allow usage Sign + * encr := Allow usage Encrypt + * auth := Allow usage Authentication + * encrypt := Alias for "encr" + * ecdsa := Use algorithm ECDSA. + * eddsa := Use algorithm EdDSA. + * ecdh := Use algorithm ECDH. + * + * There are several defaults and fallbacks depending on the + * algorithm. PART can be used to select which part of STRING is + * used: + * -1 := Both parts + * 0 := Only the part of the primary key + * 1 := If there is one part parse that one, if there are + * two parts parse the part which best matches the + * SUGGESTED_USE or in case that can't be evaluated the second part. + * Always return using the args for the primary key (R_ALGO,....). + * + */ +gpg_error_t +parse_key_parameter_string (ctrl_t ctrl, + const char *string, int part, + unsigned int suggested_use, + int *r_algo, unsigned int *r_size, + unsigned int *r_keyuse, + char const **r_curve, + char **r_keygrip, + int *r_subalgo, unsigned int *r_subsize, + unsigned int *r_subkeyuse, + char const **r_subcurve, + char **r_subkeygrip) +{ + gpg_error_t err = 0; + char *primary, *secondary; + + if (r_algo) + *r_algo = 0; + if (r_size) + *r_size = 0; + if (r_keyuse) + *r_keyuse = 0; + if (r_curve) + *r_curve = NULL; + if (r_keygrip) + *r_keygrip = NULL; + if (r_subalgo) + *r_subalgo = 0; + if (r_subsize) + *r_subsize = 0; + if (r_subkeyuse) + *r_subkeyuse = 0; + if (r_subcurve) + *r_subcurve = NULL; + if (r_subkeygrip) + *r_subkeygrip = NULL; + + if (!string || !*string + || !ascii_strcasecmp (string, "default") || !strcmp (string, "-")) + string = get_default_pubkey_algo (); + else if (!ascii_strcasecmp (string, "future-default") + || !ascii_strcasecmp (string, "futuredefault")) + string = FUTURE_STD_KEY_PARAM; + else if (!ascii_strcasecmp (string, "card")) + string = "card/cert,sign+card/encr"; + + primary = xstrdup (string); + secondary = strchr (primary, '+'); + if (secondary) + *secondary++ = 0; + if (part == -1 || part == 0) + { + err = parse_key_parameter_part (ctrl, primary, + 0, 0, r_algo, r_size, + r_keyuse, r_curve, r_keygrip); + if (!err && part == -1) + err = parse_key_parameter_part (ctrl, secondary, + 1, 0, r_subalgo, r_subsize, + r_subkeyuse, r_subcurve, + r_subkeygrip); + } + else if (part == 1) + { + /* If we have SECONDARY, use that part. If there is only one + * part consider this to be the subkey algo. In case a + * SUGGESTED_USE has been given and the usage of the secondary + * part does not match SUGGESTED_USE try again using the primary + * part. Noet thar when falling back to the primary key we need + * to force clearing the cert usage. */ + if (secondary) + { + err = parse_key_parameter_part (ctrl, secondary, + 1, 0, + r_algo, r_size, r_keyuse, r_curve, + r_keygrip); + if (!err && suggested_use && r_keyuse && !(suggested_use & *r_keyuse)) + err = parse_key_parameter_part (ctrl, primary, + 1, 1 /*(clear cert)*/, + r_algo, r_size, r_keyuse, r_curve, + r_keygrip); + } + else + err = parse_key_parameter_part (ctrl, primary, + 1, 0, + r_algo, r_size, r_keyuse, r_curve, + r_keygrip); + } + + xfree (primary); + + return err; +} + + + +/* Append R to the linked list PARA. */ +static void +append_to_parameter (struct para_data_s *para, struct para_data_s *r) +{ + log_assert (para); + while (para->next) + para = para->next; + para->next = r; +} + +/* Release the parameter list R. */ +static void +release_parameter_list (struct para_data_s *r) +{ + struct para_data_s *r2; + + for (; r ; r = r2) + { + r2 = r->next; + if (r->key == pPASSPHRASE && *r->u.value) + wipememory (r->u.value, strlen (r->u.value)); + xfree (r); + } +} + +static struct para_data_s * +get_parameter( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r; + + for( r = para; r && r->key != key; r = r->next ) + ; + return r; +} + +static const char * +get_parameter_value( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + return (r && *r->u.value)? r->u.value : NULL; +} + + +/* This is similar to get_parameter_value but also returns the empty + string. This is required so that quick_generate_keypair can use an + empty Passphrase to specify no-protection. */ +static const char * +get_parameter_passphrase (struct para_data_s *para) +{ + struct para_data_s *r = get_parameter (para, pPASSPHRASE); + return r ? r->u.value : NULL; +} + + +static int +get_parameter_algo (ctrl_t ctrl, struct para_data_s *para, enum para_name key, + int *r_default) +{ + int i; + struct para_data_s *r = get_parameter( para, key ); + + if (r_default) + *r_default = 0; + + if (!r) + return -1; + + /* Note that we need to handle the ECC algorithms specified as + strings directly because Libgcrypt folds them all to ECC. */ + if (!ascii_strcasecmp (r->u.value, "default")) + { + /* Note: If you change this default algo, remember to change it + * also in gpg.c:gpgconf_list. */ + /* FIXME: We only allow the algo here and have a separate thing + * for the curve etc. That is a ugly but demanded for backward + * compatibility with the batch key generation. It would be + * better to make full use of parse_key_parameter_string. */ + parse_key_parameter_string (ctrl, NULL, 0, 0, + &i, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); + if (r_default) + *r_default = 1; + } + else if (digitp (r->u.value)) + i = atoi( r->u.value ); + else if (!strcmp (r->u.value, "ELG-E") + || !strcmp (r->u.value, "ELG")) + i = PUBKEY_ALGO_ELGAMAL_E; + else if (!ascii_strcasecmp (r->u.value, "EdDSA")) + i = PUBKEY_ALGO_EDDSA; + else if (!ascii_strcasecmp (r->u.value, "ECDSA")) + i = PUBKEY_ALGO_ECDSA; + else if (!ascii_strcasecmp (r->u.value, "ECDH")) + i = PUBKEY_ALGO_ECDH; + else + i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value)); + + if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) + i = 0; /* we don't want to allow generation of these algorithms */ + return i; +} + + +/* Parse a usage string. The usage keywords "auth", "sign", "encr" + * may be delimited 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 if (!ascii_strcasecmp (s, "cert")) + use |= PUBKEY_USAGE_CERT; + 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. + */ +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 ); + int i; + + if (!r) + return 0; /* none (this is an optional parameter)*/ + + 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 = i; + return 1; +} + + +static int +parse_revocation_key (const char *fname, + struct para_data_s *para, enum para_name key) +{ + struct para_data_s *r = get_parameter( para, key ); + struct revocation_key revkey; + char *pn; + int i; + + if( !r ) + return 0; /* none (this is an optional parameter) */ + + pn = r->u.value; + + revkey.class=0x80; + revkey.algid=atoi(pn); + if(!revkey.algid) + goto fail; + + /* Skip to the fpr */ + while(*pn && *pn!=':') + pn++; + + if(*pn!=':') + goto fail; + + pn++; + + for(i=0;iu.revkey,&revkey,sizeof(struct revocation_key)); + + return 0; + + fail: + log_error("%s:%d: invalid revocation key\n", fname, r->lnr ); + return -1; /* error */ +} + + +static u32 +get_parameter_u32( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + + if( !r ) + return 0; + if( r->key == pKEYCREATIONDATE ) + return r->u.creation; + if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) + return r->u.expire; + if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) + return r->u.usage; + + return (unsigned int)strtoul( r->u.value, NULL, 10 ); +} + +static unsigned int +get_parameter_uint( struct para_data_s *para, enum para_name key ) +{ + return get_parameter_u32( para, key ); +} + +static struct revocation_key * +get_parameter_revkey( struct para_data_s *para, enum para_name key ) +{ + struct para_data_s *r = get_parameter( para, key ); + return r? &r->u.revkey : NULL; +} + +static int +proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, + struct output_control_s *outctrl, int card ) +{ + struct para_data_s *r; + const char *s1, *s2, *s3; + size_t n; + char *p; + int is_default = 0; + int have_user_id = 0; + int err, algo; + + /* Check that we have all required parameters. */ + r = get_parameter( para, pKEYTYPE ); + if(r) + { + algo = get_parameter_algo (ctrl, para, pKEYTYPE, &is_default); + if (openpgp_pk_test_algo2 (algo, PUBKEY_USAGE_SIG)) + { + log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); + return -1; + } + } + else + { + log_error ("%s: no Key-Type specified\n",fname); + return -1; + } + + err = parse_parameter_usage (fname, para, pKEYUSAGE); + if (!err) + { + /* Default to algo capabilities if key-usage is not provided and + no default algorithm has been requested. */ + r = xmalloc_clear(sizeof(*r)); + r->key = pKEYUSAGE; + r->u.usage = (is_default + ? (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG) + : openpgp_pk_algo_usage(algo)); + append_to_parameter (para, r); + } + else if (err == -1) + return -1; + else + { + r = get_parameter (para, pKEYUSAGE); + if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) + { + log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n", + fname, r->lnr, algo); + return -1; + } + } + + is_default = 0; + r = get_parameter( para, pSUBKEYTYPE ); + if(r) + { + algo = get_parameter_algo (ctrl, para, pSUBKEYTYPE, &is_default); + if (openpgp_pk_test_algo (algo)) + { + log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); + return -1; + } + + err = parse_parameter_usage (fname, para, pSUBKEYUSAGE); + if (!err) + { + /* Default to algo capabilities if subkey-usage is not + provided */ + r = xmalloc_clear (sizeof(*r)); + r->key = pSUBKEYUSAGE; + r->u.usage = (is_default + ? PUBKEY_USAGE_ENC + : openpgp_pk_algo_usage (algo)); + append_to_parameter (para, r); + } + else if (err == -1) + return -1; + else + { + r = get_parameter (para, pSUBKEYUSAGE); + if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) + { + log_error ("%s:%d: specified Subkey-Usage not allowed" + " for algo %d\n", fname, r->lnr, algo); + return -1; + } + } + } + + + if( get_parameter_value( para, pUSERID ) ) + have_user_id=1; + else + { + /* create the formatted user ID */ + s1 = get_parameter_value( para, pNAMEREAL ); + s2 = get_parameter_value( para, pNAMECOMMENT ); + s3 = get_parameter_value( para, pNAMEEMAIL ); + if( s1 || s2 || s3 ) + { + n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); + r = xmalloc_clear( sizeof *r + n + 20 ); + r->key = pUSERID; + p = r->u.value; + if( s1 ) + p = stpcpy(p, s1 ); + if( s2 ) + p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); + if( s3 ) + { + /* If we have only the email part, do not add the space + * and the angle brackets. */ + if (*r->u.value) + p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); + else + p = stpcpy (p, s3); + } + append_to_parameter (para, r); + have_user_id=1; + } + } + + if(!have_user_id) + { + log_error("%s: no User-ID specified\n",fname); + return -1; + } + + /* Set preferences, if any. */ + keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); + + /* Set keyserver, if any. */ + s1=get_parameter_value( para, pKEYSERVER ); + if(s1) + { + struct keyserver_spec *spec; + + spec = parse_keyserver_uri (s1, 1); + if(spec) + { + free_keyserver_spec(spec); + opt.def_keyserver_url=s1; + } + else + { + r = get_parameter (para, pKEYSERVER); + log_error("%s:%d: invalid keyserver url\n", fname, r->lnr ); + return -1; + } + } + + /* Set revoker, if any. */ + if (parse_revocation_key (fname, para, pREVOKER)) + return -1; + + + /* Make KEYCREATIONDATE from Creation-Date. */ + r = get_parameter (para, pCREATIONDATE); + if (r && *r->u.value) + { + u32 seconds; + + seconds = parse_creation_string (r->u.value); + if (!seconds) + { + log_error ("%s:%d: invalid creation date\n", fname, r->lnr ); + return -1; + } + r->u.creation = seconds; + r->key = pKEYCREATIONDATE; /* Change that entry. */ + } + + /* Make KEYEXPIRE from Expire-Date. */ + r = get_parameter( para, pEXPIREDATE ); + if( r && *r->u.value ) + { + u32 seconds; + + seconds = parse_expire_string( r->u.value ); + if( seconds == (u32)-1 ) + { + log_error("%s:%d: invalid expire date\n", fname, r->lnr ); + return -1; + } + r->u.expire = seconds; + r->key = pKEYEXPIRE; /* change hat entry */ + /* also set it for the subkey */ + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pSUBKEYEXPIRE; + r->u.expire = seconds; + append_to_parameter (para, r); + } + + do_generate_keypair (ctrl, para, outctrl, card ); + return 0; +} + + +/**************** + * Kludge to allow non interactive key generation controlled + * by a parameter file. + * Note, that string parameters are expected to be in UTF-8 + */ +static void +read_parameter_file (ctrl_t ctrl, const char *fname ) +{ + static struct { const char *name; + enum para_name key; + } keywords[] = { + { "Key-Type", pKEYTYPE}, + { "Key-Length", pKEYLENGTH }, + { "Key-Curve", pKEYCURVE }, + { "Key-Usage", pKEYUSAGE }, + { "Subkey-Type", pSUBKEYTYPE }, + { "Subkey-Length", pSUBKEYLENGTH }, + { "Subkey-Curve", pSUBKEYCURVE }, + { "Subkey-Usage", pSUBKEYUSAGE }, + { "Name-Real", pNAMEREAL }, + { "Name-Email", pNAMEEMAIL }, + { "Name-Comment", pNAMECOMMENT }, + { "Expire-Date", pEXPIREDATE }, + { "Creation-Date", pCREATIONDATE }, + { "Passphrase", pPASSPHRASE }, + { "Preferences", pPREFERENCES }, + { "Revoker", pREVOKER }, + { "Handle", pHANDLE }, + { "Keyserver", pKEYSERVER }, + { "Keygrip", pKEYGRIP }, + { "Key-Grip", pKEYGRIP }, + { "Subkey-grip", pSUBKEYGRIP }, + { NULL, 0 } + }; + IOBUF fp; + byte *line; + unsigned int maxlen, nline; + char *p; + int lnr; + const char *err = NULL; + struct para_data_s *para, *r; + int i; + struct output_control_s outctrl; + + memset( &outctrl, 0, sizeof( outctrl ) ); + outctrl.pub.afx = new_armor_context (); + + if( !fname || !*fname) + fname = "-"; + + fp = iobuf_open (fname); + if (fp && is_secured_file (iobuf_get_fd (fp))) + { + iobuf_close (fp); + fp = NULL; + gpg_err_set_errno (EPERM); + } + if (!fp) { + log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); + return; + } + iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL); + + lnr = 0; + err = NULL; + para = NULL; + maxlen = 1024; + line = NULL; + nline = 0; + while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { + char *keyword, *value; + + lnr++; + if( !maxlen ) { + err = "line too long"; + break; + } + for( p = line; isspace(*(byte*)p); p++ ) + ; + if( !*p || *p == '#' ) + continue; + keyword = p; + if( *keyword == '%' ) { + for( ; !isspace(*(byte*)p); p++ ) + ; + if( *p ) + *p++ = 0; + for( ; isspace(*(byte*)p); p++ ) + ; + value = p; + trim_trailing_ws( value, strlen(value) ); + if( !ascii_strcasecmp( keyword, "%echo" ) ) + log_info("%s\n", value ); + else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) + outctrl.dryrun = 1; + else if( !ascii_strcasecmp( keyword, "%ask-passphrase" ) ) + ; /* Dummy for backward compatibility. */ + else if( !ascii_strcasecmp( keyword, "%no-ask-passphrase" ) ) + ; /* Dummy for backward compatibility. */ + else if( !ascii_strcasecmp( keyword, "%no-protection" ) ) + outctrl.keygen_flags |= KEYGEN_FLAG_NO_PROTECTION; + else if( !ascii_strcasecmp( keyword, "%transient-key" ) ) + outctrl.keygen_flags |= KEYGEN_FLAG_TRANSIENT_KEY; + else if( !ascii_strcasecmp( keyword, "%commit" ) ) { + outctrl.lnr = lnr; + if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) + print_status_key_not_created + (get_parameter_value (para, pHANDLE)); + release_parameter_list( para ); + para = NULL; + } + else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { + if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) + ; /* still the same file - ignore it */ + else { + xfree( outctrl.pub.newfname ); + outctrl.pub.newfname = xstrdup( value ); + outctrl.use_files = 1; + } + } + else if( !ascii_strcasecmp( keyword, "%secring" ) ) { + /* Ignore this command. */ + } + else + log_info("skipping control '%s' (%s)\n", keyword, value ); + + + continue; + } + + + if( !(p = strchr( p, ':' )) || p == keyword ) { + err = "missing colon"; + break; + } + if( *p ) + *p++ = 0; + for( ; isspace(*(byte*)p); p++ ) + ; + if( !*p ) { + err = "missing argument"; + break; + } + value = p; + trim_trailing_ws( value, strlen(value) ); + + for(i=0; keywords[i].name; i++ ) { + if( !ascii_strcasecmp( keywords[i].name, keyword ) ) + break; + } + if( !keywords[i].name ) { + err = "unknown keyword"; + break; + } + if( keywords[i].key != pKEYTYPE && !para ) { + err = "parameter block does not start with \"Key-Type\""; + break; + } + + if( keywords[i].key == pKEYTYPE && para ) { + outctrl.lnr = lnr; + if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) + print_status_key_not_created + (get_parameter_value (para, pHANDLE)); + release_parameter_list( para ); + para = NULL; + } + else { + for( r = para; r; r = r->next ) { + if( r->key == keywords[i].key ) + break; + } + if( r ) { + err = "duplicate keyword"; + break; + } + } + r = xmalloc_clear( sizeof *r + strlen( value ) ); + r->lnr = lnr; + r->key = keywords[i].key; + strcpy( r->u.value, value ); + r->next = para; + para = r; + } + if( err ) + log_error("%s:%d: %s\n", fname, lnr, err ); + else if( iobuf_error (fp) ) { + log_error("%s:%d: read error\n", fname, lnr); + } + else if( para ) { + outctrl.lnr = lnr; + if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) + print_status_key_not_created (get_parameter_value (para, pHANDLE)); + } + + if( outctrl.use_files ) { /* close open streams */ + iobuf_close( outctrl.pub.stream ); + + /* Must invalidate that ugly cache to actually close it. */ + if (outctrl.pub.fname) + iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, + 0, (char*)outctrl.pub.fname); + + xfree( outctrl.pub.fname ); + xfree( outctrl.pub.newfname ); + } + + xfree (line); + release_parameter_list( para ); + iobuf_close (fp); + release_armor_context (outctrl.pub.afx); +} + + +/* Helper for quick_generate_keypair. */ +static struct para_data_s * +quickgen_set_para (struct para_data_s *para, int for_subkey, + int algo, int nbits, const char *curve, unsigned int use, + const char *keygrip) +{ + struct para_data_s *r; + + r = xmalloc_clear (sizeof *r + 30); + r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE; + if (use) + snprintf (r->u.value, 30, "%s%s%s%s", + (use & PUBKEY_USAGE_ENC)? "encr " : "", + (use & PUBKEY_USAGE_SIG)? "sign " : "", + (use & PUBKEY_USAGE_AUTH)? "auth " : "", + (use & PUBKEY_USAGE_CERT)? "cert " : ""); + else + strcpy (r->u.value, for_subkey ? "encr" : "sign"); + r->next = para; + para = r; + r = xmalloc_clear (sizeof *r + 20); + r->key = for_subkey? pSUBKEYTYPE : pKEYTYPE; + snprintf (r->u.value, 20, "%d", algo); + r->next = para; + para = r; + + if (keygrip) + { + r = xmalloc_clear (sizeof *r + strlen (keygrip)); + r->key = for_subkey? pSUBKEYGRIP : pKEYGRIP; + strcpy (r->u.value, keygrip); + r->next = para; + para = r; + } + else if (curve) + { + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = for_subkey? pSUBKEYCURVE : pKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } + else + { + r = xmalloc_clear (sizeof *r + 20); + r->key = for_subkey? pSUBKEYLENGTH : pKEYLENGTH; + sprintf (r->u.value, "%u", nbits); + r->next = para; + para = r; + } + + return para; +} + + +/* + * Unattended generation of a standard key. + */ +void +quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, + const char *usagestr, const char *expirestr) +{ + gpg_error_t err; + struct para_data_s *para = NULL; + struct para_data_s *r; + struct output_control_s outctrl; + int use_tty; + + memset (&outctrl, 0, sizeof outctrl); + + use_tty = (!opt.batch && !opt.answer_yes + && !*algostr && !*usagestr && !*expirestr + && !cpr_enabled () + && gnupg_isatty (fileno (stdin)) + && gnupg_isatty (fileno (stdout)) + && gnupg_isatty (fileno (stderr))); + + r = xmalloc_clear (sizeof *r + strlen (uid)); + r->key = pUSERID; + strcpy (r->u.value, uid); + r->next = para; + para = r; + + uid = trim_spaces (r->u.value); + if (!*uid || (!opt.allow_freeform_uid && !is_valid_user_id (uid))) + { + log_error (_("Key generation failed: %s\n"), + gpg_strerror (GPG_ERR_INV_USER_ID)); + goto leave; + } + + /* If gpg is directly used on the console ask whether a key with the + given user id shall really be created. */ + if (use_tty) + { + tty_printf (_("About to create a key for:\n \"%s\"\n\n"), uid); + if (!cpr_get_answer_is_yes_def ("quick_keygen.okay", + _("Continue? (Y/n) "), 1)) + goto leave; + } + + /* Check whether such a user ID already exists. */ + { + KEYDB_HANDLE kdbhd; + KEYDB_SEARCH_DESC desc; + + memset (&desc, 0, sizeof desc); + desc.mode = KEYDB_SEARCH_MODE_EXACT; + desc.u.name = uid; + + kdbhd = keydb_new (); + if (!kdbhd) + goto leave; + + err = keydb_search (kdbhd, &desc, 1, NULL); + keydb_release (kdbhd); + if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) + { + log_info (_("A key for \"%s\" already exists\n"), uid); + if (opt.answer_yes) + ; + else if (!use_tty + || !cpr_get_answer_is_yes_def ("quick_keygen.force", + _("Create anyway? (y/N) "), 0)) + { + write_status_error ("genkey", gpg_error (304)); + log_inc_errorcount (); /* we used log_info */ + goto leave; + } + log_info (_("creating anyway\n")); + } + } + + if (!*expirestr || strcmp (expirestr, "-") == 0) + expirestr = default_expiration_interval; + + if ((!*algostr || !ascii_strcasecmp (algostr, "default") + || !ascii_strcasecmp (algostr, "future-default") + || !ascii_strcasecmp (algostr, "futuredefault") + || !ascii_strcasecmp (algostr, "card")) + && (!*usagestr || !ascii_strcasecmp (usagestr, "default") + || !strcmp (usagestr, "-"))) + { + /* Use default key parameters. */ + int algo, subalgo; + unsigned int size, subsize; + unsigned int keyuse, subkeyuse; + const char *curve, *subcurve; + char *keygrip, *subkeygrip; + + err = parse_key_parameter_string (ctrl, algostr, -1, 0, + &algo, &size, &keyuse, &curve, + &keygrip, + &subalgo, &subsize, &subkeyuse, + &subcurve, &subkeygrip); + if (err) + { + log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + para = quickgen_set_para (para, 0, algo, size, curve, keyuse, + keygrip); + if (subalgo) + para = quickgen_set_para (para, 1, + subalgo, subsize, subcurve, subkeyuse, + subkeygrip); + if (*expirestr) + { + u32 expire; + + expire = parse_expire_string (expirestr); + if (expire == (u32)-1 ) + { + err = gpg_error (GPG_ERR_INV_VALUE); + log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); + goto leave; + } + r = xmalloc_clear (sizeof *r + 20); + r->key = pKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + } + + xfree (keygrip); + xfree (subkeygrip); + } + else + { + /* Extended unattended mode. Creates only the primary key. */ + int algo; + unsigned int use; + u32 expire; + unsigned int nbits; + const char *curve; + char *keygrip; + + err = parse_algo_usage_expire (ctrl, 0, algostr, usagestr, expirestr, + &algo, &use, &expire, &nbits, &curve, + &keygrip); + if (err) + { + log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); + goto leave; + } + + para = quickgen_set_para (para, 0, algo, nbits, curve, use, + keygrip); + r = xmalloc_clear (sizeof *r + 20); + r->key = pKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + + xfree (keygrip); + } + + /* If the pinentry loopback mode is not and we have a static + passphrase (i.e. set with --passphrase{,-fd,-file} while in batch + mode), we use that passphrase for the new key. */ + if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK + && have_static_passphrase ()) + { + const char *s = get_static_passphrase (); + + r = xmalloc_clear (sizeof *r + strlen (s)); + r->key = pPASSPHRASE; + strcpy (r->u.value, s); + r->next = para; + para = r; + } + + proc_parameter_file (ctrl, para, "[internal]", &outctrl, 0); + + leave: + release_parameter_list (para); +} + + +/* + * Generate a keypair (fname is only used in batch mode) If + * CARD_SERIALNO is not NULL the function will create the keys on an + * OpenPGP Card. If CARD_BACKUP_KEY has been set and CARD_SERIALNO is + * NOT NULL, the encryption key for the card is generated on the host, + * imported to the card and a backup file created by gpg-agent. If + * FULL is not set only the basic prompts are used (except for batch + * mode). + */ +void +generate_keypair (ctrl_t ctrl, int full, const char *fname, + const char *card_serialno, int card_backup_key) +{ + gpg_error_t err; + unsigned int nbits; + char *uid = NULL; + int algo; + unsigned int use; + int both = 0; + u32 expire; + struct para_data_s *para = NULL; + struct para_data_s *r; + struct output_control_s outctrl; + +#ifndef ENABLE_CARD_SUPPORT + (void)card_backup_key; +#endif + + memset( &outctrl, 0, sizeof( outctrl ) ); + + if (opt.batch && card_serialno) + { + /* We don't yet support unattended key generation with a card + * serial number. */ + log_error (_("can't do this in batch mode\n")); + print_further_info ("key generation with card serial number"); + return; + } + + if (opt.batch) + { + read_parameter_file (ctrl, fname); + return; + } + + if (card_serialno) + { +#ifdef ENABLE_CARD_SUPPORT + struct agent_card_info_s info; + + memset (&info, 0, sizeof (info)); + err = agent_scd_getattr ("KEY-ATTR", &info); + if (err) + { + log_error (_("error getting current key info: %s\n"), + gpg_strerror (err)); + return; + } + + r = xcalloc (1, sizeof *r + strlen (card_serialno) ); + r->key = pSERIALNO; + strcpy( r->u.value, card_serialno); + r->next = para; + para = r; + + r = xcalloc (1, sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", info.key_attr[0].algo ); + r->next = para; + para = r; + r = xcalloc (1, sizeof *r + 20 ); + r->key = pKEYUSAGE; + strcpy (r->u.value, "sign"); + r->next = para; + para = r; + + r = xcalloc (1, sizeof *r + 20 ); + r->key = pSUBKEYTYPE; + sprintf( r->u.value, "%d", info.key_attr[1].algo ); + r->next = para; + para = r; + r = xcalloc (1, sizeof *r + 20 ); + r->key = pSUBKEYUSAGE; + strcpy (r->u.value, "encrypt"); + r->next = para; + para = r; + if (info.key_attr[1].algo == PUBKEY_ALGO_RSA) + { + r = xcalloc (1, sizeof *r + 20 ); + r->key = pSUBKEYLENGTH; + sprintf( r->u.value, "%u", info.key_attr[1].nbits); + r->next = para; + para = r; + } + else if (info.key_attr[1].algo == PUBKEY_ALGO_ECDSA + || info.key_attr[1].algo == PUBKEY_ALGO_EDDSA + || info.key_attr[1].algo == PUBKEY_ALGO_ECDH) + { + r = xcalloc (1, sizeof *r + strlen (info.key_attr[1].curve)); + r->key = pSUBKEYCURVE; + strcpy (r->u.value, info.key_attr[1].curve); + r->next = para; + para = r; + } + + r = xcalloc (1, sizeof *r + 20 ); + r->key = pAUTHKEYTYPE; + sprintf( r->u.value, "%d", info.key_attr[2].algo ); + r->next = para; + para = r; + + if (card_backup_key) + { + r = xcalloc (1, sizeof *r + 1); + r->key = pCARDBACKUPKEY; + strcpy (r->u.value, "1"); + r->next = para; + para = r; + } +#endif /*ENABLE_CARD_SUPPORT*/ + } + else if (full) /* Full featured key generation. */ + { + int subkey_algo; + char *key_from_hexgrip = NULL; + + algo = ask_algo (ctrl, 0, &subkey_algo, &use, &key_from_hexgrip); + if (key_from_hexgrip) + { + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo); + r->next = para; + para = r; + + if (use) + { + r = xmalloc_clear( sizeof *r + 25 ); + r->key = pKEYUSAGE; + sprintf( r->u.value, "%s%s%s", + (use & PUBKEY_USAGE_SIG)? "sign ":"", + (use & PUBKEY_USAGE_ENC)? "encrypt ":"", + (use & PUBKEY_USAGE_AUTH)? "auth":"" ); + r->next = para; + para = r; + } + + r = xmalloc_clear( sizeof *r + 40 ); + r->key = pKEYGRIP; + strcpy (r->u.value, key_from_hexgrip); + r->next = para; + para = r; + + xfree (key_from_hexgrip); + } + else + { + const char *curve = NULL; + + if (subkey_algo) + { + /* Create primary and subkey at once. */ + both = 1; + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + { + curve = ask_curve (&algo, &subkey_algo, NULL); + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo); + r->next = para; + para = r; + nbits = 0; + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = pKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } + else + { + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo); + r->next = para; + para = r; + nbits = ask_keysize (algo, 0); + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYLENGTH; + sprintf( r->u.value, "%u", nbits); + r->next = para; + para = r; + } + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYUSAGE; + strcpy( r->u.value, "sign" ); + r->next = para; + para = r; + + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pSUBKEYTYPE; + sprintf( r->u.value, "%d", subkey_algo); + r->next = para; + para = r; + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pSUBKEYUSAGE; + strcpy( r->u.value, "encrypt" ); + r->next = para; + para = r; + + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + { + if (algo == PUBKEY_ALGO_EDDSA + && subkey_algo == PUBKEY_ALGO_ECDH) + { + /* Need to switch to a different curve for the + encryption key. */ + curve = "Curve25519"; + } + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = pSUBKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } + } + else /* Create only a single key. */ + { + /* For ECC we need to ask for the curve before storing the + algo because ask_curve may change the algo. */ + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + { + curve = ask_curve (&algo, NULL, NULL); + r = xmalloc_clear (sizeof *r + strlen (curve)); + r->key = pKEYCURVE; + strcpy (r->u.value, curve); + r->next = para; + para = r; + } + + r = xmalloc_clear( sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo ); + r->next = para; + para = r; + + if (use) + { + r = xmalloc_clear( sizeof *r + 25 ); + r->key = pKEYUSAGE; + sprintf( r->u.value, "%s%s%s", + (use & PUBKEY_USAGE_SIG)? "sign ":"", + (use & PUBKEY_USAGE_ENC)? "encrypt ":"", + (use & PUBKEY_USAGE_AUTH)? "auth":"" ); + r->next = para; + para = r; + } + nbits = 0; + } + + if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + { + /* The curve has already been set. */ + } + else + { + nbits = ask_keysize (both? subkey_algo : algo, nbits); + r = xmalloc_clear( sizeof *r + 20 ); + r->key = both? pSUBKEYLENGTH : pKEYLENGTH; + sprintf( r->u.value, "%u", nbits); + r->next = para; + para = r; + } + } + } + else /* Default key generation. */ + { + int subalgo; + unsigned int size, subsize; + unsigned int keyuse, subkeyuse; + const char *curve, *subcurve; + char *keygrip, *subkeygrip; + + tty_printf ( _("Note: Use \"%s %s\"" + " for a full featured key generation dialog.\n"), +#if USE_GPG2_HACK + GPG_NAME "2" +#else + GPG_NAME +#endif + , "--full-generate-key" ); + + err = parse_key_parameter_string (ctrl, NULL, -1, 0, + &algo, &size, &keyuse, &curve, + &keygrip, + &subalgo, &subsize, + &subkeyuse, &subcurve, + &subkeygrip); + if (err) + { + log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); + return; + } + para = quickgen_set_para (para, 0, + algo, size, curve, keyuse, + keygrip); + if (subalgo) + para = quickgen_set_para (para, 1, + subalgo, subsize, subcurve, subkeyuse, + subkeygrip); + + xfree (keygrip); + xfree (subkeygrip); + } + + + expire = full? ask_expire_interval (0, NULL) + : parse_expire_string (default_expiration_interval); + r = xcalloc (1, sizeof *r + 20); + r->key = pKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + r = xcalloc (1, sizeof *r + 20); + r->key = pSUBKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + + uid = ask_user_id (0, full, NULL); + if (!uid) + { + log_error(_("Key generation canceled.\n")); + release_parameter_list( para ); + return; + } + r = xcalloc (1, sizeof *r + strlen (uid)); + r->key = pUSERID; + strcpy (r->u.value, uid); + r->next = para; + para = r; + + proc_parameter_file (ctrl, para, "[internal]", &outctrl, !!card_serialno); + release_parameter_list (para); +} + + +/* Create and delete a dummy packet to start off a list of kbnodes. */ +static void +start_tree(KBNODE *tree) +{ + PACKET *pkt; + + pkt=xmalloc_clear(sizeof(*pkt)); + pkt->pkttype=PKT_NONE; + *tree=new_kbnode(pkt); + delete_kbnode(*tree); +} + + +/* Write the *protected* secret key to the file. */ +static gpg_error_t +card_write_key_to_backup_file (PKT_public_key *sk, const char *backup_dir) +{ + gpg_error_t err = 0; + int rc; + char keyid_buffer[2 * 8 + 1]; + char name_buffer[50]; + char *fname; + IOBUF fp; + mode_t oldmask; + PACKET *pkt = NULL; + + format_keyid (pk_keyid (sk), KF_LONG, keyid_buffer, sizeof (keyid_buffer)); + snprintf (name_buffer, sizeof name_buffer, "sk_%s.gpg", keyid_buffer); + + fname = make_filename (backup_dir, name_buffer, NULL); + /* Note that the umask call is not anymore needed because + iobuf_create now takes care of it. However, it does not harm + and thus we keep it. */ + oldmask = umask (077); + if (is_secured_filename (fname)) + { + fp = NULL; + gpg_err_set_errno (EPERM); + } + else + fp = iobuf_create (fname, 1); + umask (oldmask); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error (_("can't create backup file '%s': %s\n"), fname, strerror (errno) ); + goto leave; + } + + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SECRET_KEY; + pkt->pkt.secret_key = sk; + + rc = build_packet (fp, pkt); + if (rc) + { + log_error ("build packet failed: %s\n", gpg_strerror (rc)); + iobuf_cancel (fp); + } + else + { + char *fprbuf; + + iobuf_close (fp); + iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); + log_info (_("Note: backup of card key saved to '%s'\n"), fname); + + fprbuf = hexfingerprint (sk, NULL, 0); + if (!fprbuf) + { + err = gpg_error_from_syserror (); + goto leave; + } + write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf, + fname, strlen (fname), 0); + xfree (fprbuf); + } + + leave: + xfree (pkt); + xfree (fname); + return err; +} + + +/* Store key to card and make a backup file in OpenPGP format. */ +static gpg_error_t +card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, + const char *backup_dir) +{ + PKT_public_key *sk; + gnupg_isotime_t timestamp; + gpg_error_t err; + char *hexgrip; + int rc; + struct agent_card_info_s info; + gcry_cipher_hd_t cipherhd = NULL; + char *cache_nonce = NULL; + void *kek = NULL; + size_t keklen; + + sk = copy_public_key (NULL, sub_psk); + if (!sk) + return gpg_error_from_syserror (); + + epoch2isotime (timestamp, (time_t)sk->timestamp); + err = hexkeygrip_from_pk (sk, &hexgrip); + if (err) + return err; + + memset(&info, 0, sizeof (info)); + rc = agent_scd_getattr ("SERIALNO", &info); + if (rc) + return (gpg_error_t)rc; + + rc = agent_keytocard (hexgrip, 2, 1, info.serialno, timestamp); + xfree (info.serialno); + if (rc) + { + err = (gpg_error_t)rc; + goto leave; + } + + err = agent_keywrap_key (ctrl, 1, &kek, &keklen); + if (err) + { + log_error ("error getting the KEK: %s\n", gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (!err) + err = gcry_cipher_setkey (cipherhd, kek, keklen); + if (err) + { + log_error ("error setting up an encryption context: %s\n", + gpg_strerror (err)); + goto leave; + } + + err = receive_seckey_from_agent (ctrl, cipherhd, 0, + &cache_nonce, hexgrip, sk); + if (err) + { + log_error ("error getting secret key from agent: %s\n", + gpg_strerror (err)); + goto leave; + } + + err = card_write_key_to_backup_file (sk, backup_dir); + if (err) + log_error ("writing card key to backup file: %s\n", gpg_strerror (err)); + else + /* Remove secret key data in agent side. */ + agent_scd_learn (NULL, 1); + + leave: + xfree (cache_nonce); + gcry_cipher_close (cipherhd); + xfree (kek); + xfree (hexgrip); + free_public_key (sk); + return err; +} + + +static void +do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, + struct output_control_s *outctrl, int card) +{ + gpg_error_t err; + KBNODE pub_root = NULL; + const char *s; + PKT_public_key *pri_psk = NULL; + PKT_public_key *sub_psk = NULL; + struct revocation_key *revkey; + int did_sub = 0; + u32 timestamp; + char *cache_nonce = NULL; + int algo; + u32 expire; + const char *key_from_hexgrip = NULL; + + if (outctrl->dryrun) + { + log_info("dry-run mode - key generation skipped\n"); + return; + } + + if ( outctrl->use_files ) + { + if ( outctrl->pub.newfname ) + { + iobuf_close(outctrl->pub.stream); + outctrl->pub.stream = NULL; + if (outctrl->pub.fname) + iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, + 0, (char*)outctrl->pub.fname); + xfree( outctrl->pub.fname ); + outctrl->pub.fname = outctrl->pub.newfname; + outctrl->pub.newfname = NULL; + + if (is_secured_filename (outctrl->pub.fname) ) + { + outctrl->pub.stream = NULL; + gpg_err_set_errno (EPERM); + } + else + outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0); + if (!outctrl->pub.stream) + { + log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname, + strerror(errno) ); + return; + } + if (opt.armor) + { + outctrl->pub.afx->what = 1; + push_armor_filter (outctrl->pub.afx, outctrl->pub.stream); + } + } + log_assert( outctrl->pub.stream ); + if (opt.verbose) + log_info (_("writing public key to '%s'\n"), outctrl->pub.fname ); + } + + + /* We create the packets as a tree of kbnodes. Because the + structure we create is known in advance we simply generate a + linked list. The first packet is a dummy packet which we flag as + deleted. The very first packet must always be a KEY packet. */ + + start_tree (&pub_root); + + timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); + if (!timestamp) + timestamp = make_timestamp (); + + /* Note that, depending on the backend (i.e. the used scdaemon + version), the card key generation may update TIMESTAMP for each + key. Thus we need to pass TIMESTAMP to all signing function to + make sure that the binding signature is done using the timestamp + of the corresponding (sub)key and not that of the primary key. + An alternative implementation could tell the signing function the + node of the subkey but that is more work than just to pass the + current timestamp. */ + + algo = get_parameter_algo (ctrl, para, pKEYTYPE, NULL ); + expire = get_parameter_u32( para, pKEYEXPIRE ); + key_from_hexgrip = get_parameter_value (para, pKEYGRIP); + if (key_from_hexgrip) + err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, + pub_root, timestamp, expire, 0); + else if (!card) + err = do_create (algo, + get_parameter_uint( para, pKEYLENGTH ), + get_parameter_value (para, pKEYCURVE), + pub_root, + timestamp, + expire, 0, + outctrl->keygen_flags, + get_parameter_passphrase (para), + &cache_nonce, NULL); + else + err = gen_card_key (1, algo, + 1, pub_root, ×tamp, + expire); + + /* Get the pointer to the generated public key packet. */ + if (!err) + { + pri_psk = pub_root->next->pkt->pkt.public_key; + log_assert (pri_psk); + + /* Make sure a few fields are correctly set up before going + further. */ + pri_psk->flags.primary = 1; + keyid_from_pk (pri_psk, NULL); + /* We don't use pk_keyid to get keyid, because it also asserts + that main_keyid is set! */ + keyid_copy (pri_psk->main_keyid, pri_psk->keyid); + } + + if (!err && (revkey = get_parameter_revkey (para, pREVOKER))) + err = write_direct_sig (ctrl, pub_root, pri_psk, + revkey, timestamp, cache_nonce); + + if (!err && (s = get_parameter_value (para, pUSERID))) + { + err = write_uid (pub_root, s ); + if (!err) + err = write_selfsigs (ctrl, pub_root, pri_psk, + get_parameter_uint (para, pKEYUSAGE), timestamp, + cache_nonce); + } + + /* Write the auth key to the card before the encryption key. This + is a partial workaround for a PGP bug (as of this writing, all + versions including 8.1), that causes it to try and encrypt to + the most recent subkey regardless of whether that subkey is + actually an encryption type. In this case, the auth key is an + RSA key so it succeeds. */ + + if (!err && card && get_parameter (para, pAUTHKEYTYPE)) + { + err = gen_card_key (3, get_parameter_algo (ctrl, para, + pAUTHKEYTYPE, NULL ), + 0, pub_root, ×tamp, expire); + if (!err) + err = write_keybinding (ctrl, pub_root, pri_psk, NULL, + PUBKEY_USAGE_AUTH, timestamp, cache_nonce); + } + + if (!err && get_parameter (para, pSUBKEYTYPE)) + { + int subkey_algo = get_parameter_algo (ctrl, para, pSUBKEYTYPE, NULL); + + s = NULL; + key_from_hexgrip = get_parameter_value (para, pSUBKEYGRIP); + if (key_from_hexgrip) + err = do_create_from_keygrip (ctrl, subkey_algo, key_from_hexgrip, + pub_root, timestamp, + get_parameter_u32 (para, pSUBKEYEXPIRE), + 1); + else if (!card || (s = get_parameter_value (para, pCARDBACKUPKEY))) + { + err = do_create (subkey_algo, + get_parameter_uint (para, pSUBKEYLENGTH), + get_parameter_value (para, pSUBKEYCURVE), + pub_root, + timestamp, + get_parameter_u32 (para, pSUBKEYEXPIRE), 1, + s ? KEYGEN_FLAG_NO_PROTECTION : outctrl->keygen_flags, + get_parameter_passphrase (para), + &cache_nonce, NULL); + /* Get the pointer to the generated public subkey packet. */ + if (!err) + { + kbnode_t node; + + for (node = pub_root; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + sub_psk = node->pkt->pkt.public_key; + log_assert (sub_psk); + + if (s) + err = card_store_key_with_backup (ctrl, + sub_psk, gnupg_homedir ()); + } + } + else + { + err = gen_card_key (2, subkey_algo, 0, pub_root, ×tamp, expire); + } + + if (!err) + err = write_keybinding (ctrl, pub_root, pri_psk, sub_psk, + get_parameter_uint (para, pSUBKEYUSAGE), + timestamp, cache_nonce); + did_sub = 1; + } + + if (!err && outctrl->use_files) /* Direct write to specified files. */ + { + err = write_keyblock (outctrl->pub.stream, pub_root); + if (err) + log_error ("can't write public key: %s\n", gpg_strerror (err)); + } + else if (!err) /* Write to the standard keyrings. */ + { + KEYDB_HANDLE pub_hd; + + pub_hd = keydb_new (); + if (!pub_hd) + err = gpg_error_from_syserror (); + else + { + err = keydb_locate_writable (pub_hd); + if (err) + log_error (_("no writable public keyring found: %s\n"), + gpg_strerror (err)); + } + + if (!err && opt.verbose) + { + log_info (_("writing public key to '%s'\n"), + keydb_get_resource_name (pub_hd)); + } + + if (!err) + { + err = keydb_insert_keyblock (pub_hd, pub_root); + if (err) + log_error (_("error writing public keyring '%s': %s\n"), + keydb_get_resource_name (pub_hd), gpg_strerror (err)); + } + + keydb_release (pub_hd); + + if (!err) + { + int no_enc_rsa; + PKT_public_key *pk; + + no_enc_rsa = ((get_parameter_algo (ctrl, para, pKEYTYPE, NULL) + == PUBKEY_ALGO_RSA) + && get_parameter_uint (para, pKEYUSAGE) + && !((get_parameter_uint (para, pKEYUSAGE) + & PUBKEY_USAGE_ENC)) ); + + pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; + + update_ownertrust (ctrl, pk, + ((get_ownertrust (ctrl, pk) & ~TRUST_MASK) + | TRUST_ULTIMATE )); + + gen_standard_revoke (ctrl, pk, cache_nonce); + + /* Get rid of the first empty packet. */ + commit_kbnode (&pub_root); + + if (!opt.batch) + { + tty_printf (_("public and secret key created and signed.\n") ); + tty_printf ("\n"); + merge_keys_and_selfsig (ctrl, pub_root); + + list_keyblock_direct (ctrl, pub_root, 0, 1, + opt.fingerprint || opt.with_fingerprint, + 1); + } + + + if (!opt.batch + && (get_parameter_algo (ctrl, para, + pKEYTYPE, NULL) == PUBKEY_ALGO_DSA + || no_enc_rsa ) + && !get_parameter (para, pSUBKEYTYPE) ) + { + tty_printf(_("Note that this key cannot be used for " + "encryption. You may want to use\n" + "the command \"--edit-key\" to generate a " + "subkey for this purpose.\n") ); + } + } + } + + if (err) + { + if (opt.batch) + log_error ("key generation failed: %s\n", gpg_strerror (err) ); + else + tty_printf (_("Key generation failed: %s\n"), gpg_strerror (err) ); + write_status_error (card? "card_key_generate":"key_generate", err); + print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); + } + else + { + PKT_public_key *pk = find_kbnode (pub_root, + PKT_PUBLIC_KEY)->pkt->pkt.public_key; + print_status_key_created (did_sub? 'B':'P', pk, + get_parameter_value (para, pHANDLE)); + } + + release_kbnode (pub_root); + xfree (cache_nonce); +} + + +static gpg_error_t +parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, + const char *algostr, const char *usagestr, + const char *expirestr, + int *r_algo, unsigned int *r_usage, u32 *r_expire, + unsigned int *r_nbits, const char **r_curve, + char **r_keygrip) +{ + gpg_error_t err; + int algo; + unsigned int use, nbits; + u32 expire; + int wantuse; + const char *curve = NULL; + + *r_curve = NULL; + if (r_keygrip) + *r_keygrip = NULL; + + nbits = 0; + + /* Parse the algo string. */ + if (algostr && *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); + } + + err = parse_key_parameter_string (ctrl, algostr, for_subkey? 1 : 0, + usagestr? parse_usagestr (usagestr):0, + &algo, &nbits, &use, &curve, + r_keygrip, + NULL, NULL, NULL, NULL, NULL); + if (err) + { + if (r_keygrip) + { + xfree (*r_keygrip); + *r_keygrip = NULL; + } + return err; + } + + /* Parse the usage string. */ + if (!usagestr || !*usagestr + || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-")) + ; /* Keep usage from parse_key_parameter_string. */ + else if ((wantuse = parse_usagestr (usagestr)) != -1) + use = wantuse; + else + { + if (r_keygrip) + { + xfree (*r_keygrip); + *r_keygrip = NULL; + } + return gpg_error (GPG_ERR_INV_VALUE); + } + + /* Make sure a primary key has the CERT usage. */ + if (!for_subkey) + use |= PUBKEY_USAGE_CERT; + + /* Check that usage is possible. NB: We have the same check in + * parse_key_parameter_string but need it here again in case the + * separate usage value has been given. */ + if (/**/((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) + && !pubkey_get_nsig (algo)) + || ((use & PUBKEY_USAGE_ENC) + && !pubkey_get_nenc (algo)) + || (for_subkey && (use & PUBKEY_USAGE_CERT))) + { + if (r_keygrip) + { + xfree (*r_keygrip); + *r_keygrip = NULL; + } + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + + /* Parse the expire string. */ + expire = parse_expire_string (expirestr); + if (expire == (u32)-1 ) + { + if (r_keygrip) + { + xfree (*r_keygrip); + *r_keygrip = NULL; + } + return gpg_error (GPG_ERR_INV_VALUE); + } + + if (curve) + *r_curve = curve; + *r_algo = algo; + *r_usage = use; + *r_expire = expire; + *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; + int algo; + unsigned int use; + u32 expire; + unsigned int nbits = 0; + const char *curve = NULL; + u32 cur_time; + char *key_from_hexgrip = NULL; + char *hexgrip = NULL; + char *serialno = NULL; + char *cache_nonce = NULL; + char *passwd_nonce = NULL; + + interactive = (!algostr || !usagestr || !expirestr); + + /* Break out the primary key. */ + node = find_kbnode (keyblock, PKT_PUBLIC_KEY); + if (!node) + { + log_error ("Oops; primary key missing in keyblock!\n"); + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + pri_psk = node->pkt->pkt.public_key; + + cur_time = make_timestamp (); + + if (pri_psk->timestamp > cur_time) + { + ulong d = pri_psk->timestamp - cur_time; + log_info ( d==1 ? _("key has been created %lu second " + "in future (time warp or clock problem)\n") + : _("key has been created %lu seconds " + "in future (time warp or clock problem)\n"), d ); + if (!opt.ignore_time_conflict) + { + err = gpg_error (GPG_ERR_TIME_CONFLICT); + goto leave; + } + } + + if (pri_psk->version < 4) + { + log_info (_("Note: creating subkeys for v3 keys " + "is not OpenPGP compliant\n")); + err = gpg_error (GPG_ERR_CONFLICT); + goto leave; + } + + err = hexkeygrip_from_pk (pri_psk, &hexgrip); + if (err) + goto leave; + if (agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) + { + 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) + { + 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")); + } + + if (interactive) + { + algo = ask_algo (ctrl, 1, NULL, &use, &key_from_hexgrip); + log_assert (algo); + + if (key_from_hexgrip) + nbits = 0; + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH) + curve = ask_curve (&algo, NULL, 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_algo_usage_expire (ctrl, 1, algostr, usagestr, expirestr, + &algo, &use, &expire, &nbits, &curve, + &key_from_hexgrip); + if (err) + goto leave; + } + + /* Verify the passphrase now so that we get a cache item for the + * primary key passphrase. The agent also returns a passphrase + * nonce, which we can use to set the passphrase for the subkey to + * that of the primary key. */ + { + char *desc = gpg_format_keydesc (ctrl, pri_psk, FORMAT_KEYDESC_NORMAL, 1); + err = agent_passwd (ctrl, hexgrip, desc, 1 /*=verify*/, + &cache_nonce, &passwd_nonce); + xfree (desc); + if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED + && gpg_err_source (err) == GPG_ERR_SOURCE_GPGAGENT) + err = 0; /* Very likely that the key is on a card. */ + if (err) + goto leave; + } + + /* Start creation. */ + if (key_from_hexgrip) + { + err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, + keyblock, cur_time, expire, 1); + } + else + { + const char *passwd; + + /* If the pinentry loopback mode is not and we have a static + passphrase (i.e. set with --passphrase{,-fd,-file} while in batch + mode), we use that passphrase for the new subkey. */ + if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK + && have_static_passphrase ()) + passwd = get_static_passphrase (); + else + passwd = NULL; + + err = do_create (algo, nbits, curve, + keyblock, cur_time, expire, 1, 0, + passwd, &cache_nonce, &passwd_nonce); + } + if (err) + goto leave; + + /* Get the pointer to the generated public subkey packet. */ + for (node = keyblock; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + sub_psk = node->pkt->pkt.public_key; + + /* Write the binding signature. */ + err = write_keybinding (ctrl, keyblock, pri_psk, sub_psk, use, cur_time, + cache_nonce); + if (err) + goto leave; + + print_status_key_created ('S', sub_psk, NULL); + + + leave: + xfree (key_from_hexgrip); + xfree (hexgrip); + xfree (serialno); + xfree (cache_nonce); + xfree (passwd_nonce); + if (err) + log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); + return err; +} + + +#ifdef ENABLE_CARD_SUPPORT +/* Generate a subkey on a card. */ +gpg_error_t +generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, + int keyno, const char *serialno) +{ + gpg_error_t err = 0; + kbnode_t node; + PKT_public_key *pri_pk = NULL; + unsigned int use; + u32 expire; + u32 cur_time; + struct para_data_s *para = NULL; + PKT_public_key *sub_pk = NULL; + int algo; + struct agent_card_info_s info; + + log_assert (keyno >= 1 && keyno <= 3); + + memset (&info, 0, sizeof (info)); + err = agent_scd_getattr ("KEY-ATTR", &info); + if (err) + { + log_error (_("error getting current key info: %s\n"), gpg_strerror (err)); + return err; + } + algo = info.key_attr[keyno-1].algo; + + para = xtrycalloc (1, sizeof *para + strlen (serialno) ); + if (!para) + { + err = gpg_error_from_syserror (); + goto leave; + } + para->key = pSERIALNO; + strcpy (para->u.value, serialno); + + /* Break out the primary secret key */ + node = find_kbnode (pub_keyblock, PKT_PUBLIC_KEY); + if (!node) + { + log_error ("Oops; public key lost!\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + pri_pk = node->pkt->pkt.public_key; + + cur_time = make_timestamp(); + if (pri_pk->timestamp > cur_time) + { + ulong d = pri_pk->timestamp - cur_time; + log_info (d==1 ? _("key has been created %lu second " + "in future (time warp or clock problem)\n") + : _("key has been created %lu seconds " + "in future (time warp or clock problem)\n"), d ); + if (!opt.ignore_time_conflict) + { + err = gpg_error (GPG_ERR_TIME_CONFLICT); + goto leave; + } + } + + if (pri_pk->version < 4) + { + log_info (_("Note: creating subkeys for v3 keys " + "is not OpenPGP compliant\n")); + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + goto leave; + } + + expire = ask_expire_interval (0, NULL); + if (keyno == 1) + use = PUBKEY_USAGE_SIG; + else if (keyno == 2) + use = PUBKEY_USAGE_ENC; + else + use = PUBKEY_USAGE_AUTH; + if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", + _("Really create? (y/N) "))) + { + err = gpg_error (GPG_ERR_CANCELED); + goto leave; + } + + /* Note, that depending on the backend, the card key generation may + update CUR_TIME. */ + err = gen_card_key (keyno, algo, 0, pub_keyblock, &cur_time, expire); + /* Get the pointer to the generated public subkey packet. */ + if (!err) + { + for (node = pub_keyblock; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + sub_pk = node->pkt->pkt.public_key; + log_assert (sub_pk); + err = write_keybinding (ctrl, pub_keyblock, pri_pk, sub_pk, + use, cur_time, NULL); + } + + leave: + if (err) + log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); + else + print_status_key_created ('S', sub_pk, NULL); + release_parameter_list (para); + return err; +} +#endif /* !ENABLE_CARD_SUPPORT */ + +/* + * Write a keyblock to an output stream + */ +static int +write_keyblock( IOBUF out, KBNODE node ) +{ + for( ; node ; node = node->next ) + { + if(!is_deleted_kbnode(node)) + { + int rc = build_packet( out, node->pkt ); + if( rc ) + { + log_error("build_packet(%d) failed: %s\n", + node->pkt->pkttype, gpg_strerror (rc) ); + return rc; + } + } + } + + return 0; +} + + +/* Note that timestamp is an in/out arg. */ +static gpg_error_t +gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, + u32 *timestamp, u32 expireval) +{ +#ifdef ENABLE_CARD_SUPPORT + gpg_error_t err; + PACKET *pkt; + PKT_public_key *pk; + char keyid[10]; + unsigned char *public; + gcry_sexp_t s_key; + + snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno); + + pk = xtrycalloc (1, sizeof *pk ); + if (!pk) + return gpg_error_from_syserror (); + pkt = xtrycalloc (1, sizeof *pkt); + if (!pkt) + { + xfree (pk); + return gpg_error_from_syserror (); + } + + /* Note: SCD knows the serialnumber, thus there is no point in passing it. */ + err = agent_scd_genkey (keyno, 1, timestamp); + /* The code below is not used because we force creation of + * the a card key (3rd arg). + * if (gpg_err_code (rc) == GPG_ERR_EEXIST) + * { + * tty_printf ("\n"); + * log_error ("WARNING: key does already exists!\n"); + * tty_printf ("\n"); + * if ( cpr_get_answer_is_yes( "keygen.card.replace_key", + * _("Replace existing key? "))) + * rc = agent_scd_genkey (keyno, 1, timestamp); + * } + */ + if (err) + { + log_error ("key generation failed: %s\n", gpg_strerror (err)); + xfree (pkt); + xfree (pk); + return err; + } + + /* Send the READKEY command so that the agent creates a shadow key for + card key. We need to do that now so that we are able to create + the self-signatures. */ + err = agent_readkey (NULL, 1, keyid, &public); + if (err) + return err; + err = gcry_sexp_sscan (&s_key, NULL, public, + gcry_sexp_canon_len (public, 0, NULL, NULL)); + xfree (public); + if (err) + return err; + + if (algo == PUBKEY_ALGO_RSA) + err = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH ) + err = ecckey_from_sexp (pk->pkey, s_key, algo); + else + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + gcry_sexp_release (s_key); + + if (err) + { + log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); + free_public_key (pk); + return err; + } + + pk->timestamp = *timestamp; + pk->version = 4; + if (expireval) + pk->expiredate = pk->timestamp + expireval; + pk->pubkey_algo = algo; + + pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; + pkt->pkt.public_key = pk; + add_kbnode (pub_root, new_kbnode (pkt)); + + return 0; +#else + (void)keyno; + (void)is_primary; + (void)pub_root; + (void)timestamp; + (void)expireval; + return gpg_error (GPG_ERR_NOT_SUPPORTED); +#endif /*!ENABLE_CARD_SUPPORT*/ +} -- cgit v1.2.3