esp-rainmaker-cli 1.14.0__tar.gz → 1.14.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/PKG-INFO +14 -1
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/PKG-INFO +14 -1
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rainmaker/rainmaker.py +12 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rainmaker/version.py +1 -1
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/node.py +22 -2
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/provision.py +9 -2
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/node_cache.py +4 -1
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security2.py +66 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/integration.py +105 -21
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/raw_config.py +4 -2
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/raw_params.py +5 -2
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/esp_rainmaker_prov.py +35 -16
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/LICENSE +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/README.md +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/SOURCES.txt +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/dependency_links.txt +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/entry_points.txt +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/requires.txt +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/top_level.txt +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rainmaker/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/automation.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/browserlogin.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/cache.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/cmd_response.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/group.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/html/welcome.html +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/stream.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/test.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_cmd/user.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/aws_credentials.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/cmd_response.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/configmanager.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/constants.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/device.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/envval.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/exceptions.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/kvs_streaming.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/local_control.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/logger.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/node.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/profile_manager.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/profile_utils.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/schedule_utils.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/serverconfig.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/service.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/session.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/session_store.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/simple_local_control.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_lib/user.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/discovery/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/discovery/mdns_discovery.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/wifi_ctrl.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/wifi_prov.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/prov/wifi_scan.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security0.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security1.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/srp6a.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/ble_cli.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_ble.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_console.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_http.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/utils/convenience.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/claim.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/claim_config.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_local_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_prov.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_rainmaker_ctrl.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_rmaker_prov_local_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/proto/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/proto/proto_lc.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/prov/custom_prov.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/prov/wifi_ctrl.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec0_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec1_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec2_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/session_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/challenge_response.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/custom_cloud_config_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_chal_resp_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_claim_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/esp_rmaker_user_mapping_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/on_network_chal_resp.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/proto/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/sec0_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/sec1_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/sec2_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/python/session_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/prov_util.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/user_mapping.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/security/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/transport/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/utils/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_config_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_constants_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_ctrl_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/wifi_provisioning/python/wifi_scan_pb2.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/server_cert/__init__.py +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/server_cert/server_cert.pem +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/setup.cfg +0 -0
- {esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esp-rainmaker-cli
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.1
|
|
4
4
|
Summary: A python utility to perform host based claiming
|
|
5
5
|
Home-page: https://github.com/espressif/esp-rainmaker-cli
|
|
6
6
|
Author: Espressif Systems
|
|
@@ -122,6 +122,19 @@ Changelog
|
|
|
122
122
|
|
|
123
123
|
All major changes to ESP RainMaker CLI will be documented in this file.
|
|
124
124
|
|
|
125
|
+
## [1.14.1] - 05-Jun-2026
|
|
126
|
+
### Bugfixes
|
|
127
|
+
- Security 2 fixes across provisioning and local control:
|
|
128
|
+
- Provisioning auto-detect now reads `prov.sec_ver` from device capabilities
|
|
129
|
+
(was forcing sec1 on sec2 devices, causing firmware "Security version mismatch")
|
|
130
|
+
- `sec_patch_ver` discovered from device capabilities / `esp_local_ctrl/version`,
|
|
131
|
+
enabling the counter-in-IV scheme on ESP-IDF v5.4+ firmware
|
|
132
|
+
- `--sec2_username` option added to `--local` commands (`getnodeconfig`,
|
|
133
|
+
`getparams`, `setparams`); defaults to `wifiprov`, picks up cached
|
|
134
|
+
`local_control_username` from cloud params; `--pop` used as the SRP6a password
|
|
135
|
+
- QR code `username` / `password` fields now honoured by `provision`
|
|
136
|
+
- `Security2.serialize`/`deserialize` for local-control session resume parity with Security 1
|
|
137
|
+
|
|
125
138
|
## [1.14.0] - 24-Apr-2026
|
|
126
139
|
### Added
|
|
127
140
|
- New `group sharing` subcommand for sharing device groups / Matter fabrics between users:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esp-rainmaker-cli
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.1
|
|
4
4
|
Summary: A python utility to perform host based claiming
|
|
5
5
|
Home-page: https://github.com/espressif/esp-rainmaker-cli
|
|
6
6
|
Author: Espressif Systems
|
|
@@ -122,6 +122,19 @@ Changelog
|
|
|
122
122
|
|
|
123
123
|
All major changes to ESP RainMaker CLI will be documented in this file.
|
|
124
124
|
|
|
125
|
+
## [1.14.1] - 05-Jun-2026
|
|
126
|
+
### Bugfixes
|
|
127
|
+
- Security 2 fixes across provisioning and local control:
|
|
128
|
+
- Provisioning auto-detect now reads `prov.sec_ver` from device capabilities
|
|
129
|
+
(was forcing sec1 on sec2 devices, causing firmware "Security version mismatch")
|
|
130
|
+
- `sec_patch_ver` discovered from device capabilities / `esp_local_ctrl/version`,
|
|
131
|
+
enabling the counter-in-IV scheme on ESP-IDF v5.4+ firmware
|
|
132
|
+
- `--sec2_username` option added to `--local` commands (`getnodeconfig`,
|
|
133
|
+
`getparams`, `setparams`); defaults to `wifiprov`, picks up cached
|
|
134
|
+
`local_control_username` from cloud params; `--pop` used as the SRP6a password
|
|
135
|
+
- QR code `username` / `password` fields now honoured by `provision`
|
|
136
|
+
- `Security2.serialize`/`deserialize` for local-control session resume parity with Security 1
|
|
137
|
+
|
|
125
138
|
## [1.14.0] - 24-Apr-2026
|
|
126
139
|
### Added
|
|
127
140
|
- New `group sharing` subcommand for sharing device groups / Matter fabrics between users:
|
|
@@ -285,6 +285,10 @@ def main():
|
|
|
285
285
|
choices=[0, 1, 2],
|
|
286
286
|
default=None,
|
|
287
287
|
help='Security version for local control (default: 1)')
|
|
288
|
+
getnodeconfig_parser.add_argument('--sec2_username',
|
|
289
|
+
type=str,
|
|
290
|
+
default=None,
|
|
291
|
+
help='SRP6a username for Security 2 local control. Defaults to the username cached from cloud params, else \'wifiprov\'. --pop is used as the password.')
|
|
288
292
|
getnodeconfig_parser.add_argument('--local-raw',
|
|
289
293
|
action='store_true',
|
|
290
294
|
help='Use local control via raw endpoints (get_config with fragmentation) instead of esp_local_ctrl')
|
|
@@ -355,6 +359,10 @@ def main():
|
|
|
355
359
|
choices=[0, 1, 2],
|
|
356
360
|
default=None,
|
|
357
361
|
help='Security version for local control (default: 1)')
|
|
362
|
+
setparams_parser.add_argument('--sec2_username',
|
|
363
|
+
type=str,
|
|
364
|
+
default=None,
|
|
365
|
+
help='SRP6a username for Security 2 local control. Defaults to the username cached from cloud params, else \'wifiprov\'. --pop is used as the password.')
|
|
358
366
|
setparams_parser.add_argument('--local-raw',
|
|
359
367
|
action='store_true',
|
|
360
368
|
help='Use local control via raw endpoints (get_params/set_params) instead of esp_local_ctrl')
|
|
@@ -398,6 +406,10 @@ def main():
|
|
|
398
406
|
choices=[0, 1, 2],
|
|
399
407
|
default=None,
|
|
400
408
|
help='Security version for local control (default: 1)')
|
|
409
|
+
getparams_parser.add_argument('--sec2_username',
|
|
410
|
+
type=str,
|
|
411
|
+
default=None,
|
|
412
|
+
help='SRP6a username for Security 2 local control. Defaults to the username cached from cloud params, else \'wifiprov\'. --pop is used as the password.')
|
|
401
413
|
getparams_parser.add_argument('--local-raw',
|
|
402
414
|
action='store_true',
|
|
403
415
|
help='Use local control via raw endpoints (get_params/set_params) instead of esp_local_ctrl')
|
|
@@ -78,6 +78,9 @@ def _build_local_options_with_cache(vars_dict, node_cache=None, session_store=No
|
|
|
78
78
|
sec_ver = raw_sec_ver if raw_sec_ver is not None else 1
|
|
79
79
|
nodeid = vars_dict.get('nodeid', '')
|
|
80
80
|
|
|
81
|
+
user_sec2_username = vars_dict.get('sec2_username') # None => not supplied
|
|
82
|
+
sec2_username = user_sec2_username
|
|
83
|
+
|
|
81
84
|
if node_cache and not pop:
|
|
82
85
|
capability = node_cache.get_local_control_capability(nodeid)
|
|
83
86
|
if capability:
|
|
@@ -99,6 +102,9 @@ def _build_local_options_with_cache(vars_dict, node_cache=None, session_store=No
|
|
|
99
102
|
log.debug(f"Using cached POP for node {nodeid}")
|
|
100
103
|
if lc_info.get('sec_ver') is not None and not explicit_sec_ver:
|
|
101
104
|
sec_ver = lc_info['sec_ver']
|
|
105
|
+
if sec2_username is None and lc_info.get('username'):
|
|
106
|
+
sec2_username = lc_info['username']
|
|
107
|
+
log.debug(f"Using cached sec2 username for node {nodeid}")
|
|
102
108
|
|
|
103
109
|
if not pop and not explicit_sec_ver:
|
|
104
110
|
try:
|
|
@@ -114,15 +120,23 @@ def _build_local_options_with_cache(vars_dict, node_cache=None, session_store=No
|
|
|
114
120
|
pop = lc_info.get('pop', '')
|
|
115
121
|
if lc_info.get('sec_ver') is not None:
|
|
116
122
|
sec_ver = lc_info['sec_ver']
|
|
123
|
+
if sec2_username is None and lc_info.get('username'):
|
|
124
|
+
sec2_username = lc_info['username']
|
|
117
125
|
log.debug(f"Resolved POP from cloud for node {nodeid}")
|
|
118
126
|
except Exception as e:
|
|
119
127
|
log.debug(f"Failed to auto-resolve POP from cloud: {e}")
|
|
120
128
|
|
|
129
|
+
if not sec2_username:
|
|
130
|
+
sec2_username = 'wifiprov'
|
|
131
|
+
if sec_ver == 2:
|
|
132
|
+
print(f"Using default Security 2 username '{sec2_username}'")
|
|
133
|
+
|
|
121
134
|
local_options = {
|
|
122
135
|
'pop': pop,
|
|
123
136
|
'transport': vars_dict.get('transport', 'http'),
|
|
124
137
|
'port': vars_dict.get('port', 8080),
|
|
125
138
|
'sec_ver': sec_ver,
|
|
139
|
+
'sec2_username': sec2_username,
|
|
126
140
|
'local_raw': vars_dict.get('local_raw', False),
|
|
127
141
|
'device_name': vars_dict.get('device_name', None),
|
|
128
142
|
'node_cache': node_cache,
|
|
@@ -1259,6 +1273,7 @@ def get_node_config(vars=None):
|
|
|
1259
1273
|
'transport': vars.get('transport', 'ble'),
|
|
1260
1274
|
'port': vars.get('port', 8080),
|
|
1261
1275
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1,
|
|
1276
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1262
1277
|
'device_name': vars.get('device_name', None),
|
|
1263
1278
|
'timestamp': timestamp
|
|
1264
1279
|
}
|
|
@@ -1312,7 +1327,8 @@ def get_node_config(vars=None):
|
|
|
1312
1327
|
'pop': vars.get('pop', ''),
|
|
1313
1328
|
'transport': vars.get('transport', 'http'),
|
|
1314
1329
|
'port': vars.get('port', 8080),
|
|
1315
|
-
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1
|
|
1330
|
+
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1,
|
|
1331
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1316
1332
|
}
|
|
1317
1333
|
|
|
1318
1334
|
node_config = run_local_control_operation(
|
|
@@ -1326,7 +1342,8 @@ def get_node_config(vars=None):
|
|
|
1326
1342
|
'pop': vars.get('pop', ''),
|
|
1327
1343
|
'transport': vars.get('transport', 'http'),
|
|
1328
1344
|
'port': vars.get('port', 8080),
|
|
1329
|
-
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0
|
|
1345
|
+
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0,
|
|
1346
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1330
1347
|
}
|
|
1331
1348
|
|
|
1332
1349
|
log.info("Using simplified local control (security level 0)")
|
|
@@ -1567,6 +1584,7 @@ def set_params(vars=None):
|
|
|
1567
1584
|
'transport': vars.get('transport', 'http'),
|
|
1568
1585
|
'port': vars.get('port', 8080),
|
|
1569
1586
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0,
|
|
1587
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1570
1588
|
'local_raw': vars.get('local_raw', False),
|
|
1571
1589
|
'device_name': vars.get('device_name', None)
|
|
1572
1590
|
}
|
|
@@ -1753,6 +1771,7 @@ def get_params(vars=None):
|
|
|
1753
1771
|
'transport': vars.get('transport', 'http'),
|
|
1754
1772
|
'port': vars.get('port', 8080),
|
|
1755
1773
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 1,
|
|
1774
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1756
1775
|
'local_raw': vars.get('local_raw', False),
|
|
1757
1776
|
'device_name': vars.get('device_name', None),
|
|
1758
1777
|
'timestamp': timestamp if not (proxy_report and vars.get('local_raw', False) and not timestamp_explicitly_provided) else timestamp,
|
|
@@ -1779,6 +1798,7 @@ def get_params(vars=None):
|
|
|
1779
1798
|
'transport': vars.get('transport', 'http'),
|
|
1780
1799
|
'port': vars.get('port', 8080),
|
|
1781
1800
|
'sec_ver': vars.get('sec_ver') if vars.get('sec_ver') is not None else 0,
|
|
1801
|
+
'sec2_username': vars.get('sec2_username', 'wifiprov') or 'wifiprov',
|
|
1782
1802
|
'local_raw': vars.get('local_raw', False),
|
|
1783
1803
|
'device_name': vars.get('device_name', None),
|
|
1784
1804
|
'timestamp': timestamp if not (proxy_report and vars.get('local_raw', False) and not timestamp_explicitly_provided) else timestamp,
|
|
@@ -117,8 +117,15 @@ def provision(vars=None):
|
|
|
117
117
|
raise ValueError("Proof of possession (pop) may be required depending on security scheme. "
|
|
118
118
|
"Use --pop, provide via --qrcode, or specify --sec_ver to skip pop requirement "
|
|
119
119
|
"(Security 0 and 2 don't require pop).")
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
|
|
121
|
+
# sec2 credentials: explicit options override QR code values.
|
|
122
|
+
# QR code fields: 'username' and 'password' (sec2 SRP6a credentials).
|
|
123
|
+
sec2_username = (vars.get('sec2_username')
|
|
124
|
+
or qrcode_data.get('username')
|
|
125
|
+
or '')
|
|
126
|
+
sec2_password = (vars.get('sec2_password')
|
|
127
|
+
or qrcode_data.get('password')
|
|
128
|
+
or '')
|
|
122
129
|
ssid = vars.get('ssid')
|
|
123
130
|
passphrase = vars.get('passphrase')
|
|
124
131
|
no_wifi = vars.get('no_wifi', False)
|
|
@@ -56,7 +56,7 @@ def _get_cache_base_dir(profile_config=None):
|
|
|
56
56
|
|
|
57
57
|
def extract_local_control_info(params_or_details):
|
|
58
58
|
"""
|
|
59
|
-
Extract local control info (POP, sec_ver, transport, port) from
|
|
59
|
+
Extract local control info (POP, sec_ver, username, transport, port) from
|
|
60
60
|
node params or node details response data.
|
|
61
61
|
|
|
62
62
|
Scans for a service/device named "Local Control" or similar that
|
|
@@ -74,6 +74,9 @@ def extract_local_control_info(params_or_details):
|
|
|
74
74
|
lower_key = key.lower().replace(' ', '_').replace('-', '_')
|
|
75
75
|
if lower_key in ('pop', 'proof_of_possession'):
|
|
76
76
|
info['pop'] = str(value)
|
|
77
|
+
elif lower_key in ('username', 'user_name', 'local_control_username'):
|
|
78
|
+
if value:
|
|
79
|
+
info['username'] = str(value)
|
|
77
80
|
elif lower_key == 'sec_ver':
|
|
78
81
|
try:
|
|
79
82
|
info['sec_ver'] = int(value)
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security2.py
RENAMED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
# APIs for interpreting and creating protobuf packets for
|
|
4
4
|
# protocomm endpoint with security type protocomm_security2
|
|
5
|
+
import base64
|
|
6
|
+
import hashlib
|
|
5
7
|
import struct
|
|
6
8
|
import sys
|
|
7
9
|
import os
|
|
@@ -75,6 +77,7 @@ class Security2(Security):
|
|
|
75
77
|
|
|
76
78
|
self.client_pop_key = None
|
|
77
79
|
self.nonce = bytearray()
|
|
80
|
+
self.session_key = None
|
|
78
81
|
|
|
79
82
|
Security.__init__(self, self.security2_session)
|
|
80
83
|
|
|
@@ -170,6 +173,7 @@ class Security2(Security):
|
|
|
170
173
|
|
|
171
174
|
# Using the first 256 bits of a 512 bit key
|
|
172
175
|
session_key = shared_secret[:AES_KEY_LEN]
|
|
176
|
+
self.session_key = session_key
|
|
173
177
|
self._print_verbose(f'Session Key:\t0x{session_key.hex()}')
|
|
174
178
|
|
|
175
179
|
# 96-bit nonce
|
|
@@ -203,3 +207,65 @@ class Security2(Security):
|
|
|
203
207
|
plaintext = self.cipher.decrypt(self.nonce, data, None)
|
|
204
208
|
self._increment_nonce()
|
|
205
209
|
return plaintext
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def _creds_hash(username: str, password: str) -> str:
|
|
213
|
+
h = hashlib.sha256()
|
|
214
|
+
h.update(str_to_bytes(username or ''))
|
|
215
|
+
h.update(b'\x00')
|
|
216
|
+
h.update(str_to_bytes(password or ''))
|
|
217
|
+
return h.hexdigest()
|
|
218
|
+
|
|
219
|
+
def serialize(self) -> Any:
|
|
220
|
+
"""
|
|
221
|
+
Serialize session crypto state for disk persistence.
|
|
222
|
+
Returns a dict suitable for JSON serialization.
|
|
223
|
+
"""
|
|
224
|
+
if self.session_key is None or not self.nonce:
|
|
225
|
+
return None
|
|
226
|
+
return {
|
|
227
|
+
'sec_ver': 2,
|
|
228
|
+
'sec_patch_ver': self.sec_patch_ver,
|
|
229
|
+
'session_key': base64.b64encode(self.session_key).decode('ascii'),
|
|
230
|
+
'nonce': base64.b64encode(bytes(self.nonce)).decode('ascii'),
|
|
231
|
+
'creds_hash': Security2._creds_hash(self.username, self.password),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def deserialize(cls, data: dict, username: str = '', password: str = '',
|
|
236
|
+
verbose: bool = False) -> Any:
|
|
237
|
+
"""
|
|
238
|
+
Restore a Security2 object from serialized session data.
|
|
239
|
+
Recreates the AES-GCM cipher and restores the nonce counter.
|
|
240
|
+
|
|
241
|
+
:param data: dict from serialize() / session.json
|
|
242
|
+
:param username: SRP6a username (used for creds_hash validation)
|
|
243
|
+
:param password: SRP6a password (used for creds_hash validation)
|
|
244
|
+
:param verbose: verbose flag
|
|
245
|
+
:return: Security2 instance, or None on hash mismatch / malformed data
|
|
246
|
+
"""
|
|
247
|
+
try:
|
|
248
|
+
session_key = base64.b64decode(data['session_key'])
|
|
249
|
+
nonce = bytearray(base64.b64decode(data['nonce']))
|
|
250
|
+
sec_patch_ver = int(data.get('sec_patch_ver', 0))
|
|
251
|
+
except (KeyError, ValueError, Exception):
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
saved_hash = data.get('creds_hash', '')
|
|
255
|
+
if saved_hash:
|
|
256
|
+
if cls._creds_hash(username, password) != saved_hash:
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
obj = cls.__new__(cls)
|
|
260
|
+
obj.session_state = security_state.FINISHED
|
|
261
|
+
obj.sec_patch_ver = sec_patch_ver
|
|
262
|
+
obj.username = username
|
|
263
|
+
obj.password = password
|
|
264
|
+
obj.verbose = verbose
|
|
265
|
+
obj.client_pop_key = None
|
|
266
|
+
obj.nonce = nonce
|
|
267
|
+
obj.session_key = session_key
|
|
268
|
+
obj.cipher = AESGCM(session_key)
|
|
269
|
+
|
|
270
|
+
Security.__init__(obj, obj.security2_session)
|
|
271
|
+
return obj
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/integration.py
RENAMED
|
@@ -31,10 +31,14 @@ async def _probe_session(transport_obj, security_obj):
|
|
|
31
31
|
return False
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
async def _try_resume_session(nodeid, pop, transport_type, session_store
|
|
34
|
+
async def _try_resume_session(nodeid, pop, transport_type, session_store,
|
|
35
|
+
sec2_username='wifiprov'):
|
|
35
36
|
"""
|
|
36
37
|
Attempt to resume a saved session from disk.
|
|
37
38
|
Returns (transport_obj, security_obj) on success, (None, None) on failure.
|
|
39
|
+
|
|
40
|
+
For sec_ver=2 sessions, sec2_username is used with pop (as the SRP6a password)
|
|
41
|
+
to validate the saved creds_hash.
|
|
38
42
|
"""
|
|
39
43
|
if session_store is None:
|
|
40
44
|
return None, None
|
|
@@ -102,12 +106,20 @@ async def _try_resume_session(nodeid, pop, transport_type, session_store):
|
|
|
102
106
|
return None, None
|
|
103
107
|
|
|
104
108
|
try:
|
|
105
|
-
from ..common.security.security1 import Security1
|
|
106
109
|
from ..common.transport.transport_http import Transport_HTTP
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
saved_sec_ver = session_data.get('sec_ver')
|
|
112
|
+
if saved_sec_ver == 2:
|
|
113
|
+
from ..common.security.security2 import Security2
|
|
114
|
+
security_obj = Security2.deserialize(
|
|
115
|
+
session_data, username=sec2_username, password=pop, verbose=False
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
from ..common.security.security1 import Security1
|
|
119
|
+
security_obj = Security1.deserialize(session_data, pop=pop, verbose=False)
|
|
120
|
+
|
|
109
121
|
if security_obj is None:
|
|
110
|
-
log.debug("Failed to deserialize security object (
|
|
122
|
+
log.debug("Failed to deserialize security object (creds mismatch?)")
|
|
111
123
|
session_store.invalidate_session(nodeid)
|
|
112
124
|
return None, None
|
|
113
125
|
|
|
@@ -182,13 +194,54 @@ def _save_session_state(nodeid, transport_obj, security_obj, transport_type, sec
|
|
|
182
194
|
|
|
183
195
|
|
|
184
196
|
def _update_session_offset(nodeid, security_obj, session_store):
|
|
185
|
-
"""
|
|
197
|
+
"""
|
|
198
|
+
Update the crypto state (CTR offset for sec1, nonce for sec2) in the saved
|
|
199
|
+
session after an operation, so a future resume picks up where we left off.
|
|
200
|
+
"""
|
|
186
201
|
if session_store is None:
|
|
187
202
|
return
|
|
188
203
|
try:
|
|
189
|
-
|
|
204
|
+
if hasattr(security_obj, 'ctr_offset') and not hasattr(security_obj, 'nonce'):
|
|
205
|
+
# Security1 fast-path — cheap partial update.
|
|
206
|
+
session_store.update_session_offset(nodeid, security_obj.ctr_offset)
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
# Security2 (or any future scheme): rewrite the serialized blob so the
|
|
210
|
+
# current nonce/counter is persisted.
|
|
211
|
+
if not hasattr(security_obj, 'serialize'):
|
|
212
|
+
return
|
|
213
|
+
data = session_store.load_session(nodeid)
|
|
214
|
+
if data is None:
|
|
215
|
+
return
|
|
216
|
+
sec_data = security_obj.serialize()
|
|
217
|
+
if sec_data is None:
|
|
218
|
+
return
|
|
219
|
+
data.update(sec_data)
|
|
220
|
+
session_store.save_session(nodeid, data)
|
|
221
|
+
except Exception as e:
|
|
222
|
+
log.debug(f"Failed to update session state: {e}")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _probe_sec_patch_ver(transport_obj):
|
|
226
|
+
"""
|
|
227
|
+
Query esp_local_ctrl/version to discover the device's sec_patch_ver.
|
|
228
|
+
Returns the advertised integer, or 0 if the endpoint is missing/parse fails
|
|
229
|
+
(legacy firmware).
|
|
230
|
+
|
|
231
|
+
protocomm_httpd rejects POSTs with content_len <= 0, so we send "---"
|
|
232
|
+
as a dummy body — the version handler ignores it.
|
|
233
|
+
"""
|
|
234
|
+
try:
|
|
235
|
+
response = transport_obj.send_data('esp_local_ctrl/version', '---')
|
|
236
|
+
if isinstance(response, bytes):
|
|
237
|
+
response = response.decode('utf-8', errors='replace')
|
|
238
|
+
info = json.loads(response)
|
|
239
|
+
local_ctrl = info.get('local_ctrl') if isinstance(info, dict) else None
|
|
240
|
+
if isinstance(local_ctrl, dict) and 'sec_patch_ver' in local_ctrl:
|
|
241
|
+
return int(local_ctrl['sec_patch_ver'])
|
|
190
242
|
except Exception as e:
|
|
191
|
-
log.debug(f"
|
|
243
|
+
log.debug(f"sec_patch_ver probe failed, defaulting to 0: {e}")
|
|
244
|
+
return 0
|
|
192
245
|
|
|
193
246
|
|
|
194
247
|
async def _execute_operation(transport_obj, security_obj, operation, data):
|
|
@@ -204,11 +257,17 @@ async def _execute_operation(transport_obj, security_obj, operation, data):
|
|
|
204
257
|
return None
|
|
205
258
|
|
|
206
259
|
|
|
207
|
-
async def _fresh_establish(nodeid, pop, transport_type, sec_ver, port
|
|
260
|
+
async def _fresh_establish(nodeid, pop, transport_type, sec_ver, port,
|
|
261
|
+
sec2_username='wifiprov', sec_patch_ver=None):
|
|
208
262
|
"""
|
|
209
263
|
Establish a fresh transport + security session.
|
|
210
264
|
Returns (transport_obj, security_obj, error_reason).
|
|
211
265
|
error_reason is None on success, ERR_TRANSPORT or ERR_SECURITY on failure.
|
|
266
|
+
|
|
267
|
+
For sec_ver=2, `pop` is used as the SRP6a password. If sec_patch_ver is
|
|
268
|
+
None, the firmware is probed via esp_local_ctrl/version — newer firmware
|
|
269
|
+
advertises sec_patch_ver=1 (counter-in-IV), older firmware returns 0
|
|
270
|
+
(legacy static IV).
|
|
212
271
|
"""
|
|
213
272
|
if ':' not in nodeid:
|
|
214
273
|
if nodeid.endswith('.local'):
|
|
@@ -225,7 +284,14 @@ async def _fresh_establish(nodeid, pop, transport_type, sec_ver, port):
|
|
|
225
284
|
log.error("Failed to establish transport")
|
|
226
285
|
return None, None, ERR_TRANSPORT
|
|
227
286
|
|
|
228
|
-
|
|
287
|
+
if sec_ver == 2 and sec_patch_ver is None:
|
|
288
|
+
sec_patch_ver = _probe_sec_patch_ver(transport_obj)
|
|
289
|
+
log.debug(f"Using sec_patch_ver={sec_patch_ver} for node {nodeid}")
|
|
290
|
+
if sec_patch_ver is None:
|
|
291
|
+
sec_patch_ver = 0
|
|
292
|
+
|
|
293
|
+
security_obj = get_security(sec_ver, sec_patch_ver,
|
|
294
|
+
sec2_username, pop, pop, False)
|
|
229
295
|
if security_obj is None:
|
|
230
296
|
log.error("Failed to setup security")
|
|
231
297
|
return None, None, ERR_SECURITY
|
|
@@ -327,6 +393,8 @@ async def run_local_control_operation(nodeid, operation, data=None, **kwargs):
|
|
|
327
393
|
node_cache = kwargs.get('node_cache', None)
|
|
328
394
|
session_store = kwargs.get('session_store', None)
|
|
329
395
|
explicit_sec_ver = kwargs.get('explicit_sec_ver', False)
|
|
396
|
+
sec2_username = kwargs.get('sec2_username', 'wifiprov') or 'wifiprov'
|
|
397
|
+
sec_patch_ver = None # discovered lazily in _fresh_establish when needed
|
|
330
398
|
|
|
331
399
|
if node_cache:
|
|
332
400
|
capability = node_cache.get_local_control_capability(nodeid)
|
|
@@ -341,12 +409,14 @@ async def run_local_control_operation(nodeid, operation, data=None, **kwargs):
|
|
|
341
409
|
transport_type = capability.get('transport', transport_type)
|
|
342
410
|
if sec_ver == 0:
|
|
343
411
|
pop = ''
|
|
412
|
+
if capability and capability.get('sec_patch_ver') is not None:
|
|
413
|
+
sec_patch_ver = capability.get('sec_patch_ver')
|
|
344
414
|
|
|
345
415
|
last_err = None
|
|
346
416
|
|
|
347
417
|
try:
|
|
348
418
|
transport_obj, security_obj = await _try_resume_session(
|
|
349
|
-
nodeid, pop, transport_type, session_store
|
|
419
|
+
nodeid, pop, transport_type, session_store, sec2_username
|
|
350
420
|
)
|
|
351
421
|
|
|
352
422
|
resumed = transport_obj is not None
|
|
@@ -363,11 +433,13 @@ async def run_local_control_operation(nodeid, operation, data=None, **kwargs):
|
|
|
363
433
|
return None
|
|
364
434
|
else:
|
|
365
435
|
transport_obj, security_obj, last_err = await _fresh_establish(
|
|
366
|
-
nodeid, pop, transport_type, sec_ver, port
|
|
436
|
+
nodeid, pop, transport_type, sec_ver, port,
|
|
437
|
+
sec2_username, sec_patch_ver
|
|
367
438
|
)
|
|
368
439
|
else:
|
|
369
440
|
transport_obj, security_obj, last_err = await _fresh_establish(
|
|
370
|
-
nodeid, pop, transport_type, sec_ver, port
|
|
441
|
+
nodeid, pop, transport_type, sec_ver, port,
|
|
442
|
+
sec2_username, sec_patch_ver
|
|
371
443
|
)
|
|
372
444
|
|
|
373
445
|
if transport_obj is None or security_obj is None:
|
|
@@ -381,14 +453,25 @@ async def run_local_control_operation(nodeid, operation, data=None, **kwargs):
|
|
|
381
453
|
|
|
382
454
|
if result is not None:
|
|
383
455
|
_update_session_offset(nodeid, security_obj, session_store)
|
|
384
|
-
if node_cache
|
|
385
|
-
node_cache.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
456
|
+
if node_cache:
|
|
457
|
+
existing = node_cache.get_local_control_capability(nodeid)
|
|
458
|
+
obj_patch_ver = getattr(security_obj, 'sec_patch_ver', None)
|
|
459
|
+
if not existing:
|
|
460
|
+
cap = {
|
|
461
|
+
'supported': True,
|
|
462
|
+
'sec_ver': sec_ver,
|
|
463
|
+
'pop_required': bool(pop),
|
|
464
|
+
'transport': transport_type,
|
|
465
|
+
'port': port,
|
|
466
|
+
}
|
|
467
|
+
if sec_ver == 2 and obj_patch_ver is not None:
|
|
468
|
+
cap['sec_patch_ver'] = obj_patch_ver
|
|
469
|
+
node_cache.set_local_control_capability(nodeid, cap)
|
|
470
|
+
elif (sec_ver == 2 and obj_patch_ver is not None
|
|
471
|
+
and existing.get('sec_patch_ver') != obj_patch_ver):
|
|
472
|
+
# Persist freshly-probed patch_ver so future connects skip the probe.
|
|
473
|
+
existing['sec_patch_ver'] = obj_patch_ver
|
|
474
|
+
node_cache.set_local_control_capability(nodeid, existing)
|
|
392
475
|
|
|
393
476
|
if result is None and resumed:
|
|
394
477
|
log.debug("Operation failed on resumed session, retrying with fresh session")
|
|
@@ -396,7 +479,8 @@ async def run_local_control_operation(nodeid, operation, data=None, **kwargs):
|
|
|
396
479
|
session_store.invalidate_session(nodeid)
|
|
397
480
|
|
|
398
481
|
transport_obj, security_obj, last_err = await _fresh_establish(
|
|
399
|
-
nodeid, pop, transport_type, sec_ver, port
|
|
482
|
+
nodeid, pop, transport_type, sec_ver, port,
|
|
483
|
+
sec2_username, sec_patch_ver
|
|
400
484
|
)
|
|
401
485
|
if transport_obj is None or security_obj is None:
|
|
402
486
|
_set_error(last_err or ERR_SECURITY)
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/raw_config.py
RENAMED
|
@@ -524,6 +524,7 @@ async def run_raw_data_operation(nodeid, data_type, **kwargs):
|
|
|
524
524
|
port = kwargs.get('port', None)
|
|
525
525
|
device_name = kwargs.get('device_name', None)
|
|
526
526
|
timestamp = kwargs.get('timestamp', None)
|
|
527
|
+
sec2_username = kwargs.get('sec2_username', 'wifiprov') or 'wifiprov'
|
|
527
528
|
endpoint_name = 'get_params' if data_type == 0 else 'get_config'
|
|
528
529
|
data_name = 'params' if data_type == 0 else 'config'
|
|
529
530
|
|
|
@@ -575,8 +576,9 @@ async def run_raw_data_operation(nodeid, data_type, **kwargs):
|
|
|
575
576
|
if actual_sec_ver is None:
|
|
576
577
|
actual_sec_ver = 1
|
|
577
578
|
|
|
578
|
-
# Setup security
|
|
579
|
-
security_obj = get_security(actual_sec_ver, sec_patch_ver,
|
|
579
|
+
# Setup security. For sec_ver=2, pop is used as the SRP6a password.
|
|
580
|
+
security_obj = get_security(actual_sec_ver, sec_patch_ver,
|
|
581
|
+
sec2_username, pop, pop, False)
|
|
580
582
|
if security_obj is None:
|
|
581
583
|
print("Failed to setup security")
|
|
582
584
|
return None
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/raw_params.py
RENAMED
|
@@ -356,6 +356,7 @@ async def run_raw_params_operation(nodeid, operation, data=None, **kwargs):
|
|
|
356
356
|
sec_ver = kwargs.get('sec_ver', 0) # Default to 0 for raw endpoints
|
|
357
357
|
port = kwargs.get('port', None)
|
|
358
358
|
device_name = kwargs.get('device_name', None)
|
|
359
|
+
sec2_username = kwargs.get('sec2_username', 'wifiprov') or 'wifiprov'
|
|
359
360
|
|
|
360
361
|
# Build service name
|
|
361
362
|
# For BLE transport, prefer device_name if provided, otherwise fallback to nodeid
|
|
@@ -414,8 +415,10 @@ async def run_raw_params_operation(nodeid, operation, data=None, **kwargs):
|
|
|
414
415
|
if actual_sec_ver is None:
|
|
415
416
|
actual_sec_ver = 1 # Default to Security 1
|
|
416
417
|
|
|
417
|
-
# Setup security using detected/configured version
|
|
418
|
-
|
|
418
|
+
# Setup security using detected/configured version.
|
|
419
|
+
# For sec_ver=2, pop is used as the SRP6a password.
|
|
420
|
+
security_obj = get_security(actual_sec_ver, sec_patch_ver,
|
|
421
|
+
sec2_username, pop, pop, False)
|
|
419
422
|
if security_obj is None:
|
|
420
423
|
print("Failed to setup security")
|
|
421
424
|
return None
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/esp_rainmaker_prov.py
RENAMED
|
@@ -240,32 +240,40 @@ def provision_device(transport_mode, pop, userid, secretkey,
|
|
|
240
240
|
print("Establishing connection to node - Failed")
|
|
241
241
|
return None
|
|
242
242
|
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if not esp_prov.has_capability(obj_transport):
|
|
247
|
-
print('Security capabilities could not be determined, defaulting to Security 1')
|
|
248
|
-
security_version = 1
|
|
249
|
-
else:
|
|
250
|
-
# When no_sec is present, use security 0, else security 1
|
|
251
|
-
security_version = int(not esp_prov.has_capability(obj_transport, 'no_sec'))
|
|
252
|
-
print(f'==== Auto-detected Security Scheme: {security_version} ====')
|
|
253
|
-
|
|
254
|
-
# Fetch and print device capabilities before checking pop requirements
|
|
255
|
-
# This helps users understand why pop might be required
|
|
256
|
-
# Store the response to reuse later for challenge-response check
|
|
243
|
+
# Fetch device capabilities up-front so auto-detect can read prov.sec_ver
|
|
244
|
+
# directly (no_sec / no_pop caps alone don't distinguish sec1 from sec2).
|
|
245
|
+
# The response is reused later for challenge-response checks.
|
|
257
246
|
version_response = None
|
|
247
|
+
prov_info = {}
|
|
258
248
|
try:
|
|
259
249
|
print("Checking device capabilities...")
|
|
260
250
|
version_response = esp_prov.get_version(obj_transport)
|
|
261
251
|
if version_response:
|
|
262
252
|
print(f"Device capabilities response: {version_response}")
|
|
253
|
+
try:
|
|
254
|
+
prov_info = json.loads(version_response).get('prov', {}) or {}
|
|
255
|
+
except (ValueError, AttributeError):
|
|
256
|
+
prov_info = {}
|
|
263
257
|
else:
|
|
264
258
|
print("Device capabilities response: (empty or not available)")
|
|
265
259
|
except Exception as e:
|
|
266
260
|
# If we can't get capabilities, continue anyway
|
|
267
261
|
print(f"Could not retrieve device capabilities: {e}")
|
|
268
262
|
|
|
263
|
+
# Auto-detect security version if not specified
|
|
264
|
+
if security_version is None:
|
|
265
|
+
advertised = prov_info.get('sec_ver')
|
|
266
|
+
if isinstance(advertised, int):
|
|
267
|
+
security_version = advertised
|
|
268
|
+
elif 'no_sec' in prov_info.get('cap', []):
|
|
269
|
+
security_version = 0
|
|
270
|
+
elif prov_info:
|
|
271
|
+
security_version = 1
|
|
272
|
+
else:
|
|
273
|
+
print('Security capabilities could not be determined, defaulting to Security 1')
|
|
274
|
+
security_version = 1
|
|
275
|
+
print(f'==== Auto-detected Security Scheme: {security_version} ====')
|
|
276
|
+
|
|
269
277
|
# Handle Security 1 PoP requirements
|
|
270
278
|
if security_version == 1:
|
|
271
279
|
if not esp_prov.has_capability(obj_transport, 'no_pop'):
|
|
@@ -280,9 +288,20 @@ def provision_device(transport_mode, pop, userid, secretkey,
|
|
|
280
288
|
# Handle Security 2 credentials
|
|
281
289
|
sec_patch_ver = 0
|
|
282
290
|
if security_version == 2:
|
|
283
|
-
sec_patch_ver
|
|
291
|
+
# Prefer the sec_patch_ver already present in the cached prov_info, else probe.
|
|
292
|
+
advertised_patch = prov_info.get('sec_patch_ver') if prov_info else None
|
|
293
|
+
sec_patch_ver = int(advertised_patch) if isinstance(advertised_patch, int) \
|
|
294
|
+
else esp_prov.get_sec_patch_ver(obj_transport)
|
|
295
|
+
|
|
296
|
+
# Default username to 'wifiprov' (firmware default for the RainMaker
|
|
297
|
+
# provisioning endpoint) and treat --pop / QR pop as the SRP6a password
|
|
298
|
+
# when no explicit --sec2_password is given. Only prompt when neither
|
|
299
|
+
# pop nor sec2_password is available.
|
|
284
300
|
if len(sec2_username) == 0:
|
|
285
|
-
sec2_username =
|
|
301
|
+
sec2_username = 'wifiprov'
|
|
302
|
+
print(f"Using default Security 2 username '{sec2_username}'")
|
|
303
|
+
if len(sec2_password) == 0 and len(pop) > 0:
|
|
304
|
+
sec2_password = pop
|
|
286
305
|
if len(sec2_password) == 0:
|
|
287
306
|
sec2_password = getpass('Security Scheme 2 - SRP6a Password required: ')
|
|
288
307
|
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/requires.txt
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/esp_rainmaker_cli.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/discovery/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/__init__.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security0.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/security/security1.py
RENAMED
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/__init__.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/ble_cli.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/transport/transport_ble.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/common/utils/convenience.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_claim/claim_config.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/esp_prov.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec0_pb2.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec1_pb2.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/sec2_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_local_ctrl/session_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/challenge_response.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/config/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/proto/__init__.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/protocomm/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/__init__.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/prov_util.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/prov/user_mapping.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/security/__init__.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/transport/__init__.py
RENAMED
|
File without changes
|
{esp_rainmaker_cli-1.14.0 → esp_rainmaker_cli-1.14.1}/rmaker_tools/rmaker_prov/utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|