Pycrypto: Incrementing CTR Mode

15,541

In Python, it is perfectly valid to treat functions as objects. It is also perfectly valid to treat any object that defines __call__(self, ...) as a function.

So what you want might something like this:

class IVCounter(object):
    def __init__(self, start=1L):
        self.value = long(start)
    def __call__(self):
        self.value += 1L
        return somehow_convert_this_to_a_bitstring(self.value)

ctr = IVCounter()
... make some keys and ciphertext ...
print AES.new(key, AES.MODE_CTR, counter=ctr).decrypt(ciphertext)

However, PyCrypto provides a counter method for you that should be much faster than pure Python:

import Crypto.Util.Counter
ctr = Crypto.Util.Counter.new(NUM_COUNTER_BITS)

ctr is now a stateful function (and, simultaneously, a callable object) that increments and returns its internal state every time you call it. You can then do

print AES.new(key, AES.MODE_CTR, counter=ctr).decrypt(ciphertext)

just as before.

Here's a working example using Crypto.Cipher.AES in CTR mode with a user-specified initialization vector:

import Crypto.Cipher.AES
import Crypto.Util.Counter

key = "0123456789ABCDEF" # replace this with a sensible value, preferably the output of a hash
iv = "0000000000009001" # replace this with a RANDOMLY GENERATED VALUE, and send this with the ciphertext!

plaintext = "Attack at dawn" # replace with your actual plaintext

ctr = Crypto.Util.Counter.new(128, initial_value=long(iv.encode("hex"), 16))

cipher = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CTR, counter=ctr)
print cipher.encrypt(plaintext)
Share:
15,541
AndroidDev
Author by

AndroidDev

Updated on June 04, 2022

