Nonrevocable signatures patch

David Shaw dshaw at jabberwocky.com
Tue Aug 7 08:46:01 CEST 2001


Hi everyone,

Here's a patch against 1.0.6 to allow nonrevocable key signatures as
per section 5.2.3.12 in the RFC.  Basically, if you sign a key
nonrevocably, you can't change your mind later. :)

I did not implement signatures that are both non-revocable and
non-exportable.  As far as I can see, there is no point in this.  A
non-exportable signature by definition should never be sent off of
your keyring, so there is never a reason to revoke it when simply
deleting it is cleaner and simpler.

Note that PGP (I checked with 7.0.3 freeware) doesn't yet do anything
special with this type of signature, and treats it as if it was
revocable.  There is no danger in revoking the signature on PGP and
importing this key to GnuPG - with this patch GnuPG will ignore a
revocation certificate for a nonrevocable key.

This patch also fixes a small bug in --list-packets which displays
some nonrevocable signatures as revocable.

To use it, you can do --nrsign on the command line, or "nrsign" in the
--edit-key menu.  A nonrevocable signature is flagged in list-sigs and
the edit functions with an "NR":

Regular signature:
  sig        61AAD3A6 2001-08-07  testkey at example.com

Nonrevocable signature:
  sig NR     61AAD3A6 2001-08-07  testkey at example.com

As always, comments welcome.

David

-- 
   David Shaw  |  dshaw at jabberwocky.com  |  WWW http://www.jabberwocky.com/
+---------------------------------------------------------------------------+
   "There are two major products that come out of Berkeley: LSD and UNIX.
      We don't believe this to be a coincidence." - Jeremy S. Anderson
-------------- next part --------------
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/build-packet.c gnupg-1.0.6-nonrevoke/g10/build-packet.c
--- gnupg-1.0.6/g10/build-packet.c	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/build-packet.c	Fri Aug  3 14:59:27 2001
@@ -773,6 +773,7 @@
       case SIGSUBPKT_NOTATION:
       case SIGSUBPKT_POLICY:
       case SIGSUBPKT_REVOC_REASON:
+      case SIGSUBPKT_REVOCABLE:
 	       hashed = 1; break;
       default: hashed = 0; break;
     }
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/g10.c gnupg-1.0.6-nonrevoke/g10/g10.c
--- gnupg-1.0.6/g10/g10.c	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/g10.c	Tue Aug  7 00:57:43 2001
@@ -72,6 +72,7 @@
     aSignEncr,
     aSignKey,
     aLSignKey,
+    aNRSignKey,
     aListPackets,
     aEditKey,
     aDeleteKey,
@@ -236,6 +237,7 @@
 				    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") },
@@ -779,6 +781,7 @@
 	  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;
@@ -1270,6 +1273,14 @@
 	    wrong_args(_("--lsign-key user-id"));
 	username = make_username( fname );
 	keyedit_menu(fname, locusr, NULL, 2 );
+	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;
 
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/keyedit.c gnupg-1.0.6-nonrevoke/g10/keyedit.c
--- gnupg-1.0.6/g10/keyedit.c	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/keyedit.c	Tue Aug  7 01:16:34 2001
@@ -75,7 +75,7 @@
 
 
 struct sign_attrib {
-    int non_exportable;
+    int non_exportable,non_revocable;
     struct revocation_reason_info *reason;
 };
 
@@ -145,9 +145,10 @@
 	break;
     }
     if( sigrc != '?' || print_without_key ) {
-	tty_printf("%s%c       %08lX %s   ",
+	tty_printf("%s%c%s     %08lX %s   ",
 		is_rev? "rev":"sig",
-		sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig));
+		sigrc, sig->flags.nonrevocable?"NR":"  ",
+		(ulong)sig->keyid[1], datestr_from_sig(sig));
 	if( sigrc == '%' )
 	    tty_printf("[%s] ", g10_errstr(rc) );
 	else if( sigrc == '?' )
