Skip to content

Commit 7c1dfec

Browse files
authoredOct 29, 2023
XOR Cipher: doctests and bug fixes (#10840)
* Fixed bug with key modulus wrapping. Should be wrapping on 256, not 255. * Fixed bug with incorrect assertion type in decrypt function. * Added doctests for 4 out of 6 methods
1 parent cc22d0b commit 7c1dfec

File tree

1 file changed

+82
-9
lines changed

1 file changed

+82
-9
lines changed
 

‎ciphers/xor_cipher.py

+82-9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ def encrypt(self, content: str, key: int) -> list[str]:
3535
output: encrypted string 'content' as a list of chars
3636
if key not passed the method uses the key by the constructor.
3737
otherwise key = 1
38+
39+
Empty list
40+
>>> XORCipher().encrypt("", 5)
41+
[]
42+
43+
One key
44+
>>> XORCipher().encrypt("hallo welt", 1)
45+
['i', '`', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u']
46+
47+
Normal key
48+
>>> XORCipher().encrypt("HALLO WELT", 32)
49+
['h', 'a', 'l', 'l', 'o', '\\x00', 'w', 'e', 'l', 't']
50+
51+
Key greater than 255
52+
>>> XORCipher().encrypt("hallo welt", 256)
53+
['h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't']
3854
"""
3955

4056
# precondition
@@ -44,7 +60,7 @@ def encrypt(self, content: str, key: int) -> list[str]:
4460
key = key or self.__key or 1
4561

4662
# make sure key is an appropriate size
47-
key %= 255
63+
key %= 256
4864

4965
return [chr(ord(ch) ^ key) for ch in content]
5066

@@ -54,16 +70,32 @@ def decrypt(self, content: str, key: int) -> list[str]:
5470
output: decrypted string 'content' as a list of chars
5571
if key not passed the method uses the key by the constructor.
5672
otherwise key = 1
73+
74+
Empty list
75+
>>> XORCipher().decrypt("", 5)
76+
[]
77+
78+
One key
79+
>>> XORCipher().decrypt("hallo welt", 1)
80+
['i', '`', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u']
81+
82+
Normal key
83+
>>> XORCipher().decrypt("HALLO WELT", 32)
84+
['h', 'a', 'l', 'l', 'o', '\\x00', 'w', 'e', 'l', 't']
85+
86+
Key greater than 255
87+
>>> XORCipher().decrypt("hallo welt", 256)
88+
['h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't']
5789
"""
5890

5991
# precondition
6092
assert isinstance(key, int)
61-
assert isinstance(content, list)
93+
assert isinstance(content, str)
6294

6395
key = key or self.__key or 1
6496

6597
# make sure key is an appropriate size
66-
key %= 255
98+
key %= 256
6799

68100
return [chr(ord(ch) ^ key) for ch in content]
69101

@@ -73,6 +105,22 @@ def encrypt_string(self, content: str, key: int = 0) -> str:
73105
output: encrypted string 'content'
74106
if key not passed the method uses the key by the constructor.
75107
otherwise key = 1
108+
109+
Empty list
110+
>>> XORCipher().encrypt_string("", 5)
111+
''
112+
113+
One key
114+
>>> XORCipher().encrypt_string("hallo welt", 1)
115+
'i`mmn!vdmu'
116+
117+
Normal key
118+
>>> XORCipher().encrypt_string("HALLO WELT", 32)
119+
'hallo\\x00welt'
120+
121+
Key greater than 255
122+
>>> XORCipher().encrypt_string("hallo welt", 256)
123+
'hallo welt'
76124
"""
77125

78126
# precondition
@@ -81,9 +129,8 @@ def encrypt_string(self, content: str, key: int = 0) -> str:
81129

82130
key = key or self.__key or 1
83131

84-
# make sure key can be any size
85-
while key > 255:
86-
key -= 255
132+
# make sure key is an appropriate size
133+
key %= 256
87134

88135
# This will be returned
89136
ans = ""
@@ -99,6 +146,22 @@ def decrypt_string(self, content: str, key: int = 0) -> str:
99146
output: decrypted string 'content'
100147
if key not passed the method uses the key by the constructor.
101148
otherwise key = 1
149+
150+
Empty list
151+
>>> XORCipher().decrypt_string("", 5)
152+
''
153+
154+
One key
155+
>>> XORCipher().decrypt_string("hallo welt", 1)
156+
'i`mmn!vdmu'
157+
158+
Normal key
159+
>>> XORCipher().decrypt_string("HALLO WELT", 32)
160+
'hallo\\x00welt'
161+
162+
Key greater than 255
163+
>>> XORCipher().decrypt_string("hallo welt", 256)
164+
'hallo welt'
102165
"""
103166

104167
# precondition
@@ -107,9 +170,8 @@ def decrypt_string(self, content: str, key: int = 0) -> str:
107170

108171
key = key or self.__key or 1
109172

110-
# make sure key can be any size
111-
while key > 255:
112-
key -= 255
173+
# make sure key is an appropriate size
174+
key %= 256
113175

114176
# This will be returned
115177
ans = ""
@@ -132,6 +194,9 @@ def encrypt_file(self, file: str, key: int = 0) -> bool:
132194
assert isinstance(file, str)
133195
assert isinstance(key, int)
134196

197+
# make sure key is an appropriate size
198+
key %= 256
199+
135200
try:
136201
with open(file) as fin, open("encrypt.out", "w+") as fout:
137202
# actual encrypt-process
@@ -156,6 +221,9 @@ def decrypt_file(self, file: str, key: int) -> bool:
156221
assert isinstance(file, str)
157222
assert isinstance(key, int)
158223

224+
# make sure key is an appropriate size
225+
key %= 256
226+
159227
try:
160228
with open(file) as fin, open("decrypt.out", "w+") as fout:
161229
# actual encrypt-process
@@ -168,6 +236,11 @@ def decrypt_file(self, file: str, key: int) -> bool:
168236
return True
169237

170238

239+
if __name__ == "__main__":
240+
from doctest import testmod
241+
242+
testmod()
243+
171244
# Tests
172245
# crypt = XORCipher()
173246
# key = 67

0 commit comments

Comments
 (0)
Please sign in to comment.