From ca058399b04fedd7dcd203447e0e59080c33099e Mon Sep 17 00:00:00 2001 From: David Shaw Date: Fri, 21 Dec 2001 21:02:05 +0000 Subject: [PATCH] Nonrevocable key signature support via "nrsign". These sigs can expire, but cannot be revoked. Any revocation certificates for them are ignored. --- g10/ChangeLog | 24 ++++++++++++++++ g10/build-packet.c | 5 ++++ g10/g10.c | 11 +++++++ g10/keyedit.c | 71 +++++++++++++++++++++++----------------------- g10/keygen.c | 4 +-- g10/packet.h | 2 +- g10/parse-packet.c | 1 + g10/revoke.c | 2 +- g10/sign.c | 6 ++-- g10/trustdb.c | 22 +++++++++++++- 10 files changed, 106 insertions(+), 42 deletions(-) diff --git a/g10/ChangeLog b/g10/ChangeLog index 14ab65c44..a29c77318 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,27 @@ +2001-12-21 David Shaw + + * parse-packet.c (can_handle_critical): Can handle critical + revocation subpackets now. + + * trustdb.c (mark_usable_uid_certs): Disregard revocations for + nonrevocable sigs. Note that this allows a newer revocable + signature to override an older nonrevocable signature. + + * sign.c (make_keysig_packet): add a duration field and change all + callers. This makes make_keysig_packet closer to + write_signature_packets and removes some duplicated expiration + code. + + * keyedit.c (keyedit_menu, menu_revsig, sign_uids, + sign_mk_attrib): Add nrsign command, don't allow revoking a + nonrevocable signature, + + * g10.c (main): Add --nrsign option to nonrevocably sign a key + from the command line. + + * build-packet.c (build_sig_subpkt_from_sig): Comment to explain + the use of CRITICAL. + 2001-12-21 Werner Koch * g10.c. options.h : New option --show-keyring diff --git a/g10/build-packet.c b/g10/build-packet.c index 2025c4091..ae33b9c04 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -900,6 +900,11 @@ build_sig_subpkt_from_sig( PKT_signature *sig ) buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; + + /* Mark this CRITICAL, so if any implementation doesn't + understand sigs that can expire, it'll just disregard this + sig altogether. */ + build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL, buf, 4 ); } diff --git a/g10/g10.c b/g10/g10.c index 9b949b3d5..f594069c0 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -78,6 +78,7 @@ enum cmd_and_opt_values { aNull = 0, aSignSym, aSignKey, aLSignKey, + aNRSignKey, aListPackets, aEditKey, aDeleteKey, @@ -278,6 +279,7 @@ static ARGPARSE_OPTS opts[] = { N_("remove key from the secret keyring")}, { aSignKey, "sign-key" ,256, N_("sign a key")}, { aLSignKey, "lsign-key" ,256, N_("sign a key locally")}, + { aNRSignKey, "nrsign-key" ,256, N_("sign a key non-revocably")}, { aEditKey, "edit-key" ,256, N_("sign or edit a key")}, { aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")}, { aExport, "export" , 256, N_("export keys") }, @@ -889,6 +891,7 @@ main( int argc, char **argv ) case aKeygen: set_cmd( &cmd, aKeygen); greeting=1; break; case aSignKey: set_cmd( &cmd, aSignKey); break; case aLSignKey: set_cmd( &cmd, aLSignKey); break; + case aNRSignKey: set_cmd( &cmd, aNRSignKey); break; case aStore: set_cmd( &cmd, aStore); break; case aEditKey: set_cmd( &cmd, aEditKey); greeting=1; break; case aClearsign: set_cmd( &cmd, aClearsign); break; @@ -1580,6 +1583,14 @@ main( int argc, char **argv ) m_free(username); break; + case aNRSignKey: + if( argc != 1 ) + wrong_args(_("--nrsign-key user-id")); + username = make_username( fname ); + keyedit_menu(fname, locusr, NULL, 3 ); + m_free(username); + break; + case aEditKey: /* Edit a key signature */ if( !argc ) wrong_args(_("--edit-key user-id [commands]")); diff --git a/g10/keyedit.c b/g10/keyedit.c index c30cd1d7b..ef4854b51 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -75,16 +75,11 @@ static int enable_disable_key( KBNODE keyblock, int disable ); #define NODFLG_SELKEY (1<<9) /* indicate the selected key */ #define NODFLG_SELSIG (1<<10) /* indicate a selected signature */ - struct sign_attrib { - int non_exportable; - u32 duration; + int non_exportable,non_revocable; struct revocation_reason_info *reason; }; - - - /**************** * Print information about a signature, check it and return true * if the signature is okay. NODE must be a signature packet. @@ -241,20 +236,15 @@ sign_mk_attrib( PKT_signature *sig, void *opaque ) byte buf[8]; if( attrib->non_exportable ) { + sig->flags.exportable=0; buf[0] = 0; /* not exportable */ build_sig_subpkt( sig, SIGSUBPKT_EXPORTABLE, buf, 1 ); } - if(attrib->duration>0) { - buf[0]=(attrib->duration >> 24) & 0xff; - buf[1]=(attrib->duration >> 16) & 0xff; - buf[2]=(attrib->duration >> 8) & 0xff; - buf[3]=attrib->duration & 0xff; - /* Mark this CRITICAL, so if any implementation doesn't - understand sigs that can expire, it'll just disregard this - sig altogether. */ - build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL, - buf, 4 ); + if( attrib->non_revocable ) { + sig->flags.revocable=0; + buf[0] = 0; /* not revocable */ + build_sig_subpkt( sig, SIGSUBPKT_REVOCABLE, buf, 1 ); } if( attrib->reason ) @@ -271,7 +261,8 @@ sign_mk_attrib( PKT_signature *sig, void *opaque ) * if some user_ids are marked those will be signed. */ static int -sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) +sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, + int local , int nonrevocable ) { int rc = 0; int class=0; @@ -500,6 +491,10 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) tty_printf( _("\nThe signature will be marked as non-exportable.\n")); + if( nonrevocable ) + tty_printf( + _("\nThe signature will be marked as non-revocable.\n")); + switch(class) { case 0x11: @@ -537,10 +532,10 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) assert( primary_pk ); memset( &attrib, 0, sizeof attrib ); attrib.non_exportable = local; - attrib.duration = duration; + attrib.non_revocable = nonrevocable; node->flag &= ~NODFLG_MARK_A; - /* we force createion of a v4 signature for local + /* we force creation of a v4 signature for local * signatures, otherwise we would not generate the * subpacket with v3 keys and the signature becomes * exportable */ @@ -549,8 +544,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified, int local ) NULL, sk, class, 0, force_v4?4:0, - timestamp, sign_mk_attrib, - &attrib ); + timestamp, duration, + sign_mk_attrib, &attrib ); if( rc ) { log_error(_("signing failed: %s\n"), g10_errstr(rc)); goto leave; @@ -753,7 +748,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { enum cmdids { cmdNONE = 0, cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, - cmdLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, cmdPRIMARY, + cmdLSIGN, cmdNRSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF, @@ -780,6 +775,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, { N_("sign") , cmdSIGN , 0,1,1, N_("sign the key") }, { N_("s") , cmdSIGN , 0,1,1, NULL }, { N_("lsign") , cmdLSIGN , 0,1,1, N_("sign the key locally") }, + { N_("nrsign") , cmdNRSIGN , 0,1,1, N_("sign the key non-revocably") }, { N_("debug") , cmdDEBUG , 0,0,0, NULL }, { N_("adduid") , cmdADDUID , 1,1,0, N_("add a user ID") }, { N_("deluid") , cmdDELUID , 0,1,0, N_("delete user ID") }, @@ -826,7 +822,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, if( sign_mode ) { commands = NULL; - append_to_strlist( &commands, sign_mode == 1? "sign":"lsign" ); + append_to_strlist( &commands, sign_mode == 1? "sign": + sign_mode == 2?"lsign":"nrsign" ); have_commands = 1; } @@ -984,6 +981,7 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, case cmdSIGN: /* sign (only the public key) */ case cmdLSIGN: /* sign (only the public key) */ + case cmdNRSIGN: /* sign (only the public key) */ if( pk->is_revoked ) { tty_printf(_("Key is revoked.\n")); @@ -1007,7 +1005,8 @@ keyedit_menu( const char *username, STRLIST locusr, STRLIST commands, break; } } - if( !sign_uids( keyblock, locusr, &modified, cmd == cmdLSIGN ) + if( !sign_uids( keyblock, locusr, &modified, + cmd == cmdLSIGN , cmd == cmdNRSIGN ) && sign_mode ) goto do_cmd_save; break; @@ -1526,7 +1525,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock ) sec_where = NULL; assert(pk && sk ); - rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, + rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, 0, keygen_add_std_prefs, pk ); free_secret_key( sk ); if( rc ) { @@ -1821,11 +1820,11 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock ) /* create new self signature */ if( mainkey ) rc = make_keysig_packet( &newsig, main_pk, uid, NULL, - sk, 0x13, 0, 0, 0, + sk, 0x13, 0, 0, 0, 0, keygen_add_std_prefs, main_pk ); else rc = make_keysig_packet( &newsig, main_pk, NULL, sub_pk, - sk, 0x18, 0, 0, 0, + sk, 0x18, 0, 0, 0, 0, keygen_add_key_expire, sub_pk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", @@ -2225,9 +2224,9 @@ ask_revoke_sig( KBNODE keyblock, KBNODE node ) (ulong)sig->keyid[1], datestr_from_sig(sig) ); if( cpr_get_answer_is_yes("ask_revoke_sig.one", - _("Create a revocation certificate for this signature? (y/N)")) ) { - node->flag |= NODFLG_MARK_A; - unode->flag |= NODFLG_MARK_A; + _("Create a revocation certificate for this signature? (y/N)")) ) { + node->flag |= NODFLG_MARK_A; + unode->flag |= NODFLG_MARK_A; } } @@ -2263,9 +2262,11 @@ menu_revsig( KBNODE keyblock ) && ((sig = node->pkt->pkt.signature), !seckey_available(sig->keyid) ) ) { if( (sig->sig_class&~3) == 0x10 ) { - tty_printf(_(" signed by %08lX at %s\n"), - (ulong)sig->keyid[1], datestr_from_sig(sig) ); - node->flag |= NODFLG_SELSIG; + tty_printf(_(" signed by %08lX at %s%s\n"), + (ulong)sig->keyid[1], datestr_from_sig(sig), + sig->flags.revocable?"":" (not revocable)"); + if(sig->flags.revocable) + node->flag |= NODFLG_SELSIG; } else if( sig->sig_class == 0x30 ) { tty_printf(_(" revoked by %08lX at %s\n"), @@ -2342,7 +2343,7 @@ menu_revsig( KBNODE keyblock ) unode->pkt->pkt.user_id, NULL, sk, - 0x30, 0, 0, 0, + 0x30, 0, 0, 0, 0, sign_mk_attrib, &attrib ); free_secret_key(sk); @@ -2405,7 +2406,7 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock ) node->flag &= ~NODFLG_SELKEY; sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key ); rc = make_keysig_packet( &sig, mainpk, NULL, subpk, sk, - 0x28, 0, 0, 0, + 0x28, 0, 0, 0, 0, sign_mk_attrib, &attrib ); free_secret_key(sk); if( rc ) { diff --git a/g10/keygen.c b/g10/keygen.c index f79a4a664..80a52b4ac 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -389,7 +389,7 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, cache_public_key (pk); /* and make the signature */ - rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, + rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, 0, keygen_add_std_prefs, pk ); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); @@ -438,7 +438,7 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, /* and make the signature */ oduap.usage = use; oduap.pk = subpk; - rc = make_keysig_packet( &sig, pk, NULL, subpk, sk, 0x18, 0, 0, 0, + rc = make_keysig_packet( &sig, pk, NULL, subpk, sk, 0x18, 0, 0, 0, 0, keygen_add_key_flags_and_expire, &oduap ); if( rc ) { log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); diff --git a/g10/packet.h b/g10/packet.h index 0bddfe41b..de18d9695 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -437,7 +437,7 @@ int write_comment( IOBUF out, const char *s ); 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 sigversion, u32 timestamp, + int sigversion, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque ); int update_keysig_packet( PKT_signature **ret_sig, diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 5f2bf4ec5..1de7c85c6 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -968,6 +968,7 @@ can_handle_critical( const byte *buffer, size_t n, int type ) case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: case SIGSUBPKT_EXPORTABLE: + case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_ISSUER:/* issuer key ID */ case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_HASH: diff --git a/g10/revoke.c b/g10/revoke.c index 45ce6b3e0..ef3137109 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -193,7 +193,7 @@ gen_revoke( const char *uname ) iobuf_push_filter( out, armor_filter, &afx ); /* create it */ - rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, 0, 0, + rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, 0, 0, 0, revocation_reason_build_cb, reason ); if( rc ) { diff --git a/g10/sign.c b/g10/sign.c index 83ab54dfa..48a506901 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1012,7 +1012,7 @@ 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 sigversion, u32 timestamp, + int sigversion, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque ) { @@ -1075,7 +1075,9 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, if(timestamp) sig->timestamp=timestamp; else - sig->timestamp = make_timestamp(); + sig->timestamp=make_timestamp(); + if(duration) + sig->expiredate=sig->timestamp+duration; sig->sig_class = sigclass; if( sig->version >= 4 ) build_sig_subpkt_from_sig( sig ); diff --git a/g10/trustdb.c b/g10/trustdb.c index fae540f57..9afc1bf8e 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1056,7 +1056,27 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) continue; n->flag |= (1<<10); /* mark this node as processed */ - if (sig->timestamp >= sigdate) + + /* If the current signode is a nonrevocable signature, and + we're checking a revocation, then skip. Note that this + will let more recent signatures replace the nonrevocable + signature. Is that the proper behavior? */ + + if(IS_UID_REV(n->pkt->pkt.signature) && + IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable) + continue; + + /* A nonrevocable signature n should always replace a + revocation in signode. If n is newer, then there is no + question. If n is older, then it should still replace + signode as the revocation in signode is invalid because n + is nonrevocable. */ + + if ((sig->timestamp >= sigdate) || + (IS_UID_REV(signode->pkt->pkt.signature) && + IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable)) { signode = n; sigdate = sig->timestamp;