summaryrefslogtreecommitdiffstats
path: root/g10/keygen.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/keygen.c')
-rw-r--r--g10/keygen.c358
1 files changed, 321 insertions, 37 deletions
diff --git a/g10/keygen.c b/g10/keygen.c
index 80d65c4..52fd97a 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -54,7 +54,7 @@
/* When generating keys using the streamlined key generation dialog,
use this as a default expiration interval. */
-const char *default_expiration_interval = "2y";
+const char *default_expiration_interval = "3y";
/* Flag bits used during key generation. */
#define KEYGEN_FLAG_NO_PROTECTION 1
@@ -121,9 +121,11 @@ struct output_control_s
};
-struct opaque_data_usage_and_pk {
- unsigned int usage;
- PKT_public_key *pk;
+struct opaque_data_usage_and_pk
+{
+ unsigned int usage;
+ const char *cpl_notation;
+ PKT_public_key *pk;
};
@@ -135,6 +137,8 @@ static int nhash_prefs;
static byte zip_prefs[MAX_PREFS];
static int nzip_prefs;
static int mdc_available,ks_modify;
+static int aead_available;
+
static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
const char *algostr, const char *usagestr,
@@ -151,6 +155,9 @@ static gpg_error_t gen_card_key (int keyno, int algo, int is_primary,
u32 expireval);
static unsigned int get_keysize_range (int algo,
unsigned int *min, unsigned int *max);
+static void do_add_notation (PKT_signature *sig,
+ const char *name, const char *value,
+ int critical);
@@ -301,12 +308,16 @@ keygen_add_key_flags (PKT_signature *sig, void *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)
{
struct opaque_data_usage_and_pk *oduap = opaque;
do_add_key_flags (sig, oduap->usage);
+ if (oduap->cpl_notation)
+ do_add_notation (sig, "cpl@gnupg.org", oduap->cpl_notation, 0);
return keygen_add_key_expire (sig, oduap->pk);
}
@@ -354,8 +365,12 @@ 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. */
+ int ocb;
char dummy_string[20*4+1]; /* Enough for 20 items. */
+ /* Use OCB as default in GnuPG and de-vs mode. */
+ ocb = GNUPG;
+
if (!string || !ascii_strcasecmp (string, "default"))
{
if (opt.def_preference_list)
@@ -480,14 +495,24 @@ keygen_set_std_prefs (const char *string,int personal)
if(set_one_pref(val,3,tok,zip,&nzip))
rc=-1;
}
- else if (ascii_strcasecmp(tok,"mdc")==0)
+ else if (!ascii_strcasecmp(tok, "mdc")
+ || !ascii_strcasecmp(tok, "[mdc]"))
mdc=1;
- else if (ascii_strcasecmp(tok,"no-mdc")==0)
+ else if (!ascii_strcasecmp(tok, "no-mdc")
+ || !ascii_strcasecmp(tok, "[no-mdc]"))
mdc=0;
- else if (ascii_strcasecmp(tok,"ks-modify")==0)
+ else if (!ascii_strcasecmp(tok, "ks-modify")
+ || !ascii_strcasecmp(tok, "[ks-modify]"))
modify=1;
- else if (ascii_strcasecmp(tok,"no-ks-modify")==0)
+ else if (!ascii_strcasecmp(tok,"no-ks-modify")
+ || !ascii_strcasecmp(tok,"[no-ks-modify]"))
modify=0;
+ else if (!ascii_strcasecmp(tok,"aead")
+ || !ascii_strcasecmp(tok,"[aead]"))
+ ocb = 1;
+ else if (!ascii_strcasecmp(tok,"no-aead")
+ || !ascii_strcasecmp(tok,"[no-aead]"))
+ ocb = 0;
else
{
log_info (_("invalid item '%s' in preference string\n"),tok);
@@ -498,6 +523,10 @@ keygen_set_std_prefs (const char *string,int personal)
xfree (prefstringbuf);
}
+ /* For now we require a compat flag to set OCB into the preferences. */
+ if (!(opt.compat_flags & COMPAT_VSD_ALLOW_OCB))
+ ocb = 0;
+
if(!rc)
{
if(personal)
@@ -578,6 +607,7 @@ keygen_set_std_prefs (const char *string,int personal)
memcpy (hash_prefs, hash, (nhash_prefs=nhash));
memcpy (zip_prefs, zip, (nzip_prefs=nzip));
mdc_available = mdc;
+ aead_available = ocb;
ks_modify = modify;
prefs_initialized = 1;
}
@@ -586,6 +616,7 @@ keygen_set_std_prefs (const char *string,int personal)
return rc;
}
+
/* Return a fake user ID containing the preferences. Caller must
free. */
PKT_user_id *
@@ -624,6 +655,7 @@ keygen_get_std_prefs(void)
uid->prefs[j].value=0;
uid->flags.mdc=mdc_available;
+ uid->flags.aead=aead_available;
uid->flags.ks_modify=ks_modify;
return uid;
@@ -670,6 +702,49 @@ add_feature_mdc (PKT_signature *sig,int enabled)
xfree (buf);
}
+
+static void
+add_feature_aead (PKT_signature *sig, int enabled)
+{
+ const byte *s;
+ size_t n;
+ int i;
+ char *buf;
+
+ s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n );
+ if (s && n && ((enabled && (s[0] & 0x02)) || (!enabled && !(s[0] & 0x02))))
+ return; /* Already set or cleared */
+
+ 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] |= 0x02; /* AEAD supported */
+ else
+ buf[0] &= ~0x02;
+
+ /* Are there any bits set? */
+ for (i=0; i < n; i++)
+ if (buf[i])
+ break;
+
+ if (i == n)
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES);
+ else
+ build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n);
+
+ xfree (buf);
+}
+
+
static void
add_keyserver_modify (PKT_signature *sig,int enabled)
{
@@ -731,6 +806,14 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque)
delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM);
}
+ if (aead_available) /* The only preference is AEAD_ALGO_OCB. */
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, "\x02", 1);
+ else
+ {
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD);
+ delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD);
+ }
+
if (nhash_prefs)
build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs);
else
@@ -747,8 +830,9 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque)
delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR);
}
- /* Make sure that the MDC feature flag is set if needed. */
+ /* Make sure that the MDC and AEAD feature flags are set as needed. */
add_feature_mdc (sig,mdc_available);
+ add_feature_aead (sig, aead_available);
add_keyserver_modify (sig,ks_modify);
keygen_add_keyserver_url(sig,NULL);
@@ -789,6 +873,44 @@ keygen_add_keyserver_url(PKT_signature *sig, void *opaque)
return 0;
}
+
+/* This function is used to add a notations to a signature. In
+ * general the caller should have cleared exiting notations before
+ * adding new ones. For example by calling:
+ *
+ * delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION);
+ * delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION);
+ *
+ * Only human readable notaions may be added. NAME and value are
+ * expected to be UTF-* strings.
+ */
+static void
+do_add_notation (PKT_signature *sig, const char *name, const char *value,
+ int critical)
+{
+ unsigned char *buf;
+ unsigned int n1,n2;
+
+ n1 = strlen (name);
+ n2 = strlen (value);
+
+ buf = xmalloc (8 + n1 + n2);
+
+ buf[0] = 0x80; /* human readable. */
+ buf[1] = buf[2] = buf[3] = 0;
+ buf[4] = n1 >> 8;
+ buf[5] = n1;
+ buf[6] = n2 >> 8;
+ buf[7] = n2;
+ memcpy (buf+8, name, n1);
+ memcpy (buf+8+n1, value, n2);
+ build_sig_subpkt (sig,
+ (SIGSUBPKT_NOTATION|(critical?SIGSUBPKT_FLAG_CRITICAL:0)),
+ buf, 8+n1+n2 );
+ xfree (buf);
+}
+
+
int
keygen_add_notations(PKT_signature *sig,void *opaque)
{
@@ -838,6 +960,7 @@ keygen_add_notations(PKT_signature *sig,void *opaque)
return 0;
}
+
int
keygen_add_revkey (PKT_signature *sig, void *opaque)
{
@@ -1096,6 +1219,12 @@ write_keybinding (ctrl_t ctrl, kbnode_t root,
/* Make the signature. */
oduap.usage = use;
+ if ((use & PUBKEY_USAGE_ENC)
+ && opt.compliance == CO_DE_VS
+ && gnupg_rng_is_compliant (CO_DE_VS))
+ oduap.cpl_notation = "de-vs";
+ else
+ oduap.cpl_notation = NULL;
oduap.pk = sub_pk;
err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18,
0, timestamp, 0,
@@ -1730,6 +1859,9 @@ print_key_flags(int flags)
if(flags&PUBKEY_USAGE_AUTH)
tty_printf("%s ",_("Authenticate"));
+
+ if(flags&PUBKEY_USAGE_RENC)
+ tty_printf("%s ", "RENC");
}
@@ -1763,8 +1895,11 @@ ask_key_flags_with_mask (int algo, int subkey, unsigned int current,
}
/* Mask the possible usage flags. This is for example used for a
- * card based key. */
+ * card based key. For ECDH we need to allows additional usages if
+ * they are provided. RENC is not directly poissible here but see
+ * below for a workaround. */
possible = (openpgp_pk_algo_usage (algo) & mask);
+ possible &= ~PUBKEY_USAGE_RENC;
/* However, only primary keys may certify. */
if (subkey)
@@ -1827,6 +1962,12 @@ ask_key_flags_with_mask (int algo, int subkey, unsigned int current,
want to experiment with a cert-only primary key. */
current |= PUBKEY_USAGE_CERT;
}
+ else if ((*s == 'r' || *s == 'R') && (possible&PUBKEY_USAGE_ENC))
+ {
+ /* Allow to set RENC or an encryption capable key.
+ * This is on purpose not shown in the menu. */
+ current |= PUBKEY_USAGE_RENC;
+ }
}
break;
}
@@ -2550,26 +2691,39 @@ ask_curve (int *algo, int *subkey_algo, const char *current)
* similar.
*/
u32
-parse_expire_string( const char *string )
+parse_expire_string (const char *string)
{
int mult;
u32 seconds;
u32 abs_date = 0;
u32 curtime = make_timestamp ();
- time_t tt;
+ uint64_t tt;
+ uint64_t tmp64;
if (!string || !*string || !strcmp (string, "none")
|| !strcmp (string, "never") || !strcmp (string, "-"))
seconds = 0;
else if (!strncmp (string, "seconds=", 8))
- seconds = atoi (string+8);
+ seconds = scan_secondsstr (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 ((tt = isotime2epoch_u64 (string)) != (uint64_t)(-1))
+ {
+ tmp64 = tt - curtime;
+ if (tmp64 >= (u32)(-1))
+ seconds = (u32)(-1) - 1; /* cap value. */
+ else
+ seconds = (u32)tmp64;
+ }
else if ((mult = check_valid_days (string)))
- seconds = atoi (string) * 86400L * mult;
+ {
+ tmp64 = scan_secondsstr (string) * 86400L * mult;
+ if (tmp64 >= (u32)(-1))
+ seconds = (u32)(-1) - 1; /* cap value. */
+ else
+ seconds = (u32)tmp64;
+ }
else
seconds = (u32)(-1);
@@ -2586,11 +2740,16 @@ parse_creation_string (const char *string)
if (!*string)
seconds = 0;
else if ( !strncmp (string, "seconds=", 8) )
- seconds = atoi (string+8);
+ seconds = scan_secondsstr (string+8);
else if ( !(seconds = scan_isodatestr (string)))
{
- time_t tmp = isotime2epoch (string);
- seconds = (tmp == (time_t)(-1))? 0 : tmp;
+ uint64_t tmp = isotime2epoch_u64 (string);
+ if (tmp == (uint64_t)(-1))
+ seconds = 0;
+ else if (tmp > (u32)(-1))
+ seconds = 0;
+ else
+ seconds = tmp;
}
return seconds;
}
@@ -3542,14 +3701,29 @@ release_parameter_list (struct para_data_s *r)
}
}
+/* Return the N-th parameter of name KEY from PARA. An IDX of 0
+ * returns the first and so on. */
static struct para_data_s *
-get_parameter( struct para_data_s *para, enum para_name key )
+get_parameter_idx (struct para_data_s *para, enum para_name key,
+ unsigned int idx)
{
- struct para_data_s *r;
+ struct para_data_s *r;
+
+ for(r = para; r; r = r->next)
+ if (r->key == key)
+ {
+ if (!idx)
+ return r;
+ idx--;
+ }
+ return NULL;
+}
- for( r = para; r && r->key != key; r = r->next )
- ;
- return r;
+/* Return the first parameter of name KEY from PARA. */
+static struct para_data_s *
+get_parameter (struct para_data_s *para, enum para_name key)
+{
+ return get_parameter_idx (para, key, 0);
}
static const char *
@@ -3653,6 +3827,12 @@ parse_usagestr (const char *usagestr)
use |= PUBKEY_USAGE_AUTH;
else if (!ascii_strcasecmp (s, "cert"))
use |= PUBKEY_USAGE_CERT;
+ else if (!ascii_strcasecmp (s, "renc"))
+ use |= PUBKEY_USAGE_RENC;
+ else if (!ascii_strcasecmp (s, "time"))
+ use |= PUBKEY_USAGE_TIME;
+ else if (!ascii_strcasecmp (s, "group"))
+ use |= PUBKEY_USAGE_GROUP;
else
{
xfree (tokens);
@@ -3691,6 +3871,68 @@ parse_parameter_usage (const char *fname,
}
+/* Parse the revocation key specified by NAME, check that the public
+ * key exists (so that we can get the required public key algorithm),
+ * and return a parameter wit the revocation key information. On
+ * error print a diagnostic and return NULL. */
+static struct para_data_s *
+prepare_desig_revoker (ctrl_t ctrl, const char *name)
+{
+ gpg_error_t err;
+ struct para_data_s *para = NULL;
+ KEYDB_SEARCH_DESC desc;
+ int sensitive = 0;
+ struct revocation_key revkey;
+ PKT_public_key *revoker_pk = NULL;
+ size_t fprlen;
+
+ if (!ascii_strncasecmp (name, "sensitive:", 10) && !spacep (name+10))
+ {
+ name += 10;
+ sensitive = 1;
+ }
+
+ 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;
+ }
+
+ revoker_pk = xcalloc (1, sizeof *revoker_pk);
+ revoker_pk->req_usage = PUBKEY_USAGE_CERT;
+ err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
+ NULL, revoker_pk, name, NULL, NULL, 1);
+ if (err)
+ goto leave;
+
+ fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen);
+ if (fprlen != 20)
+ {
+ log_info (_("cannot appoint a PGP 2.x style key as a "
+ "designated revoker\n"));
+ err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
+ goto leave;
+ }
+ revkey.class = 0x80;
+ if (sensitive)
+ revkey.class |= 0x40;
+ revkey.algid = revoker_pk->pubkey_algo;
+
+ para = xcalloc (1, sizeof *para);
+ para->key = pREVOKER;
+ memcpy (&para->u.revkey, &revkey, sizeof revkey);
+
+ leave:
+ if (err)
+ log_error ("invalid revocation key '%s': %s\n", name, gpg_strerror (err));
+ free_public_key (revoker_pk);
+ return para;
+}
+
+
+/* Parse a pREVOKER parameter into its dedicated parts. */
static int
parse_revocation_key (const char *fname,
struct para_data_s *para, enum para_name key)
@@ -3769,10 +4011,11 @@ 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 )
+get_parameter_revkey (struct para_data_s *para, enum para_name key,
+ unsigned int idx)
{
- struct para_data_s *r = get_parameter( para, key );
- return r? &r->u.revkey : NULL;
+ struct para_data_s *r = get_parameter_idx (para, key, idx);
+ return r? &r->u.revkey : NULL;
}
static int
@@ -3783,6 +4026,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;
int is_default = 0;
int have_user_id = 0;
int err, algo;
@@ -3928,10 +4172,20 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname,
}
}
- /* Set revoker, if any. */
+ /* Set revoker from parameter file, if any. Must be done first so
+ * that we don't find a parameter set via prepare_desig_revoker. */
if (parse_revocation_key (fname, para, pREVOKER))
return -1;
+ /* Check and append revokers from the config file. */
+ for (sl = opt.desig_revokers; sl; sl = sl->next)
+ {
+ r = prepare_desig_revoker (ctrl, sl->d);
+ if (!r)
+ return -1;
+ append_to_parameter (para, r);
+ }
+
/* Make KEYCREATIONDATE from Creation-Date. */
r = get_parameter (para, pCREATIONDATE);
@@ -4197,14 +4451,17 @@ quickgen_set_para (struct para_data_s *para, int for_subkey,
{
struct para_data_s *r;
- r = xmalloc_clear (sizeof *r + 30);
+ r = xmalloc_clear (sizeof *r + 50);
r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE;
if (use)
- snprintf (r->u.value, 30, "%s%s%s%s",
+ snprintf (r->u.value, 30, "%s%s%s%s%s%s%s",
(use & PUBKEY_USAGE_ENC)? "encr " : "",
(use & PUBKEY_USAGE_SIG)? "sign " : "",
(use & PUBKEY_USAGE_AUTH)? "auth " : "",
- (use & PUBKEY_USAGE_CERT)? "cert " : "");
+ (use & PUBKEY_USAGE_CERT)? "cert " : "",
+ (use & PUBKEY_USAGE_RENC)? "renc " : "",
+ (use & PUBKEY_USAGE_TIME)? "time " : "",
+ (use & PUBKEY_USAGE_GROUP)?"group ": "");
else
strcpy (r->u.value, for_subkey ? "encr" : "sign");
r->next = para;
@@ -4883,22 +5140,41 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
char *cache_nonce = NULL;
void *kek = NULL;
size_t keklen;
+ char *ecdh_param_str = NULL;
sk = copy_public_key (NULL, sub_psk);
if (!sk)
return gpg_error_from_syserror ();
epoch2isotime (timestamp, (time_t)sk->timestamp);
+ if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
+ {
+ ecdh_param_str = ecdh_param_str_from_pk (sk);
+ if (!ecdh_param_str)
+ {
+ free_public_key (sk);
+ return gpg_error_from_syserror ();
+ }
+ }
err = hexkeygrip_from_pk (sk, &hexgrip);
if (err)
- return err;
+ {
+ xfree (ecdh_param_str);
+ free_public_key (sk);
+ return err;
+ }
memset(&info, 0, sizeof (info));
rc = agent_scd_getattr ("SERIALNO", &info);
if (rc)
- return (gpg_error_t)rc;
+ {
+ xfree (ecdh_param_str);
+ free_public_key (sk);
+ return (gpg_error_t)rc;
+ }
- rc = agent_keytocard (hexgrip, 2, 1, info.serialno, timestamp);
+ rc = agent_keytocard (hexgrip, 2, 1, info.serialno,
+ timestamp, ecdh_param_str);
xfree (info.serialno);
if (rc)
{
@@ -4937,10 +5213,14 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
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);
+ {
+ /* Remove secret key data in agent side. We use force 2 here to
+ * allow overwriting of the temporary private key. */
+ agent_scd_learn (NULL, 2);
+ }
leave:
+ xfree (ecdh_param_str);
xfree (cache_nonce);
gcry_cipher_close (cipherhd);
xfree (kek);
@@ -4966,6 +5246,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
int algo;
u32 expire;
const char *key_from_hexgrip = NULL;
+ unsigned int idx;
if (outctrl->dryrun)
{
@@ -5067,7 +5348,10 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
keyid_copy (pri_psk->main_keyid, pri_psk->keyid);
}
- if (!err && (revkey = get_parameter_revkey (para, pREVOKER)))
+ /* 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);