In the CTR mode of operation, the plaintext block is treated as a stream cipher, where each byte of the plaintext gets XOR-ed with each byte of the key (which is generated using a nonce and a counter) to form the ciphertext. This was a part of a challenge that I came across recently. Suppose I have a nonce of 8 bytes that is generated randomly and a counter of 8 bytes, that starts from the value 0 and gets incremented by 10 each time. Now, we have a server which takes input from the user. It returns the input back as output, but in encoded form, except one time. If the input is "!msg", it returns back the variable MSG, which is to be recovered. Here's the server code implementation that was given-

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes


key = get_random_bytes(AES.block_size)
nonce = get_random_bytes(AES.block_size//2)

if __name__ == '__main__':
    ctr_val = 0
    print("Welcome to the Server!")
    print("Input \"!msg\" to have the message sent to you.")

    while True:
        inp = input("\n$ ")
        outp = None
        if inp == "!msg":
            outp = MSG
            outp = inp
        cipher = AES.new(key, AES.MODE_CTR, nonce=nonce, initial_value=ctr_val); ctr_val += 10
        inp_enc = cipher.encrypt(inp.encode()).hex()

        cipher = AES.new(key, AES.MODE_CTR, nonce=nonce, initial_value=ctr_val); ctr_val += 10
        outp_enc = cipher.encrypt(outp.encode()).hex()

        print(f"Encrypted Input (hex): {inp_enc}")
        print(f"Encrypted Output (hex): {outp_enc}")

When we connect to the server and send the input as "!msg", it returns back the input's (which is "!msg) encrypted form and the output's encrypted form (which is the content of the variable MSG) (in hexadecimal). I suspect that this exposes the CTR mode to certain attacks, as the same nonce is being used to encrypt two different blocks when the input is "!msg", but I don't know how to exploit this vulnerability to get back the MSG. Can someone help me with a clue? I'm new to cryptography and I'm willing to learn on the go.

  • $\begingroup$ Yeah, but the nonce is only getting repeated, the keystream can still be different, right? The nonce and counter together are sent to the AES block, which is a PRG, so it can produce a completely different key notwithstanding the fact that we used the same nonce twice. $\endgroup$ Feb 3 at 17:41
  • 2
    $\begingroup$ The server performs double encryption using a counter offset. Think about what happens to large messages, note that the cipher will increase the counter block by 1 internally, and that encryption = decryption. At least I think that is the clue, this seems to be a pretty weird assignment. $\endgroup$
    – Maarten Bodewes
    Feb 3 at 17:46
  • $\begingroup$ So, the counter value starts from 0 and if it's a long enough sentence, it'll reach 10 and move on. Now, the same plaintext is encrypted with the same nonce but now, the counter starts from 10. So, we'd be having the same nonce+counter for the plaintext from the first instance from the 10th byte as the second instance. The key remains the same for both. If we pass the same nonce+counter to the AES scheme with same key for the same plaintext twice, do we get the same output in both the cases? Isn't AES a PRG? $\endgroup$ Feb 4 at 0:19
  • 1
    $\begingroup$ AES is a block cipher which is a keyed permutation, CTR is a PRG but pseudo random means that it does generate the same output if the IV or counter value is repeated for the same key. $\endgroup$
    – Maarten Bodewes
    Feb 4 at 1:00
  • 1
    $\begingroup$ Must be some error during testing, can't think of anything different. Nice that you were able to get it solved though. $\endgroup$
    – Maarten Bodewes
    Feb 4 at 23:47


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.