4

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
  • 20,466
  • 42
  • 148
  • 239

1 Answers1

15

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)
atomicinf
  • 3,596
  • 19
  • 17
  • 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! – AndroidDev Jul 26 '12 at 01:19
  • 1
    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 Jul 26 '12 at 01:23
  • 1
    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. – atomicinf Jul 26 '12 at 01:29
  • 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! – AndroidDev Jul 26 '12 at 03:43
  • 1
    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 Jul 26 '12 at 04:03
  • 1
    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 http://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()`. – atomicinf Jul 26 '12 at 04:07
  • 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. – AndroidDev Jul 26 '12 at 18:10
  • @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. – atomicinf Jul 26 '12 at 19:09
  • 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 Jul 26 '12 at 19:52
  • 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! – AndroidDev Jul 26 '12 at 20:01
  • 1
    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. – atomicinf Jul 26 '12 at 20:16
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14485/discussion-between-usr55410-and-atomicinf) – AndroidDev Jul 26 '12 at 20:52