diff options
Diffstat (limited to 'M2Crypto/PGP/packet.py')
-rw-r--r-- | M2Crypto/PGP/packet.py | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/M2Crypto/PGP/packet.py b/M2Crypto/PGP/packet.py new file mode 100644 index 0000000..d662c84 --- /dev/null +++ b/M2Crypto/PGP/packet.py @@ -0,0 +1,379 @@ +"""M2Crypto PGP2. + +This module implements PGP packets per RFC1991 and various source distributions. + +Each packet type is represented by a class; packet classes derive from +the abstract 'packet' class. + +The 'message digest' packet type, mentioned but not documented in RFC1991, +is not implemented. + +Copyright (c) 1999-2003 Ng Pheng Siong. All rights reserved.""" + +# XXX Work-in-progress. + +# Be liberal in what you accept. +# Be conservative in what you send. +# Be lazy in what you eval. + +import struct, time + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +from M2Crypto import EVP, RSA +from M2Crypto.util import octx_to_num + +from constants import * + +_OK_VERSION = ('\002', '\003') +_OK_VALIDITY = ('\000',) +_OK_PKC = ('\001',) + + +class packet: + def __init__(self, ctb, body=None): + import warnings + warnings.warn('Deprecated. No maintainer for PGP. If you use this, please inform M2Crypto maintainer.', DeprecationWarning) + + self.ctb = ctb + if body is not None: + self.body = StringIO(body) + else: + self.body = None + + def validate(self): + return 1 + + def pack(self): + raise NotImplementedError, '%s.pack(): abstract method' % (self.__class__,) + + def version(self): + if hasattr(self, '_version'): + return ord(self._version) + else: + return None + + def timestamp(self): + if hasattr(self, '_timestamp'): + return struct.unpack('>L', self._timestamp)[0] + else: + return None + + def validity(self): + if hasattr(self, '_validity'): + return struct.unpack('>H', self._validity)[0] + else: + return None + + def pkc(self): + if hasattr(self, '_pkc'): + return self._pkc + else: + return None + + def _llf(self, lenf): + if lenf < 256: + return (0, chr(lenf)) + elif lenf < 65536: + return (1, struct.pack('>H', lenf)) + else: + assert lenf < 2L**32 + return (2, struct.pack('>L', lenf)) + + def _ctb(self, llf): + ctbv = _FACTORY[self.__class__] + return chr((1 << 7) | (ctbv << 2) | llf) + + +class public_key_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if self.body is not None: + self._version = self.body.read(1) + self._timestamp = self.body.read(4) + self._validity = self.body.read(2) + self._pkc = self.body.read(1) + + self._nlen = self.body.read(2) + nlen = (struct.unpack('>H', self._nlen)[0] + 7) / 8 + self._n = self.body.read(nlen) + + self._elen = self.body.read(2) + elen = (struct.unpack('>H', self._elen)[0] + 7) / 8 + self._e = self.body.read(elen) + + def pack(self): + if self.body is None: + self.body = StringIO() + self.body.write(self._version) + self.body.write(self._timestamp) + self.body.write(self._validity) + self.body.write(self._pkc) + self.body.write(self._nlen) + self.body.write(self._n) + self.body.write(self._elen) + self.body.write(self._e) + self.body = self.body.getvalue() + llf, lenf = self._llf(len(self.body)) + ctb = self._ctb(llf) + return '%s%s%s' % (ctb, lenf, self.body) + + def pubkey(self): + return self._pubkey.pub() + + +class trust_packet(packet): + # This implementation neither interprets nor emits trust packets. + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self.trust = self.body.read(1) + + +class userid_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self._userid = body + + def pack(self): + if self.body is None: + self.body = StringIO() + self.body.write(chr(len(self._userid))) + self.body.write(self._userid) + self.body = self.body.getvalue() + return self.ctb + self.body + + def userid(self): + return self._userid + + +class comment_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self.comment = self.body.getvalue() + + def pack(self): + if self.body is None: + self.body = StringIO() + self.body.write(chr(len(self.comment))) + self.body.write(self.comment) + self.body = self.body.getvalue() + return self.ctb + self.body + + +class signature_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self._version = self.body.read(1) + self._len_md_stuff = self.body.read(1) + self._classification = self.body.read(1) + self._timestamp = self.body.read(4) + self._keyid = self.body.read(8) + self._pkc = self.body.read(1) + self._md_algo = self.body.read(1) + self._md_chksum = self.body.read(2) + self._sig = self.body.read() + + def pack(self): + if self.body is None: + self.body = StringIO() + self.body.write(self._version) + self.body.write(self._len_md_stuff) + self.body.write(self._classification) + self.body.write(self._timestamp) + self.body.write(self._keyid) + self.body.write(self._pkc) + self.body.write(self._md_algo) + self.body.write(self._md_chksum) + self.body.write(self._sig) + self.body = self.body.getvalue() + llf, lenf = self._llf(len(body)) + self.ctb = self.ctb | llf + return '%s%s%s' % (self.ctb, lenf, self.body) + + + def validate(self): + if self._version not in _OK_VERSION: + return None + if self._len_md_stuff != '\005': + return None + + +class private_key_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self._version = self.body.read(1) + self._timestamp = self.body.read(4) + self._validity = self.body.read(2) + self._pkc = self.body.read(1) + + self._nlen = self.body.read(2) + nlen = (struct.unpack('>H', self._nlen)[0] + 7) / 8 + self._n = self.body.read(nlen) + + self._elen = self.body.read(2) + elen = (struct.unpack('>H', self._elen)[0] + 7) / 8 + self._e = self.body.read(elen) + + self._cipher = self.body.read(1) + if self._cipher == '\001': + self._iv = self.body.read(8) + else: + self._iv = None + + for param in ['d', 'p', 'q', 'u']: + _plen = self.body.read(2) + setattr(self, '_'+param+'len', _plen) + plen = (struct.unpack('>H', _plen)[0] + 7) / 8 + setattr(self, '_'+param, self.body.read(plen)) + + self._cksum = self.body.read(2) + + def is_encrypted(self): + return ord(self._cipher) + + +class cke_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self._iv = self.body.read(8) + self._cksum = self.body.read(2) + self._ctxt = self.body.read() + + +class pke_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self._version = self.body.read(1) + self._keyid = self.body.read(8) + self._pkc = ord(self.body.read(1)) + + deklen = (struct.unpack('>H', self.body.read(2))[0] + 7 ) / 8 + self._dek = octx_to_num(self.body.read(deklen)) + + +class literal_packet(packet): + def __init__(self, ctb, body=None): + packet.__init__(self, ctb, body) + if body is not None: + self.fmode = self.body.read(1) + fnlen = self.body.read(1) + self.fname = self.body.read(fnlen) + self.ftime = self.body.read(4) + #self.data = self.body.read() + + +class compressed_packet(packet): + def __init__(self, ctb, stream): + packet.__init__(self, ctb, '') + if body is not None: + self.algo = stream.read(1) + # This reads the entire stream into memory. + self.data = stream.read() + + def validate(self): + return (self.algo == '\001') + + def uncompress(self): + import zlib + decomp = zlib.decompressobj(-13) # RFC 2440, pg 61. + # This doubles the memory usage. + stream = StringIO(decomp.decompress(self.data)) + return stream + + +_FACTORY = { + 1 : pke_packet, + 2 : signature_packet, + #3 : message_digest_packet, # XXX not implemented + 5 : private_key_packet, + 6 : public_key_packet, + #8 : compressed_packet, # special case + 9 : cke_packet, + 11 : literal_packet, + 12 : trust_packet, + 13 : userid_packet, + 14 : comment_packet, + pke_packet : 1, + signature_packet : 2, + #3 : message_digest_packet, + private_key_packet : 5, + public_key_packet : 6, + #8 : compressed_packet, + cke_packet : 9, + literal_packet : 11, + trust_packet : 12, + userid_packet : 13, + comment_packet : 14 +} + + +class packet_stream: + def __init__(self, input): + self.stream = input + self.under_current = None + self._count = 0 + + def close(self): + self.stream.close() + if self.under_current is not None: + self.under_current.close() + + def read(self, keep_trying=0): + while 1: + ctb0 = self.stream.read(1) + if not ctb0: + return None + ctb = ord(ctb0) + if is_ctb(ctb): + break + elif keep_trying: + continue + else: + raise XXXError + ctbt = (ctb & 0x3c) >> 2 + + if ctbt == CTB_COMPRESSED_DATA: + self.under_current = self.stream + cp = compressed_packet(ctb0, self.stream) + self.stream = cp.uncompress() + return self.read() + + # Decode the length of following data. See RFC for details. + llf = ctb & 3 + if llf == 0: + lenf = ord(self.stream.read(1)) + elif llf == 1: + lenf = struct.unpack('>H', self.stream.read(2))[0] + elif llf == 2: + lenf = struct.unpack('>L', self.stream.read(4))[0] + else: # llf == 3 + raise XXXError, 'impossible case' + + body = self.stream.read(lenf) + if not body or (len(body) != lenf): + raise XXXError, 'corrupted packet' + + self._count = self.stream.tell() + try: + return _FACTORY[ctbt](ctb0, body) + except KeyError: + return packet(ctb0, body) + + def count(self): + return self._count + +def is_ctb(ctb): + return ctb & 0xc0 + +def make_ctb(value, llf): + return chr((1 << 7) | (value << 2) | llf) |