2 # Python "binding" for GPG. I'll write GPGME bindings eventually. The
3 # intent is to use GPG to sign method calls, as a way of identifying
4 # and authenticating peers. Calls should still go over an encrypted
5 # transport such as HTTPS, with certificate checking.
7 # Mark Huang <mlhuang@cs.princeton.edu>
8 # Copyright (C) 2006 The Trustees of Princeton University
15 from StringIO import StringIO
16 from xml.dom import minidom
17 from xml.dom.ext import Canonicalize
18 from subprocess import Popen, PIPE, call
19 from tempfile import NamedTemporaryFile, mkdtemp
21 from PLC.Faults import *
23 def canonicalize(methodname, args):
25 Returns a canonicalized XML-RPC representation of the
26 specified method call.
29 xml = xmlrpclib.dumps(args, methodname, encoding = 'utf-8', allow_none = 1)
30 dom = minidom.parseString(xml)
32 # Canonicalize(), though it claims to, does not encode unicode
33 # nodes to UTF-8 properly and throws an exception unless you write
34 # the stream to a file object, so just encode it ourselves.
36 Canonicalize(dom, output = buf)
37 xml = buf.getvalue().encode('utf-8')
41 def gpg_sign(methodname, args, secret_keyring, keyring):
43 Signs the specified method call using the specified keyring files.
46 message = canonicalize(methodname, args)
48 p = Popen(["gpg", "--batch", "--no-tty",
49 "--no-default-keyring",
50 "--secret-keyring", secret_keyring,
52 "--detach-sign", "--armor"],
53 stdin = PIPE, stdout = PIPE, stderr = PIPE)
54 p.stdin.write(message)
56 signature = p.stdout.read()
59 raise PLCAuthenticationFailure, "GPG signing failed with return code %d" % rc
63 def gpg_verify(methodname, args, signature, key):
65 Verifys the signature of the method call using the specified public
69 message = canonicalize(methodname, args)
71 # Write public key to temporary file
72 keyfile = NamedTemporaryFile(suffix = '.pub')
76 # Import public key into temporary keyring
78 call(["gpg", "--batch", "--no-tty", "--homedir", homedir, "--import", keyfile.name],
79 stdin = PIPE, stdout = PIPE, stderr = PIPE)
81 # Write detached signature to temporary file
82 sigfile = NamedTemporaryFile()
83 sigfile.write(signature)
87 p = Popen(["gpg", "--batch", "--no-tty", "--homedir", homedir, "--verify", sigfile.name, "-"],
88 stdin = PIPE, stdout = PIPE, stderr = PIPE)
89 p.stdin.write(message)
95 shutil.rmtree(homedir)
99 raise PLCAuthenticationFailure, "GPG verification failed with return code %d" % rc