Initial commit
This commit is contained in:
commit
ec1d89298f
|
@ -0,0 +1,130 @@
|
|||
from time import time
|
||||
from base64 import b16encode
|
||||
from hashlib import sha3_512
|
||||
import os.path
|
||||
from security_const import *
|
||||
import logging
|
||||
import binascii
|
||||
import ndef
|
||||
from io import BytesIO
|
||||
from nfc.tag.tt2 import Type2TagCommandError
|
||||
|
||||
logging.basicConfig(level='INFO')
|
||||
log = logging.getLogger('badge')
|
||||
|
||||
START_PAGE = {
|
||||
"NXP NTAG213": 0x11,
|
||||
"NXP NTAG215": 0x6B,
|
||||
"NXP NTAG216": 0xCB
|
||||
}
|
||||
|
||||
USER_PAGE_BOUNDS = {
|
||||
"NXP NTAG213": (0x04, 0x27),
|
||||
"NXP NTAG215": (0x04, 0x81),
|
||||
"NXP NTAG216": (0x04, 0xE1),
|
||||
}
|
||||
|
||||
CONFIG_PAGES = {
|
||||
"NXP NTAG213": (0x29, 0x2A, 0x2B, 0x2C),
|
||||
"NXP NTAG215": (0x83, 0x84, 0x85, 0x86),
|
||||
"NXP NTAG216": (0xE3, 0xE4, 0xE5, 0xE6),
|
||||
}
|
||||
|
||||
class Badge:
|
||||
def __init__(self, tag, uuid=None):
|
||||
self.tag = tag
|
||||
self.uuid = uuid
|
||||
|
||||
def read_transaction(self):
|
||||
bounds = USER_PAGE_BOUNDS[self.tag.product]
|
||||
start_page = START_PAGE[self.tag.product] # We assume transactions are 22 pages wide
|
||||
|
||||
data = b""
|
||||
offset = 0
|
||||
|
||||
while 1:
|
||||
print("Reading", hex(start_page+offset), end=' ')
|
||||
|
||||
chunk = self.tag.read(start_page+offset)
|
||||
print(b16encode(chunk))
|
||||
if len(data) == 0:
|
||||
if bytes(chunk[:4]) != HEADER:
|
||||
print('header is', HEADER)
|
||||
print('chunk 4', bytes(chunk[:4]))
|
||||
print('Does not start with header :(')
|
||||
|
||||
data += chunk
|
||||
offset += 4
|
||||
|
||||
if FOOTER in chunk:
|
||||
data = data[:data.find(FOOTER)+4]
|
||||
break
|
||||
|
||||
return data
|
||||
|
||||
def write_transaction(self, tx, verify=True):
|
||||
bounds = USER_PAGE_BOUNDS[self.tag.product]
|
||||
start = START_PAGE[self.tag.product]
|
||||
|
||||
with BytesIO(tx.get_bytes()) as data:
|
||||
|
||||
offset = 0
|
||||
|
||||
# Write the transaction
|
||||
while 1:
|
||||
chunk = data.read(4)
|
||||
if not chunk: break
|
||||
|
||||
print("Writing", hex(start+offset), b16encode(chunk))
|
||||
assert bounds[0] <= start+offset <= bounds[1]
|
||||
|
||||
self.tag.write(start+offset, chunk)
|
||||
|
||||
offset += 1
|
||||
|
||||
# Read the transaction back
|
||||
if verify:
|
||||
print('expected:::', tx.get_bytes())
|
||||
print('read:::::', self.read_transaction())
|
||||
assert tx.get_bytes() == self.read_transaction()
|
||||
|
||||
def enable_count(self):
|
||||
|
||||
page_addr = CONFIG_PAGES[self.tag.product][1]
|
||||
|
||||
page = self.tag.read(page_addr)[:4]
|
||||
if not page[0] & (1<<4):
|
||||
log.info("Enabling tag counter")
|
||||
page[0] |= 1 << 4 # Set NFC_CNT_EN = 1
|
||||
self.tag.write(page_addr, page)
|
||||
else:
|
||||
log.info("Tag counter is already enabled!")
|
||||
|
||||
try:
|
||||
count = self.tag.transceive(binascii.unhexlify('3902'), timeout=0.05) # READ_CNT command
|
||||
except Type2TagCommandError:
|
||||
log.error("Tag count is enabled, but didn't get any response anyway.")
|
||||
else:
|
||||
log.info(f"Tag count is {int.from_bytes(count, byteorder='little')}")
|
||||
|
||||
def protect(self):
|
||||
self.tag.protect(self.password())
|
||||
|
||||
def password(self, with_uuid=True):
|
||||
x = self.tag.identifier
|
||||
for i in range(ITERATIONS):
|
||||
x = sha3_512(x + (self.uuid if with_uuid and self.uuid else bytes(0)) + SALT).digest()
|
||||
return x[:6]
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
tag = SimpleNamespace()
|
||||
tag.identifier = bytes(8)
|
||||
|
||||
pt = Protect(tag, uuid=b"bye")
|
||||
start = time()
|
||||
print('with uuid', b16encode(pt.password()))
|
||||
print('without uuid', b16encode(pt.password(with_uuid=False)))
|
||||
print("took", (time()-start)*1000, 'ms')
|
|
@ -0,0 +1,147 @@
|
|||
import nfc
|
||||
from nfc.clf import RemoteTarget
|
||||
import logging
|
||||
from time import time
|
||||
import os.path
|
||||
import coloredlogs
|
||||
coloredlogs.install()
|
||||
from base64 import b16encode, b16decode
|
||||
import sys
|
||||
import ndef
|
||||
import binascii
|
||||
from badge import Badge
|
||||
from os import system
|
||||
|
||||
CONFIG_PAGES = {
|
||||
"NXP NTAG213": (0x29, 0x2A, 0x2B, 0x2C),
|
||||
"NXP NTAG215": (0x83, 0x84, 0x85, 0x86),
|
||||
"NXP NTAG216": (0xE3, 0xE4, 0xE5, 0xE6),
|
||||
}
|
||||
|
||||
session_start = time()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
log = logging.getLogger('nfc')
|
||||
|
||||
nfc_type = sys.argv[1]
|
||||
balance = 0
|
||||
if len(sys.argv) > 2:
|
||||
balance = sys.argv[2]
|
||||
elif nfc_type == 'coin':
|
||||
log.error("You cannot create empty coins.")
|
||||
exit()
|
||||
|
||||
clf = nfc.ContactlessFrontend()
|
||||
|
||||
log.info(f"Enrolling mode: {nfc_type}")
|
||||
log.info(f"Initial balance: {balance}")
|
||||
|
||||
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)
|
||||
if not target: continue
|
||||
|
||||
start = time()
|
||||
|
||||
tag = nfc.tag.activate(clf, target)
|
||||
if not tag:
|
||||
print("Tag was gone while writing.")
|
||||
break
|
||||
|
||||
if tag.product not in ["NXP NTAG215"]:
|
||||
raise Exception(f"This tag ({tag.product}) is not compatible with the barcard system.")
|
||||
|
||||
#print("\n".join(tag.dump()))
|
||||
#break
|
||||
|
||||
badge = Badge(tag)
|
||||
|
||||
password = badge.password()
|
||||
try:
|
||||
has_password = tag.read(CONFIG_PAGES[tag.product][0])[3] < 0xFF
|
||||
except:
|
||||
break
|
||||
|
||||
if has_password:
|
||||
log.warning("TAG has a password already! Trying to re-enroll anyway.")
|
||||
tag.authenticate(password)
|
||||
|
||||
#tx = Transaction(tag_id=tag.identifier)
|
||||
|
||||
try:
|
||||
# Disable the mirror, for now
|
||||
page_addr = CONFIG_PAGES[tag.product][0]
|
||||
page = tag.read(page_addr)[:4]
|
||||
page[0] &= 0b111111 # Disable UID + COUNT mirror
|
||||
tag.write(page_addr, page)
|
||||
except:
|
||||
break
|
||||
|
||||
# Activate it again
|
||||
tag = nfc.tag.activate(clf, target)
|
||||
if has_password:
|
||||
tag.authenticate(password)
|
||||
|
||||
# Write the correct url depending on the type of wanted badge
|
||||
if nfc_type == 'badge':
|
||||
url = "https://go.foxo.me/fz23/bxxxxxxxxxxxxxxxxxxxxx"
|
||||
else:
|
||||
url = f"https://go.foxo.me/fz23/cxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
tag.format()
|
||||
tag.ndef.records = [ndef.uri.UriRecord(url), ]
|
||||
if tag.ndef.records[0].uri != url:
|
||||
raise Exception("Written uri is different than expected.")
|
||||
|
||||
boundary = tag.read(0x08)
|
||||
if not boundary.startswith(b"e/fz23/"):
|
||||
raise Exception(f"This tag does not seem to correctly store ndef data. Got wrong {boundary}")
|
||||
|
||||
try:
|
||||
page_addr = CONFIG_PAGES[tag.product][0]
|
||||
page = tag.read(page_addr)[:4]
|
||||
page[0] |= 0b11 << 6 # Enable UID + COUNT mirror
|
||||
if tag.product == 'NXP NTAG215':
|
||||
page[2] = 0x0A # Location of UID + COUNT
|
||||
elif tag.product == 'NXP NTAG213':
|
||||
page[2] = 0x0B
|
||||
tag.write(page_addr, page)
|
||||
log.info("UID + COUNT mirror enabled.")
|
||||
except:
|
||||
raise
|
||||
break
|
||||
|
||||
print('EC')
|
||||
badge.enable_count()
|
||||
tag = nfc.tag.activate(clf, target)
|
||||
if not has_password:
|
||||
print('PRT')
|
||||
badge.protect()
|
||||
|
||||
with open(f'log_{int(session_start)}.txt', 'a') as f:
|
||||
f.write(f"{b16encode(tag.identifier).decode()},{nfc_type},{balance}\n")
|
||||
|
||||
system('mpv success.wav')
|
||||
log.info(f"{time()-start:.2f} tag write done.")
|
||||
while clf.sense(RemoteTarget('106A')): pass
|
||||
|
||||
system
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
clf.close()
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
# ⚠️ DO NOT USE THIS! READ BELOW! ⚠️
|
||||
|
||||
Due to a flaw of nfcpy (or maybe not?) tags enrolled by this script will have the capability bit set to 0x31. **This for some reason makes the NDEF payload only compatible with Android phones**!
|
||||
|
||||
This script can be used to enroll your NFC tags in the badge system. Effectively, it will:
|
||||
|
||||
1. Enable the READ_CNT flag, to enable the read counter
|
||||
2. Add a NDEF Uri so that the badge can be scanned by attendants
|
||||
3. Protect the badge with a password, generated uniquely for every tag
|
||||
4. Log the UID and badge type, so you can import it in your backend
|
||||
|
||||
## Requirements
|
||||
|
||||
1. Check the requirements.txt file for python lib requirements
|
||||
2. Install mpv to play a sound at the end of every succesfull write, or don't (and remove the row)
|
||||
3. Change the SALT to a very secure password and adjust the iteration count to a sensible number for your computer
|
||||
4. Connect a PN532 scanner via USB
|
||||
5. Enjoy!
|
||||
|
||||
## Run the script
|
||||
|
||||
```python3 enroll.py [badge/coin] [initial balance]```
|
||||
|
||||
The badge/coin changes the url, while initial balance changes the column on the data export (the log_*.txt files)
|
||||
|
||||
## The ASCII mirror!
|
||||
|
||||
You will notice that every badge has a generic "https://go.foxo.me/fz23/bxxxxxxxxxxxxxxxxxxxxx" url written to it.
|
||||
This is on purpose since after this write the UID + COUNT mirror is enabled.
|
||||
This is a function of the NDEF tags that lets you "mirror" various registers in ascii format to other arbitrary addresses.
|
||||
The code following the ndef part will effectively make so that when you read the link will actually become `"/fz23/b[TAG IDENTIFIER]x[READ COUNT]"` (e.g. `"/fz23/b048E77929A7181x0000EB"`) without any manual intervention and in a completely tear-free way.
|
||||
|
||||
## The "toys" folder
|
||||
|
||||
Inside of the toys folder there are two files which were a proof of concept for the storage of monetary transactions inside of NFC tags, for use to buy drinks and similar. Eventually, the system was moved completely server-side (due to slowness in writing the cards and risk of tearing), and the entire system scrapped.
|
||||
|
||||
The system consisted in ed25519-based transaction signatures, where offline clients (aka the PoS) would be able to verify and write transactions. The cards would actually store two transactions and then change a pointer atomically to prevent half-written transaction from being considered.
|
||||
|
||||
The transaction format is as follows:
|
||||
|
||||
```
|
||||
[ CA$H ]
|
||||
[signature ] * 16
|
||||
[load][used]
|
||||
[keyi][txid]
|
||||
[timestamp ]
|
||||
[ uuid ]
|
||||
[ F0X0 ]
|
||||
```
|
||||
|
||||
- CA$H and F0X0 are header and footer of the transaction (4 bytes each)
|
||||
- signature = ed25519 signature of the transaction (4 * 16 = 64 bytes)
|
||||
- load (2 bytes) + used (2 bytes) = the amount of loaded and used money. These two values can only increase and the balance is calculated from subtracting them so that "replay" attacks are harder
|
||||
- keyi = id of the key used to sign the transaction (2 bytes)
|
||||
- txid = increasing id of the transaction (2 bytes)
|
||||
- timestamp = unix ts of the transaction (4 bytes)
|
||||
- uuid = a custom uuid to identify the card (this was added because at one point we accidentally bought counterfeit cards with duplicated NFC identifiers)
|
|
@ -0,0 +1,2 @@
|
|||
coloredlogs
|
||||
nfcpy
|
|
@ -0,0 +1,2 @@
|
|||
SALT = b"This is the salt to generate the badge password :)"
|
||||
ITERATIONS = 10000
|
Binary file not shown.
|
@ -0,0 +1,176 @@
|
|||
import nfc
|
||||
import sys
|
||||
from nfc.clf import RemoteTarget
|
||||
from nfc.tag.tt2 import Type2TagCommandError
|
||||
import logging
|
||||
from base64 import b64encode, b16encode
|
||||
from os.path import join
|
||||
from time import time
|
||||
from hashlib import sha3_256
|
||||
import ndef
|
||||
import coloredlogs
|
||||
import binascii
|
||||
import os
|
||||
import json
|
||||
from colorama import init, Fore, Back, Style
|
||||
|
||||
coloredlogs.install(fmt='%(levelname)s %(message)s')
|
||||
init()
|
||||
|
||||
uids = json.load(open('uids.json'))
|
||||
|
||||
def get_hash(payload, count=10000, salt=b"x!"):
|
||||
data = sha3_256(payload + (salt or bytes(0)))
|
||||
for _ in range(count):
|
||||
data = sha3_256(data.digest() + (salt or bytes(0)))
|
||||
return data.digest()
|
||||
|
||||
def activate_and_unlock(*args, **kwargs):
|
||||
tag = nfc.tag.activate(*args, **kwargs)
|
||||
tag.has_password = False
|
||||
assert tag.signature
|
||||
if not tag.ndef.is_writeable:
|
||||
tag.has_password = True
|
||||
unlock_result = tag.authenticate(password=get_hash(tag.identifier+tag.signature))
|
||||
if not unlock_result:
|
||||
log.error("This tag is locked and password is incorrect!")
|
||||
return
|
||||
return tag
|
||||
|
||||
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()
|
||||
|
||||
clf.device.chipset.set_serial_baudrate(115200)
|
||||
clf.device.chipset.transport.baudrate = 115200
|
||||
log.info(f"Setting device baud rate to {clf.device.chipset.transport.baudrate}.")
|
||||
|
||||
print(Style.BRIGHT + "What mode would you like to work in?\ns = single enroll\nm = mass enroll\n>>> ", end='')
|
||||
mode = input()[0]
|
||||
|
||||
if mode == 'm':
|
||||
print(Style.BRIGHT + "Choose the number from where to begin enrolling. To stop enrolling, just use CTRL+C to exit the program.\n>>> ", end='')
|
||||
|
||||
try:
|
||||
uid = int(input())
|
||||
except KeyboardInterrupt: exit()
|
||||
except:
|
||||
log.error("The supplied number is not valid.")
|
||||
exit()
|
||||
print(Fore.GREEN + "✔ Mass enroll mode" + Style.RESET_ALL)
|
||||
else:
|
||||
print(Fore.GREEN + "✔ Single enroll mode" + Style.RESET_ALL)
|
||||
|
||||
try:
|
||||
while 1:
|
||||
|
||||
# Ask for a number if in single enroll mode.
|
||||
if mode == 's':
|
||||
while 1:
|
||||
print(Style.BRIGHT + "Input the attendee number\n>>> ", end='')
|
||||
try:
|
||||
uid = int(input())
|
||||
except KeyboardInterrupt: exit()
|
||||
except:
|
||||
log.error("The supplied number is not valid.")
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
user_id = uids[str(uid)][0]
|
||||
url = "http://go.foxo.me/" + user_id
|
||||
|
||||
print(Fore.GREEN + "Attendee info:")
|
||||
print(f"NAME: {uids[str(uid)][1]}")
|
||||
print(f" INT: {uid}")
|
||||
print(f"CODE: {user_id}\n")
|
||||
|
||||
print(Fore.YELLOW + "⌛ Waiting for nfc tag", end='')
|
||||
|
||||
# Wait for a successful reading
|
||||
while 1:
|
||||
target = clf.sense(RemoteTarget('106A'), iterations=10)
|
||||
if not target:
|
||||
print('.', end='')
|
||||
sys.stdout.flush()
|
||||
continue
|
||||
break
|
||||
|
||||
print(Style.RESET_ALL)
|
||||
|
||||
# Tag was found, write the details
|
||||
start = time()
|
||||
tag = activate_and_unlock(clf, target)
|
||||
|
||||
if not tag:
|
||||
log.error("Tag was gone mid-write.")
|
||||
continue
|
||||
|
||||
if tag.product != 'NXP NTAG215':
|
||||
log.error("Incorrect tag type detected! Tag must be a NXP 215!")
|
||||
while clf.sense(RemoteTarget('106A')): pass
|
||||
continue
|
||||
|
||||
if not tag.signature:
|
||||
log.error("Tag signature was not read correctly. Try again.")
|
||||
while clf.sense(RemoteTarget('106A')): pass
|
||||
continue
|
||||
|
||||
password = get_hash(tag.identifier+tag.signature, 10000)[:6]
|
||||
|
||||
# If tag count is disabled, enable it
|
||||
try:
|
||||
count = tag.transceive(binascii.unhexlify('3902'), timeout=0.05) # READ_CNT command
|
||||
except Type2TagCommandError:
|
||||
page = tag.read(0x84)[:4]
|
||||
if not page[0] & (1<<4):
|
||||
log.info("Enabling tag counter")
|
||||
page[0] |= 1 << 4 # Set NFC_CNT_EN = 1
|
||||
self.tag.write(0x84, page)
|
||||
|
||||
tag = activate_and_unlock(clf, target)
|
||||
count = tag.transceive(binascii.unhexlify('3902'), timeout=0.05) # READ_CNT command
|
||||
|
||||
print(Fore.CYAN+Style.BRIGHT)
|
||||
print(f"UUID: {b16encode(tag.identifier).decode()}")
|
||||
print(f"SIGN: {b16encode(tag.signature).decode()}")
|
||||
print(f"PASS: {b16encode(password[:4]).decode()}")
|
||||
print(f"PWCK: {b16encode(password[4:]).decode()}")
|
||||
print(" CNT:", int.from_bytes(count, byteorder='little'))
|
||||
print(Style.RESET_ALL)
|
||||
|
||||
tag.ndef.records = [ndef.UriRecord(url), ]
|
||||
try:
|
||||
assert tag.ndef.has_changed is False
|
||||
except AssertionError:
|
||||
log.error("NDEF data was written incorrectly!")
|
||||
while clf.sense(RemoteTarget('106A')): pass
|
||||
continue
|
||||
else:
|
||||
log.info("Writing URL")
|
||||
|
||||
if not tag.has_password:
|
||||
log.info("Setting password")
|
||||
tag.protect(password=password, read_protect=False, protect_from=0)
|
||||
|
||||
print(Back.GREEN + Fore.BLACK + f"\n✔ Badge enrolled with success in {time()-start:.2f}s!" + Style.RESET_ALL)
|
||||
uid += 1
|
||||
|
||||
while clf.sense(RemoteTarget('106A')): pass
|
||||
|
||||
os.system('clear')
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
clf.close()
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
from time import time
|
||||
from base64 import b16encode
|
||||
import os.path
|
||||
import libnacl.utils, libnacl.sign
|
||||
from transaction_const import *
|
||||
from io import BytesIO
|
||||
from hashlib import sha3_256
|
||||
import logging
|
||||
from random import randint
|
||||
|
||||
logging.basicConfig(level='DEBUG')
|
||||
log = logging.getLogger('tx')
|
||||
|
||||
'''
|
||||
|
||||
[ CA$H ]
|
||||
[signature ] * 16
|
||||
[load][used]
|
||||
[keyi][txid]
|
||||
[timestamp ]
|
||||
[ uuid ]
|
||||
[ F0X0 ]
|
||||
|
||||
|
||||
21*4 = 84bytes
|
||||
76 bytes
|
||||
|
||||
'''
|
||||
|
||||
FIELDS = {
|
||||
'signature': (0, 63),
|
||||
'enc_key': 64,
|
||||
'key_id': 65,
|
||||
'load_amt': (66, 67),
|
||||
'used_amt': (68, 69),
|
||||
'tx_id': (70, 71),
|
||||
'ts': (72, 75),
|
||||
'uuid': (76, 79)
|
||||
}
|
||||
|
||||
class Transaction:
|
||||
def __init__(self, tag_id, nfc_bytes=None):
|
||||
|
||||
# These are needed for proper randomness
|
||||
self.tag_id = tag_id
|
||||
|
||||
self.data = bytearray(max([(x[1] if isinstance(x, tuple) else x) for x in FIELDS.values()])+1)
|
||||
|
||||
self.set_field('key_id', KEY_ID)
|
||||
self.set_field('ts', time())
|
||||
self.set_field('uuid', libnacl.randombytes(4))
|
||||
|
||||
if nfc_bytes:
|
||||
self.load(nfc_bytes)
|
||||
|
||||
def set_field(self, fid, val):
|
||||
|
||||
if isinstance(FIELDS[fid], int):
|
||||
flen = 1
|
||||
sbyte = FIELDS[fid]
|
||||
else:
|
||||
flen = 1 + FIELDS[fid][1] - FIELDS[fid][0]
|
||||
sbyte = FIELDS[fid][0]
|
||||
|
||||
if isinstance(val, float):
|
||||
val = int(val)
|
||||
|
||||
if isinstance(val, int):
|
||||
val = val.to_bytes(flen)
|
||||
|
||||
assert len(val) <= flen
|
||||
|
||||
if flen > 1:
|
||||
log.info(f"Writing {fid} at bytes {sbyte}:{sbyte+flen-1}: 0x{b16encode(val).decode()}")
|
||||
self.data[sbyte:sbyte+flen] = val
|
||||
else:
|
||||
log.info(f"Writing {fid} at byte {sbyte}: 0x{b16encode(val).decode()}")
|
||||
self.data[sbyte] = val[0]
|
||||
|
||||
def get_field(self, fid):
|
||||
if isinstance(FIELDS[fid], int):
|
||||
flen = 1
|
||||
sbyte = FIELDS[fid]
|
||||
else:
|
||||
flen = 1 + FIELDS[fid][1] - FIELDS[fid][0]
|
||||
sbyte = FIELDS[fid][0]
|
||||
|
||||
if flen > 1:
|
||||
val = bytes(self.data[sbyte:sbyte+flen])
|
||||
else:
|
||||
val = self.data[sbyte]
|
||||
|
||||
return val
|
||||
|
||||
def load(self, data):
|
||||
if data.startswith(HEADER):
|
||||
data = data[len(HEADER):]
|
||||
|
||||
if data.find(FOOTER):
|
||||
data = data[:-data.find(FOOTER)]
|
||||
|
||||
self.data = data
|
||||
|
||||
def sign(self):
|
||||
self.set_field('key_id', KEY_ID)
|
||||
signer = libnacl.sign.Signer(KEY_SEED)
|
||||
signature = signer.signature(bytes(self.data[FIELDS['signature'][1]+1:]+self.tag_id))
|
||||
self.set_field('signature', signature)
|
||||
|
||||
log.info("Signed message with success!")
|
||||
|
||||
self.verify()
|
||||
|
||||
def get_bytes(self):
|
||||
return HEADER + self.data + FOOTER
|
||||
|
||||
# TODO: implement these
|
||||
def topup(self, amt):
|
||||
self.load_amount += amt
|
||||
self.tx_bytes
|
||||
|
||||
def spend(self, amt):
|
||||
self.used_amount += amt
|
||||
|
||||
def verify(self):
|
||||
verifier = libnacl.sign.Verifier(KEYS[self.get_field('key_id')])
|
||||
verifier.verify(bytes(self.data + self.tag_id))
|
||||
log.info("Signature has passed!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Testing transaction.")
|
||||
tx = Transaction(tag_id=bytes(8))
|
||||
tx.sign()
|
||||
|
||||
#for fname in FIELDS:
|
||||
# print(' ', fname, tx.get_field(fname), 'len')
|
||||
|
||||
#res = tx.get_bytes()
|
||||
#tx.verify()
|
||||
|
||||
#tx = Transaction(tag_id=bytes(8), nfc_bytes=res)
|
||||
#res2 = tx.get_bytes()
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue