From e8762480ae3b0d8546de48ae3cf4c74af8c6b251 Mon Sep 17 00:00:00 2001 From: Olivier Gimenez <176398299+ogimenez-wirepas@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:52:59 +0200 Subject: [PATCH 1/2] Refactor data model --- examples/provisioning_config.yml | 11 +- requirements-dev.txt | 3 + tests/test_models.py | 145 +++++++++++++ wirepas_provisioning_server/data.py | 192 +++--------------- wirepas_provisioning_server/models.py | 181 ++++++++++++++++- .../provisioning_server.py | 2 +- wirepas_provisioning_server/session.py | 8 +- 7 files changed, 369 insertions(+), 173 deletions(-) create mode 100644 requirements-dev.txt create mode 100644 tests/test_models.py diff --git a/examples/provisioning_config.yml b/examples/provisioning_config.yml index b13d4d4..2a0d39b 100644 --- a/examples/provisioning_config.yml +++ b/examples/provisioning_config.yml @@ -12,8 +12,8 @@ # version: 1 # networks: # network_name: (mandatory)(string) Ex: test_network -# address: (optional)(uint) Ex: 0x1012EE -# channel: (optional)(uint) Ex: 13 +# address: (mandatory)(uint) Ex: 0x1012EE +# channel: (mandatory)(uint) Ex: 13 # authentication_key : (mandatory)(16 bytes string) Ex: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF # encryption_key : (mandatory)(16 bytes string) Ex: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF # @@ -65,4 +65,11 @@ nodes: network: network_prod node_id: 17 uid: 0x58 0xc8 0x12 0xad 0x37 0xe8 0x36 0x4a 0xa1 0x1f 0x1c 0xbc 0x63 0x3e 0x8e 0x34 + test_node_extended_uuid_only: + uid: 0x70 0xC8 0x33 0x00 0x00 0x00 0x00 0x00 0x00 0x87 0x00 0x00 0x00 0x00 0x00 0x01 0x01 0x7F 0xC8 0x33 0x00 0x00 0x00 0x00 0x00 0x00 0x87 0x00 0x00 0x00 0x00 0x00 0x01 0x01 + method: 0x03 + network: network_prod + node_id: 0x05 + factory_key: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0X09 0X0A 0X0B 0X0C 0X0D 0X0E 0X0F 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0X09 0X0A 0X0B 0X0C 0X0D 0X0E 0X0F + version: 1 diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..4a2c11d --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +# Copyright 2021 Wirepas Ltd 2021 + +pytest==8.3.3 \ No newline at end of file diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..2317089 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,145 @@ +import json +import pydantic +import pytest +from typing import Any +from wirepas_provisioning_server.message import ProvisioningMethod +from wirepas_provisioning_server.models import NodeV1, NetworkV1 + + +@pytest.fixture +def network_data() -> dict[str, Any]: + return dict( + name="test_network", + address=0x1012EE, + channel=13, + authentication_key="0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF", + encryption_key="0xAA 0xBB 0xCC 0xDD 0xEE 0xFF 0x00 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99", + ) + + +@pytest.fixture +def node_data_secured(network_data) -> dict[str, Any]: + return dict( + factory_key="0xAABBCCDDEEFF00112233445566778899AABBCCDDEEFF00112233445566778899", + method=1, + name="test_node", + network=NetworkV1(**network_data), + node_id=0x654321, + role=0x01, + uid="0x00 0x11 0x12 0x13", + user_specific={ + 128: 0xAA, + 255: 0xBB, + }, + ) + + +def test_node_v1_basics( + network_data: dict[str, Any], node_data_secured: dict[str, Any] +): + + node = NodeV1(**node_data_secured) + + assert node.factory_key == bytes.fromhex( + "AABBCCDDEEFF00112233445566778899AABBCCDDEEFF00112233445566778899" + ) + assert node.method == ProvisioningMethod.SECURED + assert node.name == "test_node" + assert node.network == NetworkV1(**network_data) + assert node.node_id == 0x654321 + assert node.role == bytes.fromhex("01") + assert node.uid == bytes.fromhex("00111213") + assert node.user_specific == {128: 0xAA, 255: 0xBB} + + +def test_node_v1_validation_key(node_data_secured: dict[str, Any]): + + with pytest.raises(ValueError) as e: + node_data_secured["factory_key"] = b"\x00\x11\x12\x13" + NodeV1(**node_data_secured) + assert 'Factory key must be 32 bytes, got "4"' in str(e) + + +def test_node_v1_method(node_data_secured: dict[str, Any]): + + for i in [-1, 2, 4]: + with pytest.raises(ValueError) as e: + node_data_secured["method"] = i + NodeV1(**node_data_secured) + assert "Input should be 0, 1 or 3 [type=enum" in str(e) + + for i in [0, 1]: + node_data_secured["method"] = i + assert NodeV1(**node_data_secured).method == i + + + +def test_node_v1_node_id(node_data_secured: dict[str, Any]): + + for i in [0x00000000, 0x80000000, 0x80FFFFFF, 0xFFFFFFFF]: + with pytest.raises(pydantic.ValidationError) as e: + node_data_secured["node_id"] = i + NodeV1(**node_data_secured) + print(str(e)) + assert "Node ID must be None, between [0x1; 0x7FFFFFFF] or [0x81000000" in str(e) + + for i in [1, 0x7FFFFFFF, 0x81000000, 0xFFFFFFFD]: + node_data_secured["node_id"] = i + assert NodeV1(**node_data_secured).node_id == i + +def test_network_v1_basics(network_data: dict[str, Any]): + + network = NetworkV1(**network_data) + + assert network.name == "test_network" + assert network.address == 0x1012EE + assert network.channel == 13 + assert network.authentication_key == bytes.fromhex( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + ) + assert network.encryption_key == bytes.fromhex("AABBCCDDEEFF00112233445566778899") + + with pytest.raises(pydantic.ValidationError): + network.name = "new_name" + + json_data = NetworkV1(**network_data).model_dump_json() + assert json.loads(json_data) == { + "name": "test_network", + "address": 0x1012EE, + "channel": 13, + "authentication_key": "0xffffffffffffffffffffffffffffffff", + "encryption_key": "0xaabbccddeeff00112233445566778899", + } + + +def test_network_v1_optional_fields(network_data: dict[str, Any]): + del network_data["address"] + del network_data["channel"] + + network = NetworkV1(**network_data) + assert network.name == "test_network" + assert network.address is None + assert network.channel is None + assert network.authentication_key == bytes.fromhex( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + ) + assert network.encryption_key == bytes.fromhex("AABBCCDDEEFF00112233445566778899") + + +def test_network_v1_keys(network_data: dict[str, Any]): + network_data["authentication_key"] = "0x01020304050607080910111213141516" + network_data["encryption_key"] = 0x01020304050607080910111213141516 + + network = NetworkV1(**network_data) + assert network.authentication_key == bytes.fromhex( + "01020304050607080910111213141516" + ) + assert network.encryption_key == bytes.fromhex("01020304050607080910111213141516") + + with pytest.raises(ValueError) as e: + NetworkV1( + name="invalid_key_length", + authentication_key=b"\x01\x02", + encryption_key=b"\x01\x02", + ) + assert "Keys must be 16 bytes" in str(e) diff --git a/wirepas_provisioning_server/data.py b/wirepas_provisioning_server/data.py index 15a6c45..733c6b8 100644 --- a/wirepas_provisioning_server/data.py +++ b/wirepas_provisioning_server/data.py @@ -11,10 +11,11 @@ import yaml import logging -from typing import Final, Optional +from typing import Optional from wirepas_provisioning_server.helpers import convert_to_bytes, convert_to_int, ProvisioningDataException from wirepas_provisioning_server.message import ProvisioningMethod +from wirepas_provisioning_server.models import NetworkV1, NodeV1 from wirepas_provisioning_server.migrate_config import ConfigFileMigration @@ -65,169 +66,34 @@ def __init__(self, config: Optional[str] = None): if cfg.get("version") != 1: raise ProvisioningDataException("Invalid data config file. Version must be 1") - # Validate network parameters + networks: dict[str, NetworkV1] = {} for name, network in cfg["networks"].items(): - try: - for parameter in [ - "authentication_key", - "encryption_key", - ]: - network[parameter] - except KeyError as e: - raise ProvisioningDataException(f"Invalid data config file. Network {name} must include {str(e)}.") - - for node_name, node_cfg in cfg["nodes"].items(): - if "network" not in node_cfg.keys(): - raise ProvisioningDataException(f"Invalid data config file. Node {node_name} must include network.") - network_name = node_cfg["network"] - - if "method" not in node_cfg.keys(): - raise ProvisioningDataException(f"Invalid data config file. Node {node_name} must include method.") - - provision_methods = [e.value for e in ProvisioningMethod] - if node_cfg["method"] not in provision_methods: - raise ProvisioningDataException(f"Node method must be one of {provision_methods}") - - if "uid" in node_cfg.keys(): - uid: str | int | bytes = node_cfg["uid"] - elif node_cfg["method"] == ProvisioningMethod.EXTENDED: - try: - uid = _generate_extended_uid( - node_cfg["authenticator_uid_type"], - node_cfg["authenticator_uid"], - node_cfg["node_uid_type"], - node_cfg["node_uid"], - ) - - except KeyError: - raise ProvisioningDataException( - f"Invalid data config file. Node {node_name} must include UID information." - ) - else: - raise ProvisioningDataException(f"Invalid data config file. Node {node_name} must include UID information") - - if "address" in cfg["networks"][network_name].keys(): - network_address = convert_to_int(cfg["networks"][network_name]["address"]) - else: - network_address = None - - if "channel" in cfg["networks"][network_name].keys(): - network_channel = convert_to_int(cfg["networks"][network_name]["channel"]) - else: - network_channel = None - - if "node_id" in node_cfg.keys(): - node_id = convert_to_int(node_cfg["node_id"]) - else: - node_id = None - - if "node_role" in node_cfg.keys(): - node_role = convert_to_bytes(node_cfg["node_role"]) - else: - node_role = None - - if "user_specific" in node_cfg.keys(): - user_specific = dict() - for k in node_cfg["user_specific"]: - if k < 128 or k > 255: - raise KeyError - user_specific[k] = node_cfg["user_specific"][k] - else: - user_specific = None + networks[name] = NetworkV1( + **network, + name=name, + ) - if "factory_key" in node_cfg.keys(): - factory_key = convert_to_bytes(node_cfg["factory_key"]) - else: - factory_key = None - - self.append( - convert_to_bytes(uid), - node_cfg["method"], - convert_to_bytes(cfg["networks"][network_name]["encryption_key"]), - convert_to_bytes(cfg["networks"][network_name]["authentication_key"]), - network_address, - network_channel, - node_id=node_id, - node_role=node_role, - user_specific=user_specific, - factory_key=factory_key, + for name, raw_node in cfg["nodes"].items(): + raw_node["network"] = networks[raw_node["network"]] + node = NodeV1( + **raw_node, + name=name, ) + self[node.uid] = node - def append( - self, - uid: bytes, - method: int, - encryption_key: bytes, - authentication_key: bytes, - network_address: Optional[int], - network_channel: Optional[int], - node_id: Optional[int] = None, - node_role: Optional[bytes] = None, - user_specific: Optional[dict[int, bytes | str]] = None, - factory_key: Optional[bytes] = None, - ) -> None: - - # TODO : parameter checks - self[uid] = dict( - method=method, - encryption_key=encryption_key, - authentication_key=authentication_key, - ) - if network_address is not None: - self[uid]["network_address"] = network_address - - if network_channel is not None: - self[uid]["network_channel"] = network_channel - - if node_id is not None: - self[uid]["node_id"] = node_id - - if node_role is not None: - self[uid]["node_role"] = node_role - - if user_specific is not None: - self[uid]["user_specific"] = dict() - for k in user_specific: - # k should be an integer [128:255] - # authorized type for value are string, byte string, integers - self[uid]["user_specific"][k] = user_specific[k] - - if factory_key is not None: - self[uid]["factory_key"] = factory_key - - logging.info("Append new UID: %s", uid.hex()) - logging.debug(" - method: %s", method) - logging.debug(" - factory_key: %s", factory_key) - logging.debug(" - encryption_key: %s", encryption_key) - logging.debug(" - authentication_key: %s", authentication_key) - logging.debug(" - network_address: %s", network_address) - logging.debug(" - network_channel: %s", network_channel) - logging.debug(" - node_id: %s", node_id) - logging.debug(" - node_role: %s", node_role) - if "user_specific" in self[uid].keys(): - for k in self[uid]["user_specific"]: - logging.debug(" - %d : %s", k, self[uid]["user_specific"][k]) - - def getCbor(self, uid: bytes) -> bytes: - self_dic = dict() - - self_dic[0] = self[uid]["encryption_key"] - self_dic[1] = self[uid]["authentication_key"] - - if "network_address" in self[uid].keys(): - self_dic[2] = self[uid]["network_address"] - - if "network_channel" in self[uid].keys(): - self_dic[3] = self[uid]["network_channel"] - - if "node_id" in self[uid].keys(): - self_dic[4] = self[uid]["node_id"] - - if "node_role" in self[uid].keys(): - self_dic[5] = self[uid]["node_role"] - - if "user_specific" in self[uid].keys(): - for key in self[uid]["user_specific"]: - self_dic[key] = self[uid]["user_specific"][key] - - return cbor2.dumps(self_dic) + logging.info("Append new UID: 0x%s", node.uid.hex().upper()) + logging.debug(" - method: %s", node.method) + if node.factory_key is not None: + logging.debug(" - factory_key: 0x%s", node.factory_key.hex().upper()) + else: + logging.debug(" - factory_key: None") + logging.debug(" - encryption_key: 0x%s", node.network.encryption_key.hex().upper()) + logging.debug(" - authentication_key: 0x%s", node.network.authentication_key.hex().upper()) + logging.debug(" - network_address: %s", node.network.address) + logging.debug(" - network_channel: %s", node.network.channel) + logging.debug(" - node_id: %s", node.node_id) + logging.debug(" - node_role: %s", node.role) + if node.user_specific is not None: + logging.debug(" - User specific data:: %s", node.role) + for index, value in node.user_specific.items(): + logging.debug(" - %d: %s", index, value) diff --git a/wirepas_provisioning_server/models.py b/wirepas_provisioning_server/models.py index e79dae0..9bb68e1 100644 --- a/wirepas_provisioning_server/models.py +++ b/wirepas_provisioning_server/models.py @@ -7,8 +7,11 @@ See file LICENSE for full license details. """ -from pydantic import BaseModel, Field, field_validator -from typing import Optional +import cbor2 +from enum import IntEnum +from pydantic import BaseModel, Field, field_validator, model_validator +from typing import Any, Final, Optional +from wirepas_provisioning_server.message import ProvisioningMethod from wirepas_provisioning_server.helpers import convert_to_bytes @@ -24,7 +27,179 @@ class NetworkV1(BaseModel): @field_validator("authentication_key", "encryption_key", mode="before") @classmethod def check_key(cls, key: bytes | int | str) -> bytes: - return convert_to_bytes(key) + key = convert_to_bytes(key) + if len(key) != 16: + raise ValueError(f'Keys must be 16 bytes, got "{len(key)}"') + return key class Config: json_encoders = {bytes: lambda value: f"0x{value.hex()}"} + + +class NodeV1(BaseModel): + """Holding device parameters for the V1 configuration file format.""" + + class AuthenticatorUIDType(IntEnum): + """Node UID types.""" + + UUID4 = 1 + + class NodeUIDType(IntEnum): + """Node UID types.""" + + UUID4 = 1 + + class UserSpecificType(BaseModel): + """Holding user specific parameters.""" + + key: int = Field(ge=128, le=255) + value: Any + + node_id: Optional[int] = Field(default=None, description="Address to be allocated to the node.") + factory_key: Optional[bytes] = Field(default=None, description="Key to secure the provisioning process.") + method: ProvisioningMethod = Field(description="Provisioning method - unsecured: 0, secured: 1, extended: 3.") + name: str = Field(description="Node label") + network: NetworkV1 = Field(description="Network parameters where the node will be provisioned.") + role: Optional[bytes] = Field( + default=None, + description="Role of the node - https://github.com/wirepas/wm-sdk-2_4/blob/rel_1.5.2_2_4/libraries/dualmcu/api/DualMcuAPI.md#cNodeRole", # noqa: E501 + ) + uid: bytes = Field(default=None, description="Unique identifier of the node.") + user_specific: Optional[dict[int, Any]] = Field( + default=None, description="Dict containing user specific parameters to be sent to the device." + ) + + authenticator_uid_type: Optional[AuthenticatorUIDType] = None + authenticator_uid: Optional[bytes] = None # TODO add a validation + node_uid_type: Optional[NodeUIDType] = None + node_uid: Optional[bytes] = None # TODO add a validation + + @staticmethod + def _generate_extended_uid( + authenticator_uid_type_raw: str | int, + authenticator_uid_raw: str | int, + node_uid_type_raw: str | int, + node_uid_raw: str | int, + ) -> bytes: + """ + Generate extended UID bytes + """ + + authenticator_uid_type = convert_to_bytes(authenticator_uid_type_raw) + authenticator_uid = convert_to_bytes(authenticator_uid_raw) + node_uid_type = convert_to_bytes(node_uid_type_raw) + node_uid = convert_to_bytes(node_uid_raw) + + def _any_is_not_bytes(*args: bytes | list[bytes]) -> bool: + return any(not isinstance(arg, bytes) for arg in args) + + if _any_is_not_bytes(authenticator_uid_type, authenticator_uid, node_uid_type, node_uid): + raise ValueError("Parameters must be convertible to bytes") + + if any(len(arg) != 1 for arg in [authenticator_uid_type, node_uid_type]): + raise ValueError("UID type must be 1 byte") + + return b"".join([authenticator_uid_type, authenticator_uid, node_uid_type, node_uid]) + + @field_validator("factory_key", mode="before") + @classmethod + def check_factory_key(cls, key: bytes | int | str) -> bytes: + key = convert_to_bytes(key) + if len(key) != 32: + raise ValueError(f'Factory key must be 32 bytes, got "{len(key)}"') + return key + + @field_validator("node_id", mode="before") + @classmethod + def check_node_id(cls, node_id: int) -> int: + if None or (node_id >= 0x00000001 and node_id <= 0x7FFFFFFF) or (node_id >= 0x81000000 and node_id <= 0xFFFFFFFD): + return node_id + else: + raise ValueError( + f'Node ID must be None, between [0x1; 0x7FFFFFFF] or [0x81000000, 0xFFFFFFFD], got "{node_id} (0x{node_id:08x})"' # noqa: E501 + ) + + @field_validator("uid", mode="before") + @classmethod + def check_uid(cls, uid: bytes | str) -> bytes: + uid = convert_to_bytes(uid) + if len(uid) < 1 or len(uid) > 79: + raise ValueError(f'UID must be between 1 and 79 bytes, got "{len(uid)}".') + return uid + + @field_validator("authenticator_uid", "node_uid", mode="before") + @classmethod + def check_extended_uids(cls, uid: bytes | str) -> bytes: + uid = convert_to_bytes(uid) + if len(uid) != 16: + raise ValueError(f'authenticator_uid and node_uid must be 16 bytes, got "{len(uid)}".') + return uid + + @field_validator("role", mode="before") + @classmethod + def check_role(cls, role_raw: bytes | int | str) -> bytes: + ALLOWED_ROLES: Final = [0x1, 0x2, 0x3, 0x11, 0x12, 0x13, 0x82, 0x83, 0x92, 0x93] + + if isinstance(role_raw, str): + role_bytes = convert_to_bytes(role_raw) + elif isinstance(role_raw, int): + role_bytes = bytes([role_raw]) + else: + role_bytes = role_raw + + if len(role_bytes) != 1: + raise ValueError(f'Role must be 1 byte, got "{len(role_bytes)}".') + + if role_bytes[0] not in ALLOWED_ROLES: + raise ValueError(f"Invalid role value: 0x{role_bytes.hex()}.") + return role_bytes + + @field_validator("user_specific", mode="before") + @classmethod + def check_user_specific_index(cls, data: dict[int, Any]) -> dict[int, Any]: + for index in data.keys(): + if index < 128 or index > 255: + raise ValueError(f'user_specific index must be between 128 and 255, got "{index}"') + return data + + @model_validator(mode="after") + def compute_uid(self: "NodeV1") -> "NodeV1": + """Compute uid from extended uid parameters, if not provided.""" + if self.method == ProvisioningMethod.EXTENDED and self.uid is None: + for attribute in ["authenticator_uid_type", "authenticator_uid", "node_uid_type", "node_uid"]: + if getattr(self, attribute) is None: + raise ValueError( + f"Invalid extended uid parameters: {attribute} should be provided if method is ProvisioningMethod.EXTENDED (3)" # noqa: E501 + ) + self.uid = self._generate_extended_uid( + self.authenticator_uid_type, + self.authenticator_uid, + self.node_uid_type, + self.node_uid, + ) + + elif self.uid is None: + raise ValueError("Invalid uid parameters: uid should be provided if method is not ProvisioningMethod.EXTENDED (3)") + return self + + def getCbor(self) -> bytes: + """Returns the CBOR representation of the node.""" + data = {} + + data[0] = self.network.encryption_key + data[1] = self.network.authentication_key + + if self.network.address is not None: + data[2] = self.network.address # type: ignore[assignment] + if self.network.channel is not None: + data[3] = self.network.channel # type: ignore[assignment] + if self.node_id is not None: + data[4] = self.node_id # type: ignore[assignment] + if self.role is not None: + data[5] = self.role + if self.user_specific is not None: + for index, value in self.user_specific.items(): + # Data conversion is handled by cbor2 + data[index] = value + + return cbor2.dumps(data) diff --git a/wirepas_provisioning_server/provisioning_server.py b/wirepas_provisioning_server/provisioning_server.py index 3d341cc..8d60f85 100644 --- a/wirepas_provisioning_server/provisioning_server.py +++ b/wirepas_provisioning_server/provisioning_server.py @@ -147,7 +147,7 @@ def main() -> None: ) parser.add_argument( "--loglevel", - default=get_default_value_from_env("WM_PROV_LOG_LEVEL", "INFO"), + default=get_default_value_from_env("WM_PROV_LOG_LEVEL", "DEBUG"), type=str, help=f'Log level, choose one of {", ".join(logging._nameToLevel.keys())} ', ) diff --git a/wirepas_provisioning_server/session.py b/wirepas_provisioning_server/session.py index 199487f..ea2f01a 100644 --- a/wirepas_provisioning_server/session.py +++ b/wirepas_provisioning_server/session.py @@ -134,8 +134,8 @@ def _send_packet(self, payload: bytes) -> GatewayResultCode: return GatewayResultCode.GW_RES_INTERNAL_ERROR def _encrypt_packet(self, uid: bytes, iv: bytes, plain_text: bytes) -> bytes: - enc_key = self.data[uid]["factory_key"][16:32] - auth_key = self.data[uid]["factory_key"][0:16] + enc_key = self.data[uid].factory_key[16:32] + auth_key = self.data[uid].factory_key[0:16] logging.info(" - Encrypt DATA packet") # Increment counter @@ -178,10 +178,10 @@ def _encrypt_packet(self, uid: bytes, iv: bytes, plain_text: bytes) -> bytes: def _process_start(self, msg: ProvisioningMessageSTART) -> None: # This is a START packet - if msg.uid in self.data.keys() and self.data[msg.uid]["method"] == msg.method: + if msg.uid in self.data.keys() and self.data[msg.uid].method == msg.method: logging.info(" - Sending Provisioning DATA for UID(%s).", msg.uid.hex()) - data_bytes = self.data.getCbor(msg.uid) + data_bytes = self.data[msg.uid].getCbor() if msg.method == ProvisioningMethod.UNSECURED: key_idx = 0 From 55910c1db01535406e03ab13ebfd5980187ab89c Mon Sep 17 00:00:00 2001 From: Olivier Gimenez <176398299+ogimenez-wirepas@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:17:20 +0100 Subject: [PATCH 2/2] Revert default log level --- wirepas_provisioning_server/provisioning_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wirepas_provisioning_server/provisioning_server.py b/wirepas_provisioning_server/provisioning_server.py index 8d60f85..3d341cc 100644 --- a/wirepas_provisioning_server/provisioning_server.py +++ b/wirepas_provisioning_server/provisioning_server.py @@ -147,7 +147,7 @@ def main() -> None: ) parser.add_argument( "--loglevel", - default=get_default_value_from_env("WM_PROV_LOG_LEVEL", "DEBUG"), + default=get_default_value_from_env("WM_PROV_LOG_LEVEL", "INFO"), type=str, help=f'Log level, choose one of {", ".join(logging._nameToLevel.keys())} ', )