I've been fascinated by the science of encryption and decryption of confidential messages for as long as I can remember. Maybe it was back when I was a kid and my elder sister showed me an instance of the Pigpen Cipher, or maybe it was when I bought Kjartan Poskitt's excellent text, Murderous Maths: Codes. At the time, I was exposed only to some really simple substitution and transposition ciphers, but they excited me to the point that I had to delve into the realm of cryptology.

Apart from learning Haskell and some Lambda Calculus during these vacations, I also decided to take the Cryptography I class on Coursera, taught by Professor Dan Boneh. A part of the Week I homework was a specific programming assignment which asked the students to attack the many-time pad. The many-time pad is just like the one-time pad; the keys, however, are not unique for individual messages. This necessarily removes the "uncrackable-ciphertext" factor that OTP is famous for: since the same key is being used for more than one message, XORing both the ciphertexts yield the XOR of their individual plaintexts. Assuming our plaintexts to be in English, we can use the redundancy of the language to break the ciphertext and retrieve them.

The assignment provided us with 10 ciphertexts encrypted using the same pad and their individual outputs encoded in hex. This was the target ciphertext:

`32510ba9babebbbefd001547a810e67149caee11d945cd7fc81a05e9f85aac650e9052ba6a8cd8257bf14d13e6f0` `a803b54fde9e77472dbff89d71b57bddef121336cb85ccb8f3315f4b52e301d16e9f52f904`

Using the fact that XORing an alphabet with a space changes its case, I wrote a Python function which basically XORs every provided ciphertext (apart from the target) with every other ciphertext (except itself), character by character. Then I checked if any of the XORd characters was an alphabet. In case there was an alphabet at the position, say k, I used this alphabet to contruct the key. In case there wasn't, I padded a 0 in that place. Here are the functions I used:

Using this unveiled the key partially. Here's what I got:

`66390000c90000cb9800002acd0000102e000000007f0000a0000000002900000000000000f800000000007000800066c` `763000012000000000000d05ba98700005d00fcec0000003a00008b60bf00003c000000000000000000ed000000000000cf0` `00000000000006edba8c20000007c0000000000000045020000000000ba000078009111000000000000000200c4ab000000` `a900000000008a0082d10000320000000000000000050000911f3edfd73e8333e8463df984ee`

See a lot of 0s there? Those were the places where I wasn't able to make any progress.

Then I used this key again and again, XORd individual ciphertexts to partly unveil their plaintexts, and using some common digrams and trigrams, I was finally able to retrieve the key. Although, the process was a bit time consuming. Here's the final key I got:

`66396e89c9dbd8cc9874352acd6395102eafce78aa7fed28a07f6bc98d29c50b69b0339a19f8aa401a9c6d708f80c066c76` `3fef0123148cdd8e802d05ba98777335daefcecd59c433a6b268b60bf4ef03c9a611098bb3e9a3161edc7b804a33522cfd20` `2d2c68c57376edba8c2ca50027c61246ce2a12b0c4502175010c0a1ba4625786d911100797d8a47e98b0200c4ab000000a90` `0000000008a0082d10000320000000000000000050000911f3edfd73e8333e8463df984ee`

XORing this key with the target ciphertext unveiled the following plaintext: `The secret message is: When using a stream cipher, never use the key more than once`

That's a really good advice now, isn't it?