~~META: status = active &relation firstimage = :project:pir.jpg ~~
For hackerspace more secure and without the annoyance of physical keys. DIY digital lock control, open/closed space status monitor and burglar alarm.
System architecture: Raspberry Pi + Adafruit PN532 shield. Supports ISO14443 cards (Mifare Classic, Desfire, Yubikey, you could even use Visa/Mastercard NFC payment cards; Android NFC Host Card Emulation could be used as well with some changes). Authentication: via UID, challenge-response with Yubikey Neo, signed public-key cryptography (Ed25519) messages with Desfire cards.
Executive summary, how to get in without a key - arrange registering your RFID card (ISO-14443A, e.g. Mifare Classic, Mifare DESFire) with council at the meetup (or over mail rada _at_ brmlab.cz). After adding your card to brmdoor DB, you will be able to unlock the door by placing the card next to the reader.
Communication is over SPI: both SEL0 and SEL1 are shorted which turns communication to be over SPI. Docs say SEL1 closed, board says both closed, both closed work.
Documentation of Adafruit PN532 shield (our revision is 1.2):
Source repository: https://github.com/hiviah/brmdoor_libnfc
Brmdoor was tried on:
Tested Raspbian 9 and 10 (Raspi OS 2021-03-04). Still works on 2023-09-17, when we needed to drop pysftp
and use paramiko
directly, allows to use ECDSA and Ed25519 keys.
Raspberry 1 and 2 - it's just slow, everything is slow there. Raspberry 3B+ or 4 with USB boot and USB flash instead of SDcard recommended.
Make sure you enable SPI in device tree or dtparam=spi=on
in /boot/config.txt
depending on Raspi model otherwise reader won't work. Raspi 3B+ boots from USB out of the box, 4B needs enabling it manually. Latest Raspbian does not enable ssh after install put empty file named ssh
in /boot
directory to enable.
Card list is on brmlab VPS (vps.brmlab.cz) are exported from JendaSAP, on /root/sap/cards.txt
.
Put the card next to the reader, then look into a log (on brmdoor raspi in /root/brmdoor_libnfc/brmdoor.log
) for Unknown UID
line.
Login to vps.brmlab.cz. In /root/sap/members
, find member's file and add line with the card UID (4, 7 or 10 byte UID), e.g.
card 0102ab89
On vps.brmlab.cz
in /root/sap
directory, run parse.py
script:
cd /root/sap parse.py
This will create cards.txt
. Copy the cards.txt
to brmdoor and run on brmdoor raspi:
cd brmdoor_libnfc/; ./import_jendasap_cards.py /path/to/cards.txt brmdoor.sqlite
No need to restart brmdoor daemon. Note that the import can take even a minute since the brmdoor Raspberry 1 is fucking slow.
members
directory on vps.brmlab.cz
.
In the members
directory, commit the changed card with
git commit -m "Added card for member Ctulhu" 1234_member_uid_file
This is similar to above, but has extra step in programming the Desfire.
Writing a signature on a Desfire card (can be done on raspi or using PN532 reader that is in the lab). You must know the private Ed25519 key matching the public key in brmdoor's config to be able to program the Desfire correctly.
./write_signed_ndef_on_desfire.py private_key_in_hex
Similar to above, but you add line “desfire” instead of “card” in JendaSAP:
desfire 04631982cc2280
The parse.py mentioned above will also create cards_desfire.txt
. Copy the cards_desfire.txt
to brmdoor and run on brmdoor raspi:
cd brmdoor_libnfc/; ./import_jendasap_cards.py --desfire /path/to/cards_desfire.txt brmdoor.sqlite
In general, set the timeout param in nfc_initiator_transceive_bytes
(last parameter) to something like 2000 (it's in milliseconds) in all libfreefare .c sources (it's 0 meaning infinite wait). Latest versions have it configurable without such hacks.
So apt-get source libfreefare
, unpack, patch, rebuild via
DEB_BUILD_OPTIONS='nostrip noopt debug' dpkg-buildpackage -b -uc -us
Link to zip with the rebuilt .deb files for current brmdoor (Raspian 9), just install with dpkg -i like usual:
fixed_desfire_timouts_libfreefare.zip
Latest versions of libfreefare have this fixed, there is either non-infinite timeout or configurable timeout in very latest versions.
Can be done with brmdoor, PN352 reader on PC or also on mobile app that can show NFC NDEF data:
cd brmdoor_libnfc/; ./test_nfc.py desfire-ndef4
You should see something like:
{"brmdoorSignature": "ebb7191d3a1133e375bd4bcfe9b271b8246d086c6c93f0753a3a09c6bbc253e7fc3e2fc34f3351989c8b31bd9937747e05173f9377f57a445e7f61c74b4d7388"}
If brmdoor stops responding for some reason, restart it using:
systemctl restart brmdoor.service
In order to get to brmdoor from outside, you'll need to find a hop server accessible from outside, like bbs.brmlab.cz
Sample entry in ~/.ssh/config
:
Host = bbs.brmlab.cz VerifyHostKeyDNS = yes User = root IdentityFile = ~/.ssh/your_key ForwardX11 = no UsePrivilegedPort = no Protocol 2 Host = brmdoor-external ProxyCommand = ssh bbs.brmlab.cz nc %h 22 Hostname = 192.168.77.30 Port = 22 User = root IdentityFile = ~/.ssh/your_key ForwardX11 = no UsePrivilegedPort = no Protocol 2
In brmdoor_nfc.config
, look at the section [open_switch]
. IRC must be enabled in [irc]
section for status
update to work.
The first part of the topic until |
character will be replaced by OPEN/CLOSED (if there is no pipe character,
OPEN/CLOSED will be prepended).
Configuration needs setting a file that is read once per second, open_value
determines which value means
“open”. Thus you can use any daemon/cron script/whatever that just writes a predetermined value to file which
will be read by brmdoor daemon.
For a simple switch (that just closes/opens the electrical connection) connected to GPIO PINs, you need to
configure a PIN in input mode and turn on internal pullup on the input PIN. An example of this is in the
brmdoor_start.sh
script below. Connect one cable of the switch to an input PIN and the other to the ground.
#!/bin/bash export PIN=22 if [ '!' -d /sys/class/gpio/gpio$PIN ]; then echo $PIN > /sys/class/gpio/export echo in > /sys/class/gpio/gpio$PIN/direction fi python -c "import wiringpi; wiringpi.wiringPiSetupGpio(); wiringpi.pinMode($PIN, wiringpi.INPUT); wiringpi.pullUpDnControl($PIN, wiringpi.PUD_UP)"
If the switch is in open position (connected to ground), the “open” value will be 0, the close value will be 1 (effect of the internal pull-up).
The numbering scheme is the same as in the lock configuration (BCM GPIO numbering,
https://projects.drogon.net/raspberry-pi/wiringpi/pins/). A copy of the mapping is in
gpio_vs_wiringpi_numbering_scheme.png
file) in case the webpage goes away.
Any IRC user in #brmlab
channel can change the topic by using the /topic
command. This value will stay
until the physical switch changes the value.
Currently since v 0.2 brmdoor can upload the status and information in SpaceAPI.net format.
The status JSON is mapped currently is mapped to https://brmlab.cz/spaceapi/status.json. The interesting non-static part of json is in under “state” key - “open” (boolean) and “lastchange” (Unix timestamp).
Currently to be compatible with old URL (https://status.brmlab.cz/brmd/brmstatus.json)
for the spaceAPI.net since we can't change it and can't submit new, there is alias
in /etc/apache2/sites-available/status.brmlab.cz.conf
to directory /home/brmdoor-web/spaceapi-status
where status file is
uploaded by brmdoor (it's over SFTP with internal-sftp chroot, ssh-key login, so that in case of compromise of brmdoor there
is not much the attacker could do with it (and password can't be bruteforced).
General GPIO PIN assignments are configurable, communication (SPI/I2C) must follow the definition in Raspberry Pi pinouts. Raspi pinouts for all versions: https://pi4j.com/1.2/pins/model-2b-rev1.html
(Follow whatever Rpi is installed for pinout as power sources keep killing them time to time)
Numbering scheme used by brmdoor for lock and open switch (based on BCM GPIO numbering, different from P1 header physical PIN numbers): https://projects.drogon.net/raspberry-pi/wiringpi/pins/
PIN assignemnts (physical PINs on P1 header, with BCM GPIO numbers used in config):
I'e bought a 16 GB USB flash that should theoretically be more stable than SD card.
I've copied the 32-GB card, extended the filesystem to 16 GB (from original 4 GB).
The copy is available in my trezor. I benchmarked SD card and the USB boot device, did not find any significant differences. We did thought with rainbof limit the max CPU/GPU freq.
To this day I am not sure whether that “hardened SD card” or my “hardened USB flash” will be a better choice.
Nevertheless, we have copies of all, so in case the SD card goes down, we still have USB flash with the system.
Jenda/Mrkva were politely asked whether they want to design a single board for all future brmdoors. Since there is like 10 parts and foundries will make the poplated boards at $10/piece.
Power source fried the Raspi 3B+ sometimes in 2022, replaced again with Raspi 2B
Added on left side is a stepdown that works with Raspi 3B+ power consumption.
Current brmdoor directory is `/root/brmdoor_libnfc` Config is in the file: `/root/brmdoor_libnfc/brmdoor_nfc.config` Full README is at https://github.com/hiviah/brmdoor_libnfc/blob/master/README.md A preferred way to add a card is to add it to JendaSAP, then import (see below). ## Import cards.txt from JendaSAP's cards.txt (WARNING: THIS WILL OVERWRITE THE TABLE WITH UIDs) cd brmdoor_libnfc/ ./import_jendasap_cards.py cards.txt brmdoor.sqlite If `brmdoor.sqlite` does not exist, it will be created. If it exists, the `authorized_uids` table will be replaced with UIDs/nick pairs from `cards.txt` (other tables are not touched). ## Adding card by UID Note: this is useful for adding card temporarily since import from JendaSAP will remove it - find out the UID: 1. put the card near the reader, 2. as root, look into the log, currently /root/brmdoor_libnfc/brmdoor.log - look into the log for the unknown UID, look for `Unknown UID` message - the following command will add a card as authenticated by UID, for other methods see full README cd brmdoor_libnfc/ ./brmdoor_adduser.py -c brmdoor_nfc.config -a uid 1234ABCD SomeUserName ## List authorized cards UIDs + nicks from DB sqlite3 ~/brmdoor_libnfc/brmdoor.sqlite 'select nick, uid_hex from authorized_uids;' ## Start/stop daemon systemctl start brmdoor.service systemctl stop brmdoor.service Systemd unit location: `/etc/systemd/system/brmdoor.service`
Source: brmdoor_libnfc
Requires libnfc, libfreefare and WiringPi2
Features:
Missing features:
Add following to your ~/.ssh/config, edit your key if necessary (the key needs to be added on brmbar and on brmdoor):
You need to have access to data.brmlab.cz
and brmdoor
as well.
You need ssh that supports ProxyJump
(should not be a problem unless you have reeeeeaaaaaly old system).
Host data.brmlab.cz User root Port 22 IdentityFile = ~/.ssh/whatever_your_key_is Protocol 2 Host = brmdoor-via-data ProxyJump = data.brmlab.cz Hostname = 192.168.77.30 Port = 22 User = root IdentityFile = ~/.ssh/whatever_your_key_is ForwardX11 = no UsePrivilegedPort = no Protocol 2
Then using
ssh brmdoor-via-data
will get proxyjump over data into brmdoor.