diff --git a/NEWS b/NEWS
index c701794ce..06fa14d78 100644
--- a/NEWS
+++ b/NEWS
@@ -13,7 +13,8 @@
except for revocation certificates and --enarmor mode.
* The command "primary" in the edit menu can be used to change the
- primary UID.
+ primary UID, "setpref" and "updpref" can be used to change the
+ preferences.
Noteworthy changes in version 1.0.6 (2001-05-29)
------------------------------------------------
diff --git a/TODO b/TODO
index 401cbd258..69a1f4f1b 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,8 @@
* add listing of notation data
+ * Make sure that we only update the latest self-signatures.
+
* Check the changes to the gpg random agtherer on all W32 platforms.
* Check that a key signature can be revoked and later be signed again.
diff --git a/doc/gpg.sgml b/doc/gpg.sgml
index 57c3f4400..80413283c 100644
--- a/doc/gpg.sgml
+++ b/doc/gpg.sgml
@@ -326,6 +326,12 @@ primary key is changed.
passwd
Change the passphrase of the secret key.
+
+ primary
+
+ Flag the current user id as the primary one, removes the primary user
+ id flag from all other user ids and sets the timestamp of all
+ affected self-signatures one second ahead.
uid &ParmN;
@@ -348,6 +354,21 @@ List preferences.
showpref
More verbose preferences listing.
+ setpref &ParmString;
+
+Set the list of user ID preferences to &ParmString;, this should be
+a string similar to the one printed by "pref". Using an empty string
+will set the default preference string, using "none" will set the
+preferences to nil. Only available algorithms are allowed. This
+command just initializes an internal list and does not change anything
+unless another command which changes the self-signatures is used.
+
+ updpref
+
+Change the preferences of all user IDs (or just of the selected ones
+to the current list of preferences. The timestamp of all affected
+self-signatures fill be advanced by one second.
+
toggle
@@ -1509,6 +1530,14 @@ Don't change the permissions of a secret keyring back to user
read/write only. Use this option only if you really know what you are doing.
+
+--preference-list &ParmString
+
+Set the list of preferences to &ParmString;, this list should be
+a string similar to the one printed by the command "pref" in the edit
+menu.
+
+
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 5a29b0cfd..5325767db 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,17 @@
+2001-08-09 Werner Koch
+
+ * g10.c (main): New option "--preference-list"
+ * keyedit.c (keyedit_menu): New commands "setpref" and "updpref".
+ (menu_set_preferences): New.
+ * keygen.c (keygen_set_std_prefs): New.
+ (set_one_pref): New.
+ (check_zip_algo): New.
+ (keygen_get_std_prefs): New.
+ (keygen_upd_std_prefs): New
+ (keygen_add_std_prefs): Move the pref setting code into the above fnc.
+ * build-packet.c (build_sig_subpkt): Updated the list of allowed
+ to update subpackets.
+
2001-08-08 Werner Koch
* packet.h (subpktarea_t): New.
diff --git a/g10/build-packet.c b/g10/build-packet.c
index ac21f8896..1235a451e 100644
--- a/g10/build-packet.c
+++ b/g10/build-packet.c
@@ -744,6 +744,9 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type,
else if (find_subpkt (sig->hashed, type, NULL, NULL) ) {
switch (type) {
case SIGSUBPKT_SIG_CREATED:
+ case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_HASH:
+ case SIGSUBPKT_PREF_COMPR:
delete_sig_subpkt (sig->hashed, type);
break;
default:
diff --git a/g10/g10.c b/g10/g10.c
index 7e5f281f8..221b7a8f8 100644
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -208,6 +208,7 @@ enum cmd_and_opt_values { aNull = 0,
oNoSigCache,
oNoSigCreateCheck,
oPreservePermissions,
+ oPreferenceList,
oEmu3DESS2KBug, /* will be removed in 1.1 */
oEmuMDEncodeBug,
aTest };
@@ -408,6 +409,7 @@ static ARGPARSE_OPTS opts[] = {
{ oNoExpensiveTrustChecks, "no-expensive-trust-checks", 0, "@" },
{ aDeleteSecretAndPublicKey, "delete-secret-and-public-key",256, "@" },
{ oPreservePermissions, "preserve-permissions", 0, "@"},
+ { oPreferenceList, "preference-list", 2, "@"},
{ oEmu3DESS2KBug, "emulate-3des-s2k-bug", 0, "@"},
{ oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"},
{0} };
@@ -619,6 +621,7 @@ main( int argc, char **argv )
char *def_digest_string = NULL;
char *s2k_cipher_string = NULL;
char *s2k_digest_string = NULL;
+ char *preference_list = NULL;
int pwfd = -1;
int with_fpr = 0; /* make an option out of --fingerprint */
#ifdef USE_SHM_COPROCESSING
@@ -992,7 +995,7 @@ main( int argc, char **argv )
break;
case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break;
case oPreservePermissions: opt.preserve_permissions=1; break;
-
+ case oPreferenceList: preference_list = pargs.r.ret_str; break;
default : pargs.err = configfp? 1:2; break;
}
}
@@ -1092,6 +1095,8 @@ main( int argc, char **argv )
log_error(_("invalid S2K mode; must be 0, 1 or 3\n"));
}
+ if (preference_list && keygen_set_std_prefs (preference_list))
+ log_error(_("invalid preferences\n"));
if( log_get_errorcount(0) )
g10_exit(2);
diff --git a/g10/helptext.c b/g10/helptext.c
index b60339d3b..35feccc71 100644
--- a/g10/helptext.c
+++ b/g10/helptext.c
@@ -197,6 +197,12 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = {
"a second one is available."
)},
+{ "keyedit.updpref.okay", N_(
+ "Change the preferences of all user IDs (or just of the selected ones)\n"
+ "to the current list of preferences. The timestamp of all affected\n"
+ "self-signatures fill be advanced by one second.\n"
+)},
+
{ "passphrase.enter", N_(
""
diff --git a/g10/keyedit.c b/g10/keyedit.c
index b388f7f5a..8f4dccf4c 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -51,6 +51,7 @@ static int menu_delsig( KBNODE pub_keyblock );
static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock );
static int menu_set_primary_uid( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_set_preferences( KBNODE pub_keyblock, KBNODE sec_keyblock );
static int menu_select_uid( KBNODE keyblock, int idx );
static int menu_select_key( KBNODE keyblock, int idx );
static int count_uids( KBNODE keyblock );
@@ -575,7 +576,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands,
cmdLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, cmdPRIMARY,
cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
- cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF,
+ cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF,
cmdINVCMD, cmdNOP };
static struct { const char *name;
enum cmdids id;
@@ -612,6 +613,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands,
{ N_("t" ) , cmdTOGGLE , 1,0,0, NULL },
{ N_("pref") , cmdPREF , 0,1,0, N_("list preferences") },
{ N_("showpref"), cmdSHOWPREF , 0,1,0, N_("list preferences") },
+ { N_("setpref") , cmdSETPREF , 1,1,0, N_("set preference list") },
+ { N_("updpref") , cmdUPDPREF , 1,1,0, N_("updated preferences") },
{ N_("passwd") , cmdPASSWD , 1,1,0, N_("change the passphrase") },
{ N_("trust") , cmdTRUST , 0,1,0, N_("change the ownertrust") },
{ N_("revsig") , cmdREVSIG , 0,1,0, N_("revoke signatures") },
@@ -682,6 +685,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands,
cur_keyblock = keyblock;
for(;;) { /* main loop */
int i, arg_number;
+ const char *arg_string = "";
char *p;
tty_printf("\n");
@@ -725,6 +729,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands,
trim_spaces(answer);
trim_spaces(p);
arg_number = atoi(p);
+ arg_string = p;
}
for(i=0; cmds[i].name; i++ ) {
@@ -952,6 +957,31 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands,
show_key_with_all_names( keyblock, 0, 0, 0, 2 );
break;
+ case cmdSETPREF:
+ keygen_set_std_prefs ( !*arg_string? "default" : arg_string );
+ break;
+
+ case cmdUPDPREF:
+ {
+ char *p = keygen_get_std_prefs ();
+ tty_printf (("Current preference list: %s\n"), p);
+ m_free (p);
+ }
+ if (cpr_get_answer_is_yes ("keyedit.updpref.okay",
+ count_selected_uids (keyblock)?
+ _("Really update the preferences"
+ " for the selected user IDs? "):
+ _("Really update the preferences? "))){
+
+ if ( menu_set_preferences (keyblock, sec_keyblock) ) {
+ update_trust_record (keyblock, 0, NULL);
+ merge_keys_and_selfsig (keyblock);
+ modified = 1;
+ redisplay = 1;
+ }
+ }
+ break;
+
case cmdNOP:
break;
@@ -1665,6 +1695,7 @@ change_primary_uid_cb ( PKT_signature *sig, void *opaque )
return 0;
}
+
/*
* Set the primary uid flag for the selected UID. We will also reset
* all other primary uid flags. For this to work with have to update
@@ -1765,6 +1796,82 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock )
}
+/*
+ * Set preferences to new values for the selected user IDs
+ */
+static int
+menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_secret_key *sk; /* copy of the main sk */
+ PKT_public_key *main_pk;
+ PKT_user_id *uid;
+ KBNODE node;
+ u32 keyid[2];
+ int selected, select_all;
+ int modified = 0;
+
+ select_all = !count_selected_uids (pub_keyblock);
+
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+ /* Now we can actually change the self signature(s) */
+ main_pk = NULL;
+ uid = NULL;
+ selected = 0;
+ for ( node=pub_keyblock; node; node = node->next ) {
+ if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break; /* ready */
+
+ if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ main_pk = node->pkt->pkt.public_key;
+ keyid_from_pk( main_pk, keyid );
+ }
+ else if ( node->pkt->pkttype == PKT_USER_ID ) {
+ uid = node->pkt->pkt.user_id;
+ selected = select_all || (node->flag & NODFLG_SELUID);
+ }
+ else if ( main_pk && uid && selected
+ && node->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+ && (uid && (sig->sig_class&~3) == 0x10)
+ && sig->version >= 4 ) {
+ /* This is a selfsignature which is to be replaced
+ * We have to ignore v3 signatures because they are
+ * not able to carry the preferences */
+ PKT_signature *newsig;
+ PACKET *newpkt;
+ int rc;
+
+ rc = update_keysig_packet (&newsig, sig,
+ main_pk, uid,
+ sk,
+ keygen_upd_std_prefs,
+ NULL );
+ if( rc ) {
+ log_error ("update_keysig_packet failed: %s\n",
+ g10_errstr(rc));
+ free_secret_key( sk );
+ return 0;
+ }
+ /* replace the packet */
+ newpkt = m_alloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = newsig;
+ free_packet( node->pkt );
+ m_free( node->pkt );
+ node->pkt = newpkt;
+ modified = 1;
+ }
+ }
+ }
+
+ free_secret_key( sk );
+ return modified;
+}
+
+
/****************
* Select one user id or remove all selection if index is 0.
* Returns: True if the selection changed;
diff --git a/g10/keygen.c b/g10/keygen.c
index 5a9145bcb..693eb3329 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -35,6 +35,9 @@
#include "status.h"
#include "i18n.h"
+#define MAX_PREFS 30
+
+
enum para_name {
pKEYTYPE,
pKEYLENGTH,
@@ -83,6 +86,15 @@ struct output_control_s {
};
+static int prefs_initialized = 0;
+static byte sym_prefs[MAX_PREFS];
+static int nsym_prefs;
+static byte hash_prefs[MAX_PREFS];
+static int nhash_prefs;
+static byte zip_prefs[MAX_PREFS];
+static int nzip_prefs;
+
+
static void do_generate_keypair( struct para_data_s *para,
struct output_control_s *outctrl );
static int write_keyblock( IOBUF out, KBNODE node );
@@ -124,6 +136,140 @@ keygen_add_key_expire( PKT_signature *sig, void *opaque )
}
+
+static int
+set_one_pref (ulong val, int type, int (*cf)(int), byte *buf, int *nbuf)
+{
+ int i;
+
+ if (!val || val > 127 || cf (val)) {
+ log_info (_("preference %c%lu is not valid\n"), type, val);
+ return -1;
+ }
+ for (i=0; i < *nbuf; i++ ) {
+ if (buf[i] == val) {
+ log_info (_("preference %c%lu duplicated\n"), type, val);
+ return -1;
+ }
+ }
+ if (*nbuf >= MAX_PREFS) {
+ log_info (_("too many `%c' preferences\n"), type);
+ return -1;
+ }
+ buf[(*nbuf)++] = val;
+ return 0;
+}
+
+static int
+check_zip_algo (int algo)
+{
+ return algo < 0 || algo > 2;
+}
+
+/*
+ * Parse the supplied string and use it to set the standard preferences.
+ * The String is expected to be in a forma like the one printed by "prefs",
+ * something like: "S10 S3 H3 H2 Z2 Z1". Use NULL to set the default
+ * preferences.
+ * Returns: 0 = okay
+ */
+int
+keygen_set_std_prefs (const char *string)
+{
+ byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS];
+ int nsym=0, nhash=0, nzip=0;
+ ulong val;
+ const char *s, *s2;
+ int rc = 0;
+
+ if (!string || !ascii_strcasecmp (string, "default"))
+ string = "S7 S10 S3 S4 H3 H2 Z2 Z1";
+ else if (!ascii_strcasecmp (string, "none"))
+ string = "";
+
+ for (s=string; *s; s = s2) {
+ if ((*s=='s' || *s == 'S') && isdigit(s[1]) ) {
+ val = strtoul (++s, (char**)&s2, 10);
+ if (set_one_pref (val, 'S', check_cipher_algo, sym, &nsym))
+ rc = -1;
+ }
+ else if ((*s=='h' || *s == 'H') && isdigit(s[1]) ) {
+ val = strtoul (++s, (char**)&s2, 10);
+ if (set_one_pref (val, 'H', check_digest_algo, hash, &nhash))
+ rc = -1;
+ }
+ else if ((*s=='z' || *s == 'Z') && isdigit(s[1]) ) {
+ val = strtoul (++s, (char**)&s2, 10);
+ if (set_one_pref (val, 'Z', check_zip_algo, zip, &nzip))
+ rc = -1;
+ }
+ else if (isspace (*s))
+ s2 = s+1;
+ else {
+ log_info (_("invalid character in string\n"));
+ return -1;
+ }
+ }
+
+ if (!rc) {
+ memcpy (sym_prefs, sym, (nsym_prefs=nsym));
+ memcpy (hash_prefs, hash, (nhash_prefs=nhash));
+ memcpy (zip_prefs, zip, (nzip_prefs=nzip));
+ prefs_initialized = 1;
+ }
+ return rc;
+}
+
+
+/*
+ * Return a printable list of preferences. Caller must free.
+ */
+char *
+keygen_get_std_prefs ()
+{
+ char *buf;
+ int i;
+
+ if (!prefs_initialized)
+ keygen_set_std_prefs (NULL);
+
+ buf = m_alloc ( MAX_PREFS*3*5 + 1);
+ *buf = 0;
+ for (i=0; i < nsym_prefs; i++ )
+ sprintf (buf+strlen(buf), "S%d ", sym_prefs[i]);
+ for (i=0; i < nhash_prefs; i++ )
+ sprintf (buf+strlen(buf), "H%d ", hash_prefs[i]);
+ for (i=0; i < nzip_prefs; i++ )
+ sprintf (buf+strlen(buf), "Z%d ", zip_prefs[i]);
+
+ if (*buf) /* trim the trailing space */
+ buf[strlen(buf)-1] = 0;
+ return buf;
+}
+
+
+int
+keygen_upd_std_prefs( PKT_signature *sig, void *opaque )
+{
+ if (!prefs_initialized)
+ keygen_set_std_prefs (NULL);
+
+ if (nsym_prefs)
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs);
+ else
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM);
+ if (nhash_prefs)
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs);
+ else
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH);
+ if (nzip_prefs)
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs);
+ else
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR);
+ return 0;
+}
+
+
/****************
* Add preference to the self signature packet.
* This is only called for packets with version > 3.
@@ -132,22 +278,9 @@ int
keygen_add_std_prefs( PKT_signature *sig, void *opaque )
{
byte buf[8];
-
+
keygen_add_key_expire( sig, opaque );
-
- buf[0] = CIPHER_ALGO_RIJNDAEL;
- buf[1] = CIPHER_ALGO_TWOFISH;
- buf[2] = CIPHER_ALGO_CAST5;
- buf[3] = CIPHER_ALGO_BLOWFISH;
- build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 4 );
-
- buf[0] = DIGEST_ALGO_RMD160;
- buf[1] = DIGEST_ALGO_SHA1;
- build_sig_subpkt( sig, SIGSUBPKT_PREF_HASH, buf, 2 );
-
- buf[0] = 2;
- buf[1] = 1;
- build_sig_subpkt( sig, SIGSUBPKT_PREF_COMPR, buf, 2 );
+ keygen_upd_std_prefs (sig, opaque);
buf[0] = 0x80; /* no modify - It is reasonable that a key holder
* has the possibility to reject signatures from users
diff --git a/g10/main.h b/g10/main.h
index 13ec8e6c6..353d5bd94 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -98,8 +98,11 @@ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds,
/*-- keygen.c --*/
u32 ask_expiredate(void);
void generate_keypair( const char *fname );
+int keygen_set_std_prefs (const char *string);
+char *keygen_get_std_prefs (void);
int keygen_add_key_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 generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock );
/*-- openfile.c --*/
diff --git a/g10/sign.c b/g10/sign.c
index f36838b4a..817099a84 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -925,7 +925,7 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
/****************
* Create a new signature packet based on an existing one.
- * Only user ID signatureare supportted for now.
+ * Only user ID signaturs are supported for now.
* TODO: Merge this with make_keysig_packet.
*/
int
@@ -967,9 +967,9 @@ update_keysig_packet( PKT_signature **ret_sig,
if ( sig->version >= 4 && mksubpkt)
rc = (*mksubpkt)(sig, opaque);
- /* we increasethe timestamp by one second so that a future import
- of this key will replace the existing one. We make sure that
- we don't produce a timestamp in the future */
+ /* we increase the timestamp by one second so that a future import
+ of this key will replace the existing one. We also make sure that
+ we don't create a timestamp in the future */
sig->timestamp++;
while (sig->timestamp >= make_timestamp())
sleep (1);