Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does PyCrypto not use the default IV?

I am trying to figure out why my Python client and the Ruby server are having a disagreement about how to encrypt data. The only difference I see in the Ruby code and my code is that they are not specifying the Initialization Vector, therefore its falling back to the default of all \x0's

When I try to instantiate PyCrypto without the iv it gives me an error. Here is an example:

from Crypto.Cipher import AES
test = "Very, very confidential data"
key = b'Thirty Two Byte key, made Beef y' 

gryp = AES.new(key, AES.MODE_CBC)

(This example is essentially the example code from the PyCrypto docs without specifying the IV) The docs say w/r/t the IV " It is optional and when not present it will be given a default value of all zeroes." However I get the error "ValueError: IV must be 16 bytes long".

So I could specify the IV, that is not the problem pre se, but I am trying to figure out that if it thinks it cannot use the default, if there is something else wrong with the way I am using the library.

like image 993
zenWeasel Avatar asked Jan 17 '13 22:01

zenWeasel


2 Answers

This appears to be an error in the class documentation for Pycrypto's AES, as the AES implementation has been changed so that the IV is not optional for those modes that require one (i.e. you will have to pass 16 bytes of zeroes yourself, if that is how you want to do it).

See this bug report for the same issue where someone didn't specify an IV and looked up the online docs. There was a change that explicitly requires the IV and essentially, nobody has updated the online docs to reflect this. The class docs in the Pycrypto source were updated, but the online documentation needs to be regenerated to reflect this.

The new documentation from the source states:

For all other modes, it must be block_size bytes longs.

Instead of the old version's

For all other modes, it must be block_size bytes longs. It is optional and when not present it will be given a default value of all zeroes.

The updated example in the source, which specifies iv, is:

from Crypto.Cipher import AES
from Crypto import Random

key = b'Sixteen byte key'
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')
like image 166
John Lyon Avatar answered Oct 04 '22 23:10

John Lyon


here is an implementation that works for me with some fixes:

class AESCipher:

    def __init__(self, key):
        self.bs = 32
        if len(key) >= 32:
            self.key = key[:32]
        else:
            self.key = self._pad(key)

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:]))

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    def _unpad(self, s):
        return s[:-ord(s[len(s)-1:])]
like image 26
mnothic Avatar answered Oct 04 '22 23:10

mnothic