async def async_step_secure_knxkeys(self,
                                        user_input: dict | None = None
                                        ) -> FlowResult:
        """Configure secure knxkeys used to authenticate."""
        errors = {}

        if user_input is not None:
            try:
                assert self._tunneling_config
                storage_key: str = (CONST_KNX_STORAGE_KEY +
                                    user_input[CONF_KNX_KNXKEY_FILENAME])
                load_key_ring(
                    self.hass.config.path(
                        STORAGE_DIR,
                        storage_key,
                    ),
                    user_input[CONF_KNX_KNXKEY_PASSWORD],
                )
                entry_data: KNXConfigEntryData = {
                    **self._tunneling_config,  # type: ignore[misc]
                    CONF_KNX_KNXKEY_FILENAME:
                    storage_key,
                    CONF_KNX_KNXKEY_PASSWORD:
                    user_input[CONF_KNX_KNXKEY_PASSWORD],
                    CONF_KNX_CONNECTION_TYPE:
                    CONF_KNX_TUNNELING_TCP_SECURE,
                }

                return self.async_create_entry(
                    title=
                    f"Secure {CONF_KNX_TUNNELING.capitalize()} @ {self._tunneling_config[CONF_HOST]}",
                    data=entry_data,
                )
            except InvalidSignature:
                errors["base"] = "invalid_signature"
            except FileNotFoundError:
                errors["base"] = "file_not_found"

        fields = {
            vol.Required(CONF_KNX_KNXKEY_FILENAME):
            selector.selector({"text": {}}),
            vol.Required(CONF_KNX_KNXKEY_PASSWORD):
            selector.selector({"text": {}}),
        }

        return self.async_show_form(step_id="secure_knxkeys",
                                    data_schema=vol.Schema(fields),
                                    errors=errors)
Example #2
0
 def test_load_keyring(self):
     """Test load keyring from knxkeys file."""
     keyring: Keyring = load_key_ring(self.keyring_test_file, "pwd")
     TestKeyRing.assert_interface(keyring, "user4", 2)
     TestKeyRing.assert_interface(keyring, "@zvI1G&_", 3)
     TestKeyRing.assert_interface(keyring, "ZvDY-:g#", 4)
     TestKeyRing.assert_interface(keyring, "user2", 5)
Example #3
0
    async def async_step_secure_knxkeys(self,
                                        user_input: dict | None = None
                                        ) -> FlowResult:
        """Configure secure knxkeys used to authenticate."""
        errors = {}

        if user_input is not None:
            assert self._tunneling_config
            storage_key = CONST_KNX_STORAGE_KEY + user_input[
                CONF_KNX_KNXKEY_FILENAME]
            try:
                load_key_ring(
                    path=self.hass.config.path(STORAGE_DIR, storage_key),
                    password=user_input[CONF_KNX_KNXKEY_PASSWORD],
                )
            except FileNotFoundError:
                errors[CONF_KNX_KNXKEY_FILENAME] = "file_not_found"
            except InvalidSignature:
                errors[CONF_KNX_KNXKEY_PASSWORD] = "invalid_signature"

            if not errors:
                entry_data = self._tunneling_config | KNXConfigEntryData(
                    connection_type=CONF_KNX_TUNNELING_TCP_SECURE,
                    knxkeys_filename=storage_key,
                    knxkeys_password=user_input[CONF_KNX_KNXKEY_PASSWORD],
                )

                return self.async_create_entry(
                    title=
                    f"Secure Tunneling @ {self._tunneling_config[CONF_HOST]}",
                    data=entry_data,
                )

        fields = {
            vol.Required(CONF_KNX_KNXKEY_FILENAME): selector.TextSelector(),
            vol.Required(CONF_KNX_KNXKEY_PASSWORD): selector.TextSelector(),
        }

        return self.async_show_form(step_id="secure_knxkeys",
                                    data_schema=vol.Schema(fields),
                                    errors=errors)
Example #4
0
    def test_load_keyring_real(self):
        """Test load keyring from knxkeys file."""
        keyring: Keyring = load_key_ring(self.testcase_file, "password")
        TestKeyRing.assert_interface(keyring, "user1", 3)
        TestKeyRing.assert_interface(keyring, "user2", 4)
        TestKeyRing.assert_interface(keyring, "user3", 5)
        TestKeyRing.assert_interface(keyring, "user4", 6)
        assert keyring.devices[
            0].decrypted_management_password == "commissioning"

        interface: XMLInterface = keyring.interfaces[0]
        device: XMLDevice = keyring.get_device_by_interface(interface)
        assert device is not None
        assert device.decrypted_authentication == "authenticationcode"
Example #5
0
    async def _start(self) -> None:
        """Start interface. Connecting KNX/IP device with the selected method."""
        if self.connection_config.connection_type == ConnectionType.ROUTING:
            await self._start_routing(local_ip=self.connection_config.local_ip)
        elif (self.connection_config.connection_type
              == ConnectionType.TUNNELING
              and self.connection_config.gateway_ip is not None):
            await self._start_tunnelling_udp(
                gateway_ip=self.connection_config.gateway_ip,
                gateway_port=self.connection_config.gateway_port,
            )
        elif (self.connection_config.connection_type
              == ConnectionType.TUNNELING_TCP
              and self.connection_config.gateway_ip is not None):
            await self._start_tunnelling_tcp(
                gateway_ip=self.connection_config.gateway_ip,
                gateway_port=self.connection_config.gateway_port,
            )
        elif (self.connection_config.connection_type
              == ConnectionType.TUNNELING_TCP_SECURE
              and self.connection_config.gateway_ip is not None
              and self.connection_config.secure_config is not None):
            secure_config = self.connection_config.secure_config
            user_id: int
            user_password: str
            device_authentication_password: str | None
            if (secure_config.knxkeys_file_path is not None
                    and secure_config.knxkeys_password is not None):
                keyring: Keyring = load_key_ring(
                    secure_config.knxkeys_file_path,
                    secure_config.knxkeys_password)
                if secure_config.user_id is not None:
                    user_id = secure_config.user_id
                    interface = keyring.get_interface_by_user_id(user_id)
                    if interface is None:
                        raise InterfaceWithUserIdNotFound()

                    user_password = interface.decrypted_password
                    device_authentication_password = interface.decrypted_authentication
                else:
                    interface = keyring.interfaces[0]
                    user_id = interface.user_id
                    user_password = interface.decrypted_password
                    device_authentication_password = interface.decrypted_authentication
            else:
                user_id = secure_config.user_id or 2
                if secure_config.user_password is None:
                    raise InvalidSecureConfiguration()

                user_password = secure_config.user_password
                device_authentication_password = (
                    secure_config.device_authentication_password)

            await self._start_secure_tunnelling_tcp(
                gateway_ip=self.connection_config.gateway_ip,
                gateway_port=self.connection_config.gateway_port,
                user_id=user_id,
                user_password=user_password,
                device_authentication_password=device_authentication_password,
            )
        else:
            await self._start_automatic()
Example #6
0
 def test_raises_error(self):
     """Test raises error if password is wrong."""
     with pytest.raises(InvalidSecureConfiguration):
         load_key_ring(self.testcase_file,
                       "wrong_password",
                       validate_signature=False)
Example #7
0
 def test_invalid_signature(self):
     """Test invalid signature throws error."""
     with pytest.raises(InvalidSignature):
         load_key_ring(self.testcase_file, "wrong_password")