Skip to content
This repository was archived by the owner on Apr 20, 2025. It is now read-only.

Commit ad80f80

Browse files
committed
Implementation of bitwise XOR function for bytes object
1 parent 81f0e95 commit ad80f80

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

rsa/_compat.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ def byte(num):
9999
return pack("B", num)
100100

101101

102+
def xor_bytes(b1, b2):
103+
"""
104+
Returns the bitwise XOR result between two bytes objects, b1 ^ b2.
105+
106+
Bitwise XOR operation is commutative, so order of parameters doesn't
107+
generate different results. If parameters have different length, extra
108+
length of the largest one is ignored.
109+
110+
:param b1:
111+
First bytes object.
112+
:param b2:
113+
Second bytes object.
114+
:returns:
115+
Bytes object, result of XOR operation.
116+
"""
117+
if PY2:
118+
return ''.join(byte(ord(x) ^ ord(y)) for x, y in zip(b1, b2))
119+
120+
return bytes(x ^ y for x, y in zip(b1, b2))
121+
122+
102123
def get_word_alignment(num, force_arch=64,
103124
_machine_word_size=MACHINE_WORD_SIZE):
104125
"""

tests/test_compat.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import unittest
1818
import struct
1919

20-
from rsa._compat import byte, is_bytes, range
20+
from rsa._compat import byte, is_bytes, range, xor_bytes
2121

2222

2323
class TestByte(unittest.TestCase):
24+
"""Tests for single bytes."""
25+
2426
def test_byte(self):
2527
for i in range(256):
2628
byt = byte(i)
@@ -33,3 +35,46 @@ def test_raises_StructError_on_overflow(self):
3335

3436
def test_byte_literal(self):
3537
self.assertIsInstance(b'abc', bytes)
38+
39+
40+
class TestBytes(unittest.TestCase):
41+
"""Tests for bytes objects."""
42+
43+
def setUp(self):
44+
self.b1 = b'\xff\xff\xff\xff'
45+
self.b2 = b'\x00\x00\x00\x00'
46+
self.b3 = b'\xf0\xf0\xf0\xf0'
47+
self.b4 = b'\x4d\x23\xca\xe2'
48+
self.b5 = b'\x9b\x61\x3b\xdc'
49+
self.b6 = b'\xff\xff'
50+
51+
self.byte_strings = (self.b1, self.b2, self.b3, self.b4, self.b5, self.b6)
52+
53+
def test_xor_bytes(self):
54+
self.assertEqual(xor_bytes(self.b1, self.b2), b'\xff\xff\xff\xff')
55+
self.assertEqual(xor_bytes(self.b1, self.b3), b'\x0f\x0f\x0f\x0f')
56+
self.assertEqual(xor_bytes(self.b1, self.b4), b'\xb2\xdc\x35\x1d')
57+
self.assertEqual(xor_bytes(self.b1, self.b5), b'\x64\x9e\xc4\x23')
58+
self.assertEqual(xor_bytes(self.b2, self.b3), b'\xf0\xf0\xf0\xf0')
59+
self.assertEqual(xor_bytes(self.b2, self.b4), b'\x4d\x23\xca\xe2')
60+
self.assertEqual(xor_bytes(self.b2, self.b5), b'\x9b\x61\x3b\xdc')
61+
self.assertEqual(xor_bytes(self.b3, self.b4), b'\xbd\xd3\x3a\x12')
62+
self.assertEqual(xor_bytes(self.b3, self.b5), b'\x6b\x91\xcb\x2c')
63+
self.assertEqual(xor_bytes(self.b4, self.b5), b'\xd6\x42\xf1\x3e')
64+
65+
def test_xor_bytes_length(self):
66+
self.assertEqual(xor_bytes(self.b1, self.b6), b'\x00\x00')
67+
self.assertEqual(xor_bytes(self.b2, self.b6), b'\xff\xff')
68+
self.assertEqual(xor_bytes(self.b3, self.b6), b'\x0f\x0f')
69+
self.assertEqual(xor_bytes(self.b4, self.b6), b'\xb2\xdc')
70+
self.assertEqual(xor_bytes(self.b5, self.b6), b'\x64\x9e')
71+
self.assertEqual(xor_bytes(self.b6, b''), b'')
72+
73+
def test_xor_bytes_commutative(self):
74+
for first in self.byte_strings:
75+
for second in self.byte_strings:
76+
min_length = min(len(first), len(second))
77+
result = xor_bytes(first, second)
78+
79+
self.assertEqual(result, xor_bytes(second, first))
80+
self.assertEqual(len(result), min_length)

0 commit comments

Comments
 (0)