From 384fb4805fad471871ef5a2b7cb641cd8a4ae25f Mon Sep 17 00:00:00 2001 From: Ed Date: Tue, 11 Jul 2023 21:49:11 +0200 Subject: [PATCH] Initial commit --- config.example.py | 13 +++++++ nfc_reader.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 16 +++++++++ 3 files changed, 120 insertions(+) create mode 100644 config.example.py create mode 100644 nfc_reader.py create mode 100644 readme.md diff --git a/config.example.py b/config.example.py new file mode 100644 index 0000000..fbc9fb4 --- /dev/null +++ b/config.example.py @@ -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 diff --git a/nfc_reader.py b/nfc_reader.py new file mode 100644 index 0000000..e7cecc6 --- /dev/null +++ b/nfc_reader.py @@ -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() + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..429cc60 --- /dev/null +++ b/readme.md @@ -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.