Skip to content
SHARUR
Search Sign in

World

Topics from outside of this forum. Views and opinions represented here may not reflect those of this forum and its members.

A world of content at your fingertips…

Think of this as your global discovery feed. It brings together interesting discussions from across the web and other communities, all in one place.

While you can browse what's trending now, the best way to use this feed is to make it your own. By creating an account, you can follow specific creators and topics to filter out the noise and see only what matters to you.

Ready to dive in? Create an account to start following others, get notified when people reply to you, and save your favorite finds.

Register Login
  • C

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are troubleshooting field communication access permissions under Management Client (Client SAP 0x0001, Logical Device 1) authenticated via High Level Security 5 (HLS 5 GMAC).

    After HLS 5 mutual authentication is established (0x60 AARQ -> 0x61 AARE -> CtoS/StoC handshake accepted), our application reads instantaneous electrical registers (OBIS 1.0.32.7.0.255, Voltage L1, Class 3) using plain Get-Request-Normal (0xC0 0x01), and the meter responds with valid voltage data.

    However, when we attempt to read Clock (Class 8, OBIS 0.0.1.0.0.255, Attribute 2 time) or operate Disconnect Control (Class 70, OBIS 0.0.96.3.10.255, Method 1 remote_disconnect) using plain APDUs, the meter persistently rejects the request with data-access-result: scope-of-access-violated (0xC4 0x01 C1 01 01)!

    Here is the failing plain GET request for Class 8 Clock:

    00 01 00 01 00 01 00 0D C0 01 C1 00 08 00 00 01 00 00 FF 02 00
    

    And the meter response:

    00 01 00 01 00 01 00 05 C4 01 C1 01 01
    

    Why is an authenticated Management Client getting scope-of-access-violated on specific objects?


    To synthesize for the community, here is the Python Gurux script demonstrating how to enforce encryption on sensitive objects: from gurux_dlms.objects import GXDLMSClock, GXDLMSDisconnectControl from gurux_dlms.enums import Security def operate_secure_objects(client, reader): # Enforce Suite 1 Authentication + Encryption client.ciphering.security = Security.AUTHENTICATION_ENCRYPTION # 1. Read Protected Clock (Class 8) clock = GXDLMSClock("0.0.1.0.0.255") current_time = reader.read(clock, 2) print(f"[+] Current Protected Meter Time: {current_time}") # 2. Execute Protected Disconnect Control (Class 70) relay = GXDLMSDisconnectControl("0.0.96.3.10.255") reader.method(relay, 1, 0) # Method 1: remote_disconnect print("[+] Breaker successfully tripped via encrypted Action-Request.") Summary Checklist: Access Matrix: Verify Association LN object_list access modes (IEC 62056-62 Table 42). Encrypted Access: Wrap Class 8 / Class 70 in 0xDB General-Glo-Ciphering. Security Policy: Default HES management associations to Security.AUTHENTICATION_ENCRYPTION.
  • M

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are integrating a modern DLMS Head-End System (HES) with newly deployed smart meters over IEC 62056-47 TCP Wrapper. When reading small scalar registers (e.g. Instantaneous Voltage 1.0.32.7.0.255, Class 3), communications succeed immediately with Get-Response-Normal (0xC4 0x01).

    However, when reading large Profile Generic buffers (OBIS 1.0.99.1.0.255, Class 7) exceeding 2048 bytes, the meter does NOT return standard Get-Response-With-Datablock (0xC4 0x02). Instead, it responds with an unexpected APDU tag 0xDB wrapping a General Block Transfer (GBT) streaming control header (0xE0 0x00 ...).

    Our HES client parser expects normal xDLMS datablocks, fails to recognize the GBT header, and stalls until timeout.

    Here is the AARQ Conformance Block sent by HES:

    5F 1F 04 00 00 7E 1F 04 00
    
    • Conformance BitString (24 bits): 00 7E 1F

    And here is the meter's AARE Negotiated Conformance:

    5F 1F 04 00 00 7E 5F 04 00
    

    Why is the meter forcing GBT framing when our HES client did not request it?


    To synthesize for the community, here is the Python Gurux snippet demonstrating how to configure Conformance Bitstrings to enable or disable GBT cleanly: from gurux_dlms.enums import Conformance def configure_conformance_bits(client, enable_gbt=False): # Base Conformance: Read, Write, UnconfirmedWrite, ParameterizedAccess client.proposed_conformance = ( Conformance.READ | Conformance.WRITE | Conformance.UN_CONFIRMED_WRITE | Conformance.SELECTIVE_ACCESS ) if enable_gbt: # Enable Bit 19 (General Block Transfer) for high-speed streaming client.proposed_conformance |= Conformance.GENERAL_BLOCK_TRANSFER print("[+] GBT (General Block Transfer) enabled in AARQ Conformance.") else: # Strict Traditional DataBlock mode client.proposed_conformance &= ~Conformance.GENERAL_BLOCK_TRANSFER print("[+] Strict Traditional DataBlock mode enforced.") Summary Checklist for Conformance Negotiation: Bit 19 Inspection: Check byte 3 bit 5 of the Conformance BitString in AARQ/AARE. GBT Capability: If HES lacks GBT parser, explicitly clear Bit 19 in AARQ. Firmware Conformance: Validate that meter respects bitwise AND negotiation ($C_{\text{neg}} = C_{\text{prop}} \land C_{\text{acc}}$).
  • E

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are implementing secure over-the-air (OTA) firmware upgrade for polyphase meters over IEC 62056-47 TCP Wrapper using Interface Class 18 (Image Transfer, OBIS 0.0.44.0.0.255).

    The firmware binary is 196 KB, divided into 980 blocks of 200 bytes each. The entire transfer lifecycle proceeds as follows:

    1. Method 1 (image_initiate) succeeds (image_transfer_status = 1: initiated);
    2. All 980 blocks are transferred sequentially via Method 2 (image_block_transfer);
    3. Attribute 3 image_transferred_blocks_status reports all bits set to 1 (100% received);
    4. Attribute 4 image_first_not_transferred_block_number equals 980.

    However, when our HES client executes Method 3 (image_verify) passing the 64-byte ECDSA P-256 digital signature, the meter's background verification task finishes with image_transfer_status = 4: verification_failed!

    Here is the image_verify Action-Request:

    00 01 00 01 00 01 00 4F C3 01 C1 00 12 00 00 2C 00 00 FF 03 01 09 40 4A 1B 88 22 91 04 FF 12 33 AA 01 C0 01 C1 00 07 01 00 63 01 00 FF 02 00 77 1B C0 44 88 F0 1A 2B 3C 4D 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99
    

    Why is ECDSA signature verification failing on 100% transferred images?


    To synthesize for the community, here is the complete Python Gurux script for OTA firmware upgrades: from gurux_dlms.objects import GXDLMSImageTransfer from gurux_dlms.enums import ImageTransferStatus def perform_ota_firmware_upgrade(client, reader, firmware_bytes, signature_der): it = GXDLMSImageTransfer("0.0.44.0.0.255") block_size = 200 # 1. Initiate Transfer (Image Identifier: "V3.2.0") print("[*] Initiating Image Transfer...") reader.method(it, 1, ("V3.2.0".encode(), len(firmware_bytes))) # 2. Transfer Blocks (Zero-Indexed: 0 to N-1) total_blocks = (len(firmware_bytes) + block_size - 1) // block_size for block_num in range(total_blocks): chunk = firmware_bytes[block_num*block_size : (block_num+1)*block_size] reader.method(it, 2, (block_num, chunk)) if block_num % 100 == 0: print(f" Transferred block {block_num}/{total_blocks}") # 3. Verify Signature (ASN.1 DER encoded ECDSA P-256) print("[*] Verifying Image Signature...") reader.method(it, 3, signature_der) status = reader.read(it, 6) assert status == ImageTransferStatus.VERIFICATION_SUCCESSFUL, f"Verification failed! Status: {status}" print("[+] Firmware Image verified successfully! Ready for activation.") Summary Checklist for Class 18 Image Transfer: Block Indexing: Always start block numbers at 0 (Blue Book Section 4.3.8). Signature Encoding: Confirm whether firmware expects ASN.1 DER (71B) or raw P1363 (64B). Digest Pre-image: Verify if image_identifier is included in SHA-256 digest. Activation Handling: Prepare for immediate TCP connection reset upon Method 4.
  • A

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are implementing High Level Security 5 (HLS 5 GMAC) mutual authentication over IEC 62056-47 TCP Wrapper for Security Suite 1 (Association LN OBIS 0.0.40.0.0.255, Management Client 0x0001).

    The initial AARQ (0x60) and AARE (0x61) handshake succeeds, and the meter returns a 16-byte Server-to-Client challenge (StoC = 50 36 77 52 4A 32 31 46 11 22 33 44 55 66 77 88).
    Next, our client generates the Client-to-Server challenge response (CtoS), encapsulates it inside Action-Request-Normal (Class 15 Method 1 reply_to_HLS_authentication), and transmits it to the meter. The meter accepts the CtoS response and replies with Action-Response-Normal containing the Server Proof (StoC authentication tag).

    However, when our HES client verifies the received Server Proof, the locally computed GMAC authentication tag fails to match the meter's returned tag, causing our HES to abort the association with an invalid-password / challenge-mismatch exception!

    Here is our client Action-Request (CtoS challenge):

    00 01 00 01 00 01 00 28 C3 01 C1 00 0F 00 00 28 00 00 FF 01 01 09 11 10 00 00 00 01 1A 52 FE 7D D3 E7 27 48 97 3C 1E 28
    

    And here is the meter's Action-Response containing the Server Proof:

    00 01 00 01 00 01 00 1E C7 01 C1 00 01 09 11 10 00 00 00 01 88 F0 1A 2B 3C 4D 99 11 22 33 44 55 66
    

    Why is our client failing to verify the meter's Server Proof?


    To synthesize this for all DLMS protocol engineers, here is the complete Python Gurux snippet demonstrating bidirectional HLS 5 verification: from gurux_dlms.secure import GXSecure from gurux_dlms.enums import Security, Authentication from gurux_dlms.objects import GXDLMSAssociationLN def verify_hls5_mutual_authentication(client, server_system_title, server_proof_bytes, ctos_challenge): # 1. Extract Server SC, Server IC, and Server GMAC Tag server_sc = server_proof_bytes[0] server_ic = int.from_bytes(server_proof_bytes[1:5], 'big') server_gmac_tag = server_proof_bytes[5:17] # 12 bytes # 2. Build Server IV: Server System Title (8B) || Server IC (4B) iv = server_system_title + server_proof_bytes[1:5] aad = bytes([server_sc]) # 3. Calculate Expected Server Proof over original CtoS challenge using GAK expected_proof = GXSecure.secure( client.settings, client.ciphering, server_ic, ctos_challenge, server_system_title ) assert server_proof_bytes == expected_proof, "Server Proof Mismatch! Mutual auth failed." print("[+] HLS 5 Mutual Authentication & Server Proof verified successfully!") Summary Checklist for HLS 5 GMAC: IV System Title Assignment: CtoS uses Client System Title; Server Proof verification uses Server System Title (Green Book Table 43). Key Separation: Use Authentication Key (GAK), not Encryption Key (GUEK). Data Payload: CtoS hashes StoC; Server Proof hashes CtoS. Anti-Replay: Guarantee monotonic $IC$ incrementation before challenge generation.
  • D

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are developing a high-performance optical handheld unit (IEC 62056-21 Mode E, 9600 baud) communicating over IEC 62056-46 HDLC with smart meters running Security Suite 1 (General-Glo-Ciphering 0xDB with AES-GCM-128). When reading a large multi-tariff historical billing record (OBIS 1.0.98.1.0.255, Class 7), the meter responds with a 320-byte encrypted APDU split across 3 HDLC segmented I-Frames.

    Our embedded parser continuously throws deciphering-failed: invalid-tag or corrupt ASN.1 structure on the continuation segments.

    Here is the sanitized 3-segment HDLC capture from the optical probe:

    • Segment 1 (S=1, Frame Length 132B):
    7E A8 84 03 21 10 94 E4 E6 E6 00 DB 08 53 48 41 00 00 00 00 01 30 00 00 00 0A 4F 12 A9 88 BC 11 02 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD 1E 2B 7E
    
    • Segment 2 (S=1, Frame Length 128B):
    7E A8 80 03 21 30 B2 14 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD 4F 9A 7E
    
    • Segment 3 (S=0, Last Segment, Frame Length 66B):
    7E A0 42 03 21 50 D8 70 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 11 22 33 44 55 66 77 9B E1 4C 8D 23 AA 12 90 77 1B C0 44 C2 8F 7E
    

    We initially attempted to feed each segment directly into the AES-GCM stream decryptor as it arrived over UART to save RAM. Why is GMAC verification failing completely?


    To synthesize this for the community, here is the complete end-to-end Python pipeline demonstrating: HDLC CRC-16 FCS checking and segment de-encapsulation; Contiguous APDU reassembly; AES-GCM-128 GMAC verification and decryption; Plain COSEM APDU parsing. import crcmod from cryptography.hazmat.primitives.ciphers.aead import AESGCM # 1. Initialize CCITT CRC-16 (IEC 62056-46 polynomial: x^16 + x^12 + x^5 + 1) crc16 = crcmod.mkCrcFun(0x11021, initCrc=0xFFFF, rev=True, xorOut=0xFFFF) def parse_and_decrypt_hdlc_stream(segments, guek_key): reassembly_buffer = bytearray() for idx, frame in enumerate(segments): # Step A: Validate FCS assert crc16(frame[1:-3]) == int.from_bytes(frame[-3:-1], 'little'), "FCS Mismatch!" frame_format = int.from_bytes(frame[1:3], 'big') is_segmented = bool(frame_format & 0x0800) # Step B: Extract payload bytes (strip Header and FCS) header_len = 7 # 1B flag + 2B format + 2B dest + 1B src + 1B ctrl + 2B HCS if idx == 0: assert frame[header_len:header_len+3] == b'\xe6\xe6\x00', "Missing LLC" header_len += 3 # Strip LLC only on first frame payload_chunk = frame[header_len:-3] reassembly_buffer.extend(payload_chunk) # Step C: Decrypt Reassembled General-Glo-Ciphering APDU apdu = bytes(reassembly_buffer) assert apdu[0] == 0xDB, "Not General-Glo-Ciphering" sys_title = apdu[2:10] # 8 bytes sc = apdu[10:11] # 1 byte (0x30) ic = apdu[11:15] # 4 bytes ciphertext_and_tag = apdu[15:] # Ciphertext + 12B GMAC tag iv = sys_title + ic # 12 bytes IV aad = sc # 1 byte AAD aesgcm = AESGCM(guek_key) decrypted_apdu = aesgcm.decrypt(iv, ciphertext_and_tag, aad) print(f"[+] Decryption successful! Decrypted Plain APDU: {decrypted_apdu.hex().upper()}") return decrypted_apdu Summary Golden Rules: Never skip LLC on continuation frames: Only Segment 1 contains 0xE6E600. Never decrypt HDLC segments individually: Reassemble all fragments into a single contiguous buffer before feeding into AES-GCM. Verify CRC-16 FCS per frame: Drop corrupted frames at the link layer before touching crypto engines. Enforce IV Construction: $\text{IV} = \text{SystemTitle} \parallel \text{InvocationCounter}$.
  • C

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are commissioning AMI firmware for multi-rate tariff switching and disconnect scheduling using Activity Calendar (Class 20, OBIS 0.0.13.0.0.255, Attribute 4 activate_passive_calendar_time) and Single Action Schedule (Class 22, OBIS 0.0.15.0.0.255, Attribute 2 execution_time).

    When utility operators attempt to disable or deactivate a pending schedule, they frequently transmit either:

    1. All-wildcards date/time: FF FF FF FF FF FF FF FF FF 80 00 00 (12 bytes),
    2. Zeroed date/time: 00 00 00 00 FF 00 00 00 00 80 00 00.

    However, on several batch meters running firmware v2.4.0, writing FF FF... causes the meter to execute the linked script action (e.g. Tariff Switch / Breaker Check) every single second in an infinite 1Hz loop, spiking MCU load to 100% and jamming the optical port! Writing 00 00... or an expired timestamp (e.g. 2020-01-01) immediately fires the script upon receiving the SET-Request!

    Here is the sanitized SET-Request transmitted over TCP 4059 wrapper for 0.0.13.0.0.255 Attribute 4:

    00 01 00 01 00 01 00 23 DB 08 53 48 41 00 00 00 00 01 30 00 00 00 12 A1 88 9F 33 22 10 44 88 99 C1 C3 01 C1 00 14 00 00 0D 00 00 FF 04 09 0C FF FF FF FF FF FF FF FF FF 80 00 00
    

    And the meter responds with SET-Response-Normal (0xC5 0x01 C1 00: success), immediately followed by the infinite execution loop.

    Our field operators assumed 0xFF was the universal DLMS way to disable scheduled execution. Why is this triggering 1Hz execution storms?


    To synthesize this for all AMI and meter firmware engineers, here is the production Python snippet using Gurux.DLMS demonstrating how to park both Class 20 and Class 22 schedules safely using the universal distant timestamp approach: from gurux_dlms.objects import GXDLMSActivityCalendar, GXDLMSSingleActionSchedule from gurux_dlms.GXDateTime import GXDateTime from datetime import datetime # The Universal Safe Distant Epoch: 2099-12-31 23:59:59 (Year 2099 = 0x0833) SAFE_DISTANT_EPOCH = GXDateTime(datetime(2099, 12, 31, 23, 59, 59)) def park_single_action_schedule_universal(client, reader, obis="0.0.15.0.0.255"): sas = GXDLMSSingleActionSchedule(obis) # 1. Try standard empty array first try: sas.executionTime = [] reader.write(sas, 2) print(f"[+] Class 22 {obis} cleared via empty array.") except Exception as err: # 2. Fallback to Universal Distant Epoch if meter rejects empty array print(f"[!] Meter rejected empty array (err: {err}). Falling back to safe distant epoch...") sas.executionTime = [SAFE_DISTANT_EPOCH] reader.write(sas, 2) print(f"[+] Class 22 {obis} successfully parked at 2099-12-31 23:59:59.") def park_activity_calendar_passive_time(client, reader, obis="0.0.13.0.0.255"): calendar = GXDLMSActivityCalendar(obis) calendar.time = SAFE_DISTANT_EPOCH reader.write(calendar, 4) # Write Attribute 4 (activate_passive_calendar_time) print(f"[+] Class 20 {obis} passive activation parked at 2099-12-31 23:59:59.") Summary Golden Rules for DLMS Schedule Management: Never use 0xFF across all time bytes to disable: In DLMS Blue Book Section 4.1.6.1, 0xFF is a wildcard that triggers continuous 1Hz execution. Never use past timestamps (2000-01-01 or all zeros): Flawed RTOS schedulers treat past timestamps as expired alarms and execute immediately upon SET. Avoid Year 9999: Exceeds 32-bit signed Unix epoch ($2^{31}-1$) and overflows into negative timestamps (Year 1911). Empty array 01 00 is valid on most devices, but rejected on ~15% of legacy models: Always support fallback to a valid distant future timestamp. Universal Bulletproof Standard: Writing a confirmed valid distant future timestamp (e.g. 2099-12-31 23:59:59 / 0x0833 0x0C 0x1F FF 0x17 0x3B 0x3B 0x00 0x8000 0x00 or 2100-01-01 00:00:00) is 100% compliant and accepted across all vendor meters without side effects.
  • E

    [!WARNING]
    Technical Redaction & Safe Harbor Notice:
    All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.

    We are testing automated bulk readout of 15-minute Load Profile Generic (Class 7, OBIS 1.0.99.1.0.255, Attribute 2 buffer) on polyphase smart meters running firmware build v3.1.2 over IEC 62056-47 TCP wrapper (port 4059). The association is established under Management Client (Client SAP 0x0001, Logical Device 1) with Security Suite 1 (General-Glo-Ciphering 0xDB with AES-GCM-128, HLS 5 GMAC).

    When we request a 15-day historical slice (approximately 420 KB, requiring ~420 datablocks at 1024 bytes/block), the transfer streams cleanly from block #1 up to block #13. However, immediately upon receiving the HES Get-Request-Next for block #14, the meter terminates the stream with a service error response (0xC4 0x01 C1 01 02: block-number-out-of-sequence).

    Here is the initial Get-Request-Normal for the Profile Generic buffer:

    00 01 00 01 00 01 00 28 DB 08 53 48 41 00 00 00 00 01 30 00 00 00 10 4A 1B 88 E2 91 04 FF 12 33 AA 01 C0 01 C1 00 07 01 00 63 01 00 FF 02 00
    

    And here is the failing exchange at block #14:

    • HES Get-Request-Next (Block #14):
    00 01 00 01 00 01 00 1C DB 08 53 48 41 00 00 00 00 01 30 00 00 00 23 88 1F 3C 90 22 EE 45 10 99 AA 01 C0 02 C1 00 00 00 0E
    
    • Meter Response:
    00 01 00 01 00 01 00 17 DB 08 53 48 41 00 00 00 00 01 30 00 00 00 1F 77 CC 11 02 AA 90 12 44 88 01 C4 01 C1 01 02
    

    Dissecting the plain APDU inside the ciphered envelope:

    • C4 01: Get-Response-Normal (with service error)
    • C1: Invoke-ID 1
    • 01: Result code 1 (hardware-fault / exception)
    • 02: Error detail 2 (block-number-out-of-sequence)

    Our initial hypothesis was that the meter's internal RAM streaming buffer suffered a memory pool exhaustion under high TCP socket throughput. Has anyone observed block sequencing rejections at specific block thresholds?


    To synthesize this for the community, here is the complete reference implementation in Python using Gurux.DLMS demonstrating how to read large Profile Generic buffers safely with Selective Access and monotonic Invocation Counter management: from gurux_dlms.objects import GXDLMSProfileGeneric, GXDLMSClock from gurux_dlms.enums import Security, AccessSelector from datetime import datetime, timedelta def read_profile_generic_safe(client, reader, obis_code="1.0.99.1.0.255"): pg = GXDLMSProfileGeneric(obis_code) # 1. Configure Selective Access by Date Range (Last 7 Days) clock = GXDLMSClock("0.0.1.0.0.255") end_time = datetime.utcnow() start_time = end_time - timedelta(days=7) # Restrict by Range Descriptor (Access Selector 1) access_params = [clock, start_time, end_time, []] # 2. Enforce Security Suite 1 with Monotonic IC Increment client.ciphering.security = Security.AUTHENTICATION_ENCRYPTION # 3. Read Rows using Block Transfer with guaranteed IC stepping print(f"[*] Reading Profile Generic {obis_code} via Selective Access...") cells = reader.readRowsByRange(pg, start_time, end_time) print(f"[+] Successfully retrieved {len(cells)} profile entries.") for row in cells[:3]: # Preview first 3 rows print(f" Timestamp: {row[0]}, +A Import: {row[1]} Wh, Status: {hex(row[3])}") return cells Summary Checklist for Engineers Encountering block-number-out-of-sequence: Anti-Replay Counter Check: Verify that every retry/next block incremented the 4-byte $IC$ (IEC 62056-53 Section 8.4.2). Buffer Snapshot Isolation: Ensure meter firmware copies row pointers to a read-only shadow buffer on AARQ (DLMS Blue Book Section 4.3.7.2). Selective Access: Always query by explicit timestamp range rather than reading unbounded Attribute 2. PDU Size Tuning: Negotiate max-pdu-size to $\ge 2048$ bytes to reduce datablock round trips.
  • S

    I am reviewing two protected DLMS captures and want to confirm the correct terminology.

    Our older parser labels 0x10 as "checksum protection" and 0x30 as "encrypted protection".

    That naming now looks wrong to me.

    The simplified traces below are synthetic, but preserve the structure I am seeing.

    Capture A — authentication protection

    C8 xx
    10
    00 00 00 2A
    C0 01 C1 00 01 00 00 01 00 00 FF 02 00
    7D 91 2A 44 35 8E 10 A0 B1 C2 D3 E4
    

    My current byte-level interpretation:

    C8
    └─ glo_GetRequest
    
    10
    └─ Security Control = authentication
    
    00 00 00 2A
    └─ Invocation Counter = 42
    
    C0 01 C1 00 01 00 00 01 00 00 FF 02 00
    └─ inner GET APDU remains visible in this example
    
    7D 91 2A 44 35 8E 10 A0 B1 C2 D3 E4
    └─ example authentication tag
    

    If I flip one byte in the visible GET APDU, the receiver rejects the packet even though the APDU is not confidential.

    Capture B — authentication + encryption

    C8 xx
    30
    00 00 00 2B
    7A 91 C4 08 2D A0 9F 6B 11 85 D2 31
    4C 83 6E A2 6D 73 05 91 9A 20 31 7F
    ...
    

    Interpretation:

    C8
    └─ glo_GetRequest
    
    30
    └─ Security Control = authentication + encryption
    
    00 00 00 2B
    └─ Invocation Counter = 43
    
    7A 91 C4 08 ...
    └─ protected payload; the original GET APDU is no longer
       directly readable from the wire
    

    So should 0x10 be described as integrity/authenticity protection, rather than a checksum?

    And is the practical difference:

    0x10 → readable payload + cryptographic authentication
    0x30 → encrypted payload + cryptographic authentication
    

    ?


    One more thing: the invocation counter belongs in your security troubleshooting dashboard. A lot of "wrong key" incidents are actually: counter reused counter lower than meter expects counter read from the wrong security context counter reset after restart So when a protected packet fails, log at least: securityControl securitySuite invocationCounter systemTitle direction APDU type verification result Do not log live keys. That usually tells you whether you have: a DLMS parser problem a replay/counter problem a security-context problem or an actual key mismatch before you start changing cryptographic configuration.
  • C

    I am comparing two associations against the same DLMS stack.

    With LOW authentication, the client can start GET requests immediately after a successful AARE.

    With HighGMAC, the AARE is accepted, but the first normal GET is rejected unless I perform another authentication step.

    The captures below are anonymized. Meter addresses, credentials, system titles, challenges, counters and tags were replaced.

    LOW authentication AARQ

    00 01 00 01 00 01 00 38
    60 36
    A1 09 06 07 60 85 74 05 08 01 01
    8A 02 07 80
    8B 07 60 85 74 05 08 02 01
    AC 0A 80 08 41 42 43 44 45 46 47 48
    BE 10 04 0E 01 00 00 00 06 5F 1F 04 00 00 3E 1F 32 00
    

    What I read from this:

    00 01 00 01 00 01 00 38
    └─ DLMS Wrapper header
    
    60 36
    └─ AARQ
    
    A1 09 06 07 60 85 74 05 08 01 01
                                        ^^
    └─ application-context-name
       LN referencing, no ciphering
    
    8B 07 60 85 74 05 08 02 01
                               ^^
    └─ mechanism-name = LOW
    
    AC 0A 80 08 41 42 43 44 45 46 47 48
                └───────────────────────┘
                 8-byte authentication value
                 ("ABCDEFGH" in this synthetic trace)
    

    In this case the flow is effectively:

    AARQ
      ↓
    meter validates LOW authentication value
      ↓
    AARE accepted
      ↓
    GET / SET / ACTION
    

    HighGMAC AARQ

    The HighGMAC association looks very different:

    60 xx
    A1 09 06 07 60 85 74 05 08 01 03
    A6 0A 04 08 43 4C 49 45 4E 54 30 31
    8A 02 07 80
    8B 07 60 85 74 05 08 02 05
    AC 0A 80 08 11 22 33 44 55 66 77 88
    BE xx 04 xx 30 00 00 00 2A ...
    

    The bytes that caught my attention are:

    A1 ... 01 03
              ^^
    LN referencing with ciphering
    
    8B ... 02 05
              ^^
    HighGMAC authentication mechanism
    
    A6 ... 43 4C 49 45 4E 54 30 31
           └───────────────────────┘
           calling AP title / client system title
    
    AC ... 11 22 33 44 55 66 77 88
           └───────────────────────┘
           client-to-server challenge
    

    The AARE then contains an accepted association, but also an authentication-related diagnostic:

    61 ...
    A1 09 06 07 60 85 74 05 08 01 03
    A2 03 02 01 00
    A3 05 A1 03 02 01 0E
    ...
    

    My current interpretation:

    A2 03 02 01 00
                ^^
    association-result = accepted
    
    A3 05 A1 03 02 01 0E
                      ^^
    authentication is still required
    

    So is the correct conclusion that AARE accepted is the end of LOW authentication, but only the middle of a HighGMAC/HLS association?


    One implementation detail that is worth logging separately: associationAccepted = true hlsAuthenticated = false I have seen HES code collapse both into one authFinish flag. That works with LOW and becomes misleading with HLS. For HighGMAC, log the transition only after the Association LN Method 1 response has been verified. A successful AARE is necessary, but it is not the final authentication event.
  • C

    Real-Time Balance & Multi-Currency Prepayment with Class 72

    Class 72 (Account Interface, OBIS 0.0.19.0.0.255) manages real-time monetary balances, debt recovery schedules, and tariff currency conversions directly inside the smart meter.

    Essential Attributes

    • Attribute 2 (account_status): Enum (Active, Suspended, In-Debt, Emergency-Credit-Active).
    • Attribute 3 (current_credit_amount): Scaler-Unit formatted monetary value (e.g. 5000 with scaler -2 = $50.00).
    • Attribute 4 (credit_configuration): Defines emergency credit limits and daily standing charges.
    • Attribute 7 (charge_schedule): Periodic deduction rules (e.g. grid access fee per 24 hours).

    When credit falls below zero, Class 72 invokes Method 1 on Class 70 (Disconnect Control) to safely disconnect the load after notifying the customer.


    All credit balance updates must use signed SET requests with Security Suite 0 or 1 to prevent unauthorized credit tampering.
  • A

    Digital Signatures for Legal Non-Repudiation in Smart Meter Billing

    Under European PTB / BSI TR-03109 regulations, billing measurements must be cryptographically signed by the meter's on-chip private key using General-Signing (0xDF).

    Wire Format (APDU Tag 0xDF)

    DF 08 47 55 52 55 58 31 32 33 00 00 00 1A 21 00 00 00 55 ... 30 44 02 20 ... 02 20 ...
    

    Field Breakdown

    1. Tag: DF (general-signing)
    2. System Title: 8 bytes (47 55 52 55 58 31 32 33)
    3. Signature Length: 00 00 00 1A
    4. Security Control Byte: 21 (Signed with Suite 1)
    5. Transaction Counter: 4 bytes
    6. Signed Plaintext APDU: Includes meter serial, OBIS code, measurement value, and timestamp.
    7. ECDSA Signature (DER format): $R$ and $S$ integers (P-256 curve, 64-72 bytes).

    Anyone (including consumers and third-party auditors) can verify the signature using the meter's publicly certified X.509 root key!


    Our HES archives the signed 0xDF APDUs in immutable cold storage for 10 years to satisfy fiscal regulatory audit mandates.
  • C

    Aggregating Gas and Water Sub-Meters through Electricity Gateways

    Smart electricity meters frequently serve as local data concentrators for nearby Wireless M-Bus (wM-Bus EN 13757-4) gas, water, and heat sub-meters using COSEM Class 74 (M-Bus Client / Setup).

    Workflow

    1. Install gas/water meter with 868 MHz Wireless M-Bus transmitter (Mode T1 or C1).
    2. Electricity meter listens on 868.95 MHz.
    3. Pairing is initiated by invoking Method 1 (slave_install) on Class 74 object 0.X.24.0.0.255.
    4. The electricity meter captures sub-meter telegrams, decrypts using pre-shared AES-CTR/CBC keys, and maps sub-meter readings into COSEM Class 3 registers (0.X.24.2.1.255 for Gas Volume, 0.X.24.2.2.255 for Water Flow).
    5. Utility HES reads all electricity, gas, and water data in a single consolidated DLMS association!

    Battery life consideration: wM-Bus gas meters only transmit every 5 minutes in T1 mode (short 10ms chirp). The electricity gateway receiver remains in continuous listen mode powered by the mains grid.
  • S

    Class 3/4 OBIS Codes for Power Quality & Grid Health Monitoring

    Modern smart meters incorporate power quality analyzers conforming to IEC 61000-4-30 Class S/A.

    Standard Power Quality OBIS Mapping

    Parameter Phase A Phase B Phase C Unit
    Voltage THD 1.0.32.7.124.255 1.0.52.7.124.255 1.0.72.7.124.255 % (Enum 29)
    Current THD 1.0.31.7.124.255 1.0.51.7.124.255 1.0.71.7.124.255 % (Enum 29)
    Voltage Sag Count 1.0.32.32.0.255 1.0.52.32.0.255 1.0.72.32.0.255 Count
    Voltage Swell Count 1.0.32.36.0.255 1.0.52.36.0.255 1.0.72.36.0.255 Count
    3rd Harmonic Current 1.0.31.7.3.255 1.0.51.7.3.255 1.0.71.7.3.255 Amperes (A)
    5th Harmonic Current 1.0.31.7.5.255 1.0.51.7.5.255 1.0.71.7.5.255 Amperes (A)

    Tracking 3rd and 5th harmonics allows distribution grid operators to identify non-linear industrial loads (such as variable frequency drives and EV fast-chargers) degrading local power factor.


    In a recent commercial park audit, reading 1.0.31.7.5.255 pinpointed a malfunctioning rooftop solar inverter generating 18% 5th harmonic distortion back into the transformer.
  • D

    Bridging Industrial Modbus Inverters and Sub-Meters into DLMS AMI Networks

    In solar microgrids and commercial complexes, thousands of legacy Modbus RTU solar inverters, thermal energy meters, and diesel generator sub-meters must be aggregated into the central DLMS/COSEM Head-End System.

    Gateway Translation Layer Architecture

    +----------------------------------------------------------------+
    |               DLMS / COSEM Interface (IEC 62056)               |
    |   - Logical Device: Microgrid Aggregator (0.0.42.0.0.255)      |
    |   - Solar Active Power Inverter 1: 1.1.1.7.0.255 (Class 3)     |
    |   - Solar Active Power Inverter 2: 1.2.1.7.0.255 (Class 3)     |
    +----------------------------------------------------------------+
                                    ^
                                    | Mapping Layer (Polling Engine)
                                    v
    +----------------------------------------------------------------+
    |                 Modbus RTU Master Polling Loop                 |
    |   - Polling Inverter 1 (Modbus ID 0x01, Reg 0x40001, Float32)  |
    |   - Polling Inverter 2 (Modbus ID 0x02, Reg 0x40001, Float32)  |
    +----------------------------------------------------------------+
    

    Key Design Considerations

    1. Data Freshness vs Latency: Maintain an internal memory-mapped cache updated periodically via background Modbus polling, so incoming DLMS GET requests return instantaneous measurements with sub-10ms response time.
    2. Quality Flags Mapping: If a Modbus slave times out on the serial bus, the corresponding DLMS Class 4 Extended Register must immediately set Bit 0 (Invalid Data) in its status byte!

    Be careful with Float32 endianness: Modbus devices often use Big-Endian (AB CD) or Mid-Little Endian (CD AB) for IEEE 754 floating point. Converting properly into COSEM Float32 (Type 0x17, strict Big-Endian) prevents inverted numerical readings.
  • D

    Preparing Smart Meters for Official DLMS UA Certification

    Passing the official DLMS User Association Conformance Testing Tool (CTT) is required for interoperability certification.

    Top 5 CTT Test Failure Modes

    1. AARQ Parameter Rejection Handling: Device must return standardized diagnostics when invalid proposed conformance bits are sent.
    2. Selective Access Boundaries: Edge cases in date range queries (e.g. querying timestamps exactly on daylight saving transition boundaries).
    3. HCS/FCS Checksum Verification: Ensuring corrupted frames are silently dropped without generating spurious NAK frames.
    4. ASN.1 Encoding Strictness: Validating that integer lengths match their declared type tags exactly.
    5. Security Suite Error Responses: Returning correct deciphering and authentication exception codes when keys or frame counters mismatch.

    Adhering to these strict checks guarantees seamless interoperability across any compliant Head-End System!


    And always ensure your environment checks all association client addresses: Public (0x10), Reader (0x20), Management (0x01), and Custom Security profiles.
  • C

    Integrating STS (Standard Transfer Specification) with DLMS Smart Meters

    COSEM Class 67 (Credit Interface) and Class 68 (Charge Interface) bridge the gap between 20-digit STS prepayment tokens and DLMS/COSEM communications.

    Workflow

    1. Consumer inputs 20-digit token via meter keypad or mobile app.
    2. Meter decodes token using STS cipher algorithm.
    3. Upon verification, Class 67 updates Attribute 2 (current_credit_amount).
    4. If credit drops below Attribute 4 (low_credit_threshold), the meter triggers a warning buzzer and sends an emergency push notification to the utility HES.

    Class 67 also tracks emergency_credit_available and debt_recovery_rate, allowing smart meters to deduct arrears automatically during top-ups.
  • S

    Grouping and Activating Energy Registers with Class 21

    Class 21 (Register Activation) dynamically maps raw measurement values into specific billing rate registers based on digital input signals or schedule triggers.

    Configuration Structure

    • register_assignment (Attribute 2): Array linking base measurement OBIS codes (e.g. 1.0.1.8.0.255) to tariff registers (1.0.1.8.1.255, 1.0.1.8.2.255, 1.0.1.8.3.255).
    • mask_list (Attribute 3): Defines active tariff masks for Rate 1 (T1), Rate 2 (T2), and Rate 3 (T3).
    • active_mask (Attribute 4): Currently energized tariff mask.

    When switching active masks, the meter registers the transition timestamp in the tariff audit log (0.0.96.14.0.255) to ensure indisputable billing settlement.
  • A

    Detecting Physical Meter Tampering and Unauthorized Parameter Modifications

    Class 65 (Parameter Monitor) tracks changes to critical metrological and security registers.

    Monitored Attributes

    • Attribute 2 (changed_parameters): Array of OBIS codes modified since last security audit.
    • Attribute 3 (last_change_time): Timestamp of the latest parameter write.
    • Attribute 4 (change_counter): Monotonically increasing 32-bit counter incremented on every valid SET or ACTION request.

    If the change_counter increments without a corresponding job logged in the utility Head-End System audit trail, an immediate on-site investigation is dispatched!


    Parameter changes are also duplicated to write-once non-volatile metrological EEPROM to comply with MID (Measuring Instruments Directive) legal metrology requirements.
  • S

    Managing Real-Time Clock (RTC) Drift and DST Shifts

    Accurate billing and load profile alignment require sub-second RTC synchronization across all grid nodes.

    Time Synchronization Methods

    1. Method 1 (adjust_to_quarter): Rounds clock to nearest 15-minute boundary.
    2. Method 2 (adjust_to_measuring_period): Resets drift without corrupting profile integration windows.
    3. Method 3 (adjust_to_minute): Synchronizes seconds to zero.
    4. Method 4 (adjust_to_preset_time): Two-step synchronized broadcast to prevent network jitter.

    Daylight Saving Time (DST) Configuration

    • daylight_savings_begin (Attribute 4): e.g. Last Sunday of March at 02:00:00.
    • daylight_savings_end (Attribute 5): e.g. Last Sunday of October at 03:00:00.
    • daylight_savings_deviation (Attribute 6): Typically +60 minutes.
    • daylight_savings_enabled (Attribute 7): Boolean true.

    When DST shifts back 1 hour, ensure your HES profile collector handles the duplicate hour timestamp by checking Clock Status bit 7 (daylight_saving_active)!


    Never execute adjust_to_minute directly during an active 15-minute load profile integration period, as this causes duplicate or missing interval records in Class 7 buffers.
  • C

    Setting up Cellular Modems via COSEM Class 45 & Class 47

    Internal 4G/NB-IoT smart meter modems use Class 45 (Auto Connect) and Class 47 (GSM Diagnostic) for autonomous network registration.

    Key Configuration Objects

    • 0.0.2.1.0.255 (Auto Connect): Controls connection trigger (e.g. Periodic, On-Alarm, On-PowerUp).
    • 0.0.25.4.0.255 (GPRS Modem Setup): Holds APN string (e.g. "iot.smartgrid.net"), PIN code, and authentication credentials.
    • 0.0.25.6.0.255 (GSM Diagnostic): Exposes real-time signal strength (RSSI, RSRP, RSRQ), Cell ID, and operator code.

    Reading 0.0.25.6.0.255 during site installation allows field engineers to instantly check 4G signal quality directly via optical probe!


    In the modem driver, we configure automatic APN fallback if the primary APN fails DNS resolution after 3 connection attempts.
Page 2 of 4