Challenge 1-2 Fixed XOR
Description
Write a function that takes two equal-length buffers and produces their XOR combination.
If your function works properly, then when you feed it the string:
1
|
1c0111001f010100061a024b53535009181c
|
… after hex decoding, and when XOR’d against:
1
|
686974207468652062756c6c277320657965
|
… should produce:
1
|
746865206b696420646f6e277420706c6179
|
Solution
I didn’t want to rewrite functions over and over again or copy/paste in between challenges so i wrote a quick lib to import functions I’ll need in various challenges. Then i started at this one. Something tricky i found that I may or may not be doing correctly is the hex conversion. I noticed whenever I convert back to Hex if there is a leading 0 it gets dropped.
Example:
1
2
3
|
09->9 // bad
0a->a // bad
1f->1f // ok
|
Anyway, this was also straight forward. I wrote a few helper functions to convert hex to decimal and to xor a string against another string of equal length. Note that i did not add in error checking for if the lengths do not match since i know the exact inputs.
Full code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# cryptopals.py
import sys
def hex_digit_to_dec_digit(d):
if '0' <= d <= '9':
return ord(d) - ord('0')
if 'a' <= d.lower() <= 'f':
return ord(d.lower()) - ord('a') + 10
def hex_to_dec(h):
if len(h) == 1:
# missing leading 0
h = "0" + h
return hex_digit_to_dec_digit(h[0]) << 4 | hex_digit_to_dec_digit(h[1])
def xor_strings(string_1, string_2):
hex_string = ""
for i in range(0, len(string_1), 2):
d1 = hex_to_dec(string_1[i:i+2])
d2 = hex_to_dec(string_2[i:i+2])
new_byte = d1 ^ d2
h_char = hex(new_byte)[2:]
if len(h_char) == 1:
# missing leading 0
h_char = "0" + h_char
hex_string += h_char
return hex_string
def xor_string_with_key(string_1, key):
hex_string = ""
key = hex_to_dec(key)
for i in range(0, len(string_1), 2):
c = string_1[i:i+2]
d1 = hex_to_dec(c) # debugging
# d1 = hex_to_dec(string_1[i:i+2])
new_byte = d1 ^ key
h_char = hex(new_byte)[2:]
if len(h_char) == 1:
# missing leading 0
h_char = "0" + h_char
hex_string += h_char
return hex_string
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# convert.py
import sys
sys.path.append('lib') # folder i keep cryptopals.py in
from cryptopals import xor_strings
original_string = "1c0111001f010100061a024b53535009181c" # "KSSP "
xor_string = "686974207468652062756c6c277320657965" # "hit the bull's eye"
expected_string = "746865206b696420646f6e277420706c6179" # "the kid don't play"
def main():
print("original: {}").format(original_string)
print("xor w/: {}").format(xor_string)
print("should yield: {}").format(expected_string)
actual_string = xor_strings(original_string, xor_string)
print("")
print("{} = {}").format(expected_string, actual_string)
if expected_string == actual_string:
print("SUCCESS!")
else:
print("FAILURE!")
if __name__ == "__main__":
main()
|
Which yields the following output:
1
2
3
4
5
6
7
|
$ python convert.py
original: 1c0111001f010100061a024b53535009181c
xor w/: 686974207468652062756c6c277320657965
should yield: 746865206b696420646f6e277420706c6179
746865206b696420646f6e277420706c6179 = 746865206b696420646f6e277420706c6179
SUCCESS!
|