This commit is contained in:
7marcus9 2022-07-11 20:27:33 +02:00
parent d28c6375c8
commit ac92458573
8 changed files with 327 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

25
INSTALL.md Normal file
View file

@ -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

8
config.py.example Normal file
View file

@ -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))"

66
eq3crypto.py Normal file
View file

@ -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)

81
keymatic.py Executable file
View file

@ -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()

30
ldap_query.py Executable file
View file

@ -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']))

82
status_alpha.py Executable file
View file

@ -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()

30
update_keys.py Executable file
View file

@ -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()