Permalink
Browse files

added an abstraction layer to the crypto module

(Could use some other module now, though it seems no other python module
offers both DSA and AESCTR).
Since the DSAKey objects are now implementation specific, they should
not be pickled anymore - instead they should be load/saved using
serializePrivateKey and parsePrivateKey.
Tests have been adjusted to reflect the new key format.
  • Loading branch information...
1 parent 852b90b commit 0d1c73b87fae849e0e5671a397ed8f4291af6add @afflux afflux committed Mar 2, 2012
View
@@ -20,8 +20,8 @@
from potr import context
from potr import proto
-from potr.crypt import human_hash
+from potr.utils import human_hash
''' version is: (major, minor, patch, sub) with sub being one of 'alpha',
'beta', 'final' '''
-VERSION = (1, 0, 0, 'beta3')
+VERSION = (1, 0, 0, 'beta4')
@@ -0,0 +1,24 @@
+# Copyright 2012 Kjell Braden <afflux@pentabarf.de>
+#
+# This file is part of the python-potr library.
+#
+# python-potr is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# python-potr is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+from potr.compatcrypto.common import *
+
+try:
+ from potr.compatcrypto.pycrypto import *
+except ImportError:
+ from potr.compatcrypto.pure import *
@@ -0,0 +1,76 @@
+
+import logging
+import struct
+
+from potr.utils import human_hash, bytes_to_long, unpack, pack_mpi
+
+pkTypes = {}
+def registerkeytype(cls):
+ if not hasattr(cls, 'parsePayload'):
+ raise TypeError('registered key types need parsePayload()')
+ pkTypes[cls.keyType] = cls
+ return cls
+
+class PK(object):
+ __slots__ = []
+ def sign(self, data):
+ raise NotImplementedError
+ def verify(self, data):
+ raise NotImplementedError
+ def fingerprint(self):
+ raise NotImplementedError
+
+ def serializePublicKey(self):
+ return struct.pack(b'!H', self.keyType) \
+ + self.getSerializedPublicPayload()
+
+ def getSerializedPublicPayload(self):
+ buf = b''
+ for x in self.getPublicPayload():
+ buf += pack_mpi(x)
+ return buf
+
+ def getPublicPayload(self):
+ raise NotImplementedError
+
+ def serializePrivateKey(self):
+ return struct.pack(b'!H', self.keyType) \
+ + self.getSerializedPrivatePayload()
+
+ def getSerializedPrivatePayload(self):
+ buf = b''
+ for x in self.getPrivatePayload():
+ buf += pack_mpi(x)
+ return buf
+
+ def getPrivatePayload(self):
+ raise NotImplementedError
+
+ def cfingerprint(self):
+ return '{0:040x}'.format(bytes_to_long(self.fingerprint()))
+
+ @classmethod
+ def parsePrivateKey(cls, data):
+ implCls, data = cls.getImplementation(data)
+ logging.debug('Got privkey of type %r' % implCls)
+ return implCls.parsePayload(data, private=True)
+
+ @classmethod
+ def parsePublicKey(cls, data):
+ implCls, data = cls.getImplementation(data)
+ logging.debug('Got pubkey of type %r' % implCls)
+ return implCls.parsePayload(data)
+
+ def __str__(self):
+ return human_hash(self.cfingerprint())
+ def __repr__(self):
+ return '<{cls}(fpr=\'{fpr}\')>'.format(
+ cls=self.__class__.__name__, fpr=str(self))
+
+ @staticmethod
+ def getImplementation(data):
+ typeid, data = unpack(b'!H', data)
+ cls = pkTypes.get(typeid, None)
+ if cls is None:
+ raise NotImplementedError('unknown typeid %r' % typeid)
+ return cls, data
@@ -0,0 +1,149 @@
+# Copyright 2012 Kjell Braden <afflux@pentabarf.de>
+#
+# This file is part of the python-potr library.
+#
+# python-potr is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# python-potr is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from Crypto import Cipher, Random
+from Crypto.Hash import SHA256 as _SHA256
+from Crypto.Hash import SHA as _SHA1
+from Crypto.Hash import HMAC as _HMAC
+from Crypto.PublicKey import DSA
+from numbers import Number
+
+from potr.compatcrypto import common
+from potr.utils import pack_mpi, read_mpi, bytes_to_long, long_to_bytes
+
+# XXX atfork?
+RNG = Random.new()
+
+def SHA256(data):
+ return _SHA256.new(data).digest()
+
+def SHA1(data):
+ return _SHA1.new(data).digest()
+
+def HMAC(key, data, mod):
+ return _HMAC.new(key, msg=data, digestmod=mod).digest()
+
+def SHA1HMAC(key, data):
+ return HMAC(key, data, _SHA1)
+
+def SHA256HMAC(key, data):
+ return HMAC(key, data, _SHA256)
+
+def SHA256HMAC160(key, data):
+ return SHA256HMAC(key, data)[:20]
+
+def AESCTR(key, counter=0):
+ if isinstance(counter, Number):
+ counter = Counter(counter)
+ if not isinstance(counter, Counter):
+ raise TypeError
+ return Cipher.AES.new(key, Cipher.AES.MODE_CTR, counter=counter)
+
+class Counter(object):
+ __slots__ = ['prefix', 'val']
+ def __init__(self, prefix):
+ self.prefix = prefix
+ self.val = 0
+
+ def inc(self):
+ self.prefix += 1
+ self.val = 0
+
+ def __setattr__(self, attr, val):
+ if attr == 'prefix':
+ self.val = 0
+ super(Counter, self).__setattr__(attr, val)
+
+ def __repr__(self):
+ return '<Counter(p={p!r},v={v!r})>'.format(p=self.prefix, v=self.val)
+
+ def byteprefix(self):
+ return long_to_bytes(self.prefix).rjust(8, b'\0')
+
+ def __call__(self):
+ val = long_to_bytes(self.val)
+ prefix = long_to_bytes(self.prefix)
+ self.val += 1
+ return self.byteprefix() + val.rjust(8, b'\0')
+
+@common.registerkeytype
+class DSAKey(common.PK):
+ __slots__ = ['priv', 'pub']
+ keyType = 0x0000
+
+ def __init__(self, key=None, private=False):
+ self.priv = self.pub = None
+
+ if not isinstance(key, tuple):
+ raise TypeError('4/5-tuple required for key')
+
+ if len(key) == 5 and private:
+ self.priv = DSA.construct(key)
+ self.pub = self.priv.publickey()
+ elif len(key) == 4 and not private:
+ self.pub = DSA.construct(key)
+ else:
+ raise TypeError('wrong number of arguments for ' \
+ 'private={0!r}: got {1} '
+ .format(private, len(key)))
+
+ def getPublicPayload(self):
+ return (self.pub.p, self.pub.q, self.pub.g, self.pub.y)
+
+ def getPrivatePayload(self):
+ return (self.priv.p, self.priv.q, self.priv.g, self.priv.y, self.priv.x)
+
+ def fingerprint(self):
+ return SHA1(self.getSerializedPublicPayload())
+
+ def sign(self, data):
+ # 2 <= K <= q = 160bit = 20 byte
+ K = bytes_to_long(RNG.read(19)) + 2
+ r, s = self.priv.sign(data, K)
+ return long_to_bytes(r) + long_to_bytes(s)
+
+ def verify(self, data, sig):
+ r, s = bytes_to_long(sig[:20]), bytes_to_long(sig[20:])
+ return self.pub.verify(data, (r, s))
+
+ def __hash__(self):
+ return bytes_to_long(self.fingerprint())
+
+ def __eq__(self, other):
+ if not isinstance(other, type(self)):
+ return False
+ return self.fingerprint() == other.fingerprint()
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ @classmethod
+ def generate(cls):
+ privkey = DSA.generate(1024)
+ return cls((privkey.key.y, privkey.key.g, privkey.key.p, privkey.key.q,
+ privkey.key.x))
+
+ @classmethod
+ def parsePayload(cls, data, private=False):
+ p, data = read_mpi(data)
+ q, data = read_mpi(data)
+ g, data = read_mpi(data)
+ y, data = read_mpi(data)
+ if private:
+ x, data = read_mpi(data)
+ return cls((y, g, p, q, x), private=True), data
+ return cls((y, g, p, q), private=False), data
Oops, something went wrong.

0 comments on commit 0d1c73b

Please sign in to comment.