PyCrypto problem using AES + CTR

I am writing a code snippet to encrypt text using symmetric encryption. But this does not return with the correct 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 am not very good at cryptography, so please bear with me. I understand that CTR mode requires the counter function to provide a random counter every time, but why does it need 16 bytes when my key is 32 bytes and he insists that my message is also a multiple of 16 bytes? This is normal?

I assume that it does not return to the original message because the counter has changed between encryption and decryption. But then, how should this work theoretically? What am I doing wrong? In any case, I am forced to return to the ECB until I find out: (

+7
python cryptography aes encryption-symmetric pycrypto
source share
5 answers

counter should return the same when decrypting as when encrypting, as you intuitively understood, so there is one ( NOT SECURE FOR ALL ) way to do this:

 >>> 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-in-time constraint, which seems to surprise you, is pretty natural.

Of course, the so-called “counter” that returns the same value on every call is extremely unsafe . It’s not worth doing much better, for example ....:

 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) 
+11
source share

AES is a block block : it is an algorithm (more precisely, a pair of algorithms) that takes a key and a message block and either encrypts or decrypts the block. The block size is always 16 bytes, regardless of the size of the key.

CTR is an operating mode . These are a couple of algorithms that are built on a block cipher to create stream encryption that can encrypt and decrypt messages of arbitrary length.

CTR works by combining consecutive message blocks with encryption of consecutive counter values. The counter size must be one block in order to be valid for block encryption.

  • It does not functionally matter what the subsequent values ​​of the counter are if the same sequence is used on the encryption and decryption side. Typically, the counter is processed as a 256-bit number and incremented for each subsequent block, and the initial value is randomly selected. Thus, as a rule, the increment method is curved in the code, but the decryption side needs to know what the initial value is, so the encryption side sends or stores the initial counter value at the beginning of the encrypted message.
  • To ensure security, it is important that you never repeat the same counter value with the given key . So, for a one-time key, start with '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' . But if the key is used several times, then the second message is not allowed to reuse any counter values ​​used by the first message, and the easiest way is to guarantee that it will generate the initial counter value in a random order (with 2 ^ 128 space, the probability of collision is acceptably negligible )

By letting the caller choose the counter function, PyCrypto library gives you a lot of rope to hang yourself. You should use Crypto.Util.Counter , not just “for better performance” as written in the documentation, but because it’s easier to create something safe than what you probably think of yourself. And even so, make sure to use a random initial value that is not the default value.

 import binascii import os from Crypto.Cipher import AES from Crypto.Util import Counter def int_of_string(s): return int(binascii.hexlify(iv), 16) def encrypt_message(key, plaintext): iv = os.urandom(16) ctr = Counter.new(128, initial_value=int_of_string(iv)) aes = AES.new(key, AES.MODE_CTR, counter=ctr) return iv + aes.encrypt(plaintext) def decrypt_message(key, ciphertext): iv = ciphertext[:16] ctr = Counter.new(128, initial_value=int_of_string(iv)) aes = AES.new(key, AES.MODE_CTR, counter=ctr) return aes.decrypt(ciphertext[16:]) 
+4
source share

The initialization vector (“counter”) should remain the same as the key between encryption and decryption. It is used so that you can encode the same text a million times and each time get a different ciphertext (preventing some known plaintext attacks and pattern matching / attacks). You should still use the same IV when decrypting, as with encryption. Usually, when you start decrypting a stream, you initialize IV with the same value that you started from when you started encrypting this stream.

See http://en.wikipedia.org/wiki/Initialization_vector for information on initialization vectors.

Note that os.urandom (16) is not “deterministic,” which is a requirement for counting functions. I suggest you use the increment function since this is the CTR mode. The initial value of the counter should be random, but subsequent values ​​should be completely predictable from the initial value (deterministic). The initial value can even take care of you (I do not know the details)

On the size of the keys, IV, and input, it looks like the cipher you selected has a block size of 16 bytes. Everything that you described corresponds to this and seems normal to me.

+1
source share

why should it be 16 bytes when my key is 32 bytes.

It should be the same length as the size of the encryption block. CTR mode simply encrypts the counter and XOR is plain text with an encrypted counter block.

Notes:

  • the counter value MUST be unique - if you ALWAYS use the same counter value to encrypt two different plaintexts under the same key, you simply gave your key.
  • like IV, the counter is NOT secret - just send it along with the ciphertext. If you make the code more complicated by trying to keep it secret, you are likely to shoot in the leg.
  • the counter value does not have to be unpredictable - starting from scratch and adding one for each block is fine. But keep in mind that if you are encrypting several messages, you need to keep track of already accepted counter values, i.e. You need to keep track of how many blocks have already been encrypted with this key (and you cannot use the same key in different copies of your program or on different machines).
  • plain text can be any length - CTR mode turns a block cipher into a stream cipher.

Standard Disclaimer: Crypto is complex. If you do not understand what you are doing, you are mistaken.

I just want to store some passwords through sessions.

Use scrypt. . The script includes encrypt and decrypt , which use AES-CTR with a password-generated key.

 $ pip install scrypt $ python >>> import scrypt >>> import getpass >>> pw = getpass.getpass("enter password:") enter password: >>> encrypted = scrypt.encrypt("Guido is a space alien.",pw) >>> out = scrypt.decrypt(encrypted,pw) >>> out 'Guido is a space alien.' 
+1
source share

Maybe I was late and I probably ignored the previous answers, but I did not find a clear description of how to do this (at least IMHO) according to PyCrypto packages.

The Crypto.Util.Counter package provides stateful called counters that are very useful, but it was easy for me to at least use them improperly.

You need to create a counter, for example. ctr = Counter.new('parameters here') . Each time your counter is called using an encryption object in counter mode to encrypt a message, it is incremented. This is necessary for good cryptography practice, otherwise information about equal blocks may leak from ciphertext.

Now you cannot call the decryption function on the same encryption object, because it will call the same counter again, which in the meantime has been increased, possibly several times. What you need to do is create a new encryption object with a different counter initialized with the same parameters. Thus, decryption works correctly, starting the counter from the same point as encryption.

The following is a working example:

 # Import modules from Crypto.Cipher import AES from Crypto import Random from Crypto.Util import Counter # Pad for short keys pad = '# constant pad for short keys ##' # Generate a random initialization vector, to be used by both encryptor and decryptor # This may be sent in clear in a real communication random_generator = Random.new() IV = random_generator.read(8) # Encryption steps # Ask user for input and pad or truncate to a 32 bytes (256 bits) key prompt = 'Input your key. It will padded or truncated at 32 bytes (256 bits).\n-: ' user_keye = raw_input(prompt) keye = (user_keye + pad)[:32] # Create counter for encryptor ctr_e = Counter.new(64, prefix=IV) # Create encryptor, ask for plaintext to encrypt, then encrypt and print ciphertext encryptor = AES.new(keye, AES.MODE_CTR, counter=ctr_e) plaintext = raw_input('Enter message to cipher: ') ciphertext = encryptor.encrypt(plaintext) print ciphertext print # Decryption steps # Ask user for key: it must be equal to that used for encryption prompt = 'Input your key. It will padded or truncated at 32 bytes (256 bits).\n-: ' user_keyd = raw_input(prompt) keyd = (user_keyd + pad)[:32] # Create counter for decryptor: it is equal to the encryptor, but restarts from the beginning ctr_d = Counter.new(64, prefix=IV) # Create decryptor, then decrypt and print decoded text decryptor = AES.new(keyd, AES.MODE_CTR, counter=ctr_d) decoded_text = decryptor.decrypt(ciphertext) print decoded_text 
0
source share

All Articles