Comments

  • AndroidDev
    AndroidDev almost 2 years

    Still can't quite get this to work. My question is about how to make the decryption line work. Here is what I have written:

    class IVCounter(object):
        @staticmethod
        def incrIV(self):
            temp = hex(int(self, 16)+1)[2:34]
            return array.array('B', temp.decode("hex")).tostring()
    
    
    def decryptCTR(key, ciphertext):
    
        iv = ciphertext[:32] #extracts the first 32 characters of the ciphertext
    
        #convert the key into a 16 byte string
        key = array.array('B', key.decode("hex")).tostring()
    
        print AES.new(key, AES.MODE_CTR, counter=IVCounter.incrIV(iv)).decrypt(ciphertext)
        return
    

    My error message is:

    ValueError: 'counter' parameter must be a callable object

    I just can't figure out how pycrypto wants me to organize that third argument to new.

    Can anyone help? Thanks!

    EDIT New code after implementing the suggestions below. Still stuck!

    class IVCounter(object):
        def __init__(self, start=1L):
            print start #outputs the number 1 (not my IV as hoped)
            self.value = long(start)
    
       def __call__(self):
            print self.value  #outputs 1 - need this to be my iv in long int form
            print self.value + 1L  #outputs 2
            self.value += 1L
            return somehow_convert_this_to_a_bitstring(self.value) #to be written
    
    def decryptCTR(key, ciphertext):
    
        iv = ciphertext[:32] #extracts the first 32 characters of the ciphertext
        iv = int(iv, 16)
    
        #convert the key into a 16 byte string
        key = array.array('B', key.decode("hex")).tostring()
    
        ctr = IVCounter()
        Crypto.Util.Counter.new(128, initial_value = iv)
    
        print AES.new(key, AES.MODE_CTR, counter=ctr).decrypt(ciphertext)
        return
    

    EDIT STILL can't get this to work. very frustrated and completely out of ideas. Here is the latest code: (please note that my input strings are 32-bit hex strings that must be interpreted in two-digit pairs to convert to long integers.)

    class IVCounter(object):
        def __init__(self, start=1L):
            self.value = long(start)
    
        def __call__(self):
            self.value += 1L
            return hex(self.value)[2:34]
    
    def decryptCTR(key, ciphertext):
        iv = ciphertext[:32] #extracts the first 32 characters of the ciphertext
        iv = array.array('B', iv.decode("hex")).tostring()
    
        ciphertext = ciphertext[32:]
    
        #convert the key into a 16 byte string
        key = array.array('B', key.decode("hex")).tostring()
    
        #ctr = IVCounter(long(iv))
        ctr = Crypto.Util.Counter.new(16, iv)
    
        print AES.new(key, AES.MODE_CTR, counter=ctr).decrypt(ciphertext)
        return
    

    TypeError: CTR counter function returned string not of length 16

  • AndroidDev
    AndroidDev almost 12 years
    Thanks a million for this. What I don't understand is how any of this can increment the IV when nowhere in this code is the IV ever passed as an argument to the class??? As I read this, coming from more of a C++ background, the variable 'self' is what appears to get incremented. But how do I set 'self' equal to my iv string in the first place? I need to increment MY iv - not something random or arbitrary. I just don't see how to connect these dots. Sorry if I am begin dense, but can you explain how this increments MY iv??? I appreciate your help!
  • atomicinf
    atomicinf almost 12 years
    Sure thing. With CTR mode, the initialization vector is simply the initial value of the counter. If you want to pass a specific IV to the counter you're creating, you need only do ctr = Crypto.Util.Counter.new(NUM_COUNTER_BITS, initial_value = 90000001L), where initial_value is your IV in long-integer form. The reason why we consider 'ctr' to be a stateful function is because it stores its own counter internally. Every time ctr() is called, its internal counter increments by one. So by initializing this function with the IV you need, you are then incrementing 'your' IV.
  • atomicinf
    atomicinf almost 12 years
    In case you're looking at my example and not quite understanding it, the callable object (think of it as a C++ functor) can take an argument 'start' when initialized; then, its own internal counter gets set to whatever you want. Every time you call that particular instance of IVCounter, its own internal counter (which you initialized) gets incremented. You could in principle have multiple instances of IVCounter defined, all mutually independent.
  • AndroidDev
    AndroidDev almost 12 years
    I'm going nuts. I can't get that 'my' iv value into this object no matter what I do. I've edited my code so you can see the problem. I've modified things so that when I instantiate my object, I try to pass the iv. But I still don't understand how this argument is received in the init method. My code sets the start value at 1L, and I don't know how to change it to the iv. Thanks for all of your help. I think that I'm almost there if your willing to stick with me a little while longer. Thank you!
  • atomicinf
    atomicinf almost 12 years
    Sure thing @usr55410. Two problems with your current code: you don't use the variable iv after you've initialized it; if you're using my IVCounter, you want to say ctr = IVCounter(long(iv)), or similar, to set the counter's initial state. If you're using Crypto.Util.Counter (preferred for speed), you want to say ctr = Crypto.Util.Counter.new(256, long(iv)). The second problem (which the preceding also solves) is that Crypto.Util.Counter.new returns a function, which you don't assign anywhere.
  • atomicinf
    atomicinf almost 12 years
    Additionally, be careful of another thing: PyCrypto's encryption routines expect (and return) bytestrings, which are most certainly not remotely legible while in encrypted form. You'll need to figure out how to convert strings to and from their hexadecimal representation; it's not a hard problem, solved e.g. at stackoverflow.com/questions/2340319. And one last thing: since you typically want IVs to be long (much longer than int can usually provide), you'll want to declare iv with long() instead of int().
  • AndroidDev
    AndroidDev almost 12 years
    This isn't working. No matter what I do, I get an error that says "TypeError: CTR counter function returned string not of length 16". I've tried everything. I'm so frustrated I want to send my computer through the window. I'll post the latest version above if you still are willing to help me. Sorry for being an idiot. Thanks.
  • atomicinf
    atomicinf almost 12 years
    @usr55410 Did I say Crypto.Util.Counter.new(256, ...) earlier? My mistake. The correct function call is Crypto.Util.Counter.new(128, initial_value = long(iv.encode("hex"), 16). Try that instead; I've updated my answer with an example that should work.
  • AndroidDev
    AndroidDev almost 12 years
    Thanks for the correction. That allows the program to run to completion. Unfortunately, my ciphertext doesn't decrypt correctly, but that seems to be a different issue. I'll think about that some more and re-post as a different question if I get stuck again. I can't tell you how much I appreciate your help. Thanks a lot!
  • AndroidDev
    AndroidDev almost 12 years
    One more thing (actually two questions about the example you posted). 1) are your key and iv strings interpreted as single digits or as hex pairs (e.g. 0x0, 0x1, ... or as 0x01, 0x23, ...). This makes a big difference in how I need to work with my strings, which are of the latter variety. 2) Why can't I add two lines onto the end of your example to decode back to the plaintext? Specifically a) ciphertext = cipher.encrypt(plaintext) and b) print cipher.decrypt(ciphertext). All I get is gibberish when I do this, but I would have expected "Attack at dawn". Thanks!
  • atomicinf
    atomicinf almost 12 years
    1: My key string is binary data; my IV string is a hexstring (e.g. baadf00d). 2: You need to reset the counter before decrypting, i.e. create a new counter with the same IV, then create a new cipher context using the new counter, and use that to decrypt.
  • AndroidDev
    AndroidDev almost 12 years