From ef76b13694d797b6df6e5866b3671cfca8d22563 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Thu, 6 May 2010 00:03:11 -0400 Subject: Making the packaging suck less. --- __init__.py | 168 ---------------------------------------------- hgsshsign/__init__.py | 170 +++++++++++++++++++++++++++++++++++++++++++++++ hgsshsign/keymanifest.py | 36 ++++++++++ hgsshsign/keys.py | 88 ++++++++++++++++++++++++ hgsshsign/sshagent.py | 88 ++++++++++++++++++++++++ hgsshsign/structutils.py | 108 ++++++++++++++++++++++++++++++ keymanifest.py | 36 ---------- keys.py | 88 ------------------------ setup.py | 15 +++++ sshagent.py | 88 ------------------------ structutils.py | 108 ------------------------------ 11 files changed, 505 insertions(+), 488 deletions(-) delete mode 100644 __init__.py create mode 100644 hgsshsign/__init__.py create mode 100644 hgsshsign/keymanifest.py create mode 100644 hgsshsign/keys.py create mode 100644 hgsshsign/sshagent.py create mode 100644 hgsshsign/structutils.py delete mode 100644 keymanifest.py delete mode 100644 keys.py create mode 100755 setup.py delete mode 100644 sshagent.py delete mode 100644 structutils.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index a783fc3..0000000 --- a/__init__.py +++ /dev/null @@ -1,168 +0,0 @@ -# vim: set filencoding=utf8 -""" -SSH Key Signing - -@author: Mike Crute (mcrute@ag.com) -@organization: American Greetings Interactive -@date: May 03, 2010 - -Commands to sign and verify revisions with your -ssh key. - -Ponder this, bitches: - http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/ssh-rsa.c - http://svn.osafoundation.org/m2crypto/trunk/SWIG/_rsa.i -""" - - -import keys -from keymanifest import KeyManifest -from structutils import bytes_to_int -from sshagent import SSHAgent - -import os, tempfile, binascii -from mercurial import util, commands, match -from mercurial import node as hgnode -from mercurial.i18n import _ - - -class SSHAuthority(object): - - @classmethod - def from_ui(cls, ui): - public_key = absolute_path(ui.config("sshsign", "public_key")) - public_key = keys.PublicKey.from_file(public_key) - - manifest_file = ui.config("sshsign", "manifest_file") - if manifest_file: - manifest = KeyManifest.from_file(absolute_path(manifest_file)) - else: - manifest = None - ui.write(_("No key manifest set. You will not be able to verify" - " signatures.\n")) - - private_key = ui.config("sshsign", "private_key", None) - agent_socket = os.environ.get(SSHAgent.AGENT_SOCK_NAME) - if private_key: - private_key = keys.load_private_key(absolute_path(private_key)) - elif agent_socket: - private_key = SSHAgent(agent_socket, key=public_key.blob) - else: - raise util.Abort(_("No private key set and no agent running.")) - - return cls(public_key, manifest, private_key) - - def __init__(self, public_key, key_manifest=None, private_key=None): - self.public_key = public_key - self.key_manifest = key_manifest - self.private_key = private_key - - def verify(self, data, signature, whom): - try: - key = self.key_manifest[whom] # XXX: More elegant error handling. - except KeyError: - raise util.Abort(_("No key found for %s" % whom)) - - return key.verify(data, signature) - - def sign(self, data): - return self.private_key.sign(data) - - -def node2txt(repo, node, ver): - """map a manifest into some text""" - if ver != "0": - raise util.Abort(_("unknown signature version")) - - return "%s\n" % hgnode.hex(node) - - -def absolute_path(path): - path = os.path.expandvars(path) - return os.path.expanduser(path) - - -def sign(ui, repo, *revs, **opts): - """add a signature for the current or given revision - - If no revision is given, the parent of the working directory is used, - or tip if no revision is checked out. - - See 'hg help dates' for a list of formats valid for -d/--date. - """ - - mygpg = SSHAuthority.from_ui(ui) - sigver = "0" - sigmessage = "" - - date = opts.get('date') - if date: - opts['date'] = util.parsedate(date) - - if revs: - nodes = [repo.lookup(n) for n in revs] - else: - nodes = [node for node in repo.dirstate.parents() - if node != hgnode.nullid] - if len(nodes) > 1: - raise util.Abort(_('uncommitted merge - please provide a ' - 'specific revision')) - if not nodes: - nodes = [repo.changelog.tip()] - - for n in nodes: - hexnode = hgnode.hex(n) - ui.write(_("Signing %d:%s\n") % (repo.changelog.rev(n), - hgnode.short(n))) - # build data - data = node2txt(repo, n, sigver) - sig = mygpg.sign(data) - if not sig: - raise util.Abort(_("Error while signing")) - sig = binascii.b2a_base64(sig) - sig = sig.replace("\n", "") - sigmessage += "%s %s %s\n" % (hexnode, sigver, sig) - - # write it - if opts['local']: - repo.opener("localsigs", "ab").write(sigmessage) - return - - msigs = match.exact(repo.root, '', ['.hgsigs']) - s = repo.status(match=msigs, unknown=True, ignored=True)[:6] - if util.any(s) and not opts["force"]: - raise util.Abort(_("working copy of .hgsigs is changed " - "(please commit .hgsigs manually " - "or use --force)")) - - repo.wfile(".hgsigs", "ab").write(sigmessage) - - if '.hgsigs' not in repo.dirstate: - repo.add([".hgsigs"]) - - if opts["no_commit"]: - return - - message = opts['message'] - if not message: - # we don't translate commit messages - message = "\n".join(["Added signature for changeset %s" - % hgnode.short(n) - for n in nodes]) - try: - repo.commit(message, opts['user'], opts['date'], match=msigs) - except ValueError, inst: - raise util.Abort(str(inst)) - - -cmdtable = { - "sign": - (sign, - [('l', 'local', None, _('make the signature local')), - ('f', 'force', None, _('sign even if the sigfile is modified')), - ('', 'no-commit', None, _('do not commit the sigfile after signing')), - ('m', 'message', '', _('commit message')), - ] + commands.commitopts2, - _('hg sign [OPTION]... [REVISION]...')), -} - diff --git a/hgsshsign/__init__.py b/hgsshsign/__init__.py new file mode 100644 index 0000000..7b3f488 --- /dev/null +++ b/hgsshsign/__init__.py @@ -0,0 +1,170 @@ +# vim: set filencoding=utf8 +""" +SSH Key Signing + +@author: Mike Crute (mcrute@ag.com) +@organization: American Greetings Interactive +@date: May 03, 2010 + +Commands to sign and verify revisions with your +ssh key. + +Ponder this, bitches: + http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/ssh-rsa.c + http://svn.osafoundation.org/m2crypto/trunk/SWIG/_rsa.i +""" + +__version__ = "0.0.1dev" + + +import keys +from keymanifest import KeyManifest +from structutils import bytes_to_int +from sshagent import SSHAgent + +import os, tempfile, binascii +from mercurial import util, commands, match +from mercurial import node as hgnode +from mercurial.i18n import _ + + +class SSHAuthority(object): + + @classmethod + def from_ui(cls, ui): + public_key = absolute_path(ui.config("sshsign", "public_key")) + public_key = keys.PublicKey.from_file(public_key) + + manifest_file = ui.config("sshsign", "manifest_file") + if manifest_file: + manifest = KeyManifest.from_file(absolute_path(manifest_file)) + else: + manifest = None + ui.write(_("No key manifest set. You will not be able to verify" + " signatures.\n")) + + private_key = ui.config("sshsign", "private_key", None) + agent_socket = os.environ.get(SSHAgent.AGENT_SOCK_NAME) + if private_key: + private_key = keys.load_private_key(absolute_path(private_key)) + elif agent_socket: + private_key = SSHAgent(agent_socket, key=public_key.blob) + else: + raise util.Abort(_("No private key set and no agent running.")) + + return cls(public_key, manifest, private_key) + + def __init__(self, public_key, key_manifest=None, private_key=None): + self.public_key = public_key + self.key_manifest = key_manifest + self.private_key = private_key + + def verify(self, data, signature, whom): + try: + key = self.key_manifest[whom] # XXX: More elegant error handling. + except KeyError: + raise util.Abort(_("No key found for %s" % whom)) + + return key.verify(data, signature) + + def sign(self, data): + return self.private_key.sign(data) + + +def node2txt(repo, node, ver): + """map a manifest into some text""" + if ver != "0": + raise util.Abort(_("unknown signature version")) + + return "%s\n" % hgnode.hex(node) + + +def absolute_path(path): + path = os.path.expandvars(path) + return os.path.expanduser(path) + + +def sign(ui, repo, *revs, **opts): + """add a signature for the current or given revision + + If no revision is given, the parent of the working directory is used, + or tip if no revision is checked out. + + See 'hg help dates' for a list of formats valid for -d/--date. + """ + + mygpg = SSHAuthority.from_ui(ui) + sigver = "0" + sigmessage = "" + + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + + if revs: + nodes = [repo.lookup(n) for n in revs] + else: + nodes = [node for node in repo.dirstate.parents() + if node != hgnode.nullid] + if len(nodes) > 1: + raise util.Abort(_('uncommitted merge - please provide a ' + 'specific revision')) + if not nodes: + nodes = [repo.changelog.tip()] + + for n in nodes: + hexnode = hgnode.hex(n) + ui.write(_("Signing %d:%s\n") % (repo.changelog.rev(n), + hgnode.short(n))) + # build data + data = node2txt(repo, n, sigver) + sig = mygpg.sign(data) + if not sig: + raise util.Abort(_("Error while signing")) + sig = binascii.b2a_base64(sig) + sig = sig.replace("\n", "") + sigmessage += "%s %s %s\n" % (hexnode, sigver, sig) + + # write it + if opts['local']: + repo.opener("localsigs", "ab").write(sigmessage) + return + + msigs = match.exact(repo.root, '', ['.hgsigs']) + s = repo.status(match=msigs, unknown=True, ignored=True)[:6] + if util.any(s) and not opts["force"]: + raise util.Abort(_("working copy of .hgsigs is changed " + "(please commit .hgsigs manually " + "or use --force)")) + + repo.wfile(".hgsigs", "ab").write(sigmessage) + + if '.hgsigs' not in repo.dirstate: + repo.add([".hgsigs"]) + + if opts["no_commit"]: + return + + message = opts['message'] + if not message: + # we don't translate commit messages + message = "\n".join(["Added signature for changeset %s" + % hgnode.short(n) + for n in nodes]) + try: + repo.commit(message, opts['user'], opts['date'], match=msigs) + except ValueError, inst: + raise util.Abort(str(inst)) + + +cmdtable = { + "sign": + (sign, + [('l', 'local', None, _('make the signature local')), + ('f', 'force', None, _('sign even if the sigfile is modified')), + ('', 'no-commit', None, _('do not commit the sigfile after signing')), + ('m', 'message', '', _('commit message')), + ] + commands.commitopts2, + _('hg sign [OPTION]... [REVISION]...')), +} + diff --git a/hgsshsign/keymanifest.py b/hgsshsign/keymanifest.py new file mode 100644 index 0000000..25fa423 --- /dev/null +++ b/hgsshsign/keymanifest.py @@ -0,0 +1,36 @@ +# vim: set filencoding=utf8 +""" +Key Manifest + +@author: Mike Crute (mcrute@gmail.com) +@organization: SoftGroup Interactive, Inc. +@date: May 05, 2010 +""" + +from keys import PublicKey + + +class KeyManifest(dict): + """ + KeyManifest stores a list of public keys indexed by their + comment field. This object acts like a dictionary and will + return public key instances for getitems. + """ + + @classmethod + def from_file(cls, filename): + inst = cls() + fp = open(filename) + + for line in fp: + line = line.strip() + if not line or line.startswith('#'): + continue + + _, key, user = line.split() + inst[user.strip()] = key.strip() + + return inst + + def __getitem__(self, key): + return PublicKey.from_string(dict.__getitem__(self, key)) diff --git a/hgsshsign/keys.py b/hgsshsign/keys.py new file mode 100644 index 0000000..d15af08 --- /dev/null +++ b/hgsshsign/keys.py @@ -0,0 +1,88 @@ +# vim: set filencoding=utf8 +""" +Key Loader Functions + +@author: Mike Crute (mcrute@gmail.com) +@organization: SoftGroup Interactive, Inc. +@date: May 05, 2010 +""" + +import os +from M2Crypto import RSA, DSA +from M2Crypto.EVP import MessageDigest +from M2Crypto.RSA import RSAError +from M2Crypto.DSA import DSAError +from structutils import unpack_string, get_packed_mp_ints, int_to_bytes + + +class PublicKey(object): + + def __init__(self, hashed=None, instance=None, key_type=None): + self.instance = instance + self.hashed = hashed + self.key_type = key_type + + @property + def blob(self): + return self.hashed.decode('base64') + + def verify(self, data, signature): + try: + return bool(self.instance.verify(data, signature)) + except (RSAError, DSAError): + return False + + def sign(self, data): + return self.instance.sign(data) + + @classmethod + def from_string(cls, key): + """ + Loads an RFC 4716 formatted public key. + """ + pubkey = cls() + + if key.startswith('ssh-'): + pubkey.hashed = key.split()[1] + else: + pubkey.hashed = key + + pubkey.key_type, remainder = unpack_string(pubkey.blob) + + if pubkey.key_type == 'ssh-rsa': + e, n = get_packed_mp_ints(remainder, 2) + pubkey.instance = RSA.new_pub_key((e, n)) + elif pubkey.key_type == 'ssh-dss': + p, q, g, y = get_packed_mp_ints(remainder, 4) + pubkey.instance = DSA.set_params(p, q, g) + + return pubkey + + @classmethod + def from_file(cls, filename): + fp = open(filename) + try: + return cls.from_string(fp.read()) + finally: + fp.close() + + +def load_private_key(filename): + fp = open(filename) + try: + first_line = fp.readline() + finally: + fp.close() + + type = DSA if 'DSA' in first_line else RSA + return type.load_key(filename) + + +def sign_like_agent(data, key): + """ + Emulates the signing behavior of an ssh key agent. + """ + digest = MessageDigest('sha1') + digest.update(data) + my_data = digest.final() + return key.sign(data) diff --git a/hgsshsign/sshagent.py b/hgsshsign/sshagent.py new file mode 100644 index 0000000..cd5fa20 --- /dev/null +++ b/hgsshsign/sshagent.py @@ -0,0 +1,88 @@ +# vim: set filencoding=utf8 +""" +SSH Agent Management + +@author: Mike Crute (mcrute@gmail.com) +@organization: SoftGroup Interactive, Inc. +@date: May 05, 2010 +""" +import os +import socket +import struct + +from structutils import int_to_bytes +from structutils import pack_string, pack_int +from structutils import unpack_int, unpack_string, unpack_mp_int + + +class SSHAgent(object): + """ + SSH Agent communication protocol for signing only. + """ + + AGENT_SOCK_NAME = 'SSH_AUTH_SOCK' + + SSH2_AGENT_SIGN_RESPONSE = 14 + SSH2_AGENTC_SIGN_REQUEST = 13 + + def __init__(self, socket_path=None, key=None): + default_path = os.environ.get(SSHAgent.AGENT_SOCK_NAME) + socket_path = default_path if not socket_path else socket_path + + if not socket_path: + raise ValueError("Could not find an ssh agent.") + + self.socket_path = socket_path + self.public_key = key + self.socket = None + + def connect(self): + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.connect(self.socket_path) + + def _build_packet(self, data, key): + key = pack_string(key) + data = pack_string(data) + flags = pack_int(0) + + to_send = ''.join([chr(SSHAgent.SSH2_AGENTC_SIGN_REQUEST), + key, data, flags]) + pkt_length = len(to_send) + packet = pack_int(pkt_length) + to_send + + return packet + + def sign(self, data, key=None): + if not self.socket: + self.connect() + + if not key and self.public_key: + key = self.public_key + else: + raise Exception("No key specified!") + + packet = self._build_packet(data, key) + + remaining = 0 + while remaining < len(packet): + sent = self.socket.send(packet[remaining:]) + remaining += sent + + return self._parse_response() + + def _parse_response(self): + response_length = unpack_int(self.socket.recv(4, socket.MSG_WAITALL))[0] + if response_length == 1: + raise ValueError("Agent failed") + + response = self.socket.recv(response_length, socket.MSG_WAITALL) + + status = ord(response[0]) + if status != SSHAgent.SSH2_AGENT_SIGN_RESPONSE: + raise ValueError("Invalid response from agent") + + _, remainder = unpack_int(response[1:]) + _, remainder = unpack_string(remainder) + response, _ = unpack_string(remainder) + + return response diff --git a/hgsshsign/structutils.py b/hgsshsign/structutils.py new file mode 100644 index 0000000..727d22e --- /dev/null +++ b/hgsshsign/structutils.py @@ -0,0 +1,108 @@ +# vim: set filencoding=utf8 +""" +Utilities for Manipulating Byte Streams + +@author: Mike Crute (mcrute@gmail.com) +@organization: SoftGroup Interactive, Inc. +@date: May 05, 2010 +""" + +import struct + + +INT_FORMAT = struct.Struct('>I') + + +def pack_string(string): + """ + Pack a string into a network style byte array. + """ + return pack_int(len(string)) + string + + +def pack_int(integer): + """ + Pack an integer into a byte array. + """ + return INT_FORMAT.pack(integer) + + +def pack_mp_int(mp_int): + """ + Pack a multiple-percision integer into a byte array. + """ + return pack_string(int_to_bytes(mp_int)) + + +def unpack_string(buf): + """ + Unpack a string from a byte array buffer returning + the string and the remainder of the buffer. + """ + length, = INT_FORMAT.unpack(buf[:4]) + string = buf[4:4+length] + remainder = buf[4+length:] + return string, remainder + + +def unpack_mp_int(buf): + """ + Unpack a multiple-percision integer from a byte array + buffer returning the string and the remainder of the + buffer. + """ + length, = INT_FORMAT.unpack(buf[:4]) + remainder = buf[4+length:] + + return bytes_to_int(buf[4:4+length]), remainder + + +def unpack_int(buf): + """ + Unpack an integer from a byte array buffer returning the + string and the remainder of the buffer. + """ + integer, = INT_FORMAT.unpack(buf[:4]) + remainder = buf[4:] + return integer, remainder + + +def get_packed_mp_ints(buf, count=1): + """ + Get a number of multiple-percision integers from a byte + array buffer but leaves them as network style mpints. + """ + ints = [] + for _ in range(count): + length, = INT_FORMAT.unpack(buf[:4]) + ints.append(buf[:4+length]) + buf = buf[4+length:] + + return ints + + +def int_to_bytes(integer): + """ + Convert an integer or a long integer to an array of + bytes. + """ + bytes = [] + + while integer > 0: + integer, chunk = divmod(integer, 256) + bytes.insert(0, chr(chunk)) + + return ''.join(bytes) + + +def bytes_to_int(buf): + """ + Convert an array of bytes into an integer or long + integer. + """ + integer = 0 + for byte in buf: + integer <<= 8 + integer += ord(byte) + + return integer diff --git a/keymanifest.py b/keymanifest.py deleted file mode 100644 index 25fa423..0000000 --- a/keymanifest.py +++ /dev/null @@ -1,36 +0,0 @@ -# vim: set filencoding=utf8 -""" -Key Manifest - -@author: Mike Crute (mcrute@gmail.com) -@organization: SoftGroup Interactive, Inc. -@date: May 05, 2010 -""" - -from keys import PublicKey - - -class KeyManifest(dict): - """ - KeyManifest stores a list of public keys indexed by their - comment field. This object acts like a dictionary and will - return public key instances for getitems. - """ - - @classmethod - def from_file(cls, filename): - inst = cls() - fp = open(filename) - - for line in fp: - line = line.strip() - if not line or line.startswith('#'): - continue - - _, key, user = line.split() - inst[user.strip()] = key.strip() - - return inst - - def __getitem__(self, key): - return PublicKey.from_string(dict.__getitem__(self, key)) diff --git a/keys.py b/keys.py deleted file mode 100644 index d15af08..0000000 --- a/keys.py +++ /dev/null @@ -1,88 +0,0 @@ -# vim: set filencoding=utf8 -""" -Key Loader Functions - -@author: Mike Crute (mcrute@gmail.com) -@organization: SoftGroup Interactive, Inc. -@date: May 05, 2010 -""" - -import os -from M2Crypto import RSA, DSA -from M2Crypto.EVP import MessageDigest -from M2Crypto.RSA import RSAError -from M2Crypto.DSA import DSAError -from structutils import unpack_string, get_packed_mp_ints, int_to_bytes - - -class PublicKey(object): - - def __init__(self, hashed=None, instance=None, key_type=None): - self.instance = instance - self.hashed = hashed - self.key_type = key_type - - @property - def blob(self): - return self.hashed.decode('base64') - - def verify(self, data, signature): - try: - return bool(self.instance.verify(data, signature)) - except (RSAError, DSAError): - return False - - def sign(self, data): - return self.instance.sign(data) - - @classmethod - def from_string(cls, key): - """ - Loads an RFC 4716 formatted public key. - """ - pubkey = cls() - - if key.startswith('ssh-'): - pubkey.hashed = key.split()[1] - else: - pubkey.hashed = key - - pubkey.key_type, remainder = unpack_string(pubkey.blob) - - if pubkey.key_type == 'ssh-rsa': - e, n = get_packed_mp_ints(remainder, 2) - pubkey.instance = RSA.new_pub_key((e, n)) - elif pubkey.key_type == 'ssh-dss': - p, q, g, y = get_packed_mp_ints(remainder, 4) - pubkey.instance = DSA.set_params(p, q, g) - - return pubkey - - @classmethod - def from_file(cls, filename): - fp = open(filename) - try: - return cls.from_string(fp.read()) - finally: - fp.close() - - -def load_private_key(filename): - fp = open(filename) - try: - first_line = fp.readline() - finally: - fp.close() - - type = DSA if 'DSA' in first_line else RSA - return type.load_key(filename) - - -def sign_like_agent(data, key): - """ - Emulates the signing behavior of an ssh key agent. - """ - digest = MessageDigest('sha1') - digest.update(data) - my_data = digest.final() - return key.sign(data) diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..2f9dd82 --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup +from hgsshsign import __version__ + + +setup( + name="hg-sshsign", + description="ssh signing for mercurial commits", + author="Mike Crute", + author_email="mcrute@gmail.com", + url="http://code.google.com/p/hg-sshsign", + license="Apache 2.0", + version=__version__, + install_requires=[ + "M2Crypto", + ]) diff --git a/sshagent.py b/sshagent.py deleted file mode 100644 index cd5fa20..0000000 --- a/sshagent.py +++ /dev/null @@ -1,88 +0,0 @@ -# vim: set filencoding=utf8 -""" -SSH Agent Management - -@author: Mike Crute (mcrute@gmail.com) -@organization: SoftGroup Interactive, Inc. -@date: May 05, 2010 -""" -import os -import socket -import struct - -from structutils import int_to_bytes -from structutils import pack_string, pack_int -from structutils import unpack_int, unpack_string, unpack_mp_int - - -class SSHAgent(object): - """ - SSH Agent communication protocol for signing only. - """ - - AGENT_SOCK_NAME = 'SSH_AUTH_SOCK' - - SSH2_AGENT_SIGN_RESPONSE = 14 - SSH2_AGENTC_SIGN_REQUEST = 13 - - def __init__(self, socket_path=None, key=None): - default_path = os.environ.get(SSHAgent.AGENT_SOCK_NAME) - socket_path = default_path if not socket_path else socket_path - - if not socket_path: - raise ValueError("Could not find an ssh agent.") - - self.socket_path = socket_path - self.public_key = key - self.socket = None - - def connect(self): - self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.socket.connect(self.socket_path) - - def _build_packet(self, data, key): - key = pack_string(key) - data = pack_string(data) - flags = pack_int(0) - - to_send = ''.join([chr(SSHAgent.SSH2_AGENTC_SIGN_REQUEST), - key, data, flags]) - pkt_length = len(to_send) - packet = pack_int(pkt_length) + to_send - - return packet - - def sign(self, data, key=None): - if not self.socket: - self.connect() - - if not key and self.public_key: - key = self.public_key - else: - raise Exception("No key specified!") - - packet = self._build_packet(data, key) - - remaining = 0 - while remaining < len(packet): - sent = self.socket.send(packet[remaining:]) - remaining += sent - - return self._parse_response() - - def _parse_response(self): - response_length = unpack_int(self.socket.recv(4, socket.MSG_WAITALL))[0] - if response_length == 1: - raise ValueError("Agent failed") - - response = self.socket.recv(response_length, socket.MSG_WAITALL) - - status = ord(response[0]) - if status != SSHAgent.SSH2_AGENT_SIGN_RESPONSE: - raise ValueError("Invalid response from agent") - - _, remainder = unpack_int(response[1:]) - _, remainder = unpack_string(remainder) - response, _ = unpack_string(remainder) - - return response diff --git a/structutils.py b/structutils.py deleted file mode 100644 index 727d22e..0000000 --- a/structutils.py +++ /dev/null @@ -1,108 +0,0 @@ -# vim: set filencoding=utf8 -""" -Utilities for Manipulating Byte Streams - -@author: Mike Crute (mcrute@gmail.com) -@organization: SoftGroup Interactive, Inc. -@date: May 05, 2010 -""" - -import struct - - -INT_FORMAT = struct.Struct('>I') - - -def pack_string(string): - """ - Pack a string into a network style byte array. - """ - return pack_int(len(string)) + string - - -def pack_int(integer): - """ - Pack an integer into a byte array. - """ - return INT_FORMAT.pack(integer) - - -def pack_mp_int(mp_int): - """ - Pack a multiple-percision integer into a byte array. - """ - return pack_string(int_to_bytes(mp_int)) - - -def unpack_string(buf): - """ - Unpack a string from a byte array buffer returning - the string and the remainder of the buffer. - """ - length, = INT_FORMAT.unpack(buf[:4]) - string = buf[4:4+length] - remainder = buf[4+length:] - return string, remainder - - -def unpack_mp_int(buf): - """ - Unpack a multiple-percision integer from a byte array - buffer returning the string and the remainder of the - buffer. - """ - length, = INT_FORMAT.unpack(buf[:4]) - remainder = buf[4+length:] - - return bytes_to_int(buf[4:4+length]), remainder - - -def unpack_int(buf): - """ - Unpack an integer from a byte array buffer returning the - string and the remainder of the buffer. - """ - integer, = INT_FORMAT.unpack(buf[:4]) - remainder = buf[4:] - return integer, remainder - - -def get_packed_mp_ints(buf, count=1): - """ - Get a number of multiple-percision integers from a byte - array buffer but leaves them as network style mpints. - """ - ints = [] - for _ in range(count): - length, = INT_FORMAT.unpack(buf[:4]) - ints.append(buf[:4+length]) - buf = buf[4+length:] - - return ints - - -def int_to_bytes(integer): - """ - Convert an integer or a long integer to an array of - bytes. - """ - bytes = [] - - while integer > 0: - integer, chunk = divmod(integer, 256) - bytes.insert(0, chr(chunk)) - - return ''.join(bytes) - - -def bytes_to_int(buf): - """ - Convert an array of bytes into an integer or long - integer. - """ - integer = 0 - for byte in buf: - integer <<= 8 - integer += ord(byte) - - return integer -- cgit v1.2.3