From ac9245857351fc32866fd69824072023335b484f Mon Sep 17 00:00:00 2001 From: 7marcus9 <7m9_c3hgit@7m9.eu> Date: Mon, 11 Jul 2022 20:27:33 +0200 Subject: [PATCH] Stuff --- .gitignore | 5 +++ INSTALL.md | 25 +++++++++++++++ config.py.example | 8 +++++ eq3crypto.py | 66 ++++++++++++++++++++++++++++++++++++++ keymatic.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++ ldap_query.py | 30 +++++++++++++++++ status_alpha.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++ update_keys.py | 30 +++++++++++++++++ 8 files changed, 327 insertions(+) create mode 100644 .gitignore create mode 100644 INSTALL.md create mode 100644 config.py.example create mode 100644 eq3crypto.py create mode 100755 keymatic.py create mode 100755 ldap_query.py create mode 100755 status_alpha.py create mode 100755 update_keys.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b242c7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..b7a6f3e --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,25 @@ +sudo useradd unlock -m -s /opt/keymatic/keymatic.py +sudo useradd lock -m -s /opt/keymatic/keymatic.py +sudo useradd open -m -s /opt/keymatic/keymatic.py +sudo useradd door -m -s /opt/keymatic/keymatic.py + +sudo mkdir /home/unlock/.ssh +sudo mkdir /home/lock/.ssh +sudo mkdir /home/open/.ssh +sudo mkdir /home/door/.ssh + +sudo ln -s /opt/keymatic/authkeyfile/authorized_keys /home/unlock/.ssh/authorized_keys +sudo ln -s /opt/keymatic/authkeyfile/authorized_keys /home/lock/.ssh/authorized_keys +sudo ln -s /opt/keymatic/authkeyfile/authorized_keys /home/open/.ssh/authorized_keys +sudo ln -s /opt/keymatic/authkeyfile/authorized_keys /home/door/.ssh/authorized_keys + + +https://www.npmjs.com/package/keyble +sudo apt install npm +sudo npm install --update --global --unsafe-perm keyble +sudo keyble-registeruser --user_name PI --qr_code_data M001A..... + +create config.py + +crontab + 2 * * * * cd /opt/keymatic;./update_keys.py diff --git a/config.py.example b/config.py.example new file mode 100644 index 0000000..1c7ce51 --- /dev/null +++ b/config.py.example @@ -0,0 +1,8 @@ +device_mac = "00:1a:22:..." +user_key = "" + +ldap_cafile = "l511_ca.crt" +ldap_server = "ldaps://leidap.server.c3h" +ldap_user = "cn=lock,ou=applications,dc=leitstelle511,dc=net" +ldap_pass = "" +ldap_filter = "(&(objectClass=posixaccount)(memberOf=cn=keymatic,ou=groups,dc=leitstelle511,dc=net))" diff --git a/eq3crypto.py b/eq3crypto.py new file mode 100644 index 0000000..d4e1c59 --- /dev/null +++ b/eq3crypto.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +from Crypto.Cipher import AES + +def dumpNum(n): + s = "" + for v in n: + if len(s) > 0: + s = s + " " + s = s + str(v) + print(s) + +def xor_bytes(a,b): + z = [] + for v in a: + z.append(v ^ b[len(z)]) + return bytes(z) + +def uInt16ToBytes(a): + return bytes([a >> 8, a & 0xff]) + +def getNonce(msgid, sessNonce, seccnt): + return bytes([msgid]) + sessNonce + bytes([0,0]) + uInt16ToBytes(seccnt) + +def crypt_data(data, msgid, sessNonce, seccnt, key): + cipher = AES.new(key, AES.MODE_ECB) + nonce = getNonce(msgid, sessNonce, seccnt) + blkid = 1 + o = cipher.encrypt(bytes([1]) + nonce + uInt16ToBytes(blkid)) + return xor_bytes(data, o) + +def compute_auth(data, msgid, sessNonce, seccnt, key): + cipher = AES.new(key, AES.MODE_ECB) + nonce = getNonce(msgid, sessNonce, seccnt) + padded_data_length = 16 + padded_data = data.ljust(padded_data_length, b'\0') + o = cipher.encrypt(bytes([9]) + nonce + uInt16ToBytes(len(data))) + #TODO: Implement for loop for chunked data + o = cipher.encrypt(xor_bytes(o, padded_data)) + xv = cipher.encrypt(bytes([1]) + nonce + bytes([0,0])) + return xor_bytes(o[0:4], xv) + +def sendMsg(msgId, data): + global remote_nonce, key + getMsg(msgId, data, remote_nonce, key) + +def getMsg(msgId, data, remote_nonce, key): + seccnt = 1 + cd = crypt_data(data, msgId, remote_nonce, seccnt, key) + bseccnt = uInt16ToBytes(seccnt) + authval = compute_auth(data, msgId, remote_nonce, seccnt, key) + seccnt += 1 +# dumpNum(bytes([msgId]) + cd + bseccnt + authval) + payload = (bytes([128, msgId]) + cd + bseccnt + authval) +# print(payload.hex()) + return payload + + +#print("TEST") +#key = bytes.fromhex("3b74f06324ad365bc25916a01c547d1d") +#local_nonce = "12345678" +#remote_nonce = bytes([123,122,83,88,105,62,85,55]) +#remote_nonce = bytes.fromhex("d7 37 24 61 da f7 0f df") +# +#padcmd = bytes([1,0,0,0,0,0,0,0]) +#dumpNum(crypt_data(padcmd, 130, remote_nonce, 1, key)) +#sendMsg(135, padcmd) diff --git a/keymatic.py b/keymatic.py new file mode 100755 index 0000000..1f122a0 --- /dev/null +++ b/keymatic.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import subprocess +import time +import eq3crypto +import os +import config +import sys + +pstart = time.time() +writeHandle = 0x411 +readHandle = 0x421 + +def tsLog(m = ""): + global pstart + print("{:.3f}".format(time.time() - pstart) + " " + m) + +def getBuffer(p): + val = p.stdout.peek() + val = p.stdout.read(len(val)) + #print(val) + return val + +def expect(p, s, t=0): + b = "" + while True: + b = b + getBuffer(p).decode() + if s in b: + return b + +def getNotif(p): + expect(p, "Notification") + + +def sendCmd(p, s): + print("<< " + s) + p.stdin.write(s.encode()) + p.stdin.write(b"\n") + p.stdin.flush() + +def getNotifVal(s): + r = s.split("value: ")[-1].split("\n")[0] + print(">> " + r) + return r + +actionStr = os.getenv("HOME") +if "door" in actionStr: + print("Door user") + actionStr = sys.argv[-1] +action = 0 +tsLog("Start") +if "open" in actionStr: + action = 2 +if "unlock" in actionStr: + action = 1 +try: + p = subprocess.Popen(["gatttool", "-I"], stdout=subprocess.PIPE, stdin=subprocess.PIPE) + expect(p, "[LE]") + tsLog("INIT DONE") + sendCmd(p, "connect " + config.device_mac) + p.stdin.flush() + expect(p, "Connection successful") + tsLog("CONNECT DONE") + sendCmd(p, "char-write-req 0x0411 8002014DC7e11BAE13FECB0000000000") + notifNonce = bytes.fromhex(getNotifVal(expect(p, "Notification"))) + if notifNonce[0:2] == b'\x80\x03': + tsLog("Nonce received") + remoteNonce = notifNonce[3:(3+8)] + msg = eq3crypto.getMsg(135, bytes([action,0,0,0,0,0,0,0]), remoteNonce, bytes.fromhex(config.user_key)) + sendCmd(p, "char-write-req 0x0411 " + msg.hex()) + getNotifVal(expect(p, "Notification")) + tsLog("MOVING") + getNotifVal(expect(p, "Notification")) + tsLog("DONE") +finally: + print("FINALLY") + try: + p.communicate(input=b"exit\n",timeout=2) + print("NORMAL EXIT") + except: + print("KILL") + p.kill() diff --git a/ldap_query.py b/ldap_query.py new file mode 100755 index 0000000..3ac1ef4 --- /dev/null +++ b/ldap_query.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import ldap +import config +ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, config.ldap_cafile) +l = ldap.initialize(config.ldap_server) +l.simple_bind_s(config.ldap_user, config.ldap_pass) +r = l.search_s("dc=leitstelle511,dc=net", ldap.SCOPE_SUBTREE, "(&(objectClass=posixaccount)(memberOf=cn=keymatic,ou=groups,dc=leitstelle511,dc=net))", ["sshKey", "memberOf", "mail"]) +#r = l.search_s("dc=leitstelle511,dc=net", ldap.SCOPE_SUBTREE, config.ldap_filter, ["sshKey", "memberOf", "mail"]) + +def checkKey(bk): + try: + k = bk.decode() + kp = k.split(' ') + if "keymatic" in kp[-1].lower(): + return True + + except: + print("# Error while checking Key") + return False + +for e in r: + if 'sshKey' in e[1]: + print("# User: {}".format(e[0])) + for k in e[1]['sshKey']: + if(checkKey(k)): + print(k.decode()) + elif False: + print("# User: {} has no Key".format(e[0])) + if('mail' in e[1]): + print("# - MAIL: {}".format(e[1]['mail'])) diff --git a/status_alpha.py b/status_alpha.py new file mode 100755 index 0000000..6628b9a --- /dev/null +++ b/status_alpha.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +import subprocess +import time +import eq3crypto +import os +import config +import sys + +pstart = time.time() +writeHandle = 0x411 +readHandle = 0x421 + +def tsLog(m = ""): + global pstart + print("{:.3f}".format(time.time() - pstart) + " " + m) + +def getBuffer(p): + val = p.stdout.peek() + val = p.stdout.read(len(val)) + #print(val) + return val + +def expect(p, s, t=0): + b = "" + while True: + b = b + getBuffer(p).decode() + if s in b: + return b + +def getNotif(p): + expect(p, "Notification") + + +def sendCmd(p, s): + print("<< " + s) + p.stdin.write(s.encode()) + p.stdin.write(b"\n") + p.stdin.flush() + +def getNotifVal(s): + r = s.split("value: ")[-1].split("\n")[0] + print(">> " + r) + return r + +actionStr = os.getenv("HOME") +if "door" in actionStr: + print("Door user") + actionStr = sys.argv[-1] +action = 0 +tsLog("Start") +if "open" in actionStr: + action = 2 +if "unlock" in actionStr: + action = 1 +try: + p = subprocess.Popen(["gatttool", "-I"], stdout=subprocess.PIPE, stdin=subprocess.PIPE) + expect(p, "[LE]") + tsLog("INIT DONE") + sendCmd(p, "connect " + config.device_mac) + p.stdin.flush() + expect(p, "Connection successful") + tsLog("CONNECT DONE") + sendCmd(p, "char-write-req 0x0411 8002014DC7e11BAE13FECB0000000000") + notifNonce = bytes.fromhex(getNotifVal(expect(p, "Notification"))) + if notifNonce[0:2] == b'\x80\x03': + tsLog("Nonce received") + remoteNonce = notifNonce[3:(3+8)] + tp = time.strftime("%y %m %d %H %M %S").split() + msg = eq3crypto.getMsg(0x82, bytes([int(tp[0]),int(tp[1]),int(tp[2]),int(tp[3]),int(tp[4]),int(tp[5]),0,0]), remoteNonce, bytes.fromhex(config.user_key)) + sendCmd(p, "char-write-req 0x0411 " + msg.hex()) + #getNotifVal(expect(p, "Notification")) + #tsLog("MOVING") + getNotifVal(expect(p, "Notification")) + tsLog("DONE") +finally: + print("FINALLY") + try: + p.communicate(input=b"exit\n",timeout=2) + print("NORMAL EXIT") + except: + print("KILL") + p.kill() diff --git a/update_keys.py b/update_keys.py new file mode 100755 index 0000000..64aaae1 --- /dev/null +++ b/update_keys.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import subprocess +import hashlib +import sys + +destfile = "authkeyfile/authorized_keys" + +p = subprocess.Popen(["./ldap_query.py"], stdout=subprocess.PIPE, stdin=subprocess.PIPE) +(po, pr) = p.communicate() +if(p.returncode != 0): + print("Data from ldap_query.py seems to be invalid. QUIT") + sys.exit(1) + +newhash = hashlib.md5(po).hexdigest() +#print(newhash) + +try: + f = open(destfile, "rb") + oldhash = hashlib.md5(f.read()).hexdigest() + f.close() +# print(oldhash) +except: + oldhash = None + print("cannot open auth key file for read") + +if newhash != oldhash: +# print("Updating file") + f = open(destfile, "wb") + f.write(po) + f.close()