So, as it turned out, I made a major screwup in my post earlier today on modes of operation. Rather than just edit the post, I'm adding a new post with the corrected description of the counter mode, and a bit of explanation. I figure that if I screw up badly, it's more honest to make a second post explaining the error than it is to just correct it and pretend that all was well.
What I got wrong was the order in which things happen. In the counter mode,
you encrypt the counter using the key, and then you exclusive-or the result of that with the plaintext to get the ciphertext. The plaintext never enters the block cipher; the block cipher just produces a complex and random looking block of bits which are then used to obscure a block of plaintext.
What I said in the original post was that you exclusive or the plaintext with the counter, and then run it through the block cipher. In my screwed up version, the plaintext is being put through the block cipher mechanism; in the correct version, it's not. Below is some of my psuedo-python showing my screwed up CTR mode,
and the (hopefully) correct CTR mode. I've also included a diagram of the correct CTR mode.
def EncryptWithMarksScrewedUpCTR(blocks, ctr, key): for b in blocks: encrypted = encrypt(key, b ^ ctr) ctr = ctr + 1 output(encrypted) def EncryptWithRealCTR(blocks, ctr, key): for b in blocks: e_ctr = encrypt(key, ctr) encrypted = e_ctr ^ b output(encrypted) ctr = ctr + 1
This can make a big difference in the effectiveness of the cipher against various attacks. I'm not going to get into details now, but over the course of future posts, I hope that I'll be able to make it clear why changes like this can have huge impacts on
the security and quality of a cipher.