diff options
Diffstat (limited to 'g10/keygen.c')
-rw-r--r-- | g10/keygen.c | 358 |
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 (¶->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); |