Esempio n. 1
0
 def check_error(self, data, err=None):
     assert (len(data) == 1)
     if err is None:
         if data[0] != 0:
             raise CtapError(data[0])
     elif data[0] != err:
         raise ValueError('Unexpected error: %02x' % data[0])
Esempio n. 2
0
 def check_error(data, err=None):
     assert len(data) == 1
     if err is None:
         if data[0] != 0:
             raise CtapError(data[0])
     elif data[0] != err:
         raise ValueError("Unexpected error: %02x" % data[0])
Esempio n. 3
0
    def test_make_credential_existing_key(self, PatchedCTAP2):
        dev = mock.Mock()
        dev.capabilities = CAPABILITY.CBOR
        ctap2 = mock.MagicMock()
        ctap2.get_info.return_value = Info(_INFO_NO_PIN)
        ctap2.make_credential.side_effect = CtapError(
            CtapError.ERR.CREDENTIAL_EXCLUDED)
        PatchedCTAP2.return_value = ctap2
        client = Fido2Client(dev, APP_ID)

        try:
            client.make_credential(
                PublicKeyCredentialCreationOptions(
                    rp,
                    user,
                    challenge,
                    [{
                        "type": "public-key",
                        "alg": -7
                    }],
                    authenticator_selection={
                        "userVerification": "discouraged"
                    },
                ))
            self.fail("make_credential did not raise error")
        except ClientError as e:
            self.assertEqual(e.code, ClientError.ERR.DEVICE_INELIGIBLE)

        ctap2.get_info.assert_called_with()
        ctap2.make_credential.assert_called_once()
    def _ctap1_get_assertion(self, client_data, rp_id, allow_list, extensions,
                             uv, pin, event, on_keepalive):
        if uv or not allow_list:
            raise CtapError(CtapError.ERR.UNSUPPORTED_OPTION)

        app_param = sha256(rp_id.encode())
        client_param = client_data.hash
        for cred in allow_list:
            try:
                auth_resp = _call_polling(
                    self.ctap1_poll_delay,
                    event,
                    on_keepalive,
                    self.ctap1.authenticate,
                    client_param,
                    app_param,
                    cred["id"],
                )
                return [
                    AssertionResponse.from_ctap1(app_param, cred, auth_resp)
                ]
            except ClientError as e:
                if e.code == ClientError.ERR.TIMEOUT:
                    raise  # Other errors are ignored so we move to the next.
        raise ClientError.ERR.DEVICE_INELIGIBLE()
Esempio n. 5
0
    def exchange_hid(self, cmd, addr=0, data=b"A" * 16):
        req = SoloClient.format_request(cmd, addr, data)

        data = self.send_data_hid(SoloBootloader.HIDCommandBoot, req)

        ret = data[0]
        if ret != CtapError.ERR.SUCCESS:
            raise CtapError(ret)

        return data[1:]
Esempio n. 6
0
    def exchange_u2f(self, cmd, addr=0, data=b"A" * 16):
        appid = b"A" * 32
        chal = b"B" * 32

        req = SoloClient.format_request(cmd, addr, data)

        res = self.ctap1.authenticate(chal, appid, req)

        ret = res.signature[0]
        if ret != CtapError.ERR.SUCCESS:
            raise CtapError(ret)

        return res.signature[1:]
    def _ctap1_make_credential(
        self,
        client_data,
        rp,
        user,
        key_params,
        exclude_list,
        extensions,
        rk,
        uv,
        pin,
        event,
        on_keepalive,
    ):
        if rk or uv or ES256.ALGORITHM not in [p.alg for p in key_params]:
            raise CtapError(CtapError.ERR.UNSUPPORTED_OPTION)

        app_param = sha256(rp["id"].encode())

        dummy_param = b"\0" * 32
        for cred in exclude_list or []:
            key_handle = cred["id"]
            try:
                self.ctap1.authenticate(dummy_param, app_param, key_handle,
                                        True)
                raise ClientError.ERR.OTHER_ERROR()  # Shouldn't happen
            except ApduError as e:
                if e.code == APDU.USE_NOT_SATISFIED:
                    _call_polling(
                        self.ctap1_poll_delay,
                        event,
                        on_keepalive,
                        self.ctap1.register,
                        dummy_param,
                        dummy_param,
                    )
                    raise ClientError.ERR.DEVICE_INELIGIBLE()

        return AttestationObject.from_ctap1(
            app_param,
            _call_polling(
                self.ctap1_poll_delay,
                event,
                on_keepalive,
                self.ctap1.register,
                client_data.hash,
                app_param,
            ),
        )
Esempio n. 8
0
    def get_assertion(self, get_assertion_request: dict) -> AssertionResponse:
        request = CtapGetAssertionRequest.create(get_assertion_request)
        if request.user_verification_required:
            self._verify_user(request.rp_id)

        creds = self._find_credentials(request.allow_list, request.rp_id)

        if len(creds) == 0:
            raise CtapError(CtapError.ERR.NO_CREDENTIALS)

        # Note: consecutive calls to get_assertion will override this context
        self._next_assertions_ctx = CtapGetNextAssertionContext(
            request=request, creds=creds, cred_counter=0)

        return self._get_assertion(request, self._next_assertions_ctx)
Esempio n. 9
0
    def test_make_credential_existing_key(self):
        dev = mock.Mock()
        dev.capabilities = CAPABILITY.CBOR
        client = Fido2Client(dev, APP_ID)
        client.ctap = mock.MagicMock()
        client.ctap.get_info.return_value = Info(_INFO_NO_PIN)
        client.ctap.make_credential.side_effect = CtapError(
            CtapError.ERR.CREDENTIAL_EXCLUDED)

        try:
            client.make_credential(rp, user, challenge, timeout=1)
            self.fail('make_credential did not raise error')
        except ClientError as e:
            self.assertEqual(e.code, ClientError.ERR.DEVICE_INELIGIBLE)

        client.ctap.get_info.assert_called_with()
        client.ctap.make_credential.assert_called_once()
    def _ctap2_get_assertion(self, client_data, rp_id, allow_list, extensions,
                             uv, up, pin, event, on_keepalive):
        pin_auth = None
        pin_protocol = None
        if pin:
            pin_protocol = self.pin_protocol.VERSION
            pin_token = self.pin_protocol.get_pin_token(pin)
            pin_auth = hmac_sha256(pin_token, client_data.hash)[:16]
        elif self.info.options.get("clientPin") and not uv:
            raise ClientError.ERR.BAD_REQUEST("PIN required but not provided")

        if uv:
            options = {"uv": True}
            if not up:
                options = {"uv": True, "up": False}
        else:
            options = None
            if not up:
                options = {"up": False}

        if allow_list:
            # Filter out credential IDs which are too long
            max_len = self.info.max_cred_id_length
            if max_len:
                allow_list = [e for e in allow_list if len(e) <= max_len]
            if not allow_list:
                raise CtapError(CtapError.ERR.NO_CREDENTIALS)

            # Reject the request if too many credentials remain.
            max_creds = self.info.max_creds_in_list
            if max_creds and len(allow_list) > max_creds:
                raise ClientError.ERR.BAD_REQUEST("allow_list too long")

        return self.ctap2.get_assertions(
            rp_id,
            client_data.hash,
            allow_list if allow_list else None,
            extensions,
            options,
            pin_auth,
            pin_protocol,
            event,
            on_keepalive,
        )
Esempio n. 11
0
    def _find_credentials(cls, allow_list: List[PublicKeyCredentialDescriptor],
                          rp_id: str) -> List[Credential]:
        service_name = cls.get_service_name(rp_id)
        if not allow_list:
            # Currently, we only support get assertion flows where a credential id is supplied;
            # because we ought to pass in a user-id to the backend keyring; this user-id is encoded in the cred-id.
            raise CtapError(CtapError.ERR.MISSING_PARAMETER)

        res = []
        for allowed_cred in allow_list:
            valid_cred = allowed_cred.id and len(allowed_cred.id) == 32
            if not valid_cred:
                continue

            user_uuid, key_password = allowed_cred.id[:16], allowed_cred.id[
                16:]
            # noinspection PyBroadException
            try:
                encoded_password = keyring.get_password(
                    service_name=service_name, username=user_uuid.hex())
                if not encoded_password:
                    continue

                decoded_password = base64.b64decode(encoded_password)

                alg = int.from_bytes(decoded_password[:2], 'big', signed=True)
                cose_key_cls = cose.CoseKey.for_alg(alg)
                if cose_key_cls == cose.UnsupportedKey:
                    continue

                private_key_bytes = decoded_password[2:]
                private_key = serialization.load_der_private_key(
                    private_key_bytes, password=key_password)

                signer = CtapPrivateKeyWrapper.create(cose_key_cls,
                                                      private_key)
                cred = Credential(allowed_cred.id, signer)
                res.append(cred)
            except Exception:
                # Best effort
                continue

        return res
Esempio n. 12
0
 def _verify_user(self, rp_id: str):
     verified = self._user_verifier.verify_user(rp_id)
     if not verified:
         raise CtapError(CtapError.ERR.NOT_ALLOWED)
Esempio n. 13
0
    def get_next_assertion(self) -> AssertionResponse:
        if not self._next_assertions_ctx:
            raise CtapError(CtapError.ERR.NOT_ALLOWED)

        return self._get_assertion(self._next_assertions_ctx.request,
                                   self._next_assertions_ctx)