forked from TheAlgorithms/Python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathxtea.py
More file actions
118 lines (91 loc) · 3.33 KB
/
xtea.py
File metadata and controls
118 lines (91 loc) · 3.33 KB
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"""
XTEA (eXtended Tiny Encryption Algorithm) is a block cipher designed to
correct weaknesses in TEA. It was published by David Wheeler and Roger
Needham in 1997. XTEA operates on 64-bit blocks with a 128-bit key and
uses a Feistel network with a recommended 64 rounds.
It's still found in embedded systems and game networking protocols due
to its simplicity and small code footprint.
Reference: https://en.wikipedia.org/wiki/XTEA
"""
import struct
DELTA = 0x9E3779B9
MASK = 0xFFFFFFFF
def xtea_encrypt(block: bytes, key: bytes, num_rounds: int = 64) -> bytes:
"""
Encrypt a single 64-bit block using XTEA.
:param block: 8 bytes of plaintext
:param key: 16 bytes (128-bit key)
:param num_rounds: number of Feistel rounds (default 64)
:return: 8 bytes of ciphertext
>>> key = b'\\x00' * 16
>>> plaintext = b'\\x00' * 8
>>> ciphertext = xtea_encrypt(plaintext, key)
>>> ciphertext.hex()
'fc924d124ad0ed50'
>>> xtea_encrypt(b'hello!!!', b'sixteenbyteskey!')
b'u\\x8d\\x00\\x17c\\xb8\\xf0*'
>>> xtea_encrypt(b'short', key)
Traceback (most recent call last):
...
ValueError: block must be 8 bytes
>>> xtea_encrypt(plaintext, b'short')
Traceback (most recent call last):
...
ValueError: key must be 16 bytes
"""
if len(block) != 8:
raise ValueError("block must be 8 bytes")
if len(key) != 16:
raise ValueError("key must be 16 bytes")
v0, v1 = struct.unpack("!II", block)
k = struct.unpack("!4I", key)
total = 0
for _ in range(num_rounds):
v0 = (v0 + ((((v1 << 4) ^ (v1 >> 5)) + v1) ^ (total + k[total & 3]))) & MASK
total = (total + DELTA) & MASK
v1 = (
v1 + ((((v0 << 4) ^ (v0 >> 5)) + v0) ^ (total + k[(total >> 11) & 3]))
) & MASK
return struct.pack("!II", v0, v1)
def xtea_decrypt(block: bytes, key: bytes, num_rounds: int = 64) -> bytes:
"""
Decrypt a single 64-bit block using XTEA.
:param block: 8 bytes of ciphertext
:param key: 16 bytes (128-bit key)
:param num_rounds: number of Feistel rounds (default 64)
:return: 8 bytes of plaintext
Roundtrip test -- encrypt then decrypt returns original plaintext:
>>> key = b'\\x00' * 16
>>> plaintext = b'\\x00' * 8
>>> xtea_decrypt(xtea_encrypt(plaintext, key), key) == plaintext
True
>>> msg = b'hello!!!'
>>> k = b'sixteenbyteskey!'
>>> xtea_decrypt(xtea_encrypt(msg, k), k) == msg
True
>>> xtea_decrypt(b'short', key)
Traceback (most recent call last):
...
ValueError: block must be 8 bytes
>>> xtea_decrypt(b'\\x00' * 8, b'short')
Traceback (most recent call last):
...
ValueError: key must be 16 bytes
"""
if len(block) != 8:
raise ValueError("block must be 8 bytes")
if len(key) != 16:
raise ValueError("key must be 16 bytes")
v0, v1 = struct.unpack("!II", block)
k = struct.unpack("!4I", key)
total = (DELTA * num_rounds) & MASK
for _ in range(num_rounds):
v1 = (
v1 - ((((v0 << 4) ^ (v0 >> 5)) + v0) ^ (total + k[(total >> 11) & 3]))
) & MASK
total = (total - DELTA) & MASK
v0 = (v0 - ((((v1 << 4) ^ (v1 >> 5)) + v1) ^ (total + k[total & 3]))) & MASK
return struct.pack("!II", v0, v1)
if __name__ == "__main__":
import doctest
doctest.testmod()