From eafe17532069e7ad64904e1d04952587a9c4dbd1 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 26 Sep 2024 10:37:32 +0200 Subject: [PATCH] gpg: New option --default-new-key-adsk and "addadsk" for edit-key. * g10/free-packet.c (copy_public_key): Factor some code out to ... (copy_public_key_basics): new. * keygen.c (keygen_add_key_flags_and_expire): Rewrite and make public. * g10/keyedit.c (enum cmdids): Add cmdADDADSK. (keyedit_menu): Add command "addadsk". (menu_addadsk): New. * g10/options.h (opt): Add field def_new_key_adsks. * g10/gpg.c (oDefaultNewKeyADSK): New. (opts): Add --default-new-key-adsk. (main): Parse option. * g10/keyedit.c (menu_addadsk): Factor some code out to ... (append_adsk_to_key): new. Add compliance check. * g10/keygen.c (pADSK): New. (para_data_s): Add adsk to the union. (release_parameter_list): Free the adsk. (prepare_adsk): New. (get_parameter_adsk): New. (get_parameter_revkey): Remove unneeded arg key and change callers. (proc_parameter_file): Prepare adsk parameter from the configured fingerprints. (do_generate_keypair): Create adsk. -- GnuPG-bug-id: 6882 (cherry picked from commit ed118e2ed521d82c1be7765a0a19d5b4f19afe10) and modified to adjust to other code changes --- doc/gpg.texi | 25 +++-- g10/build-packet.c | 12 ++- g10/free-packet.c | 28 +++++- g10/gpg.c | 13 +++ g10/gpgcompose.c | 10 ++ g10/keyedit.c | 237 ++++++++++++++++++++++++++++++++++++++++++++- g10/keyedit.h | 2 + g10/keygen.c | 184 +++++++++++++++++++++++++++++------ g10/main.h | 1 + g10/options.h | 2 + g10/packet.h | 11 ++- g10/sign.c | 25 +++-- 12 files changed, 495 insertions(+), 55 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index ee4378b13..c3b17fd51 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -1729,13 +1729,26 @@ this option at all (e.g. due to the @option{--no-options} option). @opindex add-desig-revoker Add the key specified by @var{fingerprint} as a designated revoker to newly created keys. If the fingerprint is prefixed with the keyword -``sensitive:'' that info is normally not exported wit the key. This -option may be given several time to add more than one designated +``sensitive:'' that info is normally not exported with the key. This +option may be given several times to add more than one designated revoker. If the keyword ``clear'' is used instead of a fingerprint, -all designated options previously encountered are discarded. -Designated revokers are marked on the key as non-revocable. Note that -a designated revoker specified using a parameter file will also be -added to the key. +all previously given fingerprints are discarded. Designated revokers +are marked on the key as non-revocable. Note that a designated +revoker specified using a parameter file will also be added to the +key. + +@item --default-new-key-adsk @var{fingerprint} +@opindex default-new-key-adsk +Add the subkey specified by @var{fingerprint} as an Additional +Decryption Subkey (ADSK) to newly created keys. This option may be +given several time to add more than one ADSK. It is also possible to +give several fingerprints delimited by space or comma as value to this +option. If the keyword ``clear'' is used instead of a fingerprint, +all previously specified fingerprints are discarded (useful to +override options given in a config file). The fingerprint is expected +to specify a subkey and it does not need an exclamation mark as +suffix; it must be given in cmpact format (40 or 64 hex-digits without +any spaces). @item --trust-model @{pgp|classic|tofu|tofu+pgp|direct|always|auto@} diff --git a/g10/build-packet.c b/g10/build-packet.c index 5de5114fb..b7936485c 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -1123,19 +1123,23 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, /* * Put all the required stuff from SIG into subpackets of sig. - * PKSK is the signing key. + * PKSK is the signing key. SIGNHINTS are various flags like + * SIGNHINT_ADSK. * Hmmm, should we delete those subpackets which are in a wrong area? */ void -build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk) +build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk, + unsigned int signhints) { u32 u; byte buf[1+MAX_FINGERPRINT_LEN]; size_t fprlen; /* For v4 keys we need to write the ISSUER subpacket. We do not - * want that for a future v5 format. */ - if (pksk->version < 5) + * want that for a future v5 format. We also don't write it if + * only the new RENC keyflag is set (implementations with support + * for this key flag should understand the ISSUER_FPR). */ + if (pksk->version < 5 && !(signhints & SIGNHINT_ADSK)) { u = sig->keyid[0]; buf[0] = (u >> 24) & 0xff; diff --git a/g10/free-packet.c b/g10/free-packet.c index 4df8896de..1021924b6 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -189,10 +189,10 @@ copy_prefs (const prefitem_t *prefs) /* Copy the public key S to D. If D is NULL allocate a new public key - structure. If S has seckret key infos, only the public stuff is - copied. */ + * structure. Only the basic stuff is copied; not any ancillary + * data. */ PKT_public_key * -copy_public_key (PKT_public_key *d, PKT_public_key *s) +copy_public_key_basics (PKT_public_key *d, PKT_public_key *s) { int n, i; @@ -200,8 +200,8 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) d = xmalloc (sizeof *d); memcpy (d, s, sizeof *d); d->seckey_info = NULL; - d->user_id = scopy_user_id (s->user_id); - d->prefs = copy_prefs (s->prefs); + d->user_id = NULL; + d->prefs = NULL; n = pubkey_get_npkey (s->pubkey_algo); i = 0; @@ -215,6 +215,24 @@ copy_public_key (PKT_public_key *d, PKT_public_key *s) for (; i < PUBKEY_MAX_NSKEY; i++) d->pkey[i] = NULL; + d->revkey = NULL; + d->serialno = NULL; + d->updateurl = NULL; + + return d; +} + + +/* Copy the public key S to D. If D is NULL allocate a new public key + structure. If S has seckret key infos, only the public stuff is + copied. */ +PKT_public_key * +copy_public_key (PKT_public_key *d, PKT_public_key *s) +{ + d = copy_public_key_basics (d, s); + d->user_id = scopy_user_id (s->user_id); + d->prefs = copy_prefs (s->prefs); + if (!s->revkey && s->numrevkeys) BUG(); if (s->numrevkeys) diff --git a/g10/gpg.c b/g10/gpg.c index 38ed41da1..0264d8bc3 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -422,6 +422,7 @@ enum cmd_and_opt_values oTOFUDefaultPolicy, oTOFUDBFormat, oDefaultNewKeyAlgo, + oDefaultNewKeyADSK, oWeakDigest, oUnwrap, oOnlySignTextIDs, @@ -627,6 +628,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oPGP7, "pgp7", "@"), ARGPARSE_s_n (oPGP8, "pgp8", "@"), ARGPARSE_s_s (oDefaultNewKeyAlgo, "default-new-key-algo", "@"), + ARGPARSE_s_s (oDefaultNewKeyADSK, "default-new-key-adsk", "@"), ARGPARSE_p_u (oMinRSALength, "min-rsa-length", "@"), #ifndef NO_TRUST_MODELS ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"), @@ -2309,6 +2311,7 @@ main (int argc, char **argv) const char *fname; char *username; int may_coredump; + gpg_error_t tmperr; strlist_t sl; strlist_t remusr = NULL; strlist_t locusr = NULL; @@ -3648,6 +3651,16 @@ main (int argc, char **argv) opt.def_new_key_algo = pargs.r.ret_str; break; + case oDefaultNewKeyADSK: + if (!strcmp (pargs.r.ret_str, "clear")) + FREE_STRLIST (opt.def_new_key_adsks); + else if (!tokenize_to_strlist (&opt.def_new_key_adsks, + pargs.r.ret_str, " \t,") + && (tmperr = gpg_err_code_from_syserror()) != GPG_ERR_ENOENT) + log_info (_("error parsing value for option '%s': %s\n"), + "--default-new-key-algo", gpg_strerror (tmperr)); + break; + case oUseOnlyOpenPGPCard: opt.flags.use_only_openpgp_card = 1; break; diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index d3b0c1442..42993b180 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -3122,3 +3122,13 @@ keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, (void) extended; return 0; } + + +gpg_error_t +append_adsk_to_key (ctrl_t ctrl, kbnode_t keyblock, PKT_public_key *adsk) +{ + (void)ctrl; + (void)keyblock; + (void)adsk; + return GPG_ERR_NOT_IMPLEMENTED; +} diff --git a/g10/keyedit.c b/g10/keyedit.c index 01de7bb48..2b0e378ba 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -73,6 +73,8 @@ static int menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only); static void menu_delkey (KBNODE pub_keyblock); static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive); +static int menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, + const char *adskfpr); static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended, u32 newexpiration); static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); @@ -1240,7 +1242,7 @@ enum cmdids cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, - cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, + cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, cmdADDADSK, #ifndef NO_TRUST_MODELS cmdENABLEKEY, cmdDISABLEKEY, #endif /*!NO_TRUST_MODELS*/ @@ -1303,6 +1305,8 @@ static struct { "delkey", cmdDELKEY, 0, N_("delete selected subkeys")}, { "addrevoker", cmdADDREVOKER, KEYEDIT_NEED_SK, N_("add a revocation key")}, + { "addadsk", cmdADDADSK, KEYEDIT_NEED_SK, + N_("add additional decryption subkeys")}, { "delsig", cmdDELSIG, 0, N_("delete signatures from the selected user IDs")}, { "expire", cmdEXPIRE, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, @@ -1966,6 +1970,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } break; + case cmdADDADSK: + if (menu_addadsk (ctrl, keyblock, NULL)) + { + redisplay = 1; + modified = 1; + merge_keys_and_selfsig (ctrl, keyblock); + } + break; + case cmdREVUID: { int n1; @@ -4639,6 +4652,228 @@ fail: } +/* Core function to add an ADSK to the KEYBLOCK. Returns 0 on success + * or an error code. */ +gpg_error_t +append_adsk_to_key (ctrl_t ctrl, kbnode_t keyblock, PKT_public_key *adsk) +{ + gpg_error_t err; + PKT_public_key *main_pk; /* The primary key. */ + PKT_signature *sig = NULL; + kbnode_t adsknode = NULL; + PACKET *pkt; /* (temp. use; will be put into a kbnode_t) */ + + /* First get a copy. */ + adsk = copy_public_key_basics (NULL, adsk); + + /* Check compliance. */ + if (!gnupg_pk_is_compliant (opt.compliance, adsk->pubkey_algo, 0, + adsk->pkey, nbits_from_pk (adsk), NULL)) + { + char pkhex[MAX_FINGERPRINT_LEN*2+1]; + + hexfingerprint (adsk, pkhex, sizeof pkhex); + log_error (_("WARNING: key %s is not suitable for encryption" + " in %s mode\n"), + pkhex, gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + + /* Get the primary key. */ + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); + main_pk = keyblock->pkt->pkt.public_key; + + /* Prepare and append the adsk. */ + keyid_from_pk (main_pk, adsk->main_keyid); /* Fixup main keyid. */ + log_assert ((adsk->pubkey_usage & PUBKEY_USAGE_ENC)); + adsk->pubkey_usage = PUBKEY_USAGE_RENC; /* 'e' -> 'r' */ + pkt = xtrycalloc (1, sizeof *pkt); + if (!pkt) + { + err = gpg_error_from_syserror (); + goto leave; + } + pkt->pkttype = PKT_PUBLIC_SUBKEY; /* Make sure it is a subkey. */ + pkt->pkt.public_key = adsk; + adsknode = new_kbnode (pkt); + + /* Make the signature. */ + err = make_keysig_packet (ctrl, &sig, main_pk, NULL, adsk, main_pk, 0x18, 0, + adsk->timestamp, 0, + keygen_add_key_flags_and_expire, adsk, NULL); + adsk = NULL; /* (owned by adsknode - avoid double free.) */ + if (err) + { + write_status_error ("keysig", err); + log_error ("creating key binding failed: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Append the subkey packet and the binding signature. */ + add_kbnode (keyblock, adsknode); + adsknode = NULL; + pkt = xtrycalloc (1, sizeof *pkt); + if (!pkt) + { + err = gpg_error_from_syserror (); + goto leave; + } + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode (keyblock, new_kbnode (pkt)); + + leave: + release_kbnode (adsknode); + free_public_key (adsk); /* Release our copy. */ + return err; +} + + +/* + * Ask for a new additional decryption subkey and add it to the key + * block. Returns true if the keyblock was changed and false + * otherwise. If ADSKFPR is not NULL, this function has been called + * by quick_addadsk and gives the fingerprint of the to be added key. + */ +static int +menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, const char *adskfpr) +{ + PKT_public_key *pk; + PKT_public_key *adsk_pk = NULL; + kbnode_t adsk_keyblock = NULL; + char *answer = NULL; + gpg_error_t err; + KEYDB_SEARCH_DESC desc; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + kbnode_t node, node2; + + log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); + + for (;;) + { + xfree (answer); + if (adskfpr) + answer = xstrdup (adskfpr); + else + { + answer = cpr_get_utf8 + ("keyedit.addadsk", + _("Enter the fingerprint of the additional decryption subkey: ")); + if (answer[0] == '\0' || answer[0] == CONTROL_D) + { + err = gpg_error (GPG_ERR_CANCELED); + goto leave; + } + } + if (classify_user_id (answer, &desc, 1) + || !(desc.mode == KEYDB_SEARCH_MODE_FPR + || desc.mode == KEYDB_SEARCH_MODE_FPR20)) + { + log_info (_("\"%s\" is not a fingerprint\n"), answer); + err = gpg_error (GPG_ERR_INV_USER_ID); + if (adskfpr) + goto leave; + continue; + } + + /* Force searching for that exact fingerprint and for any key + * which has a key with that fingerprint. */ + if (!strchr (answer, '!')) + { + char *tmpstr = xstrconcat (answer, "!", NULL); + xfree (answer); + answer = tmpstr; + } + + free_public_key (adsk_pk); + adsk_pk = xcalloc (1, sizeof *adsk_pk); + adsk_pk->req_usage = PUBKEY_USAGE_ENC; + release_kbnode (adsk_keyblock); + adsk_keyblock = NULL; + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, adsk_pk, answer, &adsk_keyblock, NULL, 1); + if (err) + { + log_info (_("key \"%s\" not found: %s\n"), answer, + gpg_strerror (err)); + if ((!opt.batch || adskfpr) && !opt.quiet + && gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY) + log_info (_("Did you specify the fingerprint of a subkey?\n")); + if (adskfpr) + goto leave; + continue; + } + + for (node = adsk_keyblock; node; node = node->next) + { + if (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + pk = node->pkt->pkt.public_key; + fingerprint_from_pk (pk, fpr, &fprlen); + if (fprlen == 20 + && !memcmp (fpr, desc.u.fpr, 20) + && (pk->pubkey_usage & PUBKEY_USAGE_ENC)) + break; + } + } + if (!node) + { + err = gpg_error (GPG_ERR_WRONG_KEY_USAGE); + log_info (_("key \"%s\" not found: %s\n"), answer, + gpg_strerror (err)); + if ((!opt.batch || adskfpr) && !opt.quiet) + log_info (_("Did you specify the fingerprint of a subkey?\n")); + if (adskfpr) + goto leave; + continue; + } + + /* Check that the selected subkey is not yet on our keyblock. */ + for (node2 = pub_keyblock; node2; node2 = node2->next) + { + if (node2->pkt->pkttype == PKT_PUBLIC_KEY + || node2->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + pk = node2->pkt->pkt.public_key; + fingerprint_from_pk (pk, fpr, &fprlen); + if (fprlen == 20 + && !memcmp (fpr, desc.u.fpr, 20)) + break; + } + } + if (node2) + { + log_info (_("key \"%s\" is already on this keyblock\n"), answer); + err = gpg_error (GPG_ERR_DUP_KEY); + if (adskfpr) + goto leave; + continue; + } + + break; + } + + /* Append the subkey. */ + log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); + err = append_adsk_to_key (ctrl, pub_keyblock, node->pkt->pkt.public_key); + + + leave: + xfree (answer); + free_public_key (adsk_pk); + release_kbnode (adsk_keyblock); + if (!err) + return 1; /* The keyblock was modified. */ + else + return 0; /* Not modified. */ + +} + + /* With FORCE_MAINKEY cleared this function handles the interactive * menu option "expire". With UNATTENDED set to 1 this function only * sets the expiration date of the primary key to NEWEXPIRATION and diff --git a/g10/keyedit.h b/g10/keyedit.h index e397b4a98..706afb1b1 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -55,6 +55,8 @@ void keyedit_quick_set_expire (ctrl_t ctrl, void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid); void keyedit_quick_update_pref (ctrl_t ctrl, const char *username); +gpg_error_t append_adsk_to_key (ctrl_t ctrl, kbnode_t keyblock, + PKT_public_key *adsk); void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock, int print_sec); int keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, int rc, kbnode_t keyblock, diff --git a/g10/keygen.c b/g10/keygen.c index 26126cde7..19ec52f23 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -91,6 +91,7 @@ enum para_name { pKEYSERVER, pKEYGRIP, pSUBKEYGRIP, + pADSK /* this uses u.adsk */ }; struct para_data_s { @@ -102,6 +103,7 @@ struct para_data_s { u32 creation; unsigned int usage; struct revocation_key revkey; + PKT_public_key *adsk; /* used with key == pADSK */ char value[1]; } u; }; @@ -246,22 +248,26 @@ write_uid (kbnode_t root, const char *s) static void do_add_key_flags (PKT_signature *sig, unsigned int use) { - byte buf[1]; + byte buf[2] = { 0, 0 }; - buf[0] = 0; + /* The spec says that all primary keys MUST be able to certify. */ + if(sig->sig_class!=0x18) + buf[0] |= 0x01; - /* 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; + if (use & PUBKEY_USAGE_GROUP) + buf[0] |= 0x80; - 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); + if (use & PUBKEY_USAGE_RENC) + buf[1] |= 0x04; + if (use & PUBKEY_USAGE_TIME) + buf[1] |= 0x08; + build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, buf[1]? 2:1); } @@ -308,10 +314,18 @@ keygen_add_key_flags (PKT_signature *sig, void *opaque) } +int +keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) +{ + keygen_add_key_flags (sig, opaque); + return keygen_add_key_expire (sig, opaque); +} + + /* This is only used to write the key binding signature. It is not * used for the primary key. */ static int -keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) +keygen_add_key_flags_from_oduap (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; @@ -1224,7 +1238,7 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, 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, + keygen_add_key_flags_from_oduap, &oduap, cache_nonce); if (err) { @@ -3693,6 +3707,9 @@ release_parameter_list (struct para_data_s *r) r2 = r->next; if (r->key == pPASSPHRASE && *r->u.value) wipememory (r->u.value, strlen (r->u.value)); + else if (r->key == pADSK) + free_public_key (r->u.adsk); + xfree (r); } } @@ -3889,7 +3906,8 @@ prepare_desig_revoker (ctrl_t ctrl, const char *name) } if (classify_user_id (name, &desc, 1) - || desc.mode != KEYDB_SEARCH_MODE_FPR) + || !(desc.mode == KEYDB_SEARCH_MODE_FPR + || desc.mode == KEYDB_SEARCH_MODE_FPR20)) { log_info (_("\"%s\" is not a fingerprint\n"), name); err = gpg_error (GPG_ERR_INV_NAME); @@ -3928,6 +3946,60 @@ prepare_desig_revoker (ctrl_t ctrl, const char *name) } +/* Parse asn ADSK specified by NAME, check that the public key exists + * and return a parameter with the adsk information. On error print a + * diagnostic and return NULL. */ +static struct para_data_s * +prepare_adsk (ctrl_t ctrl, const char *name) +{ + gpg_error_t err; + char *namebuffer = NULL; + struct para_data_s *para = NULL; + KEYDB_SEARCH_DESC desc; + PKT_public_key *adsk_pk = NULL; + char *p; + + if (classify_user_id (name, &desc, 1) + || !(desc.mode == KEYDB_SEARCH_MODE_FPR + || desc.mode == KEYDB_SEARCH_MODE_FPR20)) + { + log_info (_("\"%s\" is not a fingerprint\n"), name); + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + /* Force searching for that exact fingerprint. */ + if (!strchr (name, '!')) + { + namebuffer = xstrconcat (name, "!", NULL); + name = namebuffer; + } + + adsk_pk = xcalloc (1, sizeof *adsk_pk); + adsk_pk->req_usage = PUBKEY_USAGE_ENC; + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, adsk_pk, name, NULL, NULL, 1); + if (err) + goto leave; + + para = xcalloc (1, sizeof *para); + para->key = pADSK; + para->u.adsk = adsk_pk; + adsk_pk = NULL; + + leave: + if (err) + { + if (namebuffer && (p=strchr (namebuffer, '!'))) + *p = 0; /* Strip the ! for the diagnostic. */ + log_error ("invalid ADSK '%s' specified: %s\n", name, gpg_strerror (err)); + } + free_public_key (adsk_pk); + xfree (namebuffer); + return para; +} + + /* Parse a pREVOKER parameter into its dedicated parts. */ static int parse_revocation_key (const char *fname, @@ -4007,13 +4079,19 @@ get_parameter_uint( struct para_data_s *para, enum para_name key ) } static struct revocation_key * -get_parameter_revkey (struct para_data_s *para, enum para_name key, - unsigned int idx) +get_parameter_revkey (struct para_data_s *para, unsigned int idx) { - struct para_data_s *r = get_parameter_idx (para, key, idx); + struct para_data_s *r = get_parameter_idx (para, pREVOKER, idx); return r? &r->u.revkey : NULL; } +static PKT_public_key * +get_parameter_adsk (struct para_data_s *para, unsigned int idx) +{ + struct para_data_s *r = get_parameter_idx (para, pADSK, idx); + return r? r->u.adsk : NULL; +} + static int proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) @@ -4022,7 +4100,7 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, const char *s1, *s2, *s3; size_t n; char *p; - strlist_t sl; + strlist_t sl, slr; int is_default = 0; int have_user_id = 0; int err, algo; @@ -4182,8 +4260,36 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, append_to_parameter (para, r); } + /* Check and append ADSKs from the config file. While doing this + * also check for duplicate specifications. In addition we remove + * an optional '!' suffix for easier comparing; the suffix is anyway + * re-added later. */ + for (sl = opt.def_new_key_adsks; sl; sl = sl->next) + { + if (!*sl->d) + continue; + p = strchr (sl->d, '!'); + if (p) + *p = 0; + for (slr = opt.def_new_key_adsks; slr != sl; slr = slr->next) + if (!ascii_strcasecmp (sl->d, slr->d)) + { + *sl->d = 0; /* clear fpr to mark this as a duplicate. */ + break; + } + if (!*sl->d) + continue; - /* Make KEYCREATIONDATE from Creation-Date. */ + r = prepare_adsk (ctrl, sl->d); + if (!r) + return -1; + append_to_parameter (para, r); + } + + /* Make KEYCREATIONDATE from Creation-Date. We ignore this if the + * key has been taken from a card and a keycreationtime has already + * been set. This is so that we don't generate a key with a + * fingerprint different from the one stored on the OpenPGP card. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value) { @@ -5243,6 +5349,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, u32 expire; const char *key_from_hexgrip = NULL; unsigned int idx; + int any_adsk = 0; if (outctrl->dryrun) { @@ -5345,11 +5452,11 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, } /* Write all signatures specifying designated revokers. */ - for (idx=0; - !err && (revkey = get_parameter_revkey (para, pREVOKER, idx)); - idx++) - err = write_direct_sig (ctrl, pub_root, pri_psk, - revkey, timestamp, cache_nonce); + for (idx=0; !err && (revkey = get_parameter_revkey (para, idx)); idx++) + { + err = write_direct_sig (ctrl, pub_root, pri_psk, + revkey, timestamp, cache_nonce); + } if (!err && (s = get_parameter_value (para, pUSERID))) { @@ -5426,6 +5533,25 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, did_sub = 1; } + + /* Get rid of the first empty packet. */ + if (!err) + commit_kbnode (&pub_root); + + /* Add ADSKs if any are specified. */ + if (!err) + { + PKT_public_key *adsk; + + for (idx=0; (adsk = get_parameter_adsk (para, idx)); idx++) + { + err = append_adsk_to_key (ctrl, pub_root, adsk); + if (err) + break; + any_adsk++; + } + } + if (!err && outctrl->use_files) /* Direct write to specified files. */ { err = write_keyblock (outctrl->pub.stream, pub_root); @@ -5482,9 +5608,6 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, 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") ); @@ -5526,6 +5649,9 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); + es_fflush (es_stdout); + if (any_adsk) + log_info (_("Note: The key has been created with one or more ADSK!\n")); } release_kbnode (pub_root); diff --git a/g10/main.h b/g10/main.h index da181d33b..d8addf271 100644 --- a/g10/main.h +++ b/g10/main.h @@ -310,6 +310,7 @@ int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_key_flags (PKT_signature *sig, void *opaque); +int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); diff --git a/g10/options.h b/g10/options.h index c250e3ced..306d404ea 100644 --- a/g10/options.h +++ b/g10/options.h @@ -125,6 +125,8 @@ struct const char *def_new_key_algo; + strlist_t def_new_key_adsks; /* Option --default-new-key-adsk. */ + /* Options to be passed to the gpg-agent */ session_env_t session_env; char *lc_ctype; diff --git a/g10/packet.h b/g10/packet.h index 62979af51..7621d85e6 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -68,6 +68,11 @@ /* The usage bits which define encryption. */ #define PUBKEY_USAGE_XENC_MASK (PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC) +/* Bitflags to convey hints on what kind of signature is created. */ +#define SIGNHINT_KEYSIG 1 +#define SIGNHINT_SELFSIG 2 +#define SIGNHINT_ADSK 4 + /* Helper macros. */ #define is_RSA(a) ((a)==PUBKEY_ALGO_RSA || (a)==PUBKEY_ALGO_RSA_E \ @@ -863,7 +868,8 @@ gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); -void build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk); +void build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk, + unsigned int signhints); int delete_sig_subpkt(subpktarea_t *buffer, sigsubpkttype_t type ); void build_attribute_subpkt(PKT_user_id *uid,byte type, const void *buf,u32 buflen, @@ -885,7 +891,10 @@ void free_user_id( PKT_user_id *uid ); void free_comment( PKT_comment *rem ); void free_packet (PACKET *pkt, parse_packet_ctx_t parsectx); prefitem_t *copy_prefs (const prefitem_t *prefs); + +PKT_public_key *copy_public_key_basics (PKT_public_key *d, PKT_public_key *s); PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s ); + PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s ); PKT_user_id *scopy_user_id (PKT_user_id *sd ); int cmp_public_keys( PKT_public_key *a, PKT_public_key *b ); diff --git a/g10/sign.c b/g10/sign.c index def43c7ab..c978effb7 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -49,10 +49,6 @@ #define LF "\n" #endif -/* Bitflags to convey hints on what kind of signayire is created. */ -#define SIGNHINT_KEYSIG 1 -#define SIGNHINT_SELFSIG 2 - /* Hack */ static int recipient_digest_algo=0; @@ -355,7 +351,10 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, byte *dp; char *hexgrip; - if (pksk->timestamp > sig->timestamp ) + /* An ADSK key commonly has a creation date older than the primary + * key. For example because the ADSK is used as an archive key for + * a group of users. */ + if (pksk->timestamp > sig->timestamp && !(signhints & SIGNHINT_ADSK)) { ulong d = pksk->timestamp - sig->timestamp; log_info (ngettext("key %s was created %lu second" @@ -864,7 +863,7 @@ write_signature_packets (ctrl_t ctrl, if (sig->version >= 4) { - build_sig_subpkt_from_sig (sig, pk); + build_sig_subpkt_from_sig (sig, pk, 0); mk_notation_policy_etc (sig, NULL, pk); if (opt.flags.include_key_block && IS_SIG (sig)) err = mk_sig_subpkt_key_block (ctrl, sig, pk); @@ -1687,6 +1686,8 @@ make_keysig_packet (ctrl_t ctrl, { /* hash the subkey binding/backsig/revocation */ hash_public_key( md, subpk ); + if ((subpk->pubkey_usage & PUBKEY_USAGE_RENC)) + signhints |= SIGNHINT_ADSK; } else if( sigclass != 0x1F && sigclass != 0x20 ) { @@ -1709,7 +1710,7 @@ make_keysig_packet (ctrl_t ctrl, sig->expiredate=sig->timestamp+duration; sig->sig_class = sigclass; - build_sig_subpkt_from_sig (sig, pksk); + build_sig_subpkt_from_sig (sig, pksk, signhints); mk_notation_policy_etc (sig, pk, pksk); /* Crucial that the call to mksubpkt comes LAST before the calls @@ -1759,7 +1760,7 @@ update_keysig_packet (ctrl_t ctrl, int digest_algo; gcry_md_hd_t md; u32 pk_keyid[2], pksk_keyid[2]; - unsigned int signhints; + unsigned int signhints = 0; if ((!orig_sig || !pk || !pksk) || (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid) @@ -1820,6 +1821,12 @@ update_keysig_packet (ctrl_t ctrl, } } + /* Detect an ADSK key binding signature. */ + if ((sig->sig_class == 0x18 + || sig->sig_class == 0x19 || sig->sig_class == 0x28) + && (pk->pubkey_usage & PUBKEY_USAGE_RENC)) + signhints |= SIGNHINT_ADSK; + /* Note that already expired sigs will remain expired (with a duration of 1) since build-packet.c:build_sig_subpkt_from_sig detects this case. */ @@ -1828,7 +1835,7 @@ update_keysig_packet (ctrl_t ctrl, automagically lower any sig expiration dates to correctly correspond to the differences in the timestamps (i.e. the duration will shrink). */ - build_sig_subpkt_from_sig (sig, pksk); + build_sig_subpkt_from_sig (sig, pksk, signhints); if (mksubpkt) rc = (*mksubpkt)(sig, opaque);