Example #1
0
    def __init__(self):
        print('Loading the ForkPi database...')
        self.db = ForkpiDB()

        print('Loading options...')
        attempt_limit = self.load_option('attempt_limit')
        lockout_time = self.load_option('lockout_time_minutes')
        self.lockout_table = LockoutTable(attempt_limit, lockout_time)
        self.keypad_timeout = self.load_option('keypad_timeout_seconds')
        self.max_transaction_time = self.load_option(
            'max_transaction_time_seconds')
        self.lock_release_time = self.load_option('lock_release_time_seconds')

        print('Loading (F)ingerprint Scanner...')
        self.fingerprint_thread = FingerprintThread(lambda: ForkpiDB())
        print('Loading (O)LED...')
        self.led = OLED()
        print('Loading (R)FID Reader...')
        self.rfid_thread = RfidThread()
        print('Loading (K)eypad...')
        self.keypad_thread = KeypadThread()

        print('Connecting to (dummy) door lock...')
        self.door_lock = DoorLock()

        # maps RFID UIDs to incorrect streak and remaining lockout time
        self.transaction_timer = self.new_timer()

        # start polling for cards and fingers
        self.rfid_thread.start()
        self.fingerprint_thread.start()
        self.keypad_thread.start()
Example #2
0
    def __init__(self):
        print('Loading the ForkPi database...')
        self.db = ForkpiDB()
        
        print('Loading options...')
        attempt_limit = self.load_option('attempt_limit')
        lockout_time = self.load_option('lockout_time_minutes')
        self.lockout_table = LockoutTable(attempt_limit, lockout_time)
        self.keypad_timeout = self.load_option('keypad_timeout_seconds')
        self.max_transaction_time = self.load_option('max_transaction_time_seconds')
        self.lock_release_time = self.load_option('lock_release_time_seconds')

        print('Loading (F)ingerprint Scanner...')
        self.fingerprint_thread = FingerprintThread(lambda: ForkpiDB())
        print('Loading (O)LED...')
        self.led = OLED()
        print('Loading (R)FID Reader...')
        self.rfid_thread = RfidThread()
        print('Loading (K)eypad...')
        self.keypad_thread = KeypadThread()

        print('Connecting to (dummy) door lock...')
        self.door_lock = DoorLock()

        # maps RFID UIDs to incorrect streak and remaining lockout time
        self.transaction_timer = self.new_timer()

        # start polling for cards and fingers
        self.rfid_thread.start()
        self.fingerprint_thread.start()
        self.keypad_thread.start()