@@ -251,6 +252,10 @@
 	buf[0] = 0; /* not exportable */
 	build_sig_subpkt( sig, SIGSUBPKT_EXPORTABLE, buf, 1 );
     }
+    if( attrib->non_revocable ) {
+	buf[0] = 0; /* not revocable */
+	build_sig_subpkt( sig, SIGSUBPKT_REVOCABLE, buf, 1 );
+    }
     if( attrib->reason )
 	revocation_reason_build_cb( sig, attrib->reason );
 
@@ -265,7 +270,7 @@
  * 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;
     SK_LIST sk_list = NULL;
@@ -342,6 +347,9 @@
 	    tty_printf(
 		  _("The signature will be marked as non-exportable.\n\n"));
 
+	if( nonrevocable )
+	    tty_printf(
+		  _("The signature will be marked as non-revocable.\n\n"));
 
 	if( opt.batch && opt.answer_yes )
 	    ;
@@ -362,6 +370,7 @@
 		assert( primary_pk );
 		memset( &attrib, 0, sizeof attrib );
 		attrib.non_exportable = local;
+		attrib.non_revocable = nonrevocable;
 		node->flag &= ~NODFLG_MARK_A;
 		rc = make_keysig_packet( &sig, primary_pk,
 					       node->pkt->pkt.user_id,
@@ -565,7 +574,7 @@
 						    int sign_mode )
 {
     enum cmdids { cmdNONE = 0,
-	   cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
+		  cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, cmdNRSIGN,
 	   cmdLSIGN, cmdREVSIG, cmdREVKEY, cmdDELSIG,
 	   cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
 	   cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
@@ -590,6 +599,7 @@
 	{ N_("key")     , cmdSELKEY    , 0,0,0, N_("select secondary key N") },
 	{ N_("check")   , cmdCHECK     , 0,0,1, N_("list signatures") },
 	{ N_("c")       , cmdCHECK     , 0,0,1, NULL },
+	{ N_("nrsign")  , cmdNRSIGN    , 0,1,1, N_("sign the key non-revocably") },
 	{ 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") },
@@ -636,7 +646,8 @@
 
     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;
     }
 
@@ -776,6 +787,7 @@
 
 	  case cmdSIGN: /* sign (only the public key) */
 	  case cmdLSIGN: /* sign (only the public key) */
+          case cmdNRSIGN: 
 	    if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) {
 		if( !cpr_get_answer_is_yes("keyedit.sign_all.okay",
 					   _("Really sign all user IDs? ")) ) {
@@ -783,7 +795,8 @@
 		    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;
 	    /* Actually we should do a update_trust_record() here so that
@@ -1799,11 +1812,14 @@
     tty_printf(_("\"\nsigned with your key %08lX at %s\n"),
 		(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)")) ) {
+    if(sig->flags.nonrevocable)
+      tty_printf(_("\tThis signature is nonrevocable!\n"));
+    else
+      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;
-    }
+      }
 }
 
 /****************
@@ -1846,6 +1862,9 @@
 		tty_printf(_("   revoked by %08lX at %s\n"),
 			    (ulong)sig->keyid[1], datestr_from_sig(sig) );
 	    }
+
+	    if(sig->flags.nonrevocable)
+	      tty_printf(_("    nonrevocable signature\n"));
 	}
     }
 
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/keylist.c gnupg-1.0.6-nonrevoke/g10/keylist.c
--- gnupg-1.0.6/g10/keylist.c	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/keylist.c	Tue Aug  7 01:16:47 2001
@@ -350,8 +350,9 @@
 		sigrc = ' ';
 	    }
             fputs( sigstr, stdout );
-            printf("%c       %08lX %s  ",
-		    sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig));
+            printf("%c%s     %08lX %s  ",
+		   sigrc, sig->flags.nonrevocable?"NR":"  ",
+		   (ulong)sig->keyid[1], datestr_from_sig(sig));
 	    if( sigrc == '%' )
 		printf("[%s] ", g10_errstr(rc) );
 	    else if( sigrc == '?' )
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/packet.h gnupg-1.0.6-nonrevoke/g10/packet.h
--- gnupg-1.0.6/g10/packet.h	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/packet.h	Fri Aug  3 15:10:39 2001
@@ -101,6 +101,7 @@
     struct {
 	unsigned checked:1; /* signature has been checked */
 	unsigned valid:1;   /* signature is good (if checked is set) */
+        unsigned nonrevocable:1; /* signature may not be revoked */
 	unsigned unknown_critical:1;
     } flags;
     u32     keyid[2];	    /* 64 bit keyid */
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/parse-packet.c gnupg-1.0.6-nonrevoke/g10/parse-packet.c
--- gnupg-1.0.6/g10/parse-packet.c	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/parse-packet.c	Mon Aug  6 19:18:56 2001
@@ -756,7 +756,8 @@
 	p = "regular expression";
 	break;
       case SIGSUBPKT_REVOCABLE:
-	p = "revocable";
+	if( length )
+	    printf("%srevocable", *buffer? "":"not ");
 	break;
       case SIGSUBPKT_KEY_EXPIRE:
 	if( length >= 4 )
@@ -884,6 +885,7 @@
       case SIGSUBPKT_KEY_FLAGS:
           return 0;  
       case SIGSUBPKT_EXPORTABLE:
+      case SIGSUBPKT_REVOCABLE:
 	if( !n )
 	    break;
 	return 0;
@@ -938,6 +940,7 @@
       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:
@@ -1174,6 +1177,11 @@
 	    log_error("signature packet without timestamp\n");
 	else
 	    sig->timestamp = buffer_to_u32(p);
+	/* Only look for a non-revocable flag in the hashed area.  If
+           it was in the non-hashed area, it could be a forgery. */
+	p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_REVOCABLE, NULL );
+	if( p && *p==0 )
+	    sig->flags.nonrevocable=1;
 	p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL );
 	if( !p )
 	    log_error("signature packet without keyid\n");
diff --exclude=Makefile -Nur gnupg-1.0.6/g10/trustdb.c gnupg-1.0.6-nonrevoke/g10/trustdb.c
--- gnupg-1.0.6/g10/trustdb.c	Tue Aug  7 01:18:17 2001
+++ gnupg-1.0.6-nonrevoke/g10/trustdb.c	Tue Aug  7 00:28:32 2001
@@ -1317,14 +1317,28 @@
 	 */
 	for( s2 = s; s2 ; s2 = s2->next ) {
 	    for(i=0; i < sigidx; i++ ) {
-		if( s2->r.sig.sig[i].lid == siglid )
-		    goto leaveduptest;
+	      if( s2->r.sig.sig[i].lid == siglid ) {
+
+		/* If we've already noted a revoke certificate, but
+		   this new sig is nonrevocable, the nonrevocable sig
+		   wins. */
+		if(s2->r.sig.sig[i].flag&SIGF_REVOKED &&
+		   sig->flags.nonrevocable)
+		  s2->r.sig.sig[i].flag&=(~SIGF_REVOKED);
+
+		goto leaveduptest;
+	      }
 	    }
 	}
 	for( s2 = srecs; s2 ; s2 = s2->next ) {
 	    for(i=0; i < SIGS_PER_RECORD; i++ ) {
-		if( s2->r.sig.sig[i].lid == siglid )
-		    goto leaveduptest;
+	      if( s2->r.sig.sig[i].lid == siglid ) {
+		if(s2->r.sig.sig[i].flag&SIGF_REVOKED &&
+		   sig->flags.nonrevocable)
+		  s2->r.sig.sig[i].flag&=(~SIGF_REVOKED);
+
+		goto leaveduptest;
+	      }
 	    }
 	}
       leaveduptest:


More information about the Gnupg-devel mailing list