diff --git a/README.md b/README.md index 49efcdf..eba812d 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,11 @@ This is an access-control system implementation via contactless ISO 14443A cards and a PN53x-based reader. So you basically swipe your card, and if it's in database, the door unlocks. +We still run this at this day (2023-09-16) because porting to Python 3 and SWIG is +not that simple, especially the SWIG part. + It's a bit old project, so requires python 2 (didn't have time for porting). At least currently -(2010-05-28) still works on latest Raspberry 4 and Raspbian Buster. +(2023-09-16) still works on latest Raspberry 4 and Raspbian Buster. Info about authorized users and their cards and keys is stored in sqlite database. @@ -29,6 +32,9 @@ and panic trigger) or a cheap electromagnetic lock. * Yubikey Neo HMAC-SHA1 - most safe option, uncloneable * Mifare Desfire - Ed25519 signature of UID (currently no known clones available; although some features could be cloned, it's not enough for anyone to create such partial clones) + * working Mifare Desfire clones are nowhere to be seen in 2023, even though + the datasheet was leaked loooong time ago (adding encryption wouldn't be so + hard with `libfreefare`) Test code is also provided to get payment signature (cryptogram) from Visa and Mastercard, but it's not used. @@ -63,7 +69,7 @@ You need just to run `make`. Additional dependencies: - [SWIG version 2](http://www.swig.org/) - to generate Python-C++ bindings, SWIG 3 is known to cause segfaults sometimes - [WiringPi2 pythonic binding](https://github.com/WiringPi/WiringPi2-Python) (for switching lock on Raspberry), install from pip, `pip install wiringpi` - [python-irc](https://pypi.python.org/pypi/irc) >= 16.0, use "pip install irc", the one in repos is old -- [pysftp](https://pypi.org/project/pysftp/) - for uploading SpaceAPI-formatted status to some host +- [paramiko](https://pypi.org/project/paramiko/) - for uploading SpaceAPI-formatted status to some host - optional runtime dependency, not needed unless you set SFTP SpaceAPI upload to true All dependencies can be installed on Ubuntu or Debian/Raspbian via: diff --git a/brmdoor_nfc_daemon.py b/brmdoor_nfc_daemon.py index 774221d..1d724a3 100755 --- a/brmdoor_nfc_daemon.py +++ b/brmdoor_nfc_daemon.py @@ -382,21 +382,52 @@ class SpaceAPIUploader(object): :param isOpen - whether space is opened. :raises paramiko.ssh_exception.SSHException when upload fails (timeout, can't connect, host key mismatch...) """ - import pysftp + import paramiko dirname, targetFname = os.path.split(self.config.sftpDestFile) spaceApiJson = json.load(file("spaceapi_template.json")) spaceApiJson["state"] = {"open": isOpen, "lastchange": time.time()} with tempfile.NamedTemporaryFile() as tf: + log_paramiko = logging.getLogger("paramiko") + if log_paramiko: + log_paramiko.setLevel(logging.WARNING) + json.dump(spaceApiJson, tf, indent=4, ensure_ascii=True, encoding='utf-8') tf.flush() localFilename = tf.name - with pysftp.Connection(self.config.sftpHost, username=self.config.sftpUsername, port=self.config.sftpPort, - private_key=self.config.sftpKey) as sftp: - sftp.timeout = 15 - with sftp.cd(dirname): - sftp.put(localFilename, remotepath=targetFname) + + pkey = None + ssh = None + sftp = None + + for pkey_class in (paramiko.RSAKey, paramiko.DSSKey, paramiko.ECDSAKey, paramiko.Ed25519Key): + try: + pkey = pkey_class.from_private_key_file(self.config.sftpKey) + logging.debug("Loaded SSH key of type %s", pkey_class) + break + except paramiko.ssh_exception.SSHException: + pass + + if pkey is None: + logging.error("Failed to load private SSH key", self.config.sftpKey) + raise paramiko.ssh_exception.SSHException("Can't read private SSH key") + + try: + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(self.config.sftpHost, username=self.config.sftpUsername, pkey=pkey, timeout=20) + sftp = ssh.open_sftp() + + sftp.chdir(dirname) + sftp.put(localFilename, targetFname) + + finally: + if sftp: + sftp.close() + + if ssh: + ssh.close() class OpenSwitchThread(threading.Thread):