'''
Manage RSA encryption via pycrypto

The keydata consists of the following:

    pub: PEM encoded public key
    priv: PEM encoded private key
'''

SEC_BACKEND = 'pycrypto_aes'

# Import pycrypto libs
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_PSS
from Crypto.Hash import SHA256
import Crypto.Util.number

# Import table libs
import table


class Key(object):
    '''
    The management interface for rsa keys
    '''
    def __init__(self, keydata=None, **kwargs):
        self.kwargs = kwargs
        self.__generate(keydata)

    def __generate(self, keydata):
        '''
        Generate the pycrypto rsa object
        '''
        if keydata:
            if 'components' not in keydata:
                raise ValueError('Invalid keydata, no components')
            key = RSA.construct(keydata['components'])
            if key.has_private():
                self.priv = key
                self.pub = key.publickey()
                self.sign_key = PKCS1_PSS.new(self.priv)
                self.verify_key = PKCS1_PSS.new(self.pub)
                self.decrypter = PKCS1_OAEP.new(self.priv)
            else:
                self.pub = key
                self.verify_key = PKCS1_PSS.new(self.pub)
            self.keydata = keydata
        else:
            self.priv = self._gen_key()
            self.pub = self.priv.publickey()
            self.sign_key = PKCS1_PSS.new(self.priv)
            self.verify_key = PKCS1_PSS.new(self.pub)
            self.keydata = self._gen_keydata(self.priv)
            self.decrypter = PKCS1_OAEP.new(self.priv)
        self.encrypter = PKCS1_OAEP.new(self.pub)
        self.max_msg_size = self.get_max_msg_size()
        self.enc_chunk_size = self.get_enc_chunk_size()

    def _gen_keydata(self, key):
        '''
        Return the keydata of a given key
        '''
        keydata = {'components': []}
        for attr in key.keydata:
            keydata['components'].append(getattr(key, attr))
        keydata['ctime'] = table.now()
        return keydata

    def _gen_key(self):
        '''
        Generate an RSA key, ensure that it is no smaller than 2048 bits
        '''
        size = self.kwargs.get('size', 2048)
        if size < 2048:
            raise ValueError('Key size too small')
        return RSA.generate(size)

    def _string_chunks(self, msg, size, i=None):
        '''
        Yield the message in the sized chunks
        '''
        if i is None:
            i = 0
        msg_len = len(msg)
        while i <  msg_len:
            top = i + size
            if top > msg_len:
                top = msg_len
            yield msg[i:top]
            i =+ size

    def get_max_msg_size(self):
        '''
        Return the max size of a message chunk
        '''
        return (Crypto.Util.number.size(self.pub.n) / 8) -2 - (SHA256.digest_size * 2)

    def get_enc_chunk_size(self):
        '''
        Return the size of all encrypted chunks
        '''
        return Crypto.Util.number.size(self.pub.n) / 8

    def encrypt(self, pub, msg):
        '''
        Sign and encrypt a message
        '''
        pub = pub._key.pub
        ret = ''
        hash_ = SHA256.new()
        hash_.update(msg)
        ret += self.sign_key.sign(hash_)
        for chunk in self._string_chunks(msg, pub.max_msg_size):
            ret += self.encrypter.encrypt(chunk)
        return ret

    def decrypt(self, pub, msg):
        '''
        Decrypt the given message against the given public key
        '''
        pub = pub._key.pub
        c_size = pub.get_enc_chunk_size()
        sig = msg[0:c_size]
        clear = ''
        for chunk in self._string_chunks(msg, c_size, c_size):
            clear += self.decrypter.decrypt(msg)
        self.verify(sig + clear)

    def sign(self, msg):
        '''
        Sign a message
        '''
        hash_ = SHA256.new()
        hash_.update(msg)
        sig = self.sign_key.sign(hash_)
        return sig + msg

    def verify(self, msg):
        '''
        Verify a message
        '''
        sig = msg[0:self.enc_chunk_size]
        msg = msg[self.enc_chunk_size:]
        hash_ = SHA256.new()
        hash_.update(msg)
        if self.verify_key.verify(hash_, sig):
            return msg
        return False

@@ -3145,35 +3145,8 @@
 '''%0A
-        pub = pub._key.pub%0A
     
@@ -3154,24 +3154,24 @@
    ret = ''%0A
+
         hash
@@ -3304,16 +3304,21 @@
 sg, pub.
+_key.
 max_msg_
@@ -3343,20 +3343,24 @@
  ret += 
-self
+pub._key
 .encrypt
@@ -3521,35 +3521,8 @@
 '''%0A
-        pub = pub._key.pub%0A
     
@@ -3538,16 +3538,21 @@
 e = pub.
+_key.
 get_enc_
@@ -3666,32 +3666,61 @@
 _size, c_size):%0A
+            print len(chunk)%0A
             clea
@@ -3756,28 +3756,39 @@
 sg)%0A        
-self
+return pub._key
 .verify(sig 

Fix encrypt sec for pycrypto rsa