Reputation: 13
I'm writing the code for the one time pad in python, and I've run into an issue.
I randomly choose a key from the ascii table (meaning it doesn't have to be a letter) And after I xor it with the plaintext message (which is only alphabet letters)-- I get a ciphertext that is nonsense. The xor is fine, I get that the answer should be that nonsensical ascii character, but my question is- can I get the code to return back an alphabet letter?
I was trying to work this out and my pseudo code so far was:
for i in msglen:
c[i]=msg[i] ^ key[i]
if c[i]<65:
c=(c+65)%26 #add 65 to bring up to alphabet, mod 26 in case it goes over
c+=65 #add another 65 to make up for the modular reduction.
But obviously I got stuck in the decryption part.
Upvotes: 1
Views: 301
Reputation: 93958
You cannot just XOR (ASCII encoded) characters together, as they will usually not result in valid characters - even when implemented right.
There are two main ways of dealing with this. It is possible to XOR together the binary encoding of the characters and then encode it to hexadecimals or base 64. This will of course expand your ciphertext compared to your plaintext. To reverse, first decode, perform the XOR again and you'll have your binary encoded plaintext.
The other way is to encode the printable characters - the alphabet you choose to use - to a range [0, m) and then use modular addition instead of XOR with a key k
to perform the one time pad. You can then convert back to the printable characters to have your ciphertext. To reverse this you do the same thing but you subtract (or add the negative key value: -k
).
One problem is that to keep using a one time pad your key values within the key stream must also be in this range, and this can be tricky if you don't have a routine available to generate keys in a range [0, m) given some random bit string. See my answer here if you want to generate unbiased numbers from a random bit generator (RBG).
Upvotes: 0
Reputation: 15685
What you have is almost certainly not a One Time Pad. For a true OTP the key must be:
If your key (which I suspect you use to seed some RNG somewhere) is shorter than your plaintext then what you have is a Stream Cipher, not an OTP.
Stream Ciphers are perfectly respectable, but it is a common amateur mistake to think that a Stream Cipher is an OTP. Look at RC4 for a simple, though obsolescent, Stream Cipher.
Upvotes: 0
Reputation: 16174
The second part of your code is unnecessary and ruining your calculations.
One time pad is simple because it requires the same code for encryption and decryption, and that code is one line -
result = ''.join(chr(ord(s) ^ ord(k)) for s, k in zip(message, key))
Working with byte arrays will make that even simpler as
result = [s ^ k for s, k in zip(message, key)]
Since x ^ x = 0
and x ^ 0 = x
, we can resolve that s ^ k ^ k = s
, so running this line will both encrypt a plaintext with the key and decrypt an encrypted text with that key (note that, weather it encrypts or decrypts depends on your view - a computer would not recognize that hj67si#s$jv38sok?g6e73
is probably not a plaintext).
Upvotes: 3