Ejemplo n.º 1
0
    def parse_bytes(profile_bytes: bytes):
        """
        Profile generic are sent as a sequence of A-XDR encoded DlmsData.
        """
        data_decoder = a_xdr.AXdrDecoder(encoding_conf=a_xdr.EncodingConf(
            attributes=[a_xdr.Sequence(attribute_name="data")]))
        entries: List[List[Any]] = data_decoder.decode(profile_bytes)["data"]

        return AssociationObjectListParser.parse_entries(entries)
Ejemplo n.º 2
0
    encryption_key=encryption_key,
    authentication_key=authentication_key,
)

with public_client(host=host, port=port).session() as client:

    response_data = client.get(
        cosem.CosemAttribute(
            interface=enumerations.CosemInterface.DATA,
            instance=cosem.Obis(0, 0, 0x2B, 1, 0),
            attribute=2,
        )
    )
    data_decoder = a_xdr.AXdrDecoder(
        encoding_conf=a_xdr.EncodingConf(
            attributes=[a_xdr.Sequence(attribute_name="data")]
        )
    )
    invocation_counter = data_decoder.decode(response_data)["data"]
    print(f"meter_initial_invocation_counter = {invocation_counter}")

# we are not reusing the socket as of now. We just need to give the meter some time to
# close the connection on its side
sleep(1)

with management_client(
    host=host, port=port, client_initial_invocation_counter=invocation_counter + 1
).session() as client:

    profile = client.get(
        cosem.CosemAttribute(
Ejemplo n.º 3
0
class GeneralGlobalCipherApdu(AbstractXDlmsApdu):
    """
    The general-global-cipher APDU can be used to cipher other APDUs with
    either the global key or the dedicated key.

    The additional authenticated data to use for decryption is depending on the
    portection applied.

    Encrypted and authenticated: Security Control Field || Authentication Key
    Only authenticated: Security Control Field || Authentication Key || Ciphered Text
    Only encrypted: b''
    No protection: b''

    """

    TAG = 219
    NAME = "general-glo-cipher"

    ENCODING_CONF = a_xdr.EncodingConf([
        a_xdr.Attribute(attribute_name="system_title",
                        create_instance=OctetStringData),
        a_xdr.Attribute(
            attribute_name="ciphered_content",
            create_instance=OctetStringData.from_bytes,
        ),
    ])

    system_title: bytes
    security_control: SecurityControlField
    invocation_counter: int
    ciphered_text: bytes

    @classmethod
    def from_bytes(cls, source_bytes: bytes):
        data = bytearray(source_bytes)
        tag = data.pop(0)
        if tag != cls.TAG:
            raise ValueError(
                f"Tag not as expected. Expected: {cls.TAG} but got {tag}")
        decoder = a_xdr.AXdrDecoder(encoding_conf=cls.ENCODING_CONF)
        in_dict = decoder.decode(data)
        system_title = in_dict["system_title"].value
        ciphered_content = in_dict["ciphered_content"].value
        security_control = SecurityControlField.from_bytes(
            ciphered_content.pop(0).to_bytes(1, "big"))
        invocation_counter = int.from_bytes(ciphered_content[:4], "big")
        ciphered_text = bytes(ciphered_content[4:])
        return cls(system_title, security_control, invocation_counter,
                   ciphered_text)

    def to_bytes(self) -> bytes:
        out = bytearray()
        out.append(self.TAG)
        out.append(len(self.system_title))
        out.extend(self.system_title)
        out.append(
            len(self.security_control.to_bytes() +
                self.invocation_counter.to_bytes(4, "big") +
                self.ciphered_text))
        out.extend(self.security_control.to_bytes())
        out.extend(self.invocation_counter.to_bytes(4, "big"))
        out.extend(self.ciphered_text)
        return bytes(out)

    def to_plain_apdu(self, encryption_key, authentication_key) -> bytes:
        plain_text = decrypt(
            security_control=self.security_control,
            key=encryption_key,
            auth_key=authentication_key,
            invocation_counter=self.invocation_counter,
            cipher_text=self.ciphered_text,
            system_title=self.system_title,
        )

        return bytes(plain_text)
Ejemplo n.º 4
0
class InitiateRequestApdu(AbstractXDlmsApdu):
    """
    InitiateRequest ::= SEQUENCE {
    dedicated-key: OCTET STRING OPTIONAL
    response-allowed: BOOLEAN DEFAULT TRUE
    proposed-quality-of-service: IMPLICIT Integer8 OPTIONAL
    proposed-dlms-version-number: Integer8  # Always 6?
    proposed-conformance: Conformance
    client-max-receive-pdu-size: Unsigned16
    }
    """

    TAG: ClassVar[int] = 0x01  # initiateRequest XDLMS-APDU Choice.

    ENCODING_CONF = a_xdr.EncodingConf([
        a_xdr.Attribute(
            attribute_name="dedicated_key",
            create_instance=dlms_data.OctetStringData.from_bytes,
            optional=True,
        ),
        a_xdr.Attribute(attribute_name="response_allowed",
                        create_instance=bool,
                        default=True),
        a_xdr.Attribute(
            attribute_name="proposed_quality_of_service",
            create_instance=int_from_bytes,
            length=1,
        ),
        a_xdr.Attribute(
            attribute_name="proposed_dlms_version_number",
            create_instance=int_from_bytes,
            length=1,
        ),
        a_xdr.Attribute(
            attribute_name="rest",
            create_instance=dlms_data.OctetStringData.from_bytes,
            length=9,
        ),
    ])

    proposed_conformance: Conformance
    proposed_quality_of_service: Optional[int] = attr.ib(default=None)
    client_max_receive_pdu_size: int = attr.ib(default=65535)
    proposed_dlms_version_number: int = attr.ib(default=6)
    response_allowed: bool = attr.ib(default=True)
    dedicated_key: Optional[bytes] = attr.ib(default=None)

    @classmethod
    def from_bytes(cls, _bytes: bytes):
        # There is weird decoding here since it is mixed X-ADS and BER....
        data = bytearray(_bytes)
        apdu_tag = data.pop(0)
        if apdu_tag != 0x01:
            raise ValueError(
                f"Data is not a InitiateReques APDU, got apdu tag {apdu_tag}")

        decoder = a_xdr.AXdrDecoder(cls.ENCODING_CONF)
        object_dict = decoder.decode(data)

        # Since the initiate request mixes a-xdr and ber encoding we make some pragmatic
        # one-off handling of that case.

        rest = bytearray(object_dict.pop("rest").value)
        # rest contains ber endoced propesed conformance and max reciec pdu

        conformance_tag = rest[:2]
        if conformance_tag != b"\x5f\x1f":
            raise ValueError(
                f"Didnt receive conformance tag correcly, got {conformance_tag!r}"
            )
        conformance = xdlms.Conformance.from_bytes(data[-5:-2])
        max_pdu_size = int.from_bytes(data[-2:], "big")
        dedicated_key_obj = object_dict.pop("dedicated_key")
        if dedicated_key_obj:
            dedicated_key = bytes(dedicated_key_obj.value)
        else:
            dedicated_key = None
        return cls(
            **object_dict,
            dedicated_key=dedicated_key,
            proposed_conformance=conformance,
            client_max_receive_pdu_size=max_pdu_size,
        )

    def to_bytes(self):
        # Since the initiate request mixes a-xdr and ber encoding we make some pragmatic
        # one-off handling of that case.
        out = bytearray()
        out.append(self.TAG)
        if self.dedicated_key:
            out.append(0x01)
            out.append(len(self.dedicated_key))
            out.extend(self.dedicated_key)
        else:
            out.append(0x00)
        out.append(0x00)
        out.append(0x00)
        out.append(0x06)
        out.extend(b"_\x1f\x04")
        out.extend(self.proposed_conformance.to_bytes())
        out.extend(self.client_max_receive_pdu_size.to_bytes(2, "big"))
        return bytes(out)
Ejemplo n.º 5
0
def parse_as_dlms_data(data: bytes):
    data_decoder = a_xdr.AXdrDecoder(encoding_conf=a_xdr.EncodingConf(
        attributes=[a_xdr.Sequence(attribute_name="data")]))
    return data_decoder.decode(data)["data"]
Ejemplo n.º 6
0
    client_logical_address=1,
    authentication_method=auth,
    encryption_key=encryption_key,
    authentication_key=authentication_key,
)

port = "/dev/tty.usbserial-A704H991"
with public_client(serial_port=port).session() as client:

    response_data = client.get(
        cosem.CosemAttribute(
            interface=enumerations.CosemInterface.DATA,
            instance=cosem.Obis(0, 0, 0x2B, 1, 0),
            attribute=2,
        ))
    data_decoder = a_xdr.AXdrDecoder(encoding_conf=a_xdr.EncodingConf(
        attributes=[a_xdr.Sequence(attribute_name="data")]))
    invocation_counter = data_decoder.decode(response_data)["data"]
    print(f"meter_initial_invocation_counter = {invocation_counter}")

LOAD_PROFILE_BUFFER = cosem.CosemAttribute(
    interface=enumerations.CosemInterface.PROFILE_GENERIC,
    instance=cosem.Obis(1, 0, 99, 1, 0),
    attribute=2,
)

CURRENT_ASSOCIATION_OBJECTS = cosem.CosemAttribute(
    interface=enumerations.CosemInterface.ASSOCIATION_LN,
    instance=cosem.Obis(0, 0, 40, 0, 0),
    attribute=2,
)