summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2024-06-03 18:52:06 +0200
committerWerner Koch <wk@gnupg.org>2024-06-03 18:52:06 +0200
commited118e2ed521d82c1be7765a0a19d5b4f19afe10 (patch)
tree61c2a3b3cdff0907f6b1546a6b74ba5f1bcf660d
parentcommon: New function tokenize_to_strlist. (diff)
downloadgnupg2-ed118e2ed521d82c1be7765a0a19d5b4f19afe10.tar.xz
gnupg2-ed118e2ed521d82c1be7765a0a19d5b4f19afe10.zip
gpg: New option --default-new-key-adsk.
* 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
-rw-r--r--doc/gpg.texi23
-rw-r--r--g10/gpg.c16
-rw-r--r--g10/keyedit.c119
-rw-r--r--g10/keyedit.h2
-rw-r--r--g10/keygen.c135
-rw-r--r--g10/options.h2
6 files changed, 240 insertions, 57 deletions
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);
-
- /* 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;
- }
+ err = append_adsk_to_key (ctrl, pub_keyblock, node->pkt->pkt.public_key);
- /* 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;