Stuff
This commit is contained in:
parent
d28c6375c8
commit
ac92458573
8 changed files with 327 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
25
INSTALL.md
Normal file
25
INSTALL.md
Normal 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
8
config.py.example
Normal 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
66
eq3crypto.py
Normal 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
81
keymatic.py
Executable 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
30
ldap_query.py
Executable 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
82
status_alpha.py
Executable 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
30
update_keys.py
Executable 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()
|
Loading…
Reference in a new issue