Initial commit

This commit is contained in:
Ed 2023-07-11 21:49:11 +02:00
commit 384fb4805f
3 changed files with 120 additions and 0 deletions

13
config.example.py Normal file
View File

@ -0,0 +1,13 @@
# Where should i send the readings?
SERVER = 'http://127.0.0.1:8188/nfc/read'
# Should i verify the nfc tags (authenticating password)?
SECURE = False
# Is this a boopbox?
BOOPBOX_ID = None
# This is used to verify the tags
SALT = b"This is used to generate the password that will be used to encrypt/verify each tag"
ITERATIONS = 10000

91
nfc_reader.py Normal file
View File

@ -0,0 +1,91 @@
import nfc
from nfc.clf import RemoteTarget
import httpx
import logging
import binascii
from base64 import b16encode
from os.path import join
from config import *
from time import sleep
from hashlib import sha3_512
import coloredlogs
coloredlogs.install()
def password(tag):
x = tag.identifier
for i in range(ITERATIONS):
x = sha3_512(x + SALT).digest()
return x[:6]
CONFIG_PAGES = {
"NXP NTAG213": (0x29, 0x2A, 0x2B, 0x2C),
"NXP NTAG215": (0x83, 0x84, 0x85, 0x86),
"NXP NTAG216": (0xE3, 0xE4, 0xE5, 0xE6),
}
if __name__ == '__main__':
log = logging.getLogger('nfc')
clf = nfc.ContactlessFrontend()
try:
assert clf.open('tty:USB0:pn532')
except TimeoutError:
log.error('Got TimeoutError on reader connection attempt.')
except AssertionError:
log.error('There is no reader connected.')
exit()
# Set baud rate to 115200 to prevent reader from dying
clf.device.chipset.set_serial_baudrate(115200)
clf.device.chipset.transport.baudrate = 115200
try:
while 1:
target = clf.sense(RemoteTarget('106A'), iterations=10, delay=0.1)
if not target: continue
count = None
tag_id = target.sdd_res
is_secure = False
if not BOOPBOX_ID:
try:
tag = nfc.tag.activate(clf, target)
except:
log.error(f"TAG {tag_id} is gone.")
continue
try:
if SECURE and tag.product == "NXP NTAG215":
has_password = tag.read(CONFIG_PAGES[tag.product][0])[3] < 0xFF
if has_password:
psw = password(tag)
if tag.authenticate(psw):
is_secure = True
except:
log.error("Couldn't verify tag.")
# Try to get the read count as well.
try:
if tag.product == "NXP NTAG215":
count = tag.transceive(binascii.unhexlify('3902'), timeout=0.05) # READ_CNT command
count = int.from_bytes(count, byteorder='little')
except nfc.tag.tt2.Type2TagCommandError:
log.error("Tag count is enabled, couldn't get a response")
except AttributeError:
log.error("Lost connection to tag...")
try:
res = httpx.post(SERVER, json={'id': b16encode(tag_id).decode(), 'is_secure': is_secure, 'count': count, 'boopbox_id': BOOPBOX_ID})
except Exception as e:
log.error(e)
else:
log.info(f"Booped {b16encode(tag_id).decode()} (secure = {is_secure}) (count = {count})")
while clf.sense(RemoteTarget('106A')): sleep(0.1)
except KeyboardInterrupt:
clf.close()

16
readme.md Normal file
View File

@ -0,0 +1,16 @@
# Boopbox/POS NFC Client
This is a simple script that implements an "HTTP Client" that will send every read NFC tag to an http server.
Additionally, it is possible to set the reader as "secure" and this will - other than verify the id of the tag - also try to authenticate the originality of it.
## What reader?
The used reader is a raw PN532 connected through a CH430 to usb. The baud rate is immediately set to 115200 to prevent the reader from entering a "locked" state in case of connection loss with the nfcpy library. Setting the baud rate to 115200 makes sure the chip is always available to init again.
## How do you generate the password?
The password is generated by hashing the NFC identifier with a SALT numerous times, and then truncating the resulting hash. The nfc cards we used (NDEF213, 215) only supported 6 byte passwords.
## What is the read count?
During provisioning we enabled the read count of the NFC tags. This is a non-tearing read-only register which gets increased by 1 every time there is a successful ACTIVATE command (aka the chip is read correctly). This was used for statistical and anti-tampering purposes.