mirror of
https://github.com/brmlab/brmdoor_libnfc.git
synced 2025-06-08 08:34:00 +02:00
Change tabs to 4 spaces in python files
This commit is contained in:
parent
5d41737f80
commit
2482462a73
6 changed files with 420 additions and 420 deletions
|
@ -12,77 +12,77 @@ from optparse import OptionParser
|
||||||
from brmdoor_nfc_daemon import BrmdoorConfig
|
from brmdoor_nfc_daemon import BrmdoorConfig
|
||||||
|
|
||||||
def addUidAuth(cursor, uid_hex, nick):
|
def addUidAuth(cursor, uid_hex, nick):
|
||||||
"""
|
"""
|
||||||
Add user authenticated by UID. UID should be in hex, 4, 7 or 10 bytes long.
|
Add user authenticated by UID. UID should be in hex, 4, 7 or 10 bytes long.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
uid_hex.decode("hex")
|
uid_hex.decode("hex")
|
||||||
sql = """INSERT INTO authorized_uids
|
sql = """INSERT INTO authorized_uids
|
||||||
(uid_hex, nick)
|
(uid_hex, nick)
|
||||||
values (?, ?)
|
values (?, ?)
|
||||||
"""
|
"""
|
||||||
sql_data = (uid_hex, nick)
|
sql_data = (uid_hex, nick)
|
||||||
cursor.execute(sql, sql_data)
|
cursor.execute(sql, sql_data)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
print >> sys.stderr, "UID must be in proper hex encoding"
|
print >> sys.stderr, "UID must be in proper hex encoding"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def addHmacAuth(cursor, uid_hex, nick, key_hex):
|
def addHmacAuth(cursor, uid_hex, nick, key_hex):
|
||||||
"""
|
"""
|
||||||
Add user authenticated by Yubikey HMAC-SHA1. UID should be in hex, 4, 7
|
Add user authenticated by Yubikey HMAC-SHA1. UID should be in hex, 4, 7
|
||||||
or 10 bytes long. HMAC key in key_hex must be exactly 20 bytes in hex.
|
or 10 bytes long. HMAC key in key_hex must be exactly 20 bytes in hex.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
uid_hex.decode("hex")
|
uid_hex.decode("hex")
|
||||||
if len(key_hex.decode("hex")) != 20:
|
if len(key_hex.decode("hex")) != 20:
|
||||||
print >> sys.stderr, "Key must be exactly 20 bytes long!"
|
print >> sys.stderr, "Key must be exactly 20 bytes long!"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
sql = """INSERT INTO authorized_hmac_keys
|
sql = """INSERT INTO authorized_hmac_keys
|
||||||
(uid_hex, nick, key_hex)
|
(uid_hex, nick, key_hex)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
"""
|
"""
|
||||||
sql_data = (uid_hex, nick, key_hex)
|
sql_data = (uid_hex, nick, key_hex)
|
||||||
cursor.execute(sql, sql_data)
|
cursor.execute(sql, sql_data)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
print >> sys.stderr, "UID and key must be in proper hex encoding"
|
print >> sys.stderr, "UID and key must be in proper hex encoding"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
parser.add_option("-c", "--config", action="store", type="string", dest="config",
|
parser.add_option("-c", "--config", action="store", type="string", dest="config",
|
||||||
help="Configuration file")
|
help="Configuration file")
|
||||||
parser.add_option("-a", "--authtype", action="store", type="string", dest="authtype",
|
parser.add_option("-a", "--authtype", action="store", type="string", dest="authtype",
|
||||||
help="Authenthication type - uid or hmac")
|
help="Authenthication type - uid or hmac")
|
||||||
(opts, args) = parser.parse_args()
|
(opts, args) = parser.parse_args()
|
||||||
|
|
||||||
if opts.config is None:
|
if opts.config is None:
|
||||||
print >> sys.stderr, "You must specify config file via the -c option!"
|
print >> sys.stderr, "You must specify config file via the -c option!"
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if opts.authtype not in ["uid", "hmac"]:
|
if opts.authtype not in ["uid", "hmac"]:
|
||||||
print >> sys.stderr, "You must specify authentication type via -a option!"
|
print >> sys.stderr, "You must specify authentication type via -a option!"
|
||||||
print >> sys.stderr, "Acceptable choices: uid, hmac"
|
print >> sys.stderr, "Acceptable choices: uid, hmac"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
config = BrmdoorConfig(opts.config)
|
config = BrmdoorConfig(opts.config)
|
||||||
conn = sqlite3.connect(config.authDbFilename)
|
conn = sqlite3.connect(config.authDbFilename)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
if opts.authtype == "uid":
|
if opts.authtype == "uid":
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
print >> sys.stderr, "You must two additional arguments, hex UID and nick"
|
print >> sys.stderr, "You must two additional arguments, hex UID and nick"
|
||||||
print >> sys.stderr, "Example:"
|
print >> sys.stderr, "Example:"
|
||||||
print >> sys.stderr, "brmdoor_adduser.py -c brmdoor.config -a uid 34795FCC SomeUserName"
|
print >> sys.stderr, "brmdoor_adduser.py -c brmdoor.config -a uid 34795FCC SomeUserName"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
addUidAuth(cursor, args[0], args[1])
|
addUidAuth(cursor, args[0], args[1])
|
||||||
elif opts.authtype == "hmac":
|
elif opts.authtype == "hmac":
|
||||||
if len(args) < 3:
|
if len(args) < 3:
|
||||||
print >> sys.stderr, "You must three additional arguments, hex UID and nick and hex key"
|
print >> sys.stderr, "You must three additional arguments, hex UID and nick and hex key"
|
||||||
print >> sys.stderr, "brmdoor_adduser.py -c brmdoor.config -a hmac 40795FCCAB0701 SomeUserName 000102030405060708090a0b0c0d0e0f31323334"
|
print >> sys.stderr, "brmdoor_adduser.py -c brmdoor.config -a hmac 40795FCCAB0701 SomeUserName 000102030405060708090a0b0c0d0e0f31323334"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
addHmacAuth(cursor, args[0], args[1], args[2])
|
addHmacAuth(cursor, args[0], args[1], args[2])
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
|
@ -8,150 +8,150 @@ from nfc_smartcard import NFCError
|
||||||
|
|
||||||
|
|
||||||
class UidRecord(object):
|
class UidRecord(object):
|
||||||
"""Represents UID<->nick pair"""
|
"""Represents UID<->nick pair"""
|
||||||
|
|
||||||
def __init__(self, uid_hex, nick):
|
def __init__(self, uid_hex, nick):
|
||||||
"""
|
"""
|
||||||
Create instance binding UID to nick. UIDs should be either 4, 7
|
Create instance binding UID to nick. UIDs should be either 4, 7
|
||||||
or 10 bytes long, but that's ISO14443 thing - this object has
|
or 10 bytes long, but that's ISO14443 thing - this object has
|
||||||
no such limitation.
|
no such limitation.
|
||||||
|
|
||||||
UID will be stored in uppercase hex, converted if necessary.
|
UID will be stored in uppercase hex, converted if necessary.
|
||||||
|
|
||||||
@param uid_hex: uid in hex
|
@param uid_hex: uid in hex
|
||||||
@param nick: nickname this UID belongs to
|
@param nick: nickname this UID belongs to
|
||||||
"""
|
"""
|
||||||
self.uid_hex = uid_hex.upper()
|
self.uid_hex = uid_hex.upper()
|
||||||
self.nick = nick
|
self.nick = nick
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "(uid: %s, nick: %s)" % (self.uid_hex, self.nick)
|
return "(uid: %s, nick: %s)" % (self.uid_hex, self.nick)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<UidRecord: uid: %s, nick: %s>" % \
|
return "<UidRecord: uid: %s, nick: %s>" % \
|
||||||
(repr(self.uid_hex), repr(self.nick))
|
(repr(self.uid_hex), repr(self.nick))
|
||||||
|
|
||||||
class UidAuthenticator(object):
|
class UidAuthenticator(object):
|
||||||
"""Checks UIDs of ISO14443 RFID cards against database."""
|
"""Checks UIDs of ISO14443 RFID cards against database."""
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
"""
|
"""
|
||||||
Connects to database by given filename and later checks UIDs
|
Connects to database by given filename and later checks UIDs
|
||||||
against that database.
|
against that database.
|
||||||
"""
|
"""
|
||||||
#open in autocommit mode - we are not changing anything
|
#open in autocommit mode - we are not changing anything
|
||||||
self.conn = sqlite3.connect(filename, isolation_level=None)
|
self.conn = sqlite3.connect(filename, isolation_level=None)
|
||||||
|
|
||||||
def fetchUidRecord(self, uid_hex):
|
def fetchUidRecord(self, uid_hex):
|
||||||
"""
|
"""
|
||||||
Returns first record that matches given UID or None if nothing
|
Returns first record that matches given UID or None if nothing
|
||||||
is found.
|
is found.
|
||||||
|
|
||||||
@param uid_hex: uid to match in hex
|
@param uid_hex: uid to match in hex
|
||||||
@returns UidRecord instance if found, None otherwise
|
@returns UidRecord instance if found, None otherwise
|
||||||
"""
|
"""
|
||||||
cursor = self.conn.cursor()
|
cursor = self.conn.cursor()
|
||||||
sql = "SELECT nick FROM authorized_uids WHERE UPPER(uid_hex)=?"
|
sql = "SELECT nick FROM authorized_uids WHERE UPPER(uid_hex)=?"
|
||||||
sql_data =(uid_hex.upper(),)
|
sql_data =(uid_hex.upper(),)
|
||||||
|
|
||||||
cursor.execute(sql, sql_data)
|
cursor.execute(sql, sql_data)
|
||||||
record = cursor.fetchone()
|
record = cursor.fetchone()
|
||||||
|
|
||||||
if record is None:
|
if record is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
nick = record[0]
|
nick = record[0]
|
||||||
return UidRecord(uid_hex, nick)
|
return UidRecord(uid_hex, nick)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""Closes connection to database"""
|
"""Closes connection to database"""
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
|
||||||
class YubikeyHMACAuthenthicator(object):
|
class YubikeyHMACAuthenthicator(object):
|
||||||
"""
|
"""
|
||||||
Uses Yubikey Neo's built-in HMAC functionality on slot 2 (needs to be
|
Uses Yubikey Neo's built-in HMAC functionality on slot 2 (needs to be
|
||||||
configured using Yubikey tools to be on this slot).
|
configured using Yubikey tools to be on this slot).
|
||||||
"""
|
"""
|
||||||
def __init__(self, filename, nfcReader):
|
def __init__(self, filename, nfcReader):
|
||||||
"""
|
"""
|
||||||
Connects to database by given filename and later checks UIDs
|
Connects to database by given filename and later checks UIDs
|
||||||
against that database.
|
against that database.
|
||||||
"""
|
"""
|
||||||
#again autocommit mode
|
#again autocommit mode
|
||||||
self.conn = sqlite3.connect(filename, isolation_level=None)
|
self.conn = sqlite3.connect(filename, isolation_level=None)
|
||||||
self.nfcReader = nfcReader
|
self.nfcReader = nfcReader
|
||||||
|
|
||||||
def hmacCheck(self, key, challenge, result):
|
def hmacCheck(self, key, challenge, result):
|
||||||
"""
|
"""
|
||||||
Returns true iff HMAC-SHA1 with given key and challenge string
|
Returns true iff HMAC-SHA1 with given key and challenge string
|
||||||
transforms into given result.
|
transforms into given result.
|
||||||
"""
|
"""
|
||||||
hashed = hmac.new(key, challenge, hashlib.sha1)
|
hashed = hmac.new(key, challenge, hashlib.sha1)
|
||||||
#We should use hmac.compare_digest(), but that's in new Python
|
#We should use hmac.compare_digest(), but that's in new Python
|
||||||
#version only. Here timing side channels are not much of concern.
|
#version only. Here timing side channels are not much of concern.
|
||||||
return hashed.digest() == result
|
return hashed.digest() == result
|
||||||
|
|
||||||
def checkHMACforUID(self, uid_hex):
|
def checkHMACforUID(self, uid_hex):
|
||||||
"""
|
"""
|
||||||
Checks if UID is in database. If so
|
Checks if UID is in database. If so
|
||||||
@param uid_hex: uid to match in hex
|
@param uid_hex: uid to match in hex
|
||||||
@returns UidRecord instance if found, None otherwise
|
@returns UidRecord instance if found, None otherwise
|
||||||
"""
|
"""
|
||||||
cursor = self.conn.cursor()
|
cursor = self.conn.cursor()
|
||||||
sql = "SELECT nick, key_hex FROM authorized_hmac_keys WHERE UPPER(uid_hex)=?"
|
sql = "SELECT nick, key_hex FROM authorized_hmac_keys WHERE UPPER(uid_hex)=?"
|
||||||
sql_data =(uid_hex.upper(),)
|
sql_data =(uid_hex.upper(),)
|
||||||
|
|
||||||
cursor.execute(sql, sql_data)
|
cursor.execute(sql, sql_data)
|
||||||
record = cursor.fetchone()
|
record = cursor.fetchone()
|
||||||
|
|
||||||
if record is None:
|
if record is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
nick = record[0]
|
nick = record[0]
|
||||||
secretKey = record[1].decode("hex")
|
secretKey = record[1].decode("hex")
|
||||||
|
|
||||||
challenge = os.urandom(32)
|
challenge = os.urandom(32)
|
||||||
|
|
||||||
# Select HMAC-SHA1 on slot 2 from Yubikey
|
# Select HMAC-SHA1 on slot 2 from Yubikey
|
||||||
apdusHex = [
|
apdusHex = [
|
||||||
"00 A4 04 00 07 A0 00 00 05 27 20 01",
|
"00 A4 04 00 07 A0 00 00 05 27 20 01",
|
||||||
"00 01 38 00 %02x %s" % (len(challenge), challenge.encode("hex"))
|
"00 01 38 00 %02x %s" % (len(challenge), challenge.encode("hex"))
|
||||||
]
|
]
|
||||||
|
|
||||||
rapdu = None
|
rapdu = None
|
||||||
|
|
||||||
for apduHex in apdusHex:
|
for apduHex in apdusHex:
|
||||||
try:
|
try:
|
||||||
apdu = apduHex.replace(" ", "").decode("hex")
|
apdu = apduHex.replace(" ", "").decode("hex")
|
||||||
rapdu = self.nfcReader.sendAPDU(apdu)
|
rapdu = self.nfcReader.sendAPDU(apdu)
|
||||||
if not rapdu.valid or rapdu.sw() != 0x9000:
|
if not rapdu.valid or rapdu.sw() != 0x9000:
|
||||||
raise NFCError("HMAC - response SW is not 0x9000")
|
raise NFCError("HMAC - response SW is not 0x9000")
|
||||||
except NFCError, e:
|
except NFCError, e:
|
||||||
logging.debug("Yubikey HMAC command failed: %s" % e.what())
|
logging.debug("Yubikey HMAC command failed: %s" % e.what())
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not self.hmacCheck(secretKey, challenge, rapdu.data()):
|
if not self.hmacCheck(secretKey, challenge, rapdu.data()):
|
||||||
logging.info("HMAC check failed for UID %s", uid_hex)
|
logging.info("HMAC check failed for UID %s", uid_hex)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return UidRecord(uid_hex, nick)
|
return UidRecord(uid_hex, nick)
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""Closes connection to database"""
|
"""Closes connection to database"""
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
#test routine
|
#test routine
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
authenticator = UidAuthenticator("test_uids_db.sqlite")
|
authenticator = UidAuthenticator("test_uids_db.sqlite")
|
||||||
|
|
||||||
record = authenticator.fetchUidRecord("043a1482cc2280")
|
record = authenticator.fetchUidRecord("043a1482cc2280")
|
||||||
print "For UID 043a1482cc2280 we found:", repr(record)
|
print "For UID 043a1482cc2280 we found:", repr(record)
|
||||||
|
|
||||||
record = authenticator.fetchUidRecord("34795fad")
|
record = authenticator.fetchUidRecord("34795fad")
|
||||||
print "For UID 34795fad we found:", repr(record)
|
print "For UID 34795fad we found:", repr(record)
|
||||||
|
|
||||||
record = authenticator.fetchUidRecord("01020304")
|
record = authenticator.fetchUidRecord("01020304")
|
||||||
print "For UID 01020304 we found:", repr(record)
|
print "For UID 01020304 we found:", repr(record)
|
||||||
|
|
||||||
authenticator.shutdown()
|
authenticator.shutdown()
|
||||||
|
|
|
@ -13,138 +13,138 @@ from brmdoor_authenticator import UidAuthenticator, YubikeyHMACAuthenthicator
|
||||||
import unlocker
|
import unlocker
|
||||||
|
|
||||||
class BrmdoorConfigError(ConfigParser.Error):
|
class BrmdoorConfigError(ConfigParser.Error):
|
||||||
"""
|
"""
|
||||||
Signifies that config has missing or bad values.
|
Signifies that config has missing or bad values.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class BrmdoorConfig(object):
|
class BrmdoorConfig(object):
|
||||||
"""
|
"""
|
||||||
Configuration parser. Holds config variables from config file.
|
Configuration parser. Holds config variables from config file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
"lock_opened_secs": "5",
|
"lock_opened_secs": "5",
|
||||||
"unknown_uid_timeout_secs": "5",
|
"unknown_uid_timeout_secs": "5",
|
||||||
"log_level": "info"
|
"log_level": "info"
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
"""
|
"""
|
||||||
Parse and read config from given filename.
|
Parse and read config from given filename.
|
||||||
|
|
||||||
@throws ConfigParser.Error if parsing failed
|
@throws ConfigParser.Error if parsing failed
|
||||||
@throws BrmdoorConfigError if some value was missing or invalid
|
@throws BrmdoorConfigError if some value was missing or invalid
|
||||||
"""
|
"""
|
||||||
self.config = ConfigParser.SafeConfigParser(defaults=BrmdoorConfig._defaults)
|
self.config = ConfigParser.SafeConfigParser(defaults=BrmdoorConfig._defaults)
|
||||||
self.config.read(filename)
|
self.config.read(filename)
|
||||||
|
|
||||||
self.authDbFilename = self.config.get("brmdoor", "auth_db_filename")
|
self.authDbFilename = self.config.get("brmdoor", "auth_db_filename")
|
||||||
self.lockOpenedSecs = self.config.getint("brmdoor", "lock_opened_secs")
|
self.lockOpenedSecs = self.config.getint("brmdoor", "lock_opened_secs")
|
||||||
self.unknownUidTimeoutSecs = self.config.getint("brmdoor", "unknown_uid_timeout_secs")
|
self.unknownUidTimeoutSecs = self.config.getint("brmdoor", "unknown_uid_timeout_secs")
|
||||||
self.logFile = self.config.get("brmdoor", "log_file")
|
self.logFile = self.config.get("brmdoor", "log_file")
|
||||||
self.logLevel = self.convertLoglevel(self.config.get("brmdoor", "log_level"))
|
self.logLevel = self.convertLoglevel(self.config.get("brmdoor", "log_level"))
|
||||||
self.unlocker = self.config.get("brmdoor", "unlocker")
|
self.unlocker = self.config.get("brmdoor", "unlocker")
|
||||||
|
|
||||||
def convertLoglevel(self, levelString):
|
def convertLoglevel(self, levelString):
|
||||||
"""Converts string 'debug', 'info', etc. into corresponding
|
"""Converts string 'debug', 'info', etc. into corresponding
|
||||||
logging.XXX value which is returned.
|
logging.XXX value which is returned.
|
||||||
|
|
||||||
@raises BrmdoorConfigError if the level is undefined
|
@raises BrmdoorConfigError if the level is undefined
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return getattr(logging, levelString.upper())
|
return getattr(logging, levelString.upper())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise BrmdoorConfigError("No such loglevel - %s" % levelString)
|
raise BrmdoorConfigError("No such loglevel - %s" % levelString)
|
||||||
|
|
||||||
class NFCScanner(object):
|
class NFCScanner(object):
|
||||||
"""Thread reading data from NFC reader"""
|
"""Thread reading data from NFC reader"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
"""Create worker reading UIDs from PN53x reader.
|
"""Create worker reading UIDs from PN53x reader.
|
||||||
"""
|
"""
|
||||||
self.authenticator = UidAuthenticator(config.authDbFilename)
|
self.authenticator = UidAuthenticator(config.authDbFilename)
|
||||||
self.hmacAuthenticator = None
|
self.hmacAuthenticator = None
|
||||||
self.unknownUidTimeoutSecs = config.unknownUidTimeoutSecs
|
self.unknownUidTimeoutSecs = config.unknownUidTimeoutSecs
|
||||||
self.lockOpenedSecs = config.lockOpenedSecs
|
self.lockOpenedSecs = config.lockOpenedSecs
|
||||||
|
|
||||||
unlockerClassName = config.unlocker
|
unlockerClassName = config.unlocker
|
||||||
unlockerClass = getattr(unlocker, unlockerClassName)
|
unlockerClass = getattr(unlocker, unlockerClassName)
|
||||||
self.unlocker = unlockerClass(config)
|
self.unlocker = unlockerClass(config)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
Waits for a card to get into reader field. Reads its UID and
|
Waits for a card to get into reader field. Reads its UID and
|
||||||
compares to database of authorized users. Unlocks lock if
|
compares to database of authorized users. Unlocks lock if
|
||||||
authorized.
|
authorized.
|
||||||
"""
|
"""
|
||||||
self.nfc = NFCDevice()
|
self.nfc = NFCDevice()
|
||||||
self.hmacAuthenticator = YubikeyHMACAuthenthicator(
|
self.hmacAuthenticator = YubikeyHMACAuthenthicator(
|
||||||
config.authDbFilename, self.nfc
|
config.authDbFilename, self.nfc
|
||||||
)
|
)
|
||||||
#self.nfc.pollNr = 0xFF #poll indefinitely
|
#self.nfc.pollNr = 0xFF #poll indefinitely
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
uid_hex = hexlify(self.nfc.scanUID())
|
uid_hex = hexlify(self.nfc.scanUID())
|
||||||
logging.debug("Got UID %s", uid_hex)
|
logging.debug("Got UID %s", uid_hex)
|
||||||
if len(uid_hex) > 0:
|
if len(uid_hex) > 0:
|
||||||
self.actOnUid(uid_hex)
|
self.actOnUid(uid_hex)
|
||||||
else:
|
else:
|
||||||
#prevent busy loop if reader goes awry
|
#prevent busy loop if reader goes awry
|
||||||
time.sleep(0.3)
|
time.sleep(0.3)
|
||||||
except NFCError, e:
|
except NFCError, e:
|
||||||
#this exception happens also when scanUID times out
|
#this exception happens also when scanUID times out
|
||||||
logging.debug("Failed to wait for RFID card: %s", e)
|
logging.debug("Failed to wait for RFID card: %s", e)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logging.info("Exiting on keyboard interrupt")
|
logging.info("Exiting on keyboard interrupt")
|
||||||
self.nfc.close()
|
self.nfc.close()
|
||||||
self.nfc.unload()
|
self.nfc.unload()
|
||||||
self.unlocker.lock()
|
self.unlocker.lock()
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Exception in main unlock thread")
|
logging.exception("Exception in main unlock thread")
|
||||||
|
|
||||||
def actOnUid(self, uid_hex):
|
def actOnUid(self, uid_hex):
|
||||||
"""
|
"""
|
||||||
Do something with the UID scanned. Try to authenticate it against
|
Do something with the UID scanned. Try to authenticate it against
|
||||||
database and open lock if authorized.
|
database and open lock if authorized.
|
||||||
"""
|
"""
|
||||||
record = self.authenticator.fetchUidRecord(uid_hex)
|
record = self.authenticator.fetchUidRecord(uid_hex)
|
||||||
|
|
||||||
#direct UID match
|
#direct UID match
|
||||||
if record is not None:
|
if record is not None:
|
||||||
logging.info("Unlocking for UID %s", record)
|
logging.info("Unlocking for UID %s", record)
|
||||||
self.unlocker.unlock()
|
self.unlocker.unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
#test for Yubikey HMAC auth
|
#test for Yubikey HMAC auth
|
||||||
record = self.hmacAuthenticator.checkHMACforUID(uid_hex)
|
record = self.hmacAuthenticator.checkHMACforUID(uid_hex)
|
||||||
|
|
||||||
if record is not None:
|
if record is not None:
|
||||||
logging.info("Unlocking after HMAC for UID %s", record)
|
logging.info("Unlocking after HMAC for UID %s", record)
|
||||||
self.unlocker.unlock()
|
self.unlocker.unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Unknown UID %s", uid_hex)
|
logging.info("Unknown UID %s", uid_hex)
|
||||||
time.sleep(self.unknownUidTimeoutSecs)
|
time.sleep(self.unknownUidTimeoutSecs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print >> sys.stderr, "Syntax: brmdoor_nfc_daemon.py brmdoor_nfc.config"
|
print >> sys.stderr, "Syntax: brmdoor_nfc_daemon.py brmdoor_nfc.config"
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
config = BrmdoorConfig(sys.argv[1])
|
config = BrmdoorConfig(sys.argv[1])
|
||||||
|
|
||||||
if config.logFile == "-":
|
if config.logFile == "-":
|
||||||
logging.basicConfig(stream=sys.stderr, level=config.logLevel,
|
logging.basicConfig(stream=sys.stderr, level=config.logLevel,
|
||||||
format="%(asctime)s %(levelname)s %(message)s [%(pathname)s:%(lineno)d]")
|
format="%(asctime)s %(levelname)s %(message)s [%(pathname)s:%(lineno)d]")
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(filename=config.logFile, level=config.logLevel,
|
logging.basicConfig(filename=config.logFile, level=config.logLevel,
|
||||||
format="%(asctime)s %(levelname)s %(message)s [%(pathname)s:%(lineno)d]")
|
format="%(asctime)s %(levelname)s %(message)s [%(pathname)s:%(lineno)d]")
|
||||||
|
|
||||||
nfcScanner = NFCScanner(config)
|
nfcScanner = NFCScanner(config)
|
||||||
nfcScanner.run()
|
nfcScanner.run()
|
||||||
|
|
||||||
|
|
|
@ -10,23 +10,23 @@ import sys
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print >> sys.stderr, "You must specify filename as arg1 where the DB is to be created"
|
print >> sys.stderr, "You must specify filename as arg1 where the DB is to be created"
|
||||||
|
|
||||||
filename = sys.argv[1]
|
filename = sys.argv[1]
|
||||||
conn = sqlite3.connect(filename)
|
conn = sqlite3.connect(filename)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
cursor.execute("""CREATE TABLE authorized_uids(
|
cursor.execute("""CREATE TABLE authorized_uids(
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
uid_hex TEXT,
|
uid_hex TEXT,
|
||||||
nick TEXT)
|
nick TEXT)
|
||||||
""")
|
""")
|
||||||
cursor.execute("""CREATE TABLE authorized_hmac_keys(
|
cursor.execute("""CREATE TABLE authorized_hmac_keys(
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
uid_hex TEXT,
|
uid_hex TEXT,
|
||||||
key_hex TEXT,
|
key_hex TEXT,
|
||||||
nick TEXT)
|
nick TEXT)
|
||||||
""")
|
""")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
44
test_nfc.py
44
test_nfc.py
|
@ -5,8 +5,8 @@ from nfc_smartcard import NFCDevice, NFCError
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
def formatAPDU(apdu):
|
def formatAPDU(apdu):
|
||||||
return " ".join(["%02X" % ord(b) for b in apdu])
|
return " ".join(["%02X" % ord(b) for b in apdu])
|
||||||
|
|
||||||
tests = {
|
tests = {
|
||||||
# Reading of file E104, where usually NDEF message is
|
# Reading of file E104, where usually NDEF message is
|
||||||
"ndef4": [
|
"ndef4": [
|
||||||
|
@ -55,26 +55,26 @@ hex_apdus = tests[apdu_test]
|
||||||
apdus = [hex_apdu.replace(" ","").decode("hex") for hex_apdu in hex_apdus]
|
apdus = [hex_apdu.replace(" ","").decode("hex") for hex_apdu in hex_apdus]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nfc = NFCDevice()
|
nfc = NFCDevice()
|
||||||
uid = nfc.scanUID()
|
uid = nfc.scanUID()
|
||||||
print "UID", hexlify(uid)
|
print "UID", hexlify(uid)
|
||||||
#nfc.close()
|
#nfc.close()
|
||||||
#nfc.open()
|
#nfc.open()
|
||||||
|
|
||||||
print "Now trying to send ISO14443-4 APDUs"
|
print "Now trying to send ISO14443-4 APDUs"
|
||||||
try:
|
try:
|
||||||
#nfc.selectPassiveTarget()
|
#nfc.selectPassiveTarget()
|
||||||
for apdu in apdus:
|
for apdu in apdus:
|
||||||
print "Command APDU:", formatAPDU(apdu)
|
print "Command APDU:", formatAPDU(apdu)
|
||||||
rapdu = nfc.sendAPDU(apdu)
|
rapdu = nfc.sendAPDU(apdu)
|
||||||
print "Response APDU valid: %s, SW %04x, data %s" % (rapdu.valid(), rapdu.sw(), hexlify(rapdu.data()))
|
print "Response APDU valid: %s, SW %04x, data %s" % (rapdu.valid(), rapdu.sw(), hexlify(rapdu.data()))
|
||||||
except NFCError, e:
|
except NFCError, e:
|
||||||
print "Failed to transmit APDU:", e.what()
|
print "Failed to transmit APDU:", e.what()
|
||||||
|
|
||||||
print "Device is opened:", nfc.opened()
|
print "Device is opened:", nfc.opened()
|
||||||
print "Closing device"
|
print "Closing device"
|
||||||
nfc.close()
|
nfc.close()
|
||||||
print "Device is opened:", nfc.opened()
|
print "Device is opened:", nfc.opened()
|
||||||
nfc.unload()
|
nfc.unload()
|
||||||
except NFCError, e:
|
except NFCError, e:
|
||||||
print "Reading UID failed:", e.what()
|
print "Reading UID failed:", e.what()
|
||||||
|
|
88
unlocker.py
88
unlocker.py
|
@ -2,55 +2,55 @@ import time
|
||||||
import wiringpi2 as wiringpi
|
import wiringpi2 as wiringpi
|
||||||
|
|
||||||
class Unlocker(object):
|
class Unlocker(object):
|
||||||
"""Abstract class/interface for Unlocker object.
|
"""Abstract class/interface for Unlocker object.
|
||||||
Unlocker useful for simulation, but does not actually unlock any lock.
|
Unlocker useful for simulation, but does not actually unlock any lock.
|
||||||
"""
|
"""
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
"""
|
"""
|
||||||
Creates unlocked instance from config, where section named after
|
Creates unlocked instance from config, where section named after
|
||||||
the class is supposed to exist.
|
the class is supposed to exist.
|
||||||
|
|
||||||
@param config: BrmdoorConfig instance
|
@param config: BrmdoorConfig instance
|
||||||
"""
|
"""
|
||||||
self.config = config.config
|
self.config = config.config
|
||||||
self.lockOpenedSecs = config.lockOpenedSecs
|
self.lockOpenedSecs = config.lockOpenedSecs
|
||||||
self.unlockerName = type(self).__name__
|
self.unlockerName = type(self).__name__
|
||||||
|
|
||||||
def unlock(self):
|
def unlock(self):
|
||||||
"""Unlock lock for given self.lockOpenedSecs.
|
"""Unlock lock for given self.lockOpenedSecs.
|
||||||
In this class case, it's only simulated
|
In this class case, it's only simulated
|
||||||
"""
|
"""
|
||||||
time.sleep(self.lockOpenedSecs)
|
time.sleep(self.lockOpenedSecs)
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
"""
|
"""
|
||||||
Lock the lock back. Meant to be used when program is shut down
|
Lock the lock back. Meant to be used when program is shut down
|
||||||
so that lock is not left disengaged.
|
so that lock is not left disengaged.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnlockerWiringPi(Unlocker):
|
class UnlockerWiringPi(Unlocker):
|
||||||
"""Uses configured pings via WiringPi to open lock.
|
"""Uses configured pings via WiringPi to open lock.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
Unlocker.__init__(self, config)
|
Unlocker.__init__(self, config)
|
||||||
wiringpi.wiringPiSetupGpio() # pin numbers follow P1 GPIO header
|
wiringpi.wiringPiSetupGpio() # pin numbers follow P1 GPIO header
|
||||||
self.lockPin = self.config.getint("UnlockerWiringPi", "lock_pin")
|
self.lockPin = self.config.getint("UnlockerWiringPi", "lock_pin")
|
||||||
wiringpi.pinMode(self.lockPin, 1) #output
|
wiringpi.pinMode(self.lockPin, 1) #output
|
||||||
|
|
||||||
def unlock(self):
|
def unlock(self):
|
||||||
"""Unlocks lock at configured pin by pulling it high.
|
"""Unlocks lock at configured pin by pulling it high.
|
||||||
"""
|
"""
|
||||||
wiringpi.digitalWrite(self.lockPin, 1)
|
wiringpi.digitalWrite(self.lockPin, 1)
|
||||||
time.sleep(self.lockOpenedSecs)
|
time.sleep(self.lockOpenedSecs)
|
||||||
wiringpi.digitalWrite(self.lockPin, 0)
|
wiringpi.digitalWrite(self.lockPin, 0)
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
"""
|
"""
|
||||||
Lock the lock back. Meant to be used when program is shut down
|
Lock the lock back. Meant to be used when program is shut down
|
||||||
so that lock is not left disengaged.
|
so that lock is not left disengaged.
|
||||||
"""
|
"""
|
||||||
wiringpi.digitalWrite(self.lockPin, 0)
|
wiringpi.digitalWrite(self.lockPin, 0)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue