I'm writing a piece of code to encrypt a text using symmetric encryption. But it's not coming back with the right result...
from Crypto.Cipher import AES
import os
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter = lambda : os.urandom(16))
encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
print crypto.decrypt(encrypted)
Here, the decrypted text is different from the original.
I don't really understand much about cryptography so please bear with me. I understand the CTR mode requires a "counter" function to supply a random counter each time, but why does it need it to be 16 bytes when my key is 32 bytes and it insists that my message is in multiples of 16 bytes too? Is this normal?
I'm guessing that it doesn't get back to the original message because the counter changed between encrypt and decrypt. But then, how is it supposed to work theoretically anyway? What am I doing wrong? Anyway, I'm forced to resort back to ECB until I figure this out :(
The counter
must return the same on decryption as it did on encryption, as you intuit, so, one (NOT SECURE AT ALL) way to do it is:
>>> secret = os.urandom(16)
>>> crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
>>> encrypted = crypto.encrypt("aaaaaaaaaaaaaaaa")
>>> print crypto.decrypt(encrypted)
aaaaaaaaaaaaaaaa
CTR is a block cipher, so the "16-at-a-time" constraint that seems to surprise you is a pretty natural one.
Of course, a so-called "counter" returning the same value at each call is grossly insecure. Doesn't take much to do better, e.g....:
import array
class Secret(object):
def __init__(self, secret=None):
if secret is None: secret = os.urandom(16)
self.secret = secret
self.reset()
def counter(self):
for i, c in enumerate(self.current):
self.current[i] = c + 1
if self.current: break
return self.current.tostring()
def reset(self):
self.current = array.array('B', self.secret)
secret = Secret()
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=secret.counter)
encrypted = crypto.encrypt(16*'a' + 16*'b' + 16*'c')
secret.reset()
print crypto.decrypt(encrypted)