Pycrypto: Incrementing CTR Mode
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)
AndroidDev
Updated on June 04, 2022Comments
-
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 almost 12 yearsThanks 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 almost 12 yearsSure 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 timectr()
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 almost 12 yearsIn 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 ofIVCounter
defined, all mutually independent. -
AndroidDev almost 12 yearsI'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 almost 12 yearsSure 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 sayctr = 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 sayctr = 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 almost 12 yearsAdditionally, 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 ofint()
. -
AndroidDev almost 12 yearsThis 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 almost 12 years@usr55410 Did I say
Crypto.Util.Counter.new(256, ...)
earlier? My mistake. The correct function call isCrypto.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 almost 12 yearsThanks 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 almost 12 yearsOne 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 almost 12 years1: 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 almost 12 years