Skip to content

Commit 967da6d

Browse files
author
PentesterTN
committed
Add XTEA block cipher implementation
Implemented XTEA (eXtended Tiny Encryption Algorithm) with encrypt and decrypt functions. Includes input validation and full doctest coverage.
1 parent 68473af commit 967da6d

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

ciphers/xtea.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""
2+
XTEA (eXtended Tiny Encryption Algorithm) is a block cipher designed to
3+
correct weaknesses in TEA. It was published by David Wheeler and Roger
4+
Needham in 1997. XTEA operates on 64-bit blocks with a 128-bit key and
5+
uses a Feistel network with a recommended 64 rounds.
6+
7+
It's still found in embedded systems and game networking protocols due
8+
to its simplicity and small code footprint.
9+
10+
Reference: https://en.wikipedia.org/wiki/XTEA
11+
"""
12+
13+
import struct
14+
15+
DELTA = 0x9E3779B9
16+
MASK = 0xFFFFFFFF
17+
18+
19+
def xtea_encrypt(
20+
block: bytes, key: bytes, num_rounds: int = 64
21+
) -> bytes:
22+
"""
23+
Encrypt a single 64-bit block using XTEA.
24+
25+
:param block: 8 bytes of plaintext
26+
:param key: 16 bytes (128-bit key)
27+
:param num_rounds: number of Feistel rounds (default 64)
28+
:return: 8 bytes of ciphertext
29+
30+
>>> key = b'\\x00' * 16
31+
>>> plaintext = b'\\x00' * 8
32+
>>> ciphertext = xtea_encrypt(plaintext, key)
33+
>>> ciphertext.hex()
34+
'fc924d124ad0ed50'
35+
36+
>>> xtea_encrypt(b'hello!!!', b'sixteenbyteskey!')
37+
b'u\\x8d\\x00\\x17c\\xb8\\xf0*'
38+
39+
>>> xtea_encrypt(b'short', key)
40+
Traceback (most recent call last):
41+
...
42+
ValueError: block must be 8 bytes
43+
44+
>>> xtea_encrypt(plaintext, b'short')
45+
Traceback (most recent call last):
46+
...
47+
ValueError: key must be 16 bytes
48+
"""
49+
if len(block) != 8:
50+
raise ValueError("block must be 8 bytes")
51+
if len(key) != 16:
52+
raise ValueError("key must be 16 bytes")
53+
54+
v0, v1 = struct.unpack("!II", block)
55+
k = struct.unpack("!4I", key)
56+
57+
total = 0
58+
for _ in range(num_rounds):
59+
v0 = (v0 + ((((v1 << 4) ^ (v1 >> 5)) + v1) ^ (total + k[total & 3]))) & MASK
60+
total = (total + DELTA) & MASK
61+
v1 = (
62+
v1 + ((((v0 << 4) ^ (v0 >> 5)) + v0) ^ (total + k[(total >> 11) & 3]))
63+
) & MASK
64+
65+
return struct.pack("!II", v0, v1)
66+
67+
68+
def xtea_decrypt(
69+
block: bytes, key: bytes, num_rounds: int = 64
70+
) -> bytes:
71+
"""
72+
Decrypt a single 64-bit block using XTEA.
73+
74+
:param block: 8 bytes of ciphertext
75+
:param key: 16 bytes (128-bit key)
76+
:param num_rounds: number of Feistel rounds (default 64)
77+
:return: 8 bytes of plaintext
78+
79+
Roundtrip test -- encrypt then decrypt returns original plaintext:
80+
>>> key = b'\\x00' * 16
81+
>>> plaintext = b'\\x00' * 8
82+
>>> xtea_decrypt(xtea_encrypt(plaintext, key), key) == plaintext
83+
True
84+
85+
>>> msg = b'hello!!!'
86+
>>> k = b'sixteenbyteskey!'
87+
>>> xtea_decrypt(xtea_encrypt(msg, k), k) == msg
88+
True
89+
90+
>>> xtea_decrypt(b'short', key)
91+
Traceback (most recent call last):
92+
...
93+
ValueError: block must be 8 bytes
94+
95+
>>> xtea_decrypt(b'\\x00' * 8, b'short')
96+
Traceback (most recent call last):
97+
...
98+
ValueError: key must be 16 bytes
99+
"""
100+
if len(block) != 8:
101+
raise ValueError("block must be 8 bytes")
102+
if len(key) != 16:
103+
raise ValueError("key must be 16 bytes")
104+
105+
v0, v1 = struct.unpack("!II", block)
106+
k = struct.unpack("!4I", key)
107+
108+
total = (DELTA * num_rounds) & MASK
109+
for _ in range(num_rounds):
110+
v1 = (
111+
v1 - ((((v0 << 4) ^ (v0 >> 5)) + v0) ^ (total + k[(total >> 11) & 3]))
112+
) & MASK
113+
total = (total - DELTA) & MASK
114+
v0 = (v0 - ((((v1 << 4) ^ (v1 >> 5)) + v1) ^ (total + k[total & 3]))) & MASK
115+
116+
return struct.pack("!II", v0, v1)
117+
118+
119+
if __name__ == "__main__":
120+
import doctest
121+
122+
doctest.testmod()

0 commit comments

Comments
 (0)