Talking to GPG on Linux through os.popen
Andrew Kuchling
akuchlin at mems-exchange.org
Thu Aug 17 09:54:58 EDT 2000
On that note, here's a partially-written class for dealing with a GPG
subprocess. Right now it only handles verifying the signature on a
file, because that's all I needed. Anyone want to help by suggesting
what the .sign_file, .encrypt_file(), .decrypt_file() methods should
look like?
--amk
"""GPG module
Provides a GPGSubprocess class that can be used to encrypt, decrypt,
sign, and verify GPG messages. It runs GPG in batch mode, which causes it
to output lines prefixed with '[GNUPG:]' that contain well-structured
information about the results.
"""
__revision__ = '$Id: GPG.py,v 1.1 2000/06/11 23:53:57 akuchlin Exp akuchlin $'
import os, string
import cStringIO, popen2
class Signature:
"Class used to hold information about a signature result"
def __init__(self):
self.valid = 0
self.fingerprint = self.creation_date = self.timestamp = None
self.signature_id = self.key_id = None
self.username = None
def is_valid(self):
return self.valid
class GPGSubprocess:
# Default path used for searching for the GPG binary, when the
# PATH environment variable isn't set.
DEFAULT_PATH = ['/bin', '/usr/bin', '/usr/local/bin']
def __init__(self, gpg_binary = None):
"""Initialize an object instance. Options are:
gpg_binary -- full pathname for GPG binary. If not supplied,
the current value of PATH will be searched, falling back to the
DEFAULT_PATH class variable if PATH isn't available.
"""
# If needed, look for the gpg binary along the path
if gpg_binary is None:
import os
if os.environ.has_key('PATH'):
path = os.environ['PATH']
path = string.split(path, os.pathsep)
else:
path = self.DEFAULT_PATH
for dir in path:
fullname = os.path.join(dir, 'gpg')
if os.path.exists( fullname ):
gpg_binary = fullname
break
else:
raise ValueError, ("Couldn't find 'gpg' binary on path"
+ repr(path) )
self.gpg_binary = gpg_binary
def _open_subprocess(self, *args):
# Internal method: open a pipe to a GPG subprocess and return
# the file objects for communicating with it.
cmd = self.gpg_binary + ' --status-fd 2 ' + string.join(args)
child_stdout, child_stdin, child_stderr = popen2.popen3(cmd)
return child_stdout, child_stdin, child_stderr
def _read_response(self, child_stdout):
# Internal method: reads all the output from GPG, taking notice
# only of lines that begin with the magic [GNUPG:] prefix.
# (See doc/DETAILS in the GPG distribution for info on GPG's
# output when --status-fd is specified.)
#
# Returns a dictionary, mapping GPG's keywords to the arguments
# for that keyword.
resp = {}
while 1:
line = child_stdout.readline()
if line == "": break
line = string.rstrip( line )
if line[0:9] == '[GNUPG:] ':
# Chop off the prefix
line = line[9:]
L = string.split(line, None, 1)
keyword = L[0]
if len(L) > 1:
resp[ keyword ] = L[1]
else:
resp[ keyword ] = ""
return resp
# Not yet implemented, because I don't need these methods
# The methods certainly don't have all the parameters they'd need.
def verify(self, data):
"Verify the signature on the contents of the string 'data'"
file = cStringIO.StringIO( data )
return self.verify_file( file )
def verify_file(self, file):
"Verify the signature on the contents of the file-like object 'file'"
child_stdout, child_stdin, child_stderr = self._open_subprocess()
# Copy the file to the GPG subprocess
while 1:
data = file.read(1024)
if data == "": break
child_stdin.write(data)
child_stdin.close()
# Get the response information
resp = self._read_response(child_stderr)
# Create an object to return, and fill it with data
sig = Signature()
if resp.has_key('BADSIG'):
sig.valid = 0
sig.key_id, sig.username = string.split(resp['BADSIG'], None, 1)
elif resp.has_key('GOODSIG'):
sig.valid = 1
sig.key_id, sig.username = string.split(resp['GOODSIG'], None, 1)
if resp.has_key('VALIDSIG'):
L = string.split(resp['VALIDSIG'], None)
sig.fingerprint, sig.creation_date, sig.timestamp = L
if resp.has_key('SIG_ID'):
L = string.split(resp['SIG_ID'], None)
sig.signature_id, sig.creation_date, sig.timestamp = L
# Read the contents of the file from GPG's stdout
sig.data = ""
while 1:
data = child_stdout.read(1024)
if data == "": break
sig.data = sig.data + data
return sig
def sign(self, data):
"Sign the contents of the string 'data'"
input = cStringIO.StringIO( data )
output = cStringIO.StringIO()
self.sign( input, output )
return output.getvalue()
def sign_file(self, input, output,
):
"""Sign the contents of the file-like object 'input',
sending the output to 'output'
"""
pass
def encrypt_file(self, file):
"Encrypt the message read from the file-like object 'file'"
pass
def encrypt(self, data):
"Encrypt the message contained in the string 'data'"
pass
def decrypt_file(self, file):
"Decrypt the message read from the file-like object 'file'"
pass
def decrypt(self, data):
"Decrypt the message contained in the string 'data'"
pass
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
print 'Usage: GPG.py <signed file>'
sys.exit()
obj = GPGSubprocess()
file = open(sys.argv[1], 'rb')
sig = obj.verify_file( file )
print sig.__dict__
More information about the Python-list
mailing list