-
Notifications
You must be signed in to change notification settings - Fork 200
/
Copy pathcrypto1.py
110 lines (91 loc) · 3.74 KB
/
crypto1.py
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
import re
LFSR48_FILTER_A = 0x9E98
LFSR48_FILTER_B = 0xB48E
LFSR48_FILTER_C = 0xEC57E80A
LFSR48_POLY = 0xE882B0AD621
U8_TO_ODD4 = [((i & 0x80) >> 4) + ((i & 0x20) >> 3) + ((i & 0x08) >> 2) + ((i & 0x02) >> 1) for i in range(256)]
EVEN_PARITY_U8 = [0 for i in range(256)]
def u8_to_odd4(u8):
return U8_TO_ODD4[u8 & 0xFF]
def get_bit(num, x = 0):
return (num >> x) & 1
for i in range(256):
tmp = i
tmp ^= tmp >> 4
tmp ^= tmp >> 2
EVEN_PARITY_U8[i] = (tmp ^ (tmp >> 1)) & 1
def even_parity_u8(u8):
return EVEN_PARITY_U8[u8 & 0xFF]
def odd_parity_u8(u8):
return even_parity_u8(u8) ^ 1
def even_parity_u16(u16):
return even_parity_u8((u16 >> 8) ^ u16)
def even_parity_u48(u48):
return even_parity_u16((u48 >> 32) ^ (u48 >> 16) ^ u48)
def swap_endian_u16(u16):
return ((u16 & 0xFF) << 8) | ((u16 >> 8) & 0xFF)
def swap_endian_u32(u32):
return swap_endian_u16(u32 & 0xFFFF) << 16 | swap_endian_u16((u32 >> 16) & 0xFFFF)
"""
ref: https://web.archive.org/web/20081010065744/http://sar.informatik.hu-berlin.de/research/publications/SAR-PR-2008-21/SAR-PR-2008-21_.pdf
"""
class Crypto1:
def __init__(self, new_lfsr48: int = 0):
self.lfsr48 = new_lfsr48
@property
def key(self) -> bytearray:
tmp, key = self.lfsr48, bytearray(6)
for i in range(6):
key[i] = tmp & 0xFF
tmp >>= 8
return key.hex()
@key.setter
def key(self, key: str):
if not re.match(r"^[a-fA-F0-9]{12}$", key):
raise ValueError(f"Invalid hex format key: {key}")
tmp, self.lfsr48 = int(key, 16), 0
for i in range(6):
self.lfsr48 = (self.lfsr48 << 8) | tmp & 0xFF
tmp >>= 8
def lfsr48_filter(self):
f = 0
f |= get_bit(LFSR48_FILTER_B, u8_to_odd4(self.lfsr48 >> 8)) # fb4
f |= get_bit(LFSR48_FILTER_A, u8_to_odd4(self.lfsr48 >> 16)) << 1 # fa4
f |= get_bit(LFSR48_FILTER_A, u8_to_odd4(self.lfsr48 >> 24)) << 2 # fa4
f |= get_bit(LFSR48_FILTER_B, u8_to_odd4(self.lfsr48 >> 32)) << 3 # fb4
f |= get_bit(LFSR48_FILTER_A, u8_to_odd4(self.lfsr48 >> 40)) << 4 # fa4
return get_bit(LFSR48_FILTER_C, f)
def lfsr48_bit(self, bit_in: int = 0, is_encrypted: bool = False) -> int:
out_bit = self.lfsr48_filter()
bit_feedback = even_parity_u48(LFSR48_POLY & self.lfsr48) ^ (bit_in & 1) ^ (is_encrypted & out_bit)
self.lfsr48 = (bit_feedback << 47) | (self.lfsr48 >> 1)
return out_bit
def lfsr48_u8(self, u8_in: int = 0, is_encrypted: bool = False) -> int:
out_u8 = 0
for i in range(8):
tmp = self.lfsr48_bit(u8_in >> i, is_encrypted) << i
out_u8 |= tmp
return out_u8
def lfsr48_u32(self, u32_in: int = 0, is_encrypted: bool = False) -> int:
out_u32 = 0
for i in range(3, -1, -1):
bit_offset = i << 3
out_u32 |= self.lfsr48_u8(u32_in >> bit_offset, is_encrypted) << bit_offset
return out_u32
@staticmethod
def prng_next(lfsr32: int, n: int = 1) -> int:
lfsr32 = swap_endian_u32(lfsr32)
for i in range(n):
lfsr32 = even_parity_u8(0x2D & (lfsr32 >> 16)) << 31 | (lfsr32 >> 1)
return swap_endian_u32(lfsr32)
@staticmethod
def mfkey32_is_reader_has_key(uid: int, nt: int, nrEnc: int, arEnc: int, key: str) -> bool:
state = Crypto1()
state.key = key
state.lfsr48_u32(uid ^ nt, False) # ks0
state.lfsr48_u32(nrEnc, True) # ks1
ks2 = state.lfsr48_u32(0, False) # ks2
ar = arEnc ^ ks2
result = ar == Crypto1.prng_next(nt, 64)
# print(f'uid: {hex(uid)}, nt: {hex(nt)}, nrEnc: {hex(nrEnc)}, arEnc: {hex(arEnc)}, key: {key}, result = {result}')
return result