-
Notifications
You must be signed in to change notification settings - Fork 0
/
NFCReader.py
172 lines (144 loc) · 5.86 KB
/
NFCReader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import os
import threading
from typing import List
from kivy import Logger
from smartcard import util
from smartcard.CardRequest import CardRequest
from smartcard.CardType import AnyCardType
from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException, NoCardException
from smartcard.pcsc.PCSCExceptions import EstablishContextException
from smartcard.scard import *
from PythonNFCReader.CardListener import CardListener
# NFCReader class
# Is a blocking class until a card is presented to the card reader
#
class NFCReader:
# Command to get UID (Serial number) of the presented NFC chip
__GETUIDCOMMAND = "FF CA 00 00 00"
# Card response values (in HEX)
__uid = None
__status = None
# Reference to the connection of the card
__service = None
__request = None
#
# Resets variables before listening to establish a new connection
#
def __reset_local_vars(self):
# If they are None, no work is required, else set them to None
if self.__uid is not None and self.__status is not None and self.__service is not None:
self.__service = None
self.__uid = None
self.__status = None
self.__request = None
#
# Enables listening to NFC chip on card reader
# var card_type: type of cards listening for
#
def enable_card_listener(self, card_type=AnyCardType()):
# Check if resetting vars is required and if so, do so
self.__reset_local_vars()
try:
# Setup a cardRequest: waiting infinitely for any card type
self.__request = CardRequest(timeout=INFINITE, cardType=card_type)
# Once a card is presented, initialize variables to setup connection
self.__service = self.__request.waitforcard()
self.__on_card_presented()
except CardRequestTimeoutException as e:
Logger.critical("This should not happen: Timelimit reached for card presenting")
os._exit(1)
except EstablishContextException as e2:
# There is no smartcard reader found so we just ignore it.
pass
#
# Callback function for when a card is presented
# will establish a connection and attempt to send a command
#
# var service: the reference to the card
#
def __on_card_presented(self):
# Setup connection and connect to the provided card
connection = self.__service.connection
try:
connection.connect()
# Send command to acquired connection
self.__send_command(connection, self.__GETUIDCOMMAND)
except (CardConnectionException, NoCardException) as e:
Logger.warn("Could not connect to card when trying to read UID!")
return
#
# Send @command over @connection
# Used to execute commands and store response values in class variables
#
# var connection: the connection over which to send the command
# var command: the command to send over the aforementioned connection
#
def __send_command(self, connection, command):
# Send GET_UID_COMMAND to the card reader
get_uid_cmd = util.toBytes(command)
# Retrieve response from APNU request
data, sw1, sw2 = connection.transmit(get_uid_cmd)
# Convert byte response data to HEX values
self.__uid = util.toHexString(data)
# Convert byte response values identifying success/failure to HEX values
self.__status = util.toHexString([sw1, sw2])
# Called upon creation of the class
def __init__(self):
# Specify card acceptance: any card accepted by the card reader
self.enable_card_listener()
# Print results
# self.debug_print()
#
# Used to print the class' member variables
# DEBUGGING PURPOSE
#
def debug_print(self):
print("UID of card: " + self.__uid)
print("Status of request: " + self.__status)
#
# Get reference to the card
# DEBUGGING PURPOSE
#
def debug_get_service(self):
return self.__service
#
# Get UID of card
#
def get_uid(self):
return self.__uid
class CardConnectionManager:
def __init__(self):
self.listeners: List[CardListener] = []
self.nfc_thread = None
self.listener_thread = None
self.nfc_reader = None
pass
def register_listener(self, card_listener: CardListener) -> None:
if card_listener is not None:
self.listeners.append(card_listener)
def start_nfc_reader(self):
# Create thread that will block while waiting for a card
self.nfc_thread = threading.Thread(name="NFC_reader",
target=self.__generate_nfc_reader, daemon=True)
self.nfc_thread.start()
# Create thread that will listen for the nfc_thread to finish
self.listener_thread = threading.Thread(name="NFC_reader_monitor",
target=self.__monitor_nfc_reader,
args=(self.nfc_thread,), daemon=True)
self.listener_thread.start()
def __generate_nfc_reader(self):
# Blocking call (as it will start waiting for a card)
self.nfc_reader = NFCReader()
def __monitor_nfc_reader(self, thread: threading.Thread) -> None:
# Wait for the thread to finish
thread.join()
uid = self.nfc_reader.get_uid()
# Check if we got a valid uid (of the card)
if uid is not None and uid != "":
# Notify listeners that we received a new card
self.__notify_listeners(self.nfc_reader.get_uid().replace(" ", ""))
# Start waiting for a new card after 1 second.
threading.Timer(1.0, self.start_nfc_reader).start()
def __notify_listeners(self, uid: str):
for listener in self.listeners:
listener.card_is_presented(uid)