Skip to content

Instantly share code, notes, and snippets.

@marcsello
Created April 15, 2020 15:28
Show Gist options
  • Save marcsello/914460e8094f309f83a9e67492afb8e3 to your computer and use it in GitHub Desktop.
Save marcsello/914460e8094f309f83a9e67492afb8e3 to your computer and use it in GitHub Desktop.
NETLOCK

NETLOCK

This is a simple application that can broadcast cryptographically signed commands to all computers in the same broadcast domain using a pre-shared key.

Usage

Daemon

The Daemon should run on computers which should execute the commands. The pre-shared key should be provided as an evironmental variable NETLOCK_PSK. Optionally a --debug argumentum could be provided to the daemon so that it will print out debug messages.

cmd

Commands could be sent by running the netlock_cmd.py script with the command as the only argumentum. The pre-shared key should be provided as an evironmental variable NETLOCK_PSK.

Commands

A simple lock with Mate Screensaver command is implemented as an example. Any command could be easily added.

Use NTP!

Keep in mind that all computers that use this system must have their clocks synchronized. Even one second difference could cause invalid signature errors.

#!/usr/bin/env python3
import os
import sys
import socket
import hashlib
import time
PSK = os.environ['NETLOCK_PSK'].encode('utf-8')
def generate_signature(command: bytes) -> bytes:
ts = int(time.time())
full_data = str(ts).encode('utf-8') + PSK + command
m = hashlib.sha256()
m.update(full_data)
return m.digest()
def main():
if len(sys.argv) != 2:
print("Usage: netlock_cmd.py <cmd>")
return
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
command = sys.argv[1].encode('utf-8')
msg = command + b'\0' + generate_signature(command)
sock.sendto(msg, ('255.255.255.255', 55555))
print("OK")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
import os
import sys
import socket
import hashlib
import time
import logging
PSK = os.environ['NETLOCK_PSK'].encode('utf-8')
class InvalidMessage(BaseException):
pass
def check_signature(data: bytes, signature: bytes) -> bool:
ts = int(time.time())
full_data = str(ts).encode('utf-8') + PSK + data
m = hashlib.sha256()
m.update(full_data)
return m.digest() == signature
def decode_message(message: bytes) -> tuple: # (data, signature)
if message.count(b'\0') != 1:
raise InvalidMessage("Wrong amount of separators")
zeropos = message.find(b'\0')
return (message[:zeropos], message[zeropos+1:])
def lock_screen():
logging.debug("Locking screen.")
os.system("mate-screensaver-command -l")
FUNCTIONS = {
b"LCK": lock_screen
}
def main():
logging.basicConfig(filename="", format="%(asctime)s [%(levelname)s]: %(message)s",
level=logging.DEBUG if '--debug' in sys.argv else logging.INFO)
logging.debug("Setting up socket...")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 55555))
logging.info("NETLOCK daemon is ready!")
while True:
data, addr = sock.recvfrom(1500)
logging.debug("New message!")
try:
data, signature = decode_message(data)
except InvalidMessage:
logging.debug("Invalid message format. Ignoring.")
continue
if not check_signature(data, signature):
logging.debug("Invalid signature. Ignoring.")
continue
if data not in FUNCTIONS:
logging.debug("Invalid command. Ignoring.")
continue
try:
FUNCTIONS[data]()
except Exception as e:
logging.info(f"Error running command {data} : {str(e)}")
continue
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment