localeye.cam

← Back

protege_auth.py

doors version 2.5 · 2 KB

1"""ICT Protege WX login handshake maths, reproduced from the controller's secureLogin.js / encryption.js."""
2import hashlib
3
4
5def wx_xor(text, number):
6 """encryption.js xor(): XOR each UTF-8 byte of text with the bytes of `number` (lowest byte first,
7 cycling), as 2-digit uppercase hex."""
8 bits = bin(int(number))[2:].rjust(32, "0")
9 pos, out = len(bits), []
10
11 def substr(start, length): # JavaScript String.prototype.substr semantics
12 if start < 0:
13 start = max(len(bits) + start, 0)
14 return bits[start:start + length]
15
16 for ch in text.encode("utf-8"):
17 pos = len(bits) - 8 if pos == 0 else pos - 8
18 chunk = substr(pos, 8)
19 out.append(f"{ch ^ (int(chunk, 2) if chunk else 0):02x}") # parseInt("") is NaN -> XOR with 0
20 return "".join(out).upper()
21
22
23def sha1(text):
24 """jsSHA(text, "TEXT") with 8-bit chars: each JS UTF-16 code unit contributes only its low byte."""
25 units = text.encode("utf-16-le")
26 return hashlib.sha1(bytes(units[0::2])).hexdigest()
27
28
29def login_proof(username, password, challenge):
30 """-> (name_hash, password_hash) sent with CheckPassword."""
31 d = sha1(password)
32 return (sha1(wx_xor(username.lower(), int(challenge) + 1)).upper(),
33 sha1(wx_xor(d, int(challenge))).upper())
34
35
36def session_key(password, reply):
37 """The 16-char WXKey derived from CheckPassword's reply."""
38 return sha1(wx_xor(sha1(password), int(reply))).upper()[:16]