[ANN] autosign extension

Lasse Kliemann lasse-list-mercurial-2009 at mail.plastictree.net
Wed May 13 09:02:57 CDT 2009


* Message by -Martin Geisler- from Wed 2009-05-13:
 
> So please go test it:
> 
>   http://bitbucket.org/mg/autosign/src/tip/autosign.py
> 
> You can verify some or all signatures with 'hg checksigs'. This looks
> like this:
> 
>   85:286738f92d3f: ** no signature
>   86:bc7de65702b7: ** bad signature on 1e9a254defd6
>   87:88f1c9bc2f9a: good signature
> 
> and could probably use some improvements. Also, calling it 'verifysigs'
> might be a better name?

Thank you for your efforts!

Some suggestions and observations:

'checksigs' seems to ignore the first revision if no revision is 
specified. See the attached patch for what maybe a simple fix 
(start range at 0). (BTW, I had never written a line of Python 
code before. Please excuse any uglyness in my patch.)

The test for 'GOODSIG' in the output may be too weak. Someone 
might just create a key with 'GOODSIG' in the username. See the 
attached patch for a first attempt to fix this (and to implement 
the scheme sketched in the next paragraph, which I however do not 
consider a final solution; see further below for that).

We need a way to connect the 'user' field in the changeset with 
the key. More precisely: there should be a way to specify which 
keys are allowed to give - via a good signature in the sense of 
'gpg --verify' - testimony about authorship of which users. A 
straightforward way is to only consider a signature valid if the 
username in the key matches the username in the 'user' field of 
the changeset. This is a common practice with signed e-mail 
(username in key is compared to 'From:' header). This alone is 
not enough, however, since anyone can create a key with anyone's 
identity. As an additional measure, the trust of the key could be 
considered.

However, I would prefer something quite different. We could allow 
a configurable set of pairs (F,U) consisting of a key fingerprint 
F and a username U each. The configuration could be done in 
~/.hgrc or such.

Verification would work as follows. Let C be a changeset and S 
its signature. Then S is considered _valid_ for C if there exists 
a pair (F,U) such that:
* S it is a good signature for C (in the sense of gpg --verify) 
  for a key that has fingerprint F 
* and U is the exact string in the 'user' field of C.

Then, I can say: well, I know a co-worker by some name U, and he 
signs his changesets with a key that has fingerprint F. I would 
then add pair (F,U) to the list in ~/.hgrc. After each pull, I 
would run 'hg checksigs'. If it does not complain, I henceforth 
know that I can trust all changesets bearing the name U of the 
co-worker to truly originate from him. So, I would do a 
verification once, and henceforth could simply rely on the output 
of 'hg log'.

Advantages: no fiddling with signatures on keys (to increase 
their trust), and no need for committers to have the exact same 
username in Mercurial ([ui] username = ...) which they have on 
their key.


> Unlike the gpg extension there is no support for changing signing keys
> or the path to gpg -- we should probably add that.
> 
> When I started I of course figured that I would combine this with the
> gpg extension, but haven't done so yet. One reason is that I want this
> to support signatures made by other programs and mechanisms (say, X.509
> certs via openssl) and then the name 'gpg' is a misnormer :-)

X.509 should be supported, yes. We would have to adopt the above 
scheme of fingerprint-username pairs to it, if accepted. I could 
make a proposal for it then.

-------------- next part --------------
diff -r cfcfce96d0fa autosign.py
--- a/autosign.py	Wed May 13 00:17:10 2009 +0200
+++ b/autosign.py	Wed May 13 15:58:31 2009 +0200
@@ -14,7 +14,7 @@
 including the signature.
 """
 
-import os, tempfile, subprocess, binascii
+import os, tempfile, subprocess, binascii, re
 
 from mercurial import (util, cmdutil, extensions, revlog, error,
                        encoding, changelog)
@@ -29,7 +29,7 @@
     return binascii.b2a_base64(sig).strip()
 
 
-def verify(msg, sig, quiet=False):
+def verify(msg, user, sig, quiet=False):
     sig = binascii.a2b_base64(sig)
     try:
         fd, filename = tempfile.mkstemp(prefix="hg-", suffix=".sig")
@@ -43,7 +43,8 @@
                              stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                              stderr=stderr)
         out, err = p.communicate(msg)
-        return 'GOODSIG' in out
+        return (re.search('^\[GNUPG:\] GOODSIG [^ ]* ' + re.escape(user) + '$', out, re.MULTILINE) is not None 
+                and re.search('^\[GNUPG:\] (TRUST_FULLY|TRUST_ULTIMATE)', out, re.MULTILINE) is not None)
     finally:
         try:
             os.unlink(filename)
@@ -105,7 +106,7 @@
     The final return code is the highest of the above.
     """
     if not revrange:
-        revs = xrange(1, len(repo))
+        revs = xrange(0, len(repo))
     else:
         revs = cmdutil.revrange(repo, revrange)
 
@@ -121,7 +122,7 @@
         else:
             ui.debug(_("signature: %s\n") % sig)
             try:
-                if verify(hex(h), sig, quiet=True):
+                if verify(hex(h), ctx.user(), sig, quiet=True):
                     msg = _("good signature")
                 else:
                     msg = _("** bad signature on %s") % short(h)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: not available
Url : http://selenic.com/pipermail/mercurial/attachments/20090513/bec1f1ff/attachment.pgp 


More information about the Mercurial mailing list