diff --git a/doc/gpg.texi b/doc/gpg.texi index 02131da75..67c6012c9 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -1809,12 +1809,25 @@ this option at all (e.g. due to the @option{--no-options} option). 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 with the key. This -option may be given several time to add more than one designated +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 fiven 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/gpg.c b/g10/gpg.c index c2165dafc..5359d1582 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -435,6 +435,7 @@ enum cmd_and_opt_values oTOFUDefaultPolicy, oTOFUDBFormat, oDefaultNewKeyAlgo, + oDefaultNewKeyADSK, oWeakDigest, oUnwrap, oOnlySignTextIDs, @@ -650,6 +651,7 @@ static gpgrt_opt_t 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", "@"), @@ -2372,6 +2374,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; @@ -3778,6 +3781,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; @@ -4291,8 +4304,7 @@ main (int argc, char **argv) && (ALWAYS_ADD_KEYRINGS || (cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfTest))) { - gpg_error_t tmperr = 0; - + tmperr = 0; if (!nrings || default_keyring > 0) /* Add default ring. */ tmperr = keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG, KEYDB_RESOURCE_FLAG_DEFAULT); diff --git a/g10/keyedit.c b/g10/keyedit.c index f41e53f6d..303309b79 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -4893,6 +4893,85 @@ 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, + 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 @@ -4903,22 +4982,16 @@ static int menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, const char *adskfpr) { PKT_public_key *pk; - PKT_public_key *sub_pk; - PKT_public_key *main_pk; PKT_public_key *adsk_pk = NULL; kbnode_t adsk_keyblock = NULL; - PKT_signature *sig = NULL; char *answer = NULL; gpg_error_t err; KEYDB_SEARCH_DESC desc; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; kbnode_t node, node2; - kbnode_t subkeynode = NULL; - PACKET *pkt; /* (temp. use; will be put into a kbnode_t) */ log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); - main_pk = pub_keyblock->pkt->pkt.public_key; for (;;) { @@ -5024,46 +5097,16 @@ menu_addadsk (ctrl_t ctrl, kbnode_t pub_keyblock, const char *adskfpr) break; } - /* Append the subkey. - * Note that we don't use the ADSK_PK directly because this is the - * primary key and in general we use a subkey to which NODE points. - * ADSK_PK has only been used to pass the requested key usage to - * get_pubkey_byname. SUB_PK will point to the actual adsk. */ + /* Append the subkey. */ log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); - sub_pk = copy_public_key_basics (NULL, node->pkt->pkt.public_key); - keyid_from_pk (main_pk, sub_pk->main_keyid); /* Fixup main keyid. */ - log_assert ((sub_pk->pubkey_usage & PUBKEY_USAGE_ENC)); - sub_pk->pubkey_usage = PUBKEY_USAGE_RENC; /* 'e' -> 'r' */ - pkt = xcalloc (1, sizeof *pkt); - pkt->pkttype = PKT_PUBLIC_SUBKEY; /* Make sure it is a subkey. */ - pkt->pkt.public_key = sub_pk; - subkeynode = new_kbnode (pkt); + err = append_adsk_to_key (ctrl, pub_keyblock, node->pkt->pkt.public_key); - /* Make the signature. */ - err = make_keysig_packet (ctrl, &sig, main_pk, NULL, sub_pk, main_pk, 0x18, - sub_pk->timestamp, 0, - keygen_add_key_flags_and_expire, sub_pk, NULL); - 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 (pub_keyblock, subkeynode); - subkeynode = NULL; - pkt = xcalloc (1, sizeof *pkt); - pkt->pkttype = PKT_SIGNATURE; - pkt->pkt.signature = sig; - add_kbnode (pub_keyblock, new_kbnode (pkt)); leave: xfree (answer); free_public_key (adsk_pk); release_kbnode (adsk_keyblock); - release_kbnode (subkeynode); if (!err) return 1; /* The keyblock was modified. */ else diff --git a/g10/keyedit.h b/g10/keyedit.h index 7cb01268e..1b2aec2b8 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -59,6 +59,8 @@ void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, void keyedit_quick_update_pref (ctrl_t ctrl, const char *username); void keyedit_quick_set_ownertrust (ctrl_t ctrl, const char *username, const char *value); +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 f6498e9fc..5908a09d0 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -97,6 +97,7 @@ enum para_name { pKEYSERVER, pKEYGRIP, pSUBKEYGRIP, + pADSK, /* this uses u.adsk */ pVERSION, /* Desired version of the key packet. */ pSUBVERSION, /* Ditto for the subpacket. */ pCARDKEY /* The keygrips have been taken from active card (bool). */ @@ -112,6 +113,7 @@ struct para_data_s { int abool; unsigned int usage; struct revocation_key revkey; + PKT_public_key *adsk; /* used with key == pADSK */ char value[1]; } u; }; @@ -4294,6 +4296,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); } } @@ -4530,6 +4535,59 @@ 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) + { + 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, @@ -4614,13 +4672,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 get_parameter_bool (struct para_data_s *para, enum para_name key) { @@ -4637,7 +4701,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; @@ -4800,6 +4864,33 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, } + /* 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; + + 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 @@ -5957,6 +6048,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, int cardkey; unsigned int keygen_flags; unsigned int idx; + int any_adsk = 0; if (outctrl->dryrun) { @@ -6093,11 +6185,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, signtimestamp, cache_nonce); + for (idx=0; !err && (revkey = get_parameter_revkey (para, idx)); idx++) + { + err = write_direct_sig (ctrl, pub_root, pri_psk, + revkey, signtimestamp, cache_nonce); + } if (!err && (s = get_parameter_value (para, pUSERID))) { @@ -6216,6 +6308,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); @@ -6273,9 +6384,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") ); @@ -6320,6 +6428,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/options.h b/g10/options.h index 644eff359..3edcf2f21 100644 --- a/g10/options.h +++ b/g10/options.h @@ -133,6 +133,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;