Bitwise XOR of hexadecimal numbers
Solution 1
Whoa. You're really over-complicating it by a very long distance. Try:
>>> print(hex(0x12ef ^ 0xabcd))
0xb922
You seem to be ignoring these handy facts, at least:
- Python has native support for hexadecimal integer literals, with the
0x
prefix. - "Hexadecimal" is just a presentation detail; the arithmetic is done in binary, and then the result is printed as hex.
- There is no connection between the format of the inputs (the hexadecimal literals) and the output, there is no such thing as a "hexadecimal number" in a Python variable.
- The
hex()
function can be used to convert any number into a hexadecimal string for display.
If you already have the numbers as strings, you can use the int()
function to convert to numbers, by providing the expected base (16 for hexadecimal numbers):
>>> print(int("12ef", 16))
4874
So you can do two conversions, perform the XOR, and then convert back to hex:
>>> print(hex(int("12ef", 16) ^ int("abcd", 16)))
0xb922
Solution 2
If the two hex strings are the same length and you want a hex string output then you might try this.
def hexxor(a, b): # xor two hex strings of the same length return "".join(["%x" % (int(x,16) ^ int(y,16)) for (x, y) in zip(a, b)])
Solution 3
here's a better function
def strxor(a, b): # xor two strings of different lengths
if len(a) > len(b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])
Solution 4
If the strings are the same length, then I would go for '%x' % ()
of the built-in xor (^
).
Examples -
>>>a = '290b6e3a'
>>>b = 'd6f491c5'
>>>'%x' % (int(a,16)^int(b,16))
'ffffffff'
>>>c = 'abcd'
>>>d = '12ef'
>>>'%x' % (int(a,16)^int(b,16))
'b922'
If the strings are not the same length, truncate the longer string to the length of the shorter using a slice longer = longer[:len(shorter)]
Solution 5
For performance purpose, here's a little code to benchmark these two alternatives:
#!/bin/python
def hexxorA(a, b):
if len(a) > len(b):
return "".join(["%x" % (int(x,16) ^ int(y,16)) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join(["%x" % (int(x,16) ^ int(y,16)) for (x, y) in zip(a, b[:len(a)])])
def hexxorB(a, b):
if len(a) > len(b):
return '%x' % (int(a[:len(b)],16)^int(b,16))
else:
return '%x' % (int(a,16)^int(b[:len(a)],16))
def testA():
strstr = hexxorA("b4affa21cbb744fa9d6e055a09b562b87205fe73cd502ee5b8677fcd17ad19fce0e0bba05b1315e03575fe2a783556063f07dcd0b9d15188cee8dd99660ee751", "5450ce618aae4547cadc4e42e7ed99438b2628ff15d47b20c5e968f086087d49ec04d6a1b175701a5e3f80c8831e6c627077f290c723f585af02e4c16122b7e2")
if not int(strstr, 16) == int("e0ff3440411901bd57b24b18ee58fbfbf923d68cd88455c57d8e173d91a564b50ce46d01ea6665fa6b4a7ee2fb2b3a644f702e407ef2a40d61ea3958072c50b3", 16):
raise KeyError
return strstr
def testB():
strstr = hexxorB("b4affa21cbb744fa9d6e055a09b562b87205fe73cd502ee5b8677fcd17ad19fce0e0bba05b1315e03575fe2a783556063f07dcd0b9d15188cee8dd99660ee751", "5450ce618aae4547cadc4e42e7ed99438b2628ff15d47b20c5e968f086087d49ec04d6a1b175701a5e3f80c8831e6c627077f290c723f585af02e4c16122b7e2")
if not int(strstr, 16) == int("e0ff3440411901bd57b24b18ee58fbfbf923d68cd88455c57d8e173d91a564b50ce46d01ea6665fa6b4a7ee2fb2b3a644f702e407ef2a40d61ea3958072c50b3", 16):
raise KeyError
return strstr
if __name__ == '__main__':
import timeit
print("Time-it 100k iterations :")
print("\thexxorA: ", end='')
print(timeit.timeit("testA()", setup="from __main__ import testA", number=100000), end='s\n')
print("\thexxorB: ", end='')
print(timeit.timeit("testB()", setup="from __main__ import testB", number=100000), end='s\n')
Here are the results :
Time-it 100k iterations :
hexxorA: 8.139988073991844s
hexxorB: 0.240523161992314s
Seems like '%x' % (int(a,16)^int(b,16))
is faster then the zip version.
Related videos on Youtube
Comments
-
Pratibha almost 2 years
How can we XOR hexadecimal numbers in Python? For example, I want to XOR
'ABCD'
and'12EF'
, the answer should be'B922'
.I used the code below, but it gives the wrong results.
# xor two strings of different lengths def strxor(a, b): if len(a) > len(b): return "".join(["%s" % (ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)]) else: return "".join(["%s" % (ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])]) key = '12ef' m1 = 'abcd' print(strxor(key, m1))
-
eumiro almost 12 yearsJust a hint for
zip
: it does the stripping of the longer argument automatically. Anyway, @unwind is right with his short solution. -
jfs over 9 years
-
-
Jon Clements almost 12 yearsOf course if @pratibha only has string literals then an alternative is
hex(int('12ef', 16) ^ int('abcd', 16))
-
DaV almost 12 years@unwind, the second number should be 0xabcd, hence the expected answer of 0xB922.
-
Rahil Arora about 10 yearsLooks like the exact code from one of the assignments in Crypto 1 course on Coursera. ;)