I have a stream cipher with a key (that is unknown to me) that is used more than once. Messages are ciphered in this way:
ciphered_message = message XOR key
Let's assume that I have 23 messages (all ciphered with same key) and the original messages contain only upper and lower case letters and spaces.
I've understood that if you xor a space with a space the result is zero, and if you do Letter uppercase xor Space = Letter lowercase
(and vice-versa).
I have to get the original messages (at least a part...). How can I do that? I have to use frequency analysis, right? I'm trying to write some code but I'm stuck!
import binascii
def xor(a, b):
return bytes(x ^ y for (x, y) in zip(a, b))
def read_msgs(input_filename):
try:
with open(input_filename) as f:
rawmsgs = f.read().splitlines()
except Exception as e:
raise Exception("Cannot read {}, error={}".format(input_filename, e))
return [ binascii.unhexlify(m) for m in rawmsgs ] # each message is an array of byte
def xor_all_msgs( messages ): # return a matrix, first row is the first message xored with the others
ret= []
for current_msg in messages: # xor message "current_msg" with the others
xor_with_current= [ xor(current_msg, other_msg) for other_msg in messages if current_msg is not other_msg ]
ret.append(xor_with_current)
return ret
def xor_is_space( c ):
return (ord('a') <= c <= ord('z')) or (ord('A') <= c <= ord('Z'))
def main():
msgs= read_msgs("ciphertexts.txt")
xor_all_msgs(msgs)
if __name__ == "__main__":
main()