Example #3
0
class SpoonPi:
    def __init__(self):
        print('Loading the ForkPi database...')
        self.db = ForkpiDB()

        print('Loading options...')
        attempt_limit = self.load_option('attempt_limit')
        lockout_time = self.load_option('lockout_time_minutes')
        self.lockout_table = LockoutTable(attempt_limit, lockout_time)
        self.keypad_timeout = self.load_option('keypad_timeout_seconds')
        self.max_transaction_time = self.load_option(
            'max_transaction_time_seconds')
        self.lock_release_time = self.load_option('lock_release_time_seconds')

        print('Loading (F)ingerprint Scanner...')
        self.fingerprint_thread = FingerprintThread(lambda: ForkpiDB())
        print('Loading (O)LED...')
        self.led = OLED()
        print('Loading (R)FID Reader...')
        self.rfid_thread = RfidThread()
        print('Loading (K)eypad...')
        self.keypad_thread = KeypadThread()

        print('Connecting to (dummy) door lock...')
        self.door_lock = DoorLock()

        # maps RFID UIDs to incorrect streak and remaining lockout time
        self.transaction_timer = self.new_timer()

        # start polling for cards and fingers
        self.rfid_thread.start()
        self.fingerprint_thread.start()
        self.keypad_thread.start()

    def new_timer(self):
        # ends a pending transaction after a timer if the current self.factors are not enough to be authenticated
        # call the function `deny_then_end_transaction` after `max_transaction_time` seconds have elapsed
        return Timer(self.max_transaction_time,
                     self.deny_then_end_transaction,
                     kwargs={'reason': 'insufficient credentials'})

    def load_option(self, name):
        value, default = self.db.fetch_option(name)
        if value.isdigit() and value != default:
            print('  custom : {} = {}'.format(name, value))
            return int(value)
        else:
            print('  default: {} = {}'.format(name, default))
            return int(default)

    def allow_access(self, names, *args, **kwargs):
        assert len(args) == 0
        if 'pin' in kwargs.keys():
            # convert pin to asterisks so pin is not exposed in the logs
            kwargs['pin'] = '*' * len(kwargs['pin'])
        names = ', '.join(names)
        print("> Allowed %s" % names)
        self.db.log_allowed(names=names, **kwargs)

        self.door_lock.unlock()
        self.led.clear_then_puts("Access\n granted")
        time.sleep(self.lock_release_time)
        self.door_lock.lock()

    def deny_access(self,
                    reason,
                    led_message="Access\n denied",
                    *args,
                    **kwargs):
        assert len(args) == 0
        print("> %s" % led_message.replace('\n ', ' '), kwargs)
        self.db.log_denied(reason=reason, **kwargs)
        self.led.clear_then_puts(led_message)
        time.sleep(0.5)

    def single_factor_authentication(self, *args, **kwargs):
        assert len(args) == 0
        assert len(kwargs.keys()) == 1

        is_authorized, names = self.db.authenticate(pin='', **kwargs)
        if is_authorized:
            self.allow_access(names=names, pin='', **kwargs)
        return is_authorized

    def two_factor_authentication(self, *args, **kwargs):
        assert len(args) == 0
        assert len(kwargs) == 2

        use_lockout_table = False
        if 'pin' in kwargs and 'rfid_uid' in kwargs:
            use_lockout_table = True

        if use_lockout_table:
            rfid_uid = kwargs['rfid_uid']
            is_locked_out, time_left = self.lockout_table.get_lockout(rfid_uid)
            if is_locked_out:
                self.deny_access(reason="locked out",
                                 led_message="Locked out\n for %sm" %
                                 time_left,
                                 **kwargs)
                return False

        is_authorized, names = self.db.authenticate(**kwargs)
        if is_authorized:
            self.allow_access(names=names, **kwargs)
            if use_lockout_table:
                self.lockout_table.reset_streak(rfid_uid)
        else:
            self.deny_access(reason='invalid key pair', **kwargs)
            if use_lockout_table:
                self.lockout_table.failed_attempt(rfid_uid)
        return is_authorized

    def pin_authentication(self):
        '''
        Returns (pin, timeout) where
          pin = the pin entered (string)
          timeout = whether the keypad timed out (boolean)
        '''
        pin = ''
        self.led.clear_then_puts("Enter PIN:\n")
        # First key that triggered this pin authentication
        key = self.keypad_thread.get_key()

        while True:
            if key is None:  # timed out!
                return pin, True
            elif key.isdigit():
                pin += str(key)
                self.led.puts('*')
            elif key == '*':  # backspace
                pin = pin[:-1]
                self.led.puts('\b')
            elif key == '#':  # enter
                return pin, False
            key = self.keypad_thread.getch(timeout=self.keypad_timeout)

    def start_transaction_timer(self):
        self.transaction_timer = self.new_timer()
        self.transaction_timer.start()

    def stop_transaction_timer(self):
        self.transaction_timer.cancel()

    def deny_then_end_transaction(self, reason):
        self.deny_access(reason=reason, **self.factors)
        self.end_transaction()

    def end_transaction(self):
        self.is_transacting = False
        self.stop_transaction_timer()

    def new_transaction(self):
        self.is_transacting = True

        self.factors = dict()

        self.led.clear_then_puts("Swipe, scan,\nor push key")

        self.rfid_thread.start_polling()
        self.fingerprint_thread.start_polling()
        self.keypad_thread.start_polling()

        while self.is_transacting:

            if self.rfid_thread.tag_swiped:
                self.stop_transaction_timer()

                self.rfid_thread.stop_polling()
                self.factors['rfid_uid'] = self.rfid_thread.get_rfid_uid()

                self.lockout_table.update_timers()

                if self.single_factor_authentication(
                        rfid_uid=self.factors['rfid_uid']):
                    # single factor succeeded; end transaction
                    self.end_transaction()
                elif len(self.factors) == 1:
                    # single factor failed; wait for another factor
                    self.led.clear_then_puts("RFID tag\n swiped!")
                    self.start_transaction_timer()
                else:  # len(self.factors) == 2
                    self.two_factor_authentication(**self.factors)
                    self.end_transaction()

            elif self.fingerprint_thread.finger_scanned:
                self.stop_transaction_timer()

                self.fingerprint_thread.stop_polling()
                self.factors[
                    'fingerprint_matches'] = self.fingerprint_thread.get_matches(
                    )

                if self.single_factor_authentication(
                        fingerprint_matches=self.factors['fingerprint_matches']
                ):
                    # single factor succeeded; end transaction
                    self.end_transaction()
                elif len(self.factors) == 1:
                    # single factor failed; wait for another factor
                    self.led.clear_then_puts("Finger\n scanned!")
                    self.start_transaction_timer()
                else:  # len(self.factors) == 2
                    self.two_factor_authentication(**self.factors)
                    self.end_transaction()

            elif self.keypad_thread.key_pressed:
                self.stop_transaction_timer()

                self.keypad_thread.stop_polling()
                self.factors['pin'], timed_out = self.pin_authentication()

                if timed_out:
                    self.deny_then_end_transaction(reason='keypad timeout')
                # pin was entered correctly
                elif len(self.factors) == 1:
                    # wait for another factor
                    self.led.clear_then_puts("PIN\n entered!")
                    self.start_transaction_timer()
                else:  # len(self.factors) == 2
                    self.stop_transaction_timer()
                    self.two_factor_authentication(**self.factors)
                    self.end_transaction()
Example #4
0
class SpoonPi:

    def __init__(self):
        print('Loading the ForkPi database...')
        self.db = ForkpiDB()
        
        print('Loading options...')
        attempt_limit = self.load_option('attempt_limit')
        lockout_time = self.load_option('lockout_time_minutes')
        self.lockout_table = LockoutTable(attempt_limit, lockout_time)
        self.keypad_timeout = self.load_option('keypad_timeout_seconds')
        self.max_transaction_time = self.load_option('max_transaction_time_seconds')
        self.lock_release_time = self.load_option('lock_release_time_seconds')

        print('Loading (F)ingerprint Scanner...')
        self.fingerprint_thread = FingerprintThread(lambda: ForkpiDB())
        print('Loading (O)LED...')
        self.led = OLED()
        print('Loading (R)FID Reader...')
        self.rfid_thread = RfidThread()
        print('Loading (K)eypad...')
        self.keypad_thread = KeypadThread()

        print('Connecting to (dummy) door lock...')
        self.door_lock = DoorLock()

        # maps RFID UIDs to incorrect streak and remaining lockout time
        self.transaction_timer = self.new_timer()

        # start polling for cards and fingers
        self.rfid_thread.start()
        self.fingerprint_thread.start()
        self.keypad_thread.start()

    def new_timer(self):
        # ends a pending transaction after a timer if the current self.factors are not enough to be authenticated
        # call the function `deny_then_end_transaction` after `max_transaction_time` seconds have elapsed
        return Timer(self.max_transaction_time, self.deny_then_end_transaction, kwargs={'reason':'insufficient credentials'})

    def load_option(self, name):
        value, default = self.db.fetch_option(name)
        if value.isdigit() and value != default:
            print('  custom : {} = {}'.format(name, value))
            return int(value)
        else:
            print('  default: {} = {}'.format(name, default))
            return int(default)

    def allow_access(self, names, *args, **kwargs):
        assert len(args) == 0
        if 'pin' in kwargs.keys():
            # convert pin to asterisks so pin is not exposed in the logs
            kwargs['pin'] = '*' * len(kwargs['pin'])
        names = ', '.join(names)
        print("> Allowed %s" % names)
        self.db.log_allowed(names=names, **kwargs)

        self.door_lock.unlock()
        self.led.clear_then_puts("Access\n granted")
        time.sleep(self.lock_release_time)
        self.door_lock.lock()

    def deny_access(self, reason, led_message="Access\n denied", *args, **kwargs):
        assert len(args) == 0
        print("> %s" % led_message.replace('\n ', ' '), kwargs)
        self.db.log_denied(reason=reason, **kwargs)
        self.led.clear_then_puts(led_message)
        time.sleep(0.5)

    def single_factor_authentication(self, *args, **kwargs):
        assert len(args) == 0
        assert len(kwargs.keys()) == 1

        is_authorized, names = self.db.authenticate(pin='', **kwargs)
        if is_authorized:
            self.allow_access(names=names, pin='', **kwargs)
        return is_authorized

    def two_factor_authentication(self, *args, **kwargs):
        assert len(args) == 0
        assert len(kwargs) == 2

        use_lockout_table = False
        if 'pin' in kwargs and 'rfid_uid' in kwargs:
            use_lockout_table = True

        if use_lockout_table:
            rfid_uid = kwargs['rfid_uid']
            is_locked_out, time_left = self.lockout_table.get_lockout(rfid_uid)
            if is_locked_out:
                self.deny_access(
                    reason="locked out",
                    led_message="Locked out\n for %sm" % time_left,
                    **kwargs
                )
                return False

        is_authorized, names = self.db.authenticate(**kwargs)
        if is_authorized:
            self.allow_access(names=names, **kwargs)
            if use_lockout_table:
                self.lockout_table.reset_streak(rfid_uid)
        else:
            self.deny_access(reason='invalid key pair', **kwargs)
            if use_lockout_table:
                self.lockout_table.failed_attempt(rfid_uid)
        return is_authorized

    def pin_authentication(self):
        '''
        Returns (pin, timeout) where
          pin = the pin entered (string)
          timeout = whether the keypad timed out (boolean)
        '''
        pin = ''
        self.led.clear_then_puts("Enter PIN:\n")
        # First key that triggered this pin authentication
        key = self.keypad_thread.get_key()

        while True:
            if key is None: # timed out!
                return pin, True
            elif key.isdigit():
                pin += str(key)
                self.led.puts('*')
            elif key == '*': # backspace
                pin = pin[:-1]
                self.led.puts('\b')
            elif key == '#': # enter
                return pin, False
            key = self.keypad_thread.getch(timeout=self.keypad_timeout)

    def start_transaction_timer(self):
        self.transaction_timer = self.new_timer()
        self.transaction_timer.start()

    def stop_transaction_timer(self):
        self.transaction_timer.cancel()

    def deny_then_end_transaction(self, reason):
        self.deny_access(reason=reason, **self.factors)
        self.end_transaction()

    def end_transaction(self):
        self.is_transacting = False
        self.stop_transaction_timer()

    def new_transaction(self):
        self.is_transacting = True

        self.factors = dict()

        self.led.clear_then_puts("Swipe, scan,\nor push key")

        self.rfid_thread.start_polling()
        self.fingerprint_thread.start_polling()
        self.keypad_thread.start_polling()

        while self.is_transacting:

            if self.rfid_thread.tag_swiped:
                self.stop_transaction_timer()

                self.rfid_thread.stop_polling()
                self.factors['rfid_uid'] = self.rfid_thread.get_rfid_uid()
                
                self.lockout_table.update_timers()

                if self.single_factor_authentication(rfid_uid=self.factors['rfid_uid']):
                    # single factor succeeded; end transaction
                    self.end_transaction()
                elif len(self.factors) == 1:
                    # single factor failed; wait for another factor
                    self.led.clear_then_puts("RFID tag\n swiped!")
                    self.start_transaction_timer()
                else: # len(self.factors) == 2
                    self.two_factor_authentication(**self.factors)
                    self.end_transaction()

            elif self.fingerprint_thread.finger_scanned:
                self.stop_transaction_timer()

                self.fingerprint_thread.stop_polling()
                self.factors['fingerprint_matches'] = self.fingerprint_thread.get_matches()

                if self.single_factor_authentication(fingerprint_matches=self.factors['fingerprint_matches']):
                    # single factor succeeded; end transaction
                    self.end_transaction()
                elif len(self.factors) == 1:
                    # single factor failed; wait for another factor
                    self.led.clear_then_puts("Finger\n scanned!")
                    self.start_transaction_timer()
                else: # len(self.factors) == 2
                    self.two_factor_authentication(**self.factors)
                    self.end_transaction()

            elif self.keypad_thread.key_pressed:
                self.stop_transaction_timer()

                self.keypad_thread.stop_polling()
                self.factors['pin'], timed_out = self.pin_authentication()

                if timed_out:
                    self.deny_then_end_transaction(reason='keypad timeout')
                # pin was entered correctly
                elif len(self.factors) == 1:
                    # wait for another factor
                    self.led.clear_then_puts("PIN\n entered!")
                    self.start_transaction_timer()
                else: # len(self.factors) == 2
                    self.stop_transaction_timer()
                    self.two_factor_authentication(**self.factors)
                    self.end_transaction()