gotr: provide potr (rev f5551d6). now only depends on PyCrypto>=2.1
This commit is contained in:
24
gotr/potr/compatcrypto/__init__.py
Normal file
24
gotr/potr/compatcrypto/__init__.py
Normal file
@@ -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 *
|
||||
104
gotr/potr/compatcrypto/common.py
Normal file
104
gotr/potr/compatcrypto/common.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# 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/>.
|
||||
|
||||
# some python3 compatibilty
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import struct
|
||||
|
||||
from potr.utils import human_hash, bytes_to_long, unpack, pack_mpi
|
||||
|
||||
DEFAULT_KEYTYPE = 0x0000
|
||||
pkTypes = {}
|
||||
def registerkeytype(cls):
|
||||
if not hasattr(cls, 'parsePayload'):
|
||||
raise TypeError('registered key types need parsePayload()')
|
||||
pkTypes[cls.keyType] = cls
|
||||
return cls
|
||||
|
||||
def generateDefaultKey():
|
||||
return pkTypes[DEFAULT_KEYTYPE].generate()
|
||||
|
||||
class PK(object):
|
||||
__slots__ = []
|
||||
|
||||
@classmethod
|
||||
def generate(cls):
|
||||
raise NotImplementedError
|
||||
|
||||
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
|
||||
149
gotr/potr/compatcrypto/pycrypto.py
Normal file
149
gotr/potr/compatcrypto/pycrypto.py
Normal file
@@ -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), private=True)
|
||||
|
||||
@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
|
||||
Reference in New Issue
Block a user