Writing AES-CTR decryption routine in python

1.9k views Asked by At

I have the following code

func encrypt(key, data string) (string, error) {
    byteKey := []byte(key)
    plaintext := []byte(data)

    block, err := aes.NewCipher(byteKey)
    if err != nil {
        return "", err
    }
    ciphertext := make([]byte, len(plaintext))
    stream := cipher.NewCTR(block, byteKey[aes.BlockSize:])
    stream.XORKeyStream(ciphertext, plaintext)
    return base64.StdEncoding.EncodeToString(ciphertext), nil
}

Where byteKey = []byte("4e8f1670f502a3d40717709e5f80d67c") (Not sure if that's right syntax, but that's the key in hex.)

I have been tasked with writing the decryption routine in any language and this is what I have so far:

import base64
from Crypto.Cipher import AES

def decrypt(ct):
        key = '4e8f1670f502a3d40717709e5f80d67c'.decode('hex')
        nonce = 0
        ct1 = base64.b64decode(ct)
        cipher = AES.new(key, mode=AES.MODE_CTR, counter=lambda: nonce)
        print str(cipher.decrypt(ct1))

I'm doing something wrong I just don't know what. Can an expert please help? Thanks in advance.

1

There are 1 answers

0
Gilles 'SO- stop being evil' On

Hint: think about how counter mode works. What is the initial counter value in your code and what are the subsequent values? What should they be?


counter=lambda: nonce

This is a constant function that always returns the same value, which is 0 in your program. But that's not what you need to pass to counter: you need to pass a function which returns the current counter value each time it is called. For example, if the initial counter value is 0, then this function must return 0 the first time it's called, 1 the second time, 2 the third time, etc. More precisely, it must return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', then '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01', etc.

The Python interface is badly designed here. It's overly flexible, but this flexibility is never useful in practice and makes it hard to use the API correctly. Furthermore the documentation is not clear at all:

counter (callable) - (Only MODE_CTR). A stateful function that returns the next counter block, which is a byte string of block_size bytes. For better performance, use Crypto.Util.Counter.

In fact you pretty much need to use Crypto.Util.Counter, not just for “performance”, but just to make the calculation correct. Don't feel bad: you aren't the first to trip over this.

Confronted with an API that you can't figure out, you might turn to Stack Overflow next… and this very question has been answered, at PyCrypto problem using AES+CTR, but beware that for 7 years this question did not have a correct answer, despite having one that's upvoted and accepted. My answer shows how to use MODE_CTR and counter.

A second problem may be the initial counter value. There are two main strategies for choosing an initial counter value for CTR mode:

  • For a single-use key, start at 0, and don't transmit the ICV with the message since it's a well-known constant.
  • For a multiple-use key, generate a random value each time, and send the ICV at the beginning of the ciphertext.

I'm not familiar with the API in the encryption code you posted. But since it returns ciphertext that's the same length as the plaintext, it probably uses a constant ICV (which is fine for a single-use key, but catastrophic if the key is reused), most likely 0. Still, check the documentation of that API.

(If you need further coding help, ask on Stack Overflow rather than Cryptography, because coding questions are off-topic on Cryptography.SE. And be sure to post complete code to reproduce the problem, including inputs and outputs.)