diff --git a/zwave_js/DOCS.md b/zwave_js/DOCS.md index 4f9a474..ab45645 100644 --- a/zwave_js/DOCS.md +++ b/zwave_js/DOCS.md @@ -22,12 +22,12 @@ change if other devices are added to the system. the device name in quotes: e.g., something like `"/dev/serial/by-id/usb-0658_0200-if00"`, `"/dev/ttyUSB0"`, `"/dev/ttyAMA0"`, or `"/dev/ttyACM0"`. -2. Set your 16-byte (32 character hex) network key in the form `2232666D1...` - used in order to connect securely to compatible devices. It is recommended - that a network key is configured as some security enabled devices (locks, etc) +2. Set your 16-byte (32 character hex) security keys in the form `2232666D1...` + in order to connect securely to compatible devices. It is recommended + that all four network keys are configured as some security enabled devices (locks, etc) may not function correctly if they are not added securely. - * As a note, it is not recommended to securely connect *all* devices unless - necessary as it triples the amount of messages sent on the mesh. + * As a note, it is not recommended to securely connect *all* devices unless they support S2 security + as the S0 security triples the amount of messages sent on the mesh. 3. Click on "SAVE" to save the add-on configuration. 4. Start the add-on. 5. Add the Z-Wave JS integration to Home Assistant, see documentation: @@ -40,7 +40,10 @@ Add-on configuration: ```yaml device: /dev/ttyUSB0 -network_key: 2232666D100F795E5BB17F0A1BB7A146 +s0_legacy_key: 2232666D100F795E5BB17F0A1BB7A146 +s2_access_control_key: A97D2A51A6D4022998BEFC7B5DAE8EA1 +s2_authenticated_key: 309D4AAEF63EFD85967D76ECA014D1DF +s2_unauthenticated_key: CF338FE0CB99549F7C0EA96308E5A403 ``` ### Option `device` @@ -59,16 +62,20 @@ In most cases this looks like one of the following: - `"/dev/ttyAMA0"` - `"/dev/ttyACM0"` -### Option `network_key` +### Security Keys -Security Z-Wave devices require a network key before being added to the network. -You must set the `network_key` configuration option to use a network key before -adding these devices. +There are four different security keys required to take full advantage of the +different inclusion methods that Z-Wave JS supports: `s0_legacy_key`, +`s2_access_control_key`, `s2_authenticated_key`, and `s2_unauthenticated_key`. -If you don't add a network key, it will autogenerate one for you. +If you are coming from a previous version of `zwave-js`, you likely have a key +stored in the `network_key` configuration option. When the addon is first +started, the key will be migrated from `network_key` to `s0_legacy_key` which +will ensure that your S0 secured devices will continue to function. -To generate a network key manually, you can use the following script in, e.g., -the SSH add-on: +If any of these keys are missing on startup, the addon will autogenerate one for +you. To generate a network key manually, you can use the following script in, +e.g., the SSH add-on: ```bash hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random @@ -78,10 +85,43 @@ You can also use sites like this one to generate the required data: -Ensure you keep a backup of this key. If you have to rebuild your system and -don't have a backup of this key, you won't be able to reconnect to any securely -included devices. This may mean you have to do a factory reset on those devices -and your controller, before rebuilding your Z-Wave network. +Ensure you keep a backup of these keys. If you have to rebuild your system and +don't have a backup of these keys, you won't be able to communicate to any +securely included devices. This may mean you have to do a factory reset on +those devices and your controller, before rebuilding your Z-Wave network. + +> NOTE: Sharing keys between multiple security classes is a security risk, so +> if you choose to configure these keys on your own, be sure to make them +> unique! + +#### Option `s0_legacy_key` + +S0 Security Z-Wave devices require a network key before being added to the network. +This configuration option is required, but if it is unset the addon will generate +a new one automatically on startup. + +#### Option `s2_access_control_key` + +The `s2_access_control_key` must be provided in order to include devices with the +S2 Access Control security class. This security class is needed by devices such +as door locks and garage door openers. This configuration option is required, +but if it is unset the addon will generate a new one automatically on startup. + +#### Option `s2_authenticated_key` + +The `s2_authenticated_key` must be provided in order to include devices with +the S2 Authenticated security class. Devices such as security systems, sensors, +lighting, etc. can request this security class. This configuration option is +required, but if it is unset the addon will generate a new one automatically +on startup. + +### Option `s2_unauthenticated_key` + +The `s2_unauthenticated_key` must be provided in order to include devices with +the S2 Unauthenticated security class. This is similar to S2 Authenticated, but +without verification that the correct device was included. This configuration +option is required, but if it is unset the addon will generate a new one +automatically on startup. ### Option `log_level` (optional) @@ -102,6 +142,13 @@ the Supervisor. If you don't have a USB stick, you can use a fake stick for testing purposes. It will not be able to control any real devices. +### Option `network_key` (deprecated) + +In previous versions of the addon, this was the only key that was needed. With +the introduction of S2 security inclusion in zwave-js, this option has been +deprecated in favor of `s0_legacy_key`. If still set, the `network_key` value will be +migrated to `s0_legacy_key` on first startup. + ## Known issues and limitations - Your hardware needs to be compatible with the Z-Wave JS library diff --git a/zwave_js/config.json b/zwave_js/config.json index 17bdd5d..838c447 100644 --- a/zwave_js/config.json +++ b/zwave_js/config.json @@ -18,14 +18,21 @@ "discovery": ["zwave_js"], "options": { "device": null, - "network_key": "", + "s0_legacy_key": "", + "s2_access_control_key": "", + "s2_authenticated_key": "", + "s2_unauthenticated_key": "", "log_level": "info" }, "schema": { "device": "device(subsystem=tty)", - "network_key": "match(|[0-9a-fA-F]{32,32})", + "s0_legacy_key": "match(|[0-9a-fA-F]{32,32})?", + "s2_access_control_key": "match(|[0-9a-fA-F]{32,32})?", + "s2_authenticated_key": "match(|[0-9a-fA-F]{32,32})?", + "s2_unauthenticated_key": "match(|[0-9a-fA-F]{32,32})?", "log_level": "list(silly|debug|verbose|http|info|warn|error)?", - "emulate_hardware": "bool?" + "emulate_hardware": "bool?", + "network_key": "match(|[0-9a-fA-F]{32,32})?" }, "image": "homeassistant/{arch}-addon-zwave_js" } diff --git a/zwave_js/rootfs/etc/cont-init.d/config.sh b/zwave_js/rootfs/etc/cont-init.d/config.sh index bcc7796..246005d 100644 --- a/zwave_js/rootfs/etc/cont-init.d/config.sh +++ b/zwave_js/rootfs/etc/cont-init.d/config.sh @@ -4,32 +4,76 @@ # ============================================================================== declare network_key -readonly DOCS_EXAMPLE_KEY="2232666D100F795E5BB17F0A1BB7A146" +readonly DOCS_EXAMPLE_KEY_1="2232666D100F795E5BB17F0A1BB7A146" +readonly DOCS_EXAMPLE_KEY_2="A97D2A51A6D4022998BEFC7B5DAE8EA1" +readonly DOCS_EXAMPLE_KEY_3="309D4AAEF63EFD85967D76ECA014D1DF" +readonly DOCS_EXAMPLE_KEY_4="CF338FE0CB99549F7C0EA96308E5A403" -if [[ "${DOCS_EXAMPLE_KEY}" == "$(bashio::config 'network_key')" ]]; then - bashio::log.fatal - bashio::log.fatal 'The add-on detected that the Z-Wave network key used' - bashio::log.fatal 'is from the documented example.' - bashio::log.fatal - bashio::log.fatal 'Using this key is insecure, because it is publicly' - bashio::log.fatal 'listed in the documentation.' - bashio::log.fatal - bashio::log.fatal 'Please check the add-on documentation on how to' - bashio::log.fatal 'create your own, secret, "network_key" and replace' - bashio::log.fatal 'the one you have configured.' - bashio::log.fatal - bashio::log.fatal 'Click on the "Documentation" tab in the Z-Wave JS' - bashio::log.fatal 'add-on panel for more information.' - bashio::log.fatal - bashio::exit.nok -elif ! bashio::config.has_value 'network_key'; then - bashio::log.info "No Network Key is set, generate one..." - network_key=$(hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random) - bashio::addon.option network_key "${network_key}" -else - network_key=$(bashio::config 'network_key') +if bashio::config.has_value 'network_key'; then + # If both 'network_key' and 's0_legacy_key' are set and keys don't match, + # we don't know which one to pick so we have to exit. If they are both set + # and do match, we will drop 'network_key' + if bashio::config.has_value 's0_legacy_key'; then + if bashio::config.equals 's0_legacy_key' "$(bashio::config \"network_key\")"; then + bashio::log.info "Both 'network_key' and 's0_legacy_key' are set and match. Dropping 'network_key' value..." + bashio::addon.option network_key + else + bashio::log.fatal "Both 'network_key' and 's0_legacy_key' are set to different values " + bashio::log.fatal "so we are unsure which one to use. One needs to be removed from the " + bashio::log.fatal "configuration in order to start the addon." + bashio::exit.nok + fi + # If we get here, 'network_key' is set and 's0_legacy_key' is not set so we need + # to migrate the key from 'network_key' to 's0_legacy_key' + else + bashio::log.info "Migrating \"network_key\" option to \"s0_legacy_key\"..." + network_key=$(bashio::string.upper "$(bashio::config 'network_key')") + bashio::addon.option s0_legacy_key "${network_key}" + bashio::addon.option network_key + bashio::log.info "Flushing config to disk due to key migration..." + bashio::addon.options > "/data/options.json" + fi fi +# Validate that no keys are using the example from the docs and generate new random +# keys for any missing keys. +for key in "s0_legacy_key" "s2_access_control_key" "s2_authenticated_key" "s2_unauthenticated_key"; do + network_key=$(bashio::config "${key}") + if [ "${network_key}" == "${DOCS_EXAMPLE_KEY_1}" ] || [ "${network_key}" == "${DOCS_EXAMPLE_KEY_2}" ] || [ "${network_key}" == "${DOCS_EXAMPLE_KEY_3}" ] || [ "${network_key}" == "${DOCS_EXAMPLE_KEY_4}" ]; then + bashio::log.fatal + bashio::log.fatal "The add-on detected that the Z-Wave network key used" + bashio::log.fatal "is from the documented example." + bashio::log.fatal + bashio::log.fatal "Using this key is insecure, because it is publicly" + bashio::log.fatal "listed in the documentation." + bashio::log.fatal + bashio::log.fatal "Please check the add-on documentation on how to" + bashio::log.fatal "create your own, secret, \"${key}\" and replace" + bashio::log.fatal "the one you have configured." + bashio::log.fatal + bashio::log.fatal "Click on the \"Documentation\" tab in the Z-Wave JS" + bashio::log.fatal "add-on panel for more information." + bashio::log.fatal + bashio::exit.nok + elif ! bashio::var.has_value "${network_key}"; then + bashio::log.info "No ${key} is set, generating one..." + bashio::addon.option ${key} "$(hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random)" + flush_to_disk=1 + fi +done + +# If flush_to_disk is set, it means we have generated new key(s) and they need to get +# flushed to disk +if [[ ${flush_to_disk:+x} ]]; then + bashio::log.info "Flushing config to disk due to creation of new key(s)..." + bashio::addon.options > "/data/options.json" +fi + +s0_legacy=$(bashio::config "s0_legacy_key") +s2_access_control=$(bashio::config "s2_access_control_key") +s2_authenticated=$(bashio::config "s2_authenticated_key") +s2_unauthenticated=$(bashio::config "s2_unauthenticated_key") + if ! bashio::config.has_value 'log_level'; then log_level=$(bashio::info.logging) bashio::log.info "No log level specified, falling back to Supervisor" @@ -41,7 +85,10 @@ fi # Generate config bashio::var.json \ - network_key "${network_key}" \ + s0_legacy "${s0_legacy}" \ + s2_access_control "${s2_access_control}" \ + s2_authenticated "${s2_authenticated}" \ + s2_unauthenticated "${s2_unauthenticated}" \ log_level "${log_level}" \ | tempio \ -template /usr/share/tempio/zwave_config.conf \ diff --git a/zwave_js/rootfs/usr/share/tempio/zwave_config.conf b/zwave_js/rootfs/usr/share/tempio/zwave_config.conf index 22fc223..48c5391 100644 --- a/zwave_js/rootfs/usr/share/tempio/zwave_config.conf +++ b/zwave_js/rootfs/usr/share/tempio/zwave_config.conf @@ -8,5 +8,10 @@ "cacheDir": "/data/cache", "throttle": "slow" }, - "networkKey": "{{ .network_key }}" + "securityKeys": { + "S0_Legacy": "{{ .s0_legacy }}", + "S2_AccessControl": "{{ .s2_access_control }}", + "S2_Authenticated": "{{ .s2_authenticated }}", + "S2_Unauthenticated": "{{ .s2_unauthenticated }}" + } }