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.
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')
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:])]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With