def _ctap2_enroll(dev, alg, application, user, pin, resident): """Enroll a new security key using CTAP version 2""" ctap2 = CTAP2(dev) application = application.decode('utf-8') rp = {'id': application, 'name': application} user = {'id': user.encode('utf-8'), 'name': user} key_params = [{'type': 'public-key', 'alg': alg}] options = {'rk': resident} if pin: pin_protocol = PinProtocolV1(ctap2) pin_token = pin_protocol.get_pin_token(pin) pin_version = pin_protocol.VERSION pin_auth = hmac.new(pin_token, _dummy_hash, sha256).digest()[:16] else: pin_version = None pin_auth = None cred = ctap2.make_credential(_dummy_hash, rp, user, key_params, options=options, pin_auth=pin_auth, pin_protocol=pin_version) cdata = cred.auth_data.credential_data return _decode_public_key(alg, cdata.public_key), cdata.credential_id
def get_assertion(ctap2: CTAP2, clientDataJSON_hash, allowList): return ctap2.get_assertions( RP_ID, clientDataJSON_hash, allowList, options={'up': False}, )
def test_get_info(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + _INFO info = ctap.get_info() ctap.device.call.assert_called_with(0x10, b'\4', None, None) self.assertIsInstance(info, Info)
def sign_hash(self, credential_id, dgst, pin): ctap2 = CTAP2(self.get_current_hid_device()) client = self.get_current_fido_client() if pin: pin_token = client.client_pin.get_pin_token(pin) pin_auth = hmac_sha256(pin_token, dgst)[:16] return ctap2.send_cbor( 0x50, { 1: dgst, 2: { "id": credential_id, "type": "public-key" }, 3: pin_auth }, ) else: return ctap2.send_cbor(0x50, { 1: dgst, 2: { "id": credential_id, "type": "public-key" } })
def find_device(self, dev=None, solo_serial=None): if dev is None: devices = list(CtapHidDevice.list_devices()) if solo_serial is not None: devices = [ d for d in devices if d.descriptor["serial_number"] == solo_serial ] if len(devices) > 1: raise solo.exceptions.NonUniqueDeviceError if len(devices) == 0: raise RuntimeError("No FIDO device found") dev = devices[0] self.dev = dev self.ctap1 = CTAP1(dev) self.ctap2 = CTAP2(dev) try: self.client = Fido2Client(dev, self.origin) except CtapError: print("Not using FIDO2 interface.") self.client = None if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") return self.dev
def test_send_cbor_ok(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b"\0" + cbor.encode({1: b"response"}) self.assertEqual({1: b"response"}, ctap.send_cbor(2, b"foobar")) ctap.device.call.assert_called_with(0x10, b"\2" + cbor.encode(b"foobar"), mock.ANY, None)
def test_send_cbor_ok(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + cbor.encode({1: b'response'}) self.assertEqual({1: b'response'}, ctap.send_cbor(2, b'foobar')) ctap.device.call.assert_called_with(0x10, b'\2' + cbor.encode(b'foobar'), None, None)
def find_device(self,): dev = next(CtapHidDevice.list_devices(), None) if not dev: raise RuntimeError('No FIDO device found') self.dev = dev self.ctap1 = CTAP1(dev) self.ctap2 = CTAP2(dev) self.client = Fido2Client(dev, self.origin) if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11')
def _ctap2_sign(dev, message_hash, application, key_handle, touch_required): """Sign a message with a security key using CTAP version 2""" ctap2 = CTAP2(dev) application = application.decode('utf-8') allow_creds = [{'type': 'public-key', 'id': key_handle}] options = {'up': touch_required} assertion = ctap2.get_assertions(application, message_hash, allow_creds, options=options)[0] auth_data = assertion.auth_data return auth_data.flags, auth_data.counter, assertion.signature
def test_get_assertion(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + _GA_RESP resp = ctap.get_assertion(1, 2) ctap.device.call.assert_called_with(0x10, b'\2' + cbor.encode({ 1: 1, 2: 2 }), None, None) self.assertIsInstance(resp, AssertionResponse) self.assertEqual(resp, _GA_RESP) self.assertEqual(resp.credential, _CRED) self.assertEqual(resp.auth_data, _AUTH_DATA_GA) self.assertEqual(resp.signature, _SIGNATURE) self.assertIsNone(resp.user) self.assertIsNone(resp.number_of_credentials)
def uv_supported(device): """ Determine whether user verification is supported by the device. :param device: The device to check user verification capability for. :return: True if user verification is supported, False otherwise """ try: ctap2 = CTAP2(device) except ValueError: return False try: info = ctap2.get_info() except OSError: return False return bool('uv' in info.options and info.options['uv'])
def __init__(self, device, origin, verify=verify_rp_id): super(Fido2Client, self).__init__(origin, verify) self.ctap1_poll_delay = 0.25 try: self.ctap2 = CTAP2(device) self.info = self.ctap2.get_info() if PinProtocolV1.VERSION in self.info.pin_protocols: self.pin_protocol = PinProtocolV1(self.ctap2) else: self.pin_protocol = None self._do_make_credential = self._ctap2_make_credential self._do_get_assertion = self._ctap2_get_assertion except (ValueError, CtapError): self.ctap1 = CTAP1(device) self.info = _CTAP1_INFO self._do_make_credential = self._ctap1_make_credential self._do_get_assertion = self._ctap1_get_assertion
def sk_get_resident(application, user, pin): """Get keys resident on a security key""" app_hash = sha256(application).digest() result = [] for dev in CtapHidDevice.list_devices(): try: ctap2 = CTAP2(dev) pin_protocol = PinProtocolV1(ctap2) pin_token = pin_protocol.get_pin_token(pin) cred_mgmt = CredentialManagement(ctap2, pin_protocol.VERSION, pin_token) for cred in cred_mgmt.enumerate_creds(app_hash): name = cred[CredentialManagement.RESULT.USER]['name'] if user and name != user: continue cred_id = cred[CredentialManagement.RESULT.CREDENTIAL_ID] key_handle = cred_id['id'] public_key = cred[CredentialManagement.RESULT.PUBLIC_KEY] alg = public_key[3] public_value = _decode_public_key(alg, public_key) result.append((alg, name, public_value, key_handle)) except CtapError as exc: if exc.code == CtapError.ERR.NO_CREDENTIALS: continue elif exc.code == CtapError.ERR.PIN_INVALID: raise ValueError('Invalid PIN') from None elif exc.code == CtapError.ERR.PIN_NOT_SET: raise ValueError('PIN not set') from None else: raise ValueError(str(exc)) from None finally: dev.close() return result
def test_make_credential(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + _MC_RESP resp = ctap.make_credential(1, 2, 3, 4) ctap.device.call.assert_called_with( 0x10, b'\1' + cbor.encode({ 1: 1, 2: 2, 3: 3, 4: 4 }), None, None) self.assertIsInstance(resp, AttestationObject) self.assertEqual(resp, _MC_RESP) self.assertEqual(resp.fmt, 'packed') self.assertEqual(resp.auth_data, _AUTH_DATA_MC) self.assertSetEqual(set(resp.att_statement.keys()), {'alg', 'sig', 'x5c'})
def test_make_credential(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b"\0" + _MC_RESP resp = ctap.make_credential(1, 2, 3, 4) ctap.device.call.assert_called_with( 0x10, b"\1" + cbor.encode({ 1: 1, 2: 2, 3: 3, 4: 4 }), mock.ANY, None) self.assertIsInstance(resp, AttestationObject) self.assertEqual(resp, _MC_RESP) self.assertEqual(resp.fmt, "packed") self.assertEqual(resp.auth_data, _AUTH_DATA_MC) self.assertSetEqual(set(resp.att_statement.keys()), {"alg", "sig", "x5c"})
def find_device(self, dev=None, solo_serial: str = None): devices = [] if dev is None: if solo_serial is not None: if solo_serial.startswith("device="): solo_serial = solo_serial.split("=")[1] dev = open_device(solo_serial) else: devices = list(CtapHidDevice.list_devices()) devices = [ d for d in devices if d.descriptor.serial_number == solo_serial ] else: devices = list(CtapHidDevice.list_devices()) if len(devices) > 1: raise pynitrokey.exceptions.NonUniqueDeviceError if len(devices) > 0: dev = devices[0] if dev is None: raise RuntimeError("No FIDO device found") self.dev = dev self.ctap1 = CTAP1(dev) try: self.ctap2: Optional[CTAP2] = CTAP2(dev) except CtapError as e: self.ctap2 = None try: self.client: Optional[Fido2Client] = Fido2Client(dev, self.origin) except CtapError: print("Not using FIDO2 interface.") self.client = None if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") return self.dev
def make_credential(ctap2: CTAP2, clientDataJSON_hash): att_obj = ctap2.make_credential( clientDataJSON_hash, { 'id': RP_ID, 'name': 'Test RP' }, { 'id': USER_ID, 'name': '*****@*****.**', 'displayName': 'Test' }, [{ 'alg': -7, 'type': 'public-key' }], ) return { 'id': att_obj.auth_data.credential_data.credential_id, 'type': 'public-key' }
def find_device(self, dev=None, solo_serial=None): if dev is None: devices = list(CtapHidDevice.list_devices()) if solo_serial is not None: for d in devices: if not hasattr(d, "serial_number"): print( "Currently serial numbers are not supported with current fido2 library. Please upgrade: pip3 install fido2 --upgrade" ) sys.exit(1) devices = [ d for d in devices if d.descriptor.serial_number == solo_serial ] if len(devices) > 1: raise solo.exceptions.NonUniqueDeviceError if len(devices) == 0: raise RuntimeError("No FIDO device found") dev = devices[0] self.dev = dev self.ctap1 = CTAP1(dev) try: self.ctap2 = CTAP2(dev) except CtapError: self.ctap2 = None try: self.client = Fido2Client(dev, self.origin) except CtapError: print("Not using FIDO2 interface.") self.client = None if self.exchange == self.exchange_hid: self.send_data_hid(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11") return self.dev
def create_ctap2_handle(): for dev in CtapHidDevice.list_devices(): return CTAP2(dev) print("No FIDO device found")
def mock_ctap(self): device = mock.MagicMock() device.call.return_value = b"\0" + _INFO return CTAP2(device)
from fido2.hid import CtapHidDevice from fido2.ctap2 import CTAP2 def get_device(): devs = list(CtapHidDevice.list_devices()) assert len(devs) == 1 return devs[0] if __name__ == '__main__': try: dev = get_device() except Exception: print("Unable to find authenticator") exit(-1) ctap = CTAP2(dev) try: ctap.reset() print("Device successfully reset") except Exception as e: print(e)
def __init__(self, dev): origin = RP_ID self.client = Fido2Client(dev, origin, verify=verify_rp_id) self.ctap2 = CTAP2(dev) self.pin = os.getenv('SOLOPIN')
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"]
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ Connects to each attached FIDO device, and: 1. If the device supports CBOR commands, perform a getInfo command. 2. If the device supports WINK, perform the wink command. """ from __future__ import print_function, absolute_import, unicode_literals from fido2.hid import CtapHidDevice, CAPABILITY from fido2.ctap2 import CTAP2 for dev in CtapHidDevice.list_devices(): print('CONNECT: %s' % dev) if dev.capabilities & CAPABILITY.CBOR: ctap2 = CTAP2(dev) info = ctap2.get_info() print('DEVICE INFO: %s' % info) else: print('Device does not support CBOR') if dev.capabilities & CAPABILITY.WINK: dev.wink() print('WINK sent!') else: print('Device does not support WINK')
def __init__(self, driver): self.ctap = CTAP2(driver._dev) self.pin = PinProtocolV1(self.ctap) self._info = self.ctap.get_info() self._pin = self._info.options['clientPin']
def cred_mgmt(self, pin): client = self.get_current_fido_client() token = client.client_pin.get_pin_token(pin) ctap2 = CTAP2(self.get_current_hid_device()) return CredentialManagement(ctap2, client.client_pin.protocol, token)
def program_kbd(self, cmd): ctap2 = CTAP2(self.get_current_hid_device()) return ctap2.send_cbor(0x51, cmd)
def reset(self, ): CTAP2(self.get_current_hid_device()).reset()