mirror of
https://github.com/brmlab/brmdoor_libnfc.git
synced 2025-06-08 00:24:00 +02:00
Added Yubikey Neo HMAC authenthication
This commit is contained in:
parent
5b77dff18d
commit
b281c7a0bc
4 changed files with 109 additions and 8 deletions
|
@ -1,4 +1,10 @@
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from brmdoor_nfc import NFCError
|
||||||
|
|
||||||
|
|
||||||
class UidRecord(object):
|
class UidRecord(object):
|
||||||
"""Represents UID<->nick pair"""
|
"""Represents UID<->nick pair"""
|
||||||
|
@ -61,6 +67,78 @@ class UidAuthenticator(object):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
class YubikeyHMACAuthenthicator(object):
|
||||||
|
"""
|
||||||
|
Uses Yubikey Neo's built-in HMAC functionality on slot 2 (needs to be
|
||||||
|
configured using Yubikey tools to be on this slot).
|
||||||
|
"""
|
||||||
|
def __init__(self, filename, nfcReader):
|
||||||
|
"""
|
||||||
|
Connects to database by given filename and later checks UIDs
|
||||||
|
against that database.
|
||||||
|
"""
|
||||||
|
#again autocommit mode
|
||||||
|
self.conn = sqlite3.connect(filename, isolation_level=None)
|
||||||
|
self.nfcReader = nfcReader
|
||||||
|
|
||||||
|
def hmacCheck(self, key, challenge, result):
|
||||||
|
"""
|
||||||
|
Returns true iff HMAC-SHA1 with given key and challenge string
|
||||||
|
transforms into given result.
|
||||||
|
"""
|
||||||
|
hashed = hmac.new(key, challenge, hashlib.sha1)
|
||||||
|
#We should use hmac.compare_digest(), but that's in new Python
|
||||||
|
#version only. Here timing side channels are not much of concern.
|
||||||
|
return hashed.digest() == result
|
||||||
|
|
||||||
|
def checkHMACforUID(self, uid_hex):
|
||||||
|
"""
|
||||||
|
Checks if UID is in database. If so
|
||||||
|
@param uid_hex: uid to match in hex
|
||||||
|
@returns UidRecord instance if found, None otherwise
|
||||||
|
"""
|
||||||
|
cursor = self.conn.cursor()
|
||||||
|
sql = "SELECT nick, key_hex FROM authorized_hmac_keys WHERE UPPER(uid_hex)=?"
|
||||||
|
sql_data =(uid_hex.upper(),)
|
||||||
|
|
||||||
|
cursor.execute(sql, sql_data)
|
||||||
|
record = cursor.fetchone()
|
||||||
|
|
||||||
|
if record is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
nick = record[0]
|
||||||
|
secretKey = record[1].decode("hex")
|
||||||
|
|
||||||
|
challenge = 'Sample #2'
|
||||||
|
|
||||||
|
# Select HMAC-SHA1 on slot 2 from Yubikey
|
||||||
|
apdusHex = [
|
||||||
|
"00 A4 04 00 07 A0 00 00 05 27 20 01",
|
||||||
|
"00 01 38 00 %02x %s" % (len(challenge), challenge.encode("hex"))
|
||||||
|
]
|
||||||
|
|
||||||
|
rapdu = None
|
||||||
|
|
||||||
|
for apduHex in apdusHex:
|
||||||
|
try:
|
||||||
|
apdu = apduHex.replace(" ", "").decode("hex")
|
||||||
|
rapdu = self.nfcReader.sendAPDU(apdu)
|
||||||
|
if not rapdu.valid or rapdu.sw() != 0x9000:
|
||||||
|
raise NFCError("HMAC - response SW is not 0x9000")
|
||||||
|
except NFCError, e:
|
||||||
|
logging.debug("Yubikey HMAC command failed: %s" % e.what())
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not self.hmacCheck(secretKey, challenge, rapdu.data()):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return UidRecord(uid_hex, nick)
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""Closes connection to database"""
|
||||||
|
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")
|
||||||
|
|
|
@ -9,7 +9,7 @@ from binascii import hexlify
|
||||||
|
|
||||||
|
|
||||||
from brmdoor_nfc import NFCDevice, NFCError
|
from brmdoor_nfc import NFCDevice, NFCError
|
||||||
from brmdoor_authenticator import UidAuthenticator
|
from brmdoor_authenticator import UidAuthenticator, YubikeyHMACAuthenthicator
|
||||||
import unlocker
|
import unlocker
|
||||||
|
|
||||||
class BrmdoorConfigError(ConfigParser.Error):
|
class BrmdoorConfigError(ConfigParser.Error):
|
||||||
|
@ -64,6 +64,7 @@ class NFCScanner(object):
|
||||||
"""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.unknownUidTimeoutSecs = config.unknownUidTimeoutSecs
|
self.unknownUidTimeoutSecs = config.unknownUidTimeoutSecs
|
||||||
self.lockOpenedSecs = config.lockOpenedSecs
|
self.lockOpenedSecs = config.lockOpenedSecs
|
||||||
|
|
||||||
|
@ -78,6 +79,9 @@ class NFCScanner(object):
|
||||||
authorized.
|
authorized.
|
||||||
"""
|
"""
|
||||||
self.nfc = NFCDevice()
|
self.nfc = NFCDevice()
|
||||||
|
self.hmacAuthenticator = YubikeyHMACAuthenthicator(
|
||||||
|
config.authDbFilename, self.nfc
|
||||||
|
)
|
||||||
#self.nfc.pollNr = 0xFF #poll indefinitely
|
#self.nfc.pollNr = 0xFF #poll indefinitely
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -106,14 +110,23 @@ class NFCScanner(object):
|
||||||
"""
|
"""
|
||||||
record = self.authenticator.fetchUidRecord(uid_hex)
|
record = self.authenticator.fetchUidRecord(uid_hex)
|
||||||
|
|
||||||
#no match
|
#direct UID match
|
||||||
if record is None:
|
if record is not None:
|
||||||
logging.info("Unknown UID %s", uid_hex)
|
logging.info("Unlocking for UID %s", record)
|
||||||
time.sleep(self.unknownUidTimeoutSecs)
|
self.unlocker.unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Unlocking for UID %s", record)
|
#test for Yubikey HMAC auth
|
||||||
self.unlocker.unlock()
|
record = self.hmacAuthenticator.checkHMACforUID(uid_hex)
|
||||||
|
|
||||||
|
if record is not None:
|
||||||
|
logging.info("Unlocking after HMAC for UID %s", record)
|
||||||
|
self.unlocker.unlock()
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("Unknown UID %s", uid_hex)
|
||||||
|
time.sleep(self.unknownUidTimeoutSecs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -17,6 +17,16 @@ if __name__ == "__main__":
|
||||||
conn = sqlite3.connect(filename)
|
conn = sqlite3.connect(filename)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
cursor.execute("CREATE TABLE authorized_uids(id INTEGER PRIMARY KEY AUTOINCREMENT, uid_hex TEXT, nick TEXT)")
|
cursor.execute("""CREATE TABLE authorized_uids(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
uid_hex TEXT,
|
||||||
|
nick TEXT)
|
||||||
|
""")
|
||||||
|
cursor.execute("""CREATE TABLE authorized_hmac_keys(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
uid_hex TEXT,
|
||||||
|
key_hex TEXT,
|
||||||
|
nick TEXT)
|
||||||
|
""")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue