Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Dark

Collapse
SHARUR
Community Wiki DLMS/COSEM Tools
Search Sign in

Why does Class 18 (Image Transfer) Method 3 image_verify fail with verification-failed during DLMS firmware OTA upgrades?

Scheduled Pinned Locked Moved General Discussion
dlmscosemclass-18image-transferota-firmware
10 Posts 5 Posters 42 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
  • E Offline
    E Offline
    elena_rostova
    wrote on last edited by
    #1

    [!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?

    1 Reply Last reply
    0
    • A Offline
      A Offline
      alex_turner
      wrote on last edited by
      #2

      Elena, this is a very well-known cryptographic divergence between raw IEEE P1363 ECDSA signatures and ASN.1 DER-encoded signatures in smart meter firmware.

      Per DLMS Blue Book Ed. 17 Section 4.3.8 (Image Transfer) and IEC 62056-62 Section 6.2.16:
      Method 3 image_verify takes an integer parameter (typically 0) or an octet-string containing the digital signature depending on the firmware profile.

      The Cryptographic Discrepancy:

      1. Raw IEEE P1363 Format (64 Bytes): $\text{Signature} = R_{32\text{ bytes}} \parallel S_{32\text{ bytes}}$.
      2. ASN.1 DER Format (70-72 Bytes):
        0x30 [len] 0x02 [r_len] [R...] 0x02 [s_len] [S...]

      In your Action-Request hex:
      You passed a 64-byte raw Octet-String (Tag 0x09 0x40). If the meter firmware uses mbedTLS, OpenSSL, or micro-ecc configured to parse standard ASN.1 DER structures, the DER parser expects tag 0x30 at byte 0. When it sees 0x4A, it fails ASN.1 parsing and flags verification_failed!

      1 Reply Last reply
      0
      • D Offline
        D Offline
        david_miller
        wrote on last edited by
        #3

        In addition to the signature encoding, look at how the SHA-256 Hash Digest was calculated across the image blocks.

        Per DLMS Green Book 10th Ed Section 9.2.5.3 (Security Suite 1 & 2 Digital Signatures):
        The digital signature is generated over the SHA-256 digest of the unpadded, raw application image, NOT the concatenated transport blocks!

        The Two Major Image Hashing Traps:

        1. Image Identifier Inclusion: Some meter vendors include image_identifier (Attribute 6, Octet-String) and image_size (Double-Long-Unsigned) in the SHA-256 hash pre-image ($H = \text{SHA256}(\text{ImageIdentifier} \parallel \text{ImageBytes})$);
        2. Last Block Padding: If your 980th block was 160 bytes and your HES padded it with zeros to 200 bytes, the hash mismatch occurs immediately.
        1 Reply Last reply
        0
        • S Offline
          S Offline
          sarah_jenkins
          wrote on last edited by
          #4

          Let's check Attribute 3 (image_transferred_blocks_status) and Attribute 4 (image_first_not_transferred_block_number).

          Per DLMS Blue Book Ed. 17 Section 4.3.8 Table 19:

          • Attribute 3 is a Bit-String where bit $i = 1$ indicates block $i$ has been received.
          • Zero-Based vs One-Based Indexing:
            In DLMS, block numbers in image_block_transfer are 0-indexed (block_number: 0 to block_number: N-1).
            If your HES sent block numbers starting from 1 up to 980, block 0 was never transferred!
            Attribute 4 would report image_first_not_transferred_block_number = 0, causing Method 3 image_verify to fail instantly because the image is incomplete!
          1 Reply Last reply
          0
          • E Offline
            E Offline
            elena_rostova
            wrote on last edited by
            #5

            Sarah and Alex, we just checked our firmware logs, and it was precisely the Zero-Based Block Number Indexing and the ASN.1 DER signature format:

            1. Our HES was transmitting block numbers starting at 1 (1 to 980). The meter's bitmask had bit 0 clear (0x7F...), meaning the firmware considered block 0 missing!
            2. Our crypto module was expecting a standard ASN.1 DER signature (71 bytes), but HES transmitted a raw 64-byte $(R \parallel S)$ buffer.

            Once we adjusted HES to start block transmission at block_number = 0 and encoded the ECDSA P-256 signature in ASN.1 DER, image_verify took 1.4 seconds and transitioned cleanly to image_transfer_status = 3: verification_successful!

            1 Reply Last reply
            0
            • M Offline
              M Offline
              marcus_vance
              wrote on last edited by
              #6

              Now that verification is successful, let's address Method 4 (image_activate) execution.

              Per DLMS Blue Book Section 4.3.8 Method 4:
              image_activate takes (activation_time: Octet-String).

              If you want immediate activation, what parameter should be passed?

              • In DLMS standard, passing an empty date/time (0x09 0x00) or wildcard timestamp (0xFF...) triggers immediate reboot and image activation.
              • However, during reboot, the TCP socket will close with a FIN or RST. HES must NOT treat this socket closure as a communication failure.
              1 Reply Last reply
              0
              • A Offline
                A Offline
                alex_turner
                wrote on last edited by
                #7

                Here is the verified ASN.1 DER signature format for ECDSA P-256 in Method 3:

                Signature ::= SEQUENCE {
                  r  INTEGER (32 bytes with leading 0x00 if MSB is set),
                  s  INTEGER (32 bytes with leading 0x00 if MSB is set)
                }
                

                Hex encoding:
                30 45 02 21 00 [R_32B] 02 20 [S_32B] (Total: 71 bytes).

                When passed inside Action-Request for Method 3 (0.0.44.0.0.255), the meter's public key successfully validates the image digest.

                1 Reply Last reply
                0
                • D Offline
                  D Offline
                  david_miller
                  wrote on last edited by
                  #8

                  And here is how to handle packet loss recovery using Attribute 3 (image_transferred_blocks_status😞

                  If cellular NB-IoT or G3-PLC drops blocks 45, 112, and 205:
                  HES reads Attribute 3 (Bit-String) or Attribute 4 (image_first_not_transferred_block_number), locates the first zero bit, and retransmits ONLY the missing blocks before calling Method 3.

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    sarah_jenkins
                    wrote on last edited by
                    #9

                    Here is the verified Action-Response-Normal from Method 3 indicating verification success:

                    00 01 00 01 00 01 00 07 C7 01 C1 00 00
                    
                    • C7 01: Action-Response-Normal
                    • C1: Invoke-ID 1
                    • 00: Result success (verification initiated)

                    Reading Attribute 6 (image_transfer_status) returns 0x03 (verification_successful).

                    1 Reply Last reply
                    0
                    • E Offline
                      E Offline
                      elena_rostova
                      wrote on last edited by
                      #10

                      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:

                      1. Block Indexing: Always start block numbers at 0 (Blue Book Section 4.3.8).
                      2. Signature Encoding: Confirm whether firmware expects ASN.1 DER (71B) or raw P1363 (64B).
                      3. Digest Pre-image: Verify if image_identifier is included in SHA-256 digest.
                      4. Activation Handling: Prepare for immediate TCP connection reset upon Method 4.
                      1 Reply Last reply
                      0

                      Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                      Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                      With your input, this post could be even better 💗

                      Register Login
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      Powered by NodeBB Contributors
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups