class Fido2Controller(object): def __init__(self, ctap_device): self.ctap = CTAP2(ctap_device) self.pin = ClientPin(self.ctap) self._info = self.ctap.get_info() self._pin = self._info.options["clientPin"] @property def has_pin(self): return self._pin def get_resident_credentials(self, pin): _credman = CredentialManagement( self.ctap, self.pin.protocol, self.pin.get_pin_token(pin, ClientPin.PERMISSION.CREDENTIAL_MGMT), ) for rp in _credman.enumerate_rps(): for cred in _credman.enumerate_creds( rp[CredentialManagement.RESULT.RP_ID_HASH]): yield ResidentCredential(cred, rp) def delete_resident_credential(self, credential_id, pin): _credman = CredentialManagement( self.ctap, self.pin.protocol, self.pin.get_pin_token(pin, ClientPin.PERMISSION.CREDENTIAL_MGMT), ) for cred in self.get_resident_credentials(pin): if credential_id == cred.credential_id: _credman.delete_cred(credential_id) def get_pin_retries(self): return self.pin.get_pin_retries() def set_pin(self, pin): self.pin.set_pin(pin) self._pin = True def change_pin(self, old_pin, new_pin): self.pin.change_pin(old_pin, new_pin) def reset(self, touch_callback=None): if touch_callback: touch_timer = Timer(0.500, touch_callback) touch_timer.start() try: self.ctap.reset() self._pin = False finally: if touch_callback: touch_timer.cancel() @property def is_fips(self): return False
def test_change_pin(self): prot = ClientPin(mock.MagicMock(), PinProtocolV1()) prot._get_shared_secret = mock.Mock(return_value=({}, SHARED)) prot.change_pin("1234", "4321") prot.ctap.client_pin.assert_called_with( 1, 4, key_agreement={}, new_pin_enc=a2b_hex( "4280e14aac4fcbf02dd079985f0c0ffc9ea7d5f9c173fd1a4c843826f7590cb3c2d080c6923e2fe6d7a52c31ea1309d3fcca3dedae8a2ef14b6330cafc79339e" # noqa E501 ), pin_uv_param=a2b_hex("fb97e92f3724d7c85e001d7f93e6490a"), pin_hash_enc=a2b_hex("afe8327ce416da8ee3d057589c2ce1a9"), )
def fido_change_pin(self, current_pin, new_pin): try: with self._open_device([FidoConnection]) as conn: ctap2 = Ctap2(conn) if len(new_pin) < ctap2.info.min_pin_length: return failure('too short') client_pin = ClientPin(ctap2) client_pin.change_pin(current_pin, new_pin) return success() except CtapError as e: if e.code == CtapError.ERR.INVALID_LENGTH or \ e.code == CtapError.ERR.PIN_POLICY_VIOLATION: return failure('too long') if e.code == CtapError.ERR.PIN_INVALID: return failure('wrong pin') if e.code == CtapError.ERR.PIN_AUTH_BLOCKED: return failure('currently blocked') if e.code == CtapError.ERR.PIN_BLOCKED: return failure('blocked') raise