aboutsummaryrefslogtreecommitdiffstats
path: root/g10/keyedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/keyedit.c')
-rw-r--r--g10/keyedit.c1426
1 files changed, 934 insertions, 492 deletions
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 4369e9a86..73772328b 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <errno.h>
#include <assert.h>
+#include <ctype.h>
#include "options.h"
#include "packet.h"
@@ -37,73 +38,60 @@
#include "ttyio.h"
#include "i18n.h"
+static void show_key_with_all_names( KBNODE keyblock,
+ int only_marked, int with_fpr, int with_subkeys );
+static void show_key_and_fingerprint( KBNODE keyblock );
+static void show_fingerprint( PKT_public_key *pk );
+static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock );
+static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_select_uid( KBNODE keyblock, int index );
+static int menu_select_key( KBNODE keyblock, int index );
+static int count_uids( KBNODE keyblock );
+static int count_uids_with_flag( KBNODE keyblock, unsigned flag );
+static int count_keys_with_flag( KBNODE keyblock, unsigned flag );
+static int count_selected_uids( KBNODE keyblock );
+static int count_selected_keys( KBNODE keyblock );
-static void
-show_fingerprint( PKT_public_key *pk )
-{
- byte *array, *p;
- size_t i, n;
- p = array = fingerprint_from_pk( pk, NULL, &n );
- tty_printf(" Fingerprint:");
- if( n == 20 ) {
- for(i=0; i < n ; i++, i++, p += 2 ) {
- if( i == 10 )
- tty_printf(" ");
- tty_printf(" %02X%02X", *p, p[1] );
- }
- }
- else {
- for(i=0; i < n ; i++, p++ ) {
- if( i && !(i%8) )
- tty_printf(" ");
- tty_printf(" %02X", *p );
- }
- }
- tty_printf("\n");
- m_free(array);
-}
+#define NODFLG_BADSIG (1<<0) /* bad signature */
+#define NODFLG_NOKEY (1<<1) /* no public key */
+#define NODFLG_SIGERR (1<<2) /* other sig error */
+
+#define NODFLG_MARK_A (1<<4) /* temporary mark */
+
+#define NODFLG_SELUID (1<<8) /* indicate the selected userid */
+#define NODFLG_SELKEY (1<<9) /* indicate the selected key */
-/****************
- * Ask whether the user is willing to sign the key. Return true if so.
- */
static int
-sign_it_p( PKT_public_key *pk, PKT_user_id *uid )
+get_keyblock_byname( KBNODE *keyblock, KBPOS *kbpos, const char *username )
{
- char *answer;
- int yes;
+ int rc;
- tty_printf("\n");
- tty_printf(_("Are you really sure that you want to sign this key:\n\n"));
- tty_printf("pub %4u%c/%08lX %s ",
- nbits_from_pk( pk ),
- pubkey_letter( pk->pubkey_algo ),
- (ulong)keyid_from_pk( pk, NULL ),
- datestr_from_pk( pk ) );
- tty_print_string( uid->name, uid->len );
- tty_printf("\n");
- show_fingerprint(pk);
- tty_printf("\n");
- answer = tty_get(_("Sign this key? "));
- tty_kill_prompt();
- yes = answer_is_yes(answer);
- m_free(answer);
- return yes;
+ *keyblock = NULL;
+ /* search the userid */
+ rc = find_keyblock_byname( kbpos, username );
+ if( rc ) {
+ log_error(_("%s: user not found\n"), username );
+ return rc;
+ }
+
+ /* read the keyblock */
+ rc = read_keyblock( kbpos, keyblock );
+ if( rc )
+ log_error("%s: keyblock read problem: %s\n", username, g10_errstr(rc));
+ return rc;
}
/****************
* Check the keysigs and set the flags to indicate errors.
- * Usage of nodes flag bits:
- * Bit 0 = bad signature
- * 1 = no public key
- * 2 = other error
* Returns true if error found.
*/
static int
-check_all_keysigs( KBNODE keyblock )
+check_all_keysigs( KBNODE keyblock, int only_selected )
{
KBNODE kbctx;
KBNODE node;
@@ -111,337 +99,217 @@ check_all_keysigs( KBNODE keyblock )
int inv_sigs = 0;
int no_key = 0;
int oth_err = 0;
+ int has_selfsig = 0;
+ int mis_selfsig = 0;
+ int selected = !only_selected;
+ int anyuid = 0;
for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
- if( node->pkt->pkttype == PKT_SIGNATURE
- && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
- PKT_signature *sig = node->pkt->pkt.signature;
- int sigrc;
-
- tty_printf("sig");
- switch( (rc = check_key_signature( keyblock, node,NULL)) ) {
- case 0: node->flag = 0; sigrc = '!'; break;
- case G10ERR_BAD_SIGN: inv_sigs++; node->flag = 1; sigrc = '-'; break;
- case G10ERR_NO_PUBKEY: no_key++; node->flag = 2; sigrc = '?'; break;
- default: oth_err++; node->flag = 4; sigrc = '%'; break;
- }
- tty_printf("%c %08lX %s ",
- sigrc, sig->keyid[1], datestr_from_sig(sig));
- if( sigrc == '%' )
- tty_printf("[%s] ", g10_errstr(rc) );
- else if( sigrc == '?' )
- ;
- else {
- size_t n;
- char *p = get_user_id( sig->keyid, &n );
- tty_print_string( p, n > 40? 40 : n );
- m_free(p);
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+
+ if( only_selected )
+ selected = (node->flag & NODFLG_SELUID);
+ if( selected ) {
+ tty_printf("uid ");
+ tty_print_string( uid->name, uid->len );
+ tty_printf("\n");
+ if( anyuid && !has_selfsig )
+ mis_selfsig++;
+ has_selfsig = 0;
+ anyuid = 1;
}
- tty_printf("\n");
- /* FIXME: update the trustdb */
}
- }
- if( inv_sigs )
- tty_printf(_("%d bad signatures\n"), inv_sigs );
- if( no_key )
- tty_printf(_("No public key for %d signatures\n"), no_key );
- if( oth_err )
- tty_printf(_("%d signatures not checked due to errors\n"), oth_err );
- return inv_sigs || no_key || oth_err;
-}
-
-
-/****************
- * Ask and remove invalid signatures that are to be removed.
- */
-static int
-remove_keysigs( KBNODE keyblock, u32 *keyid, int all )
-{
- KBNODE kbctx;
- KBNODE node;
- char *answer;
- int yes;
- int count;
-
- count = 0;
- for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
- if( ((node->flag & 7) || all )
- && node->pkt->pkttype == PKT_SIGNATURE
- && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
+ else if( selected && node->pkt->pkttype == PKT_SIGNATURE
+ && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
PKT_signature *sig = node->pkt->pkt.signature;
+ int sigrc, selfsig;
- tty_printf("\n \"%08lX %s ",
- sig->keyid[1], datestr_from_sig(sig));
- if( node->flag & 6 )
- tty_printf(_("[User name not available] "));
- else {
- size_t n;
- char *p = get_user_id( sig->keyid, &n );
- tty_print_string( p, n );
- m_free(p);
- }
- tty_printf("\"\n");
- if( node->flag & 1 )
- tty_printf(_("This is a BAD signature!\n"));
- else if( node->flag & 2 )
- tty_printf(_("Public key not available.\n"));
- else if( node->flag & 4 )
- tty_printf(_("The signature could not be checked!\n"));
-
- if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
- tty_printf(_("Skipped self-signature\n"));
- continue; /* do not remove self-signatures */
+ switch( (rc = check_key_signature( keyblock, node, &selfsig)) ) {
+ case 0:
+ node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR);
+ sigrc = '!';
+ break;
+ case G10ERR_BAD_SIGN:
+ node->flag = NODFLG_BADSIG;
+ sigrc = '-';
+ inv_sigs++;
+ break;
+ case G10ERR_NO_PUBKEY:
+ node->flag = NODFLG_NOKEY;
+ sigrc = '?';
+ no_key++;
+ break;
+ default:
+ node->flag = NODFLG_SIGERR;
+ sigrc = '%';
+ oth_err++;
+ break;
}
-
- tty_printf("\n");
- answer = tty_get(_("Remove this signature? "));
- tty_kill_prompt();
- if( answer_is_yes(answer) ) {
- node->flag |= 128; /* use bit 7 to mark this node */
- count++;
+ if( sigrc != '?' ) {
+ tty_printf("sig%c %08lX %s ",
+ sigrc, sig->keyid[1], datestr_from_sig(sig));
+ if( sigrc == '%' )
+ tty_printf("[%s] ", g10_errstr(rc) );
+ else if( sigrc == '?' )
+ ;
+ else if( selfsig ) {
+ tty_printf( _("[self-signature]") );
+ if( sigrc == '!' )
+ has_selfsig = 1;
+ }
+ else {
+ size_t n;
+ char *p = get_user_id( sig->keyid, &n );
+ tty_print_string( p, n > 40? 40 : n );
+ m_free(p);
+ }
+ tty_printf("\n");
+ /* fixme: Should we update the trustdb here */
}
- m_free(answer);
}
}
+ if( !has_selfsig )
+ mis_selfsig++;
+ if( inv_sigs == 1 )
+ tty_printf(_("1 bad signature\n"), inv_sigs );
+ else if( inv_sigs )
+ tty_printf(_("%d bad signatures\n"), inv_sigs );
+ if( no_key == 1 )
+ tty_printf(_("1 signature not checked due to a missing key\n") );
+ else if( no_key )
+ tty_printf(_("%d signatures not checked due to missing keys\n"), no_key );
+ if( oth_err == 1 )
+ tty_printf(_("1 signature not checked due to an error\n") );
+ else if( oth_err )
+ tty_printf(_("%d signatures not checked due to errors\n"), oth_err );
+ if( mis_selfsig == 1 )
+ tty_printf(_("1 user id without valid self-signature detected\n"));
+ else if( mis_selfsig )
+ tty_printf(_("%d user ids without valid self-signatures detected\n"),
+ mis_selfsig);
- if( !count )
- return 0; /* nothing to remove */
- answer = tty_get(_("Do you really want to remove the selected signatures? "));
- tty_kill_prompt();
- yes = answer_is_yes(answer);
- m_free(answer);
- if( !yes )
- return 0;
-
- for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 1)) ; ) {
- if( node->flag & 128)
- delete_kbnode(node );
- }
-
- return 1;
+ return inv_sigs || no_key || oth_err || mis_selfsig;
}
+
/****************
- * This function signs the key of USERNAME with all users listed in
- * LOCUSR. If LOCUSR is NULL the default secret certificate will
- * be used. This works on all keyrings, so there is no armor or
- * compress stuff here.
+ * Loop over all locusr and and sign the uids after asking.
+ * If no user id is marked, all user ids will be signed;
+ * if some user_ids are marked those will be signed.
+ *
+ * fixme: Add support for our proposed sign-all scheme
*/
-int
-sign_key( const char *username, STRLIST locusr )
+static int
+sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
{
- md_filter_context_t mfx;
int rc = 0;
SK_LIST sk_list = NULL;
SK_LIST sk_rover = NULL;
- KBNODE keyblock = NULL;
- KBNODE kbctx, node;
- KBPOS kbpos;
- PKT_public_key *pk;
- u32 pk_keyid[2];
- char *answer;
-
- memset( &mfx, 0, sizeof mfx);
-
- /* search the userid */
- rc = find_keyblock_byname( &kbpos, username );
- if( rc ) {
- log_error(_("%s: user not found\n"), username );
- goto leave;
- }
+ KBNODE node, uidnode;
+ PKT_public_key *primary_pk;
+ int select_all = !count_selected_uids(keyblock);
/* build a list of all signators */
rc=build_sk_list( locusr, &sk_list, 0, 1 );
if( rc )
goto leave;
-
- /* read the keyblock */
- rc = read_keyblock( &kbpos, &keyblock );
- if( rc ) {
- log_error("error reading the certificate: %s\n", g10_errstr(rc) );
- goto leave;
- }
-
- /* get the keyid from the keyblock */
- node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
- if( !node ) {
- log_error("Oops; public key not found anymore!\n");
- rc = G10ERR_GENERAL;
- goto leave;
- }
-
- pk = node->pkt->pkt.public_key;
- keyid_from_pk( pk, pk_keyid );
- tty_printf(_("Checking signatures of this public key certificate:\n"));
- tty_printf("pub %4u%c/%08lX %s ",
- nbits_from_pk( pk ),
- pubkey_letter( pk->pubkey_algo ),
- pk_keyid[1], datestr_from_pk(pk) );
- {
+ /* loop over all signaturs */
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ u32 sk_keyid[2];
size_t n;
- char *p = get_user_id( pk_keyid, &n );
- tty_print_string( p, n > 40? 40 : n );
- m_free(p);
- tty_printf("\n");
- }
+ char *p;
- clear_kbnode_flags( keyblock );
- if( check_all_keysigs( keyblock ) ) {
- if( !opt.batch ) {
- /* ask whether we really should do anything */
- answer = tty_get(
- _("Do you want to remove some of the invalid signatures? "));
- tty_kill_prompt();
- if( answer_is_yes(answer) )
- remove_keysigs( keyblock, pk_keyid, 0 );
- m_free(answer);
+ keyid_from_sk( sk_rover->sk, sk_keyid );
+ /* set mark A for all selected user ids */
+ for( node=keyblock; node; node = node->next ) {
+ if( select_all || (node->flag & NODFLG_SELUID) )
+ node->flag |= NODFLG_MARK_A;
+ else
+ node->flag &= ~NODFLG_MARK_A;
}
- }
-
- /* check whether we it is possible to sign this key */
- for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
- u32 akeyid[2];
-
- keyid_from_sk( sk_rover->sk, akeyid );
- for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
- if( node->pkt->pkttype == PKT_USER_ID )
- sk_rover->mark = 1;
- else if( node->pkt->pkttype == PKT_SIGNATURE
+ /* reset mark for uids which are already signed */
+ uidnode = NULL;
+ for( node=keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
+ }
+ else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE
&& (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
- if( akeyid[0] == node->pkt->pkt.signature->keyid[0]
- && akeyid[1] == node->pkt->pkt.signature->keyid[1] ) {
- log_info(_("Already signed by keyid %08lX\n"),
- (ulong)akeyid[1] );
- sk_rover->mark = 0;
+ if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
+ && sk_keyid[1] == node->pkt->pkt.signature->keyid[1] ) {
+ tty_printf(_("Already signed by key %08lX\n"),
+ (ulong)sk_keyid[1] );
+ uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */
}
}
}
- }
- for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
- if( sk_rover->mark )
- break;
- }
- if( !sk_rover ) {
- log_info(_("Nothing to sign\n"));
- goto leave;
- }
-
- /* Loop over all signers and all user ids and sign */
- /* FIXME: we have to change it: Present all user-ids and
- * then ask whether all those ids shall be signed if the user
- * answers yes, go and make a 0x14 sign class packet and remove
- * old one-user-id-only-sigs (user should be noted of this
- * condition while presenting the user-ids); if he had answered
- * no, present each user-id in turn and ask which one should be signed
- * (only one) - if there is already a single-user-sig, do nothing.
- * (this is propably already out in the world) */
- for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
- if( !sk_rover->mark )
+ /* check whether any uids are left for signing */
+ if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) ) {
+ tty_printf(_("Nothing to sign with key %08lX\n"),
+ (ulong)sk_keyid[1] );
continue;
- for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
- if( node->pkt->pkttype == PKT_USER_ID ) {
- if( sign_it_p( pk, node->pkt->pkt.user_id ) ) {
- PACKET *pkt;
- PKT_signature *sig;
-
- rc = make_keysig_packet( &sig, pk,
- node->pkt->pkt.user_id,
- NULL,
- sk_rover->sk,
- 0x10, 0, NULL, NULL );
- if( rc ) {
- log_error("make_keysig_packet failed: %s\n", g10_errstr(rc));
- goto leave;
- }
-
- pkt = m_alloc_clear( sizeof *pkt );
- pkt->pkttype = PKT_SIGNATURE;
- pkt->pkt.signature = sig;
- insert_kbnode( node, new_kbnode(pkt), PKT_USER_ID );
+ }
+ /* Ask whether we realy should sign these user id(s) */
+ tty_printf("\n");
+ show_key_with_all_names( keyblock, 1, 1, 0 );
+ tty_printf("\n");
+ tty_printf(_(
+ "Are you really sure that you want to sign this key\n"
+ "with your key: \""));
+ p = get_user_id( sk_keyid, &n );
+ tty_print_string( p, n );
+ tty_printf("\"\n\n");
+ m_free(p);
+ p = tty_get(_("Really sign? "));
+ tty_kill_prompt();
+ if( !answer_is_yes(p) ) {
+ m_free(p);
+ continue; /* No */
+ }
+ m_free(p);
+ /* now we can sign the user ids */
+ reloop: /* (must use this, because we are modifing the list) */
+ primary_pk = NULL;
+ for( node=keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ primary_pk = node->pkt->pkt.public_key;
+ else if( node->pkt->pkttype == PKT_USER_ID
+ && (node->flag & NODFLG_MARK_A) ) {
+ PACKET *pkt;
+ PKT_signature *sig;
+
+ assert( primary_pk );
+ node->flag &= ~NODFLG_MARK_A;
+ rc = make_keysig_packet( &sig, primary_pk,
+ node->pkt->pkt.user_id,
+ NULL,
+ sk_rover->sk,
+ 0x10, 0, NULL, NULL );
+ if( rc ) {
+ log_error(_("signing failed: %s\n"), g10_errstr(rc));
+ goto leave;
}
+ *ret_modified = 1; /* we changed the keyblock */
+
+ pkt = m_alloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE );
+ goto reloop;
}
}
- }
-
- rc = update_keyblock( &kbpos, keyblock );
- if( rc ) {
- log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
- goto leave;
- }
+ } /* end loop over signators */
leave:
- release_kbnode( keyblock );
release_sk_list( sk_list );
- md_close( mfx.md );
return rc;
}
-int
-edit_keysigs( const char *username )
-{
- int rc = 0;
- KBNODE keyblock = NULL;
- KBNODE node;
- KBPOS kbpos;
- PKT_public_key *pk;
- u32 pk_keyid[2];
-
- /* search the userid */
- rc = find_keyblock_byname( &kbpos, username );
- if( rc ) {
- log_error(_("%s: user not found\n"), username );
- goto leave;
- }
-
- /* read the keyblock */
- rc = read_keyblock( &kbpos, &keyblock );
- if( rc ) {
- log_error("%s: certificate read problem: %s\n", username, g10_errstr(rc) );
- goto leave;
- }
-
- /* get the keyid from the keyblock */
- node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
- if( !node ) {
- log_error("Oops; public key not found anymore!\n");
- rc = G10ERR_GENERAL;
- goto leave;
- }
-
- pk = node->pkt->pkt.public_key;
- keyid_from_pk( pk, pk_keyid );
- tty_printf(_("Checking signatures of this public key certificate:\n"));
- tty_printf("pub %4u%c/%08lX %s ",
- nbits_from_pk( pk ),
- pubkey_letter( pk->pubkey_algo ),
- pk_keyid[1], datestr_from_pk(pk) );
- {
- size_t n;
- char *p = get_user_id( pk_keyid, &n );
- tty_print_string( p, n > 40? 40 : n );
- m_free(p);
- tty_printf("\n");
- }
-
- clear_kbnode_flags( keyblock );
- check_all_keysigs( keyblock );
- if( remove_keysigs( keyblock, pk_keyid, 1 ) ) {
- rc = update_keyblock( &kbpos, keyblock );
- if( rc ) {
- log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
- goto leave;
- }
- }
-
- leave:
- release_kbnode( keyblock );
- return rc;
-}
-
/****************
* Delete a public or secret key from a keyring.
@@ -560,66 +428,26 @@ delete_key( const char *username, int secret )
}
-int
-change_passphrase( const char *username )
+/****************
+ * Change the passphrase of the primary and all secondary keys.
+ * We use only one passphrase for all keys.
+ */
+static int
+change_passphrase( KBNODE keyblock )
{
int rc = 0;
- KBNODE keyblock = NULL;
+ int changed=0;
KBNODE node;
- KBPOS kbpos;
PKT_secret_key *sk;
- u32 keyid[2];
- char *answer;
- int changed=0;
char *passphrase = NULL;
- /* find the userid */
- rc = find_secret_keyblock_byname( &kbpos, username );
- if( rc ) {
- log_error("secret key for user '%s' not found\n", username );
- goto leave;
- }
-
- /* read the keyblock */
- rc = read_keyblock( &kbpos, &keyblock );
- if( rc ) {
- log_error("error reading the certificate: %s\n", g10_errstr(rc) );
- goto leave;
- }
-
- /* get the keyid from the keyblock */
node = find_kbnode( keyblock, PKT_SECRET_KEY );
if( !node ) {
log_error("Oops; secret key not found anymore!\n");
- rc = G10ERR_GENERAL;
goto leave;
}
-
sk = node->pkt->pkt.secret_key;
- keyid_from_sk( sk, keyid );
- tty_printf("sec %4u%c/%08lX %s ",
- nbits_from_sk( sk ),
- pubkey_letter( sk->pubkey_algo ),
- keyid[1], datestr_from_sk(sk) );
- {
- size_t n;
- char *p = get_user_id( keyid, &n );
- tty_print_string( p, n );
- m_free(p);
- tty_printf("\n");
- }
- for(node=keyblock; node; node = node->next ) {
- if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
- PKT_secret_key *subsk = node->pkt->pkt.secret_key;
- keyid_from_sk( subsk, keyid );
- tty_printf("sub %4u%c/%08lX %s\n",
- nbits_from_sk( subsk ),
- pubkey_letter( subsk->pubkey_algo ),
- keyid[1], datestr_from_sk(subsk) );
- }
- }
- clear_kbnode_flags( keyblock );
switch( is_secret_key_protected( sk ) ) {
case -1:
rc = G10ERR_PUBKEY_ALGO;
@@ -636,13 +464,11 @@ change_passphrase( const char *username )
}
/* unprotect all subkeys (use the supplied passphrase or ask)*/
- for(node=keyblock; node; node = node->next ) {
+ for(node=keyblock; !rc && node; node = node->next ) {
if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
PKT_secret_key *subsk = node->pkt->pkt.secret_key;
set_next_passphrase( passphrase );
rc = check_secret_key( subsk );
- if( rc )
- break;
}
}
@@ -666,11 +492,9 @@ change_passphrase( const char *username )
rc = 0;
tty_printf(_( "You don't want a passphrase -"
" this is probably a *bad* idea!\n\n"));
- answer = tty_get(_("Do you really want to do this? "));
- tty_kill_prompt();
- if( answer_is_yes(answer) )
+ if( tty_get_answer_is_yes(_(
+ "Do you really want to do this? ")))
changed++;
- m_free(answer);
break;
}
else { /* okay */
@@ -696,128 +520,746 @@ change_passphrase( const char *username )
m_free(dek);
}
+ leave:
+ m_free( passphrase );
+ set_next_passphrase( NULL );
+ return changed && !rc;
+}
+
+
+
+
+/****************
+ * Menu driven key editor
+ *
+ * Note: to keep track of some selection we use node->mark MARKBIT_xxxx.
+ */
+
+void
+keyedit_menu( const char *username, STRLIST locusr )
+{
+ enum cmdids { cmdNONE = 0,
+ cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
+ cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
+ cmdTOGGLE, cmdSELKEY, cmdPASSWD,
+ cmdNOP };
+ static struct { const char *name;
+ enum cmdids id;
+ int need_sk;
+ const char *desc;
+ } cmds[] = {
+ { N_("quit") , cmdQUIT , 0, N_("quit this menu") },
+ { N_("q") , cmdQUIT , 0, NULL },
+ { N_("save") , cmdSAVE , 0, N_("save and quit") },
+ { N_("help") , cmdHELP , 0, N_("show this help") },
+ { "?" , cmdHELP , 0, NULL },
+ { N_("fpr") , cmdFPR , 0, N_("show fingerprint") },
+ { N_("list") , cmdLIST , 0, N_("list key and user ids") },
+ { N_("l") , cmdLIST , 0, NULL },
+ { N_("uid") , cmdSELUID , 0, N_("select user id N") },
+ { N_("key") , cmdSELKEY , 0, N_("select secondary key N") },
+ { N_("check") , cmdCHECK , 0, N_("list signatures") },
+ { N_("c") , cmdCHECK , 0, NULL },
+ { N_("sign") , cmdSIGN , 0, N_("sign the key") },
+ { N_("s") , cmdSIGN , 0, NULL },
+ { N_("debug") , cmdDEBUG , 0, NULL },
+ { N_("adduid") , cmdADDUID , 1, N_("add a user id") },
+ { N_("deluid") , cmdDELUID , 0, N_("delete user id") },
+ { N_("addkey") , cmdADDKEY , 1, N_("add a secondary key") },
+ { N_("delkey") , cmdDELKEY , 0, N_("delete a secondary key") },
+ { N_("toggle") , cmdTOGGLE , 1, N_("toggle between secret "
+ "and public key listing") },
+ { N_("t" ) , cmdTOGGLE , 1, NULL },
+ { N_("passwd") , cmdPASSWD , 1, N_("change the passphrase") },
+
+ { NULL, cmdNONE } };
+ enum cmdids cmd;
+ int rc = 0;
+ KBNODE keyblock = NULL;
+ KBPOS keyblockpos;
+ KBNODE sec_keyblock = NULL;
+ KBPOS sec_keyblockpos;
+ KBNODE cur_keyblock;
+ char *answer = NULL;
+ int redisplay = 1;
+ int modified = 0;
+ int sec_modified = 0;
+ int toggle;
+
+
+ if( opt.batch ) {
+ log_error(_("can't do that in batch-mode\n"));
+ goto leave;
+ }
- if( changed ) {
- rc = update_keyblock( &kbpos, keyblock );
+ /* first try to locate it as secret key */
+ rc = find_secret_keyblock_byname( &sec_keyblockpos, username );
+ if( !rc ) {
+ rc = read_keyblock( &sec_keyblockpos, &sec_keyblock );
if( rc ) {
- log_error("update_keyblock failed: %s\n", g10_errstr(rc) );
+ log_error("%s: secret keyblock read problem: %s\n",
+ username, g10_errstr(rc));
goto leave;
}
}
+ /* and now get the public key */
+ rc = get_keyblock_byname( &keyblock, &keyblockpos, username );
+ if( rc )
+ goto leave;
+
+ if( sec_keyblock ) { /* check that they match */
+ /* FIXME: check that they both match */
+ tty_printf(_("Secret key is available.\n"));
+ }
+
+ toggle = 0;
+ cur_keyblock = keyblock;
+ for(;;) { /* main loop */
+ int i, arg_number;
+ char *p;
+
+ tty_printf("\n");
+ if( redisplay ) {
+ show_key_with_all_names( cur_keyblock, 0, 0, 1 );
+ tty_printf("\n");
+ redisplay = 0;
+ }
+ m_free(answer);
+ answer = tty_get(_("Command> "));
+ tty_kill_prompt();
+ trim_spaces(answer);
+
+ arg_number = 0;
+ if( !*answer )
+ cmd = cmdLIST;
+ else if( isdigit( *answer ) ) {
+ cmd = cmdSELUID;
+ arg_number = atoi(answer);
+ }
+ else {
+ if( (p=strchr(answer,' ')) ) {
+ *p++ = 0;
+ trim_spaces(answer);
+ trim_spaces(p);
+ arg_number = atoi(p);
+ }
+
+ for(i=0; cmds[i].name; i++ )
+ if( !stricmp( answer, cmds[i].name ) )
+ break;
+ if( cmds[i].need_sk && !sec_keyblock ) {
+ tty_printf(_("Need the secret key to to this.\n"));
+ cmd = cmdNOP;
+ }
+ else
+ cmd = cmds[i].id;
+ }
+ switch( cmd ) {
+ case cmdHELP:
+ for(i=0; cmds[i].name; i++ ) {
+ if( cmds[i].need_sk && !sec_keyblock )
+ ; /* skip if we do not have the secret key */
+ else if( cmds[i].desc )
+ tty_printf("%-10s %s\n", cmds[i].name, cmds[i].desc );
+ }
+ break;
+
+ case cmdQUIT:
+ if( !modified )
+ goto leave;
+ m_free(answer);
+ answer = tty_get(_("Save changes? "));
+ if( !answer_is_yes(answer) ) {
+ m_free(answer);
+ answer = tty_get(_("Quit without saving? "));
+ if( answer_is_yes(answer) )
+ goto leave;
+ break;
+ }
+ /* fall thru */
+ case cmdSAVE:
+ if( modified ) {
+ rc = update_keyblock( &keyblockpos, keyblock );
+ if( rc ) {
+ log_error(_("update failed: %s\n"), g10_errstr(rc) );
+ break;
+ }
+ if( sec_modified ) {
+ rc = update_keyblock( &sec_keyblockpos, sec_keyblock );
+ if( rc ) {
+ log_error(_("update secret failed: %s\n"),
+ g10_errstr(rc) );
+ break;
+ }
+ }
+ /* FIXME: UPDATE/INVALIDATE trustdb !! */
+ }
+ else
+ tty_printf(_("Key not changed so no update needed.\n"));
+ goto leave;
+
+ case cmdLIST:
+ redisplay = 1;
+ break;
+
+ case cmdFPR:
+ show_key_and_fingerprint( keyblock );
+ break;
+
+ case cmdSELUID:
+ if( menu_select_uid( cur_keyblock, arg_number ) )
+ redisplay = 1;
+ break;
+
+ case cmdSELKEY:
+ if( menu_select_key( cur_keyblock, arg_number ) )
+ redisplay = 1;
+ break;
+
+ case cmdCHECK:
+ /* we can only do this with the public key becuase the
+ * check functions can't cope with secret keys and it
+ * is questionable whether this would make sense at all */
+ check_all_keysigs( keyblock, count_selected_uids(keyblock) );
+ break;
+
+ case cmdSIGN: /* sign (only the public key) */
+ if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) {
+ if( !tty_get_answer_is_yes(_("Really sign all user ids? ")) ) {
+ tty_printf(_("Hint: Select the user ids to sign\n"));
+ break;
+ }
+ }
+ sign_uids( keyblock, locusr, &modified );
+ break;
+
+ case cmdDEBUG:
+ dump_kbnode( cur_keyblock );
+ break;
+
+ case cmdTOGGLE:
+ toggle = !toggle;
+ cur_keyblock = toggle? sec_keyblock : keyblock;
+ redisplay = 1;
+ break;
+
+ case cmdADDUID:
+ if( menu_adduid( keyblock, sec_keyblock ) ) {
+ redisplay = 1;
+ sec_modified = modified = 1;
+ }
+ break;
+
+ case cmdDELUID: {
+ int n1;
+
+ if( !(n1=count_selected_uids(keyblock)) )
+ tty_printf(_("You must select at least one user id.\n"));
+ else if( count_uids(keyblock) - n1 < 1 )
+ tty_printf(_("You can't delete the last user id!\n"));
+ else if( tty_get_answer_is_yes(
+ n1 > 1? _("Really remove all selected user ids? ")
+ : _("Really remove this user id? ")
+ ) ) {
+ menu_deluid( keyblock, sec_keyblock );
+ redisplay = 1;
+ modified = 1;
+ if( sec_keyblock )
+ sec_modified = 1;
+ }
+ }
+ break;
+
+ case cmdADDKEY:
+ if( generate_subkeypair( keyblock, sec_keyblock ) ) {
+ redisplay = 1;
+ sec_modified = modified = 1;
+ }
+ break;
+
+
+ case cmdDELKEY: {
+ int n1;
+
+ if( !(n1=count_selected_keys( keyblock )) )
+ tty_printf(_("You must select at least one key.\n"));
+ else if( sec_keyblock && !tty_get_answer_is_yes(
+ n1 > 1?
+ _("Do you really want to delete the selected keys? "):
+ _("Do you really want to delete this key? ")
+ ))
+ ;
+ else {
+ menu_delkey( keyblock, sec_keyblock );
+ redisplay = 1;
+ modified = 1;
+ if( sec_keyblock )
+ sec_modified = 1;
+ }
+ }
+ break;
+
+ case cmdPASSWD:
+ if( change_passphrase( sec_keyblock ) )
+ sec_modified = 1;
+ break;
+
+ case cmdNOP:
+ break;
+
+ default:
+ tty_printf("\n");
+ tty_printf(_("Invalid command (try \"help\")\n"));
+ break;
+ }
+ } /* end main loop */
+
leave:
- m_free( passphrase );
release_kbnode( keyblock );
- set_next_passphrase( NULL );
- return rc;
+ release_kbnode( sec_keyblock );
+ m_free(answer);
}
+
/****************
- * Create a signature packet for the given public key certificate
- * and the user id and return it in ret_sig. User signature class SIGCLASS
- * user-id is not used (and may be NULL if sigclass is 0x20)
- * If digest_algo is 0 the function selects an appropriate one.
+ * Display the key a the user ids, if only_marked is true, do only
+ * so for user ids with mark A flag set and dont display the index number
*/
-int
-make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
- PKT_user_id *uid, PKT_public_key *subpk,
- PKT_secret_key *sk,
- int sigclass, int digest_algo,
- int (*mksubpkt)(PKT_signature *, void *), void *opaque
- )
+static void
+show_key_with_all_names( KBNODE keyblock, int only_marked,
+ int with_fpr, int with_subkeys )
{
- PKT_signature *sig;
- int rc=0;
- MD_HANDLE md;
-
- assert( (sigclass >= 0x10 && sigclass <= 0x13)
- || sigclass == 0x20 || sigclass == 0x18 );
- if( !digest_algo ) {
- switch( sk->pubkey_algo ) {
- case PUBKEY_ALGO_DSA: digest_algo = DIGEST_ALGO_SHA1; break;
- case PUBKEY_ALGO_RSA_S:
- case PUBKEY_ALGO_RSA: digest_algo = DIGEST_ALGO_MD5; break;
- default: digest_algo = DIGEST_ALGO_RMD160; break;
- }
- }
- md = md_open( digest_algo, 0 );
-
- /* hash the public key certificate and the user id */
- hash_public_key( md, pk );
- if( sigclass == 0x18 ) { /* subkey binding */
- hash_public_key( md, subpk );
- }
- else if( sigclass != 0x20 ) {
- if( sk->version >=4 ) {
- byte buf[5];
- buf[0] = 0xb4; /* indicates a userid packet */
- buf[1] = uid->len >> 24; /* always use 4 length bytes */
- buf[2] = uid->len >> 16;
- buf[3] = uid->len >> 8;
- buf[4] = uid->len;
- md_write( md, buf, 5 );
- }
- md_write( md, uid->name, uid->len );
- }
- /* and make the signature packet */
- sig = m_alloc_clear( sizeof *sig );
- sig->version = sk->version;
- keyid_from_sk( sk, sig->keyid );
- sig->pubkey_algo = sk->pubkey_algo;
- sig->digest_algo = digest_algo;
- sig->timestamp = make_timestamp();
- sig->sig_class = sigclass;
- if( sig->version >= 4 )
- build_sig_subpkt_from_sig( sig );
-
- if( sig->version >= 4 && mksubpkt )
- rc = (*mksubpkt)( sig, opaque );
-
- if( !rc ) {
- if( sig->version >= 4 )
- md_putc( md, sig->version );
- md_putc( md, sig->sig_class );
- if( sig->version < 4 ) {
- u32 a = sig->timestamp;
- md_putc( md, (a >> 24) & 0xff );
- md_putc( md, (a >> 16) & 0xff );
- md_putc( md, (a >> 8) & 0xff );
- md_putc( md, a & 0xff );
+ KBNODE node;
+ int i;
+
+ /* the keys */
+ for( node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+ tty_printf("%s%c %4u%c/%08lX created: %s expires: %s\n",
+ node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
+ (node->flag & NODFLG_SELKEY)? '*':' ',
+ nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ (ulong)keyid_from_pk(pk,NULL),
+ datestr_from_pk(pk),
+ expirestr_from_pk(pk) );
+ if( with_fpr && node->pkt->pkttype == PKT_PUBLIC_KEY )
+ show_fingerprint( pk );
}
- else {
- byte buf[6];
- size_t n;
-
- md_putc( md, sig->pubkey_algo );
- md_putc( md, sig->digest_algo );
- if( sig->hashed_data ) {
- n = (sig->hashed_data[0] << 8) | sig->hashed_data[1];
- md_write( md, sig->hashed_data, n+2 );
- n += 6;
+ else if( node->pkt->pkttype == PKT_SECRET_KEY
+ || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) ) {
+ PKT_secret_key *sk = node->pkt->pkt.secret_key;
+ tty_printf("%s%c %4u%c/%08lX created: %s expires: %s\n",
+ node->pkt->pkttype == PKT_SECRET_KEY? "sec":"sbb",
+ (node->flag & NODFLG_SELKEY)? '*':' ',
+ nbits_from_sk( sk ),
+ pubkey_letter( sk->pubkey_algo ),
+ (ulong)keyid_from_sk(sk,NULL),
+ datestr_from_sk(sk),
+ expirestr_from_sk(sk) );
+ }
+ }
+ /* the user ids */
+ i = 0;
+ for( node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ ++i;
+ if( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A))){
+ if( only_marked )
+ tty_printf(" ");
+ else if( node->flag & NODFLG_SELUID )
+ tty_printf("(%d)* ", i);
+ else
+ tty_printf("(%d) ", i);
+ tty_print_string( uid->name, uid->len );
+ tty_printf("\n");
}
- else
- n = 6;
- /* add some magic */
- buf[0] = sig->version;
- buf[1] = 0xff;
- buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */
- buf[3] = n >> 16;
- buf[4] = n >> 8;
- buf[5] = n;
- md_write( md, buf, 6 );
+ }
+ }
+}
+
+static void
+show_key_and_fingerprint( KBNODE keyblock )
+{
+ KBNODE node;
+ PKT_public_key *pk = NULL;
+ for( node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ pk = node->pkt->pkt.public_key;
+ tty_printf("pub %4u%c/%08lX %s ",
+ nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ (ulong)keyid_from_pk(pk,NULL),
+ datestr_from_pk(pk) );
}
- md_final(md);
+ else if( node->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ tty_print_string( uid->name, uid->len );
+ break;
+ }
+ }
+ tty_printf("\n");
+ if( pk )
+ show_fingerprint( pk );
+}
+
- rc = complete_sig( sig, sk, md );
+static void
+show_fingerprint( PKT_public_key *pk )
+{
+ byte *array, *p;
+ size_t i, n;
+
+ p = array = fingerprint_from_pk( pk, NULL, &n );
+ tty_printf(" Fingerprint:");
+ if( n == 20 ) {
+ for(i=0; i < n ; i++, i++, p += 2 ) {
+ if( i == 10 )
+ tty_printf(" ");
+ tty_printf(" %02X%02X", *p, p[1] );
+ }
}
+ else {
+ for(i=0; i < n ; i++, p++ ) {
+ if( i && !(i%8) )
+ tty_printf(" ");
+ tty_printf(" %02X", *p );
+ }
+ }
+ tty_printf("\n");
+ m_free(array);
+}
- md_close( md );
- if( rc )
- free_seckey_enc( sig );
+
+/****************
+ * Ask for a new user id , do the selfsignature and put it into
+ * both keyblocks.
+ * Return true if there is a new user id
+ */
+static int
+menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_user_id *uid;
+ PKT_public_key *pk=NULL;
+ PKT_secret_key *sk=NULL;
+ PKT_signature *sig=NULL;
+ PACKET *pkt;
+ KBNODE node;
+ KBNODE pub_where=NULL, sec_where=NULL;
+ int rc;
+
+ uid = generate_user_id();
+ if( !uid )
+ return 0;
+
+ for( node = pub_keyblock; node; pub_where = node, node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ pk = node->pkt->pkt.public_key;
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break;
+ }
+ if( !node ) /* no subkey */
+ pub_where = NULL;
+ for( node = sec_keyblock; node; sec_where = node, node = node->next ) {
+ if( node->pkt->pkttype == PKT_SECRET_KEY )
+ sk = node->pkt->pkt.secret_key;
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
+ break;
+ }
+ if( !node ) /* no subkey */
+ sec_where = NULL;
+ assert(pk && sk );
+
+ rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0,
+ keygen_add_std_prefs, sk );
+ if( rc ) {
+ log_error("signing failed: %s\n", g10_errstr(rc) );
+ free_user_id(uid);
+ return 0;
+ }
+
+ /* insert/append to secret keyblock */
+ pkt = m_alloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_USER_ID;
+ pkt->pkt.user_id = copy_user_id(NULL, uid);
+ node = new_kbnode(pkt);
+ if( sec_where )
+ insert_kbnode( sec_where, node, 0 );
else
- *ret_sig = sig;
- return rc;
+ add_kbnode( sec_keyblock, node );
+ pkt = m_alloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = copy_signature(NULL, sig);
+ if( sec_where )
+ insert_kbnode( node, new_kbnode(pkt), 0 );
+ else
+ add_kbnode( sec_keyblock, new_kbnode(pkt) );
+ /* insert/append to public keyblock */
+ pkt = m_alloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_USER_ID;
+ pkt->pkt.user_id = uid;
+ node = new_kbnode(pkt);
+ if( pub_where )
+ insert_kbnode( pub_where, node, 0 );
+ else
+ add_kbnode( pub_keyblock, node );
+ pkt = m_alloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = copy_signature(NULL, sig);
+ if( pub_where )
+ insert_kbnode( node, new_kbnode(pkt), 0 );
+ else
+ add_kbnode( pub_keyblock, new_kbnode(pkt) );
+ return 1;
+}
+
+
+/****************
+ * Remove all selceted userids from the keyrings
+ */
+static void
+menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ KBNODE node;
+ int selected=0;
+
+ for( node = pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ selected = node->flag & NODFLG_SELUID;
+ if( selected ) {
+ delete_kbnode( node );
+ if( sec_keyblock ) {
+ KBNODE snode;
+ int s_selected = 0;
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ for( snode = sec_keyblock; snode; snode = snode->next ) {
+ if( snode->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *suid = snode->pkt->pkt.user_id;
+
+ s_selected =
+ (uid->len == suid->len
+ && !memcmp( uid->name, suid->name, uid->len));
+ if( s_selected )
+ delete_kbnode( snode );
+ }
+ else if( s_selected
+ && snode->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( snode );
+ else if( snode->pkt->pkttype == PKT_SECRET_SUBKEY )
+ s_selected = 0;
+ }
+ }
+ }
+ }
+ else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( node );
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ selected = 0;
+ }
+ commit_kbnode( &pub_keyblock );
+ if( sec_keyblock )
+ commit_kbnode( &sec_keyblock );
+}
+
+
+/****************
+ * Remove some of the secondary keys
+ */
+static void
+menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ KBNODE node;
+ int selected=0;
+
+ for( node = pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ selected = node->flag & NODFLG_SELKEY;
+ if( selected ) {
+ delete_kbnode( node );
+ if( sec_keyblock ) {
+ KBNODE snode;
+ int s_selected = 0;
+ u32 ki[2];
+
+ keyid_from_pk( node->pkt->pkt.public_key, ki );
+ for( snode = sec_keyblock; snode; snode = snode->next ) {
+ if( snode->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ u32 ki2[2];
+
+ keyid_from_sk( snode->pkt->pkt.secret_key, ki2 );
+ s_selected = (ki[0] == ki2[0] && ki[1] == ki2[1]);
+ if( s_selected )
+ delete_kbnode( snode );
+ }
+ else if( s_selected
+ && snode->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( snode );
+ else
+ s_selected = 0;
+ }
+ }
+ }
+ }
+ else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( node );
+ else
+ selected = 0;
+ }
+ commit_kbnode( &pub_keyblock );
+ if( sec_keyblock )
+ commit_kbnode( &sec_keyblock );
+}
+
+
+/****************
+ * Select one user id or remove all selection if index is 0.
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_uid( KBNODE keyblock, int index )
+{
+ KBNODE node;
+ int i;
+
+ /* first check that the index is valid */
+ if( index ) {
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( ++i == index )
+ break;
+ }
+ }
+ if( !node ) {
+ tty_printf(_("No user id with index %d\n"), index );
+ return 0;
+ }
+ }
+ else { /* reset all */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID )
+ node->flag &= ~NODFLG_SELUID;
+ }
+ return 1;
+ }
+ /* and toggle the new index */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( ++i == index )
+ if( (node->flag & NODFLG_SELUID) )
+ node->flag &= ~NODFLG_SELUID;
+ else
+ node->flag |= NODFLG_SELUID;
+ }
+ }
+
+ return 1;
+}
+
+/****************
+ * Select secondary keys
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_key( KBNODE keyblock, int index )
+{
+ KBNODE node;
+ int i;
+
+ /* first check that the index is valid */
+ if( index ) {
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ if( ++i == index )
+ break;
+ }
+ }
+ if( !node ) {
+ tty_printf(_("No secondary key with index %d\n"), index );
+ return 0;
+ }
+ }
+ else { /* reset all */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY )
+ node->flag &= ~NODFLG_SELKEY;
+ }
+ return 1;
+ }
+ /* and set the new index */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ if( ++i == index )
+ if( (node->flag & NODFLG_SELKEY) )
+ node->flag &= ~NODFLG_SELKEY;
+ else
+ node->flag |= NODFLG_SELKEY;
+ }
+ }
+
+ return 1;
+}
+
+
+static int
+count_uids_with_flag( KBNODE keyblock, unsigned flag )
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ if( node->pkt->pkttype == PKT_USER_ID && (node->flag & flag) )
+ i++;
+ return i;
+}
+
+static int
+count_keys_with_flag( KBNODE keyblock, unsigned flag )
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ if( ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ && (node->flag & flag) )
+ i++;
+ return i;
+}
+
+static int
+count_uids( KBNODE keyblock )
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ if( node->pkt->pkttype == PKT_USER_ID )
+ i++;
+ return i;
+}
+
+
+/****************
+ * Returns true if there is at least one selected user id
+ */
+static int
+count_selected_uids( KBNODE keyblock )
+{
+ return count_uids_with_flag( keyblock, NODFLG_SELUID);
+}
+
+static int
+count_selected_keys( KBNODE keyblock )
+{
+ return count_keys_with_flag( keyblock, NODFLG_SELKEY);
}