Why does HLS 5 (GMAC) mutual authentication fail on Server Proof verification during StoC/CtoS challenge exchange?
-
[!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 LNOBIS0.0.40.0.0.255, Management Client0x0001).The initial
AARQ(0x60) andAARE(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 insideAction-Request-Normal(Class 15 Method 1reply_to_HLS_authentication), and transmits it to the meter. The meter accepts theCtoSresponse and replies withAction-Response-Normalcontaining the Server Proof (StoCauthentication 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-mismatchexception!Here is our client
Action-Request(CtoSchallenge):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 28And here is the meter's
Action-Responsecontaining 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 66Why is our client failing to verify the meter's Server Proof?
-
Alex, this is one of the most classic identity traps in DLMS Security Suite 1 implementation.
Per DLMS Green Book 10th Ed Section 9.2.5.3 (High Level Security Mechanism 5) and Table 43 (HLS 5 Standard Test Vectors), HLS 5 uses AES-GCM-128 authentication in two asymmetric directions:
- Client to Server Proof (CtoS):
- Input challenge:
StoC(generated by meter) - IV Construction: $\text{IV}_{\text{CtoS}} = \text{Client System Title (8B)} \parallel \text{Client Invocation Counter (4B)}$
- AAD: $\text{Security Control (0x10)} \parallel \text{Authentication Key (GAK)}$
- Input challenge:
- Server to Client Proof (StoC):
- Input challenge:
CtoS(generated by client inAARQ) - IV Construction: $\text{IV}_{\text{StoC}} = \text{Server System Title (8B)} \parallel \text{Server Invocation Counter (4B)}$
- Input challenge:
Look closely at how your HES client verifies the meter's response:
If your client code re-used the Client System Title (or client IV) instead of extracting the Server System Title from theAAREresponse (0x61), the AES-GCM GHASH polynomial state will calculate a completely different tag! - Client to Server Proof (CtoS):
-
David's point regarding System Title directionality is 100% correct. Furthermore, check the Authentication Key (GAK) and Block Cipher Key (GUEK) separation:
In DLMS Green Book Section 9.2.5.3, HLS 5 authentication strictly uses the Authentication Key (GAK) (e.g.
D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF), NOT the Global Unicast Encryption Key (GUEK:000102030405060708090A0B0C0D0E0F).In your
Action-Requesthex:
10 00 00 00 01 1A 52 FE 7D D3 E7 27 48 97 3C 1E 2810: Security Control (0x10= Global Key, Authenticated Only, Security Suite 1)00 00 00 01: Client Invocation Counter11A 52 FE ...: 12-byte GMAC tag.
Notice that
1A 52 FE 7D D3 E7 27 48 97 3C 1E 28exactly matches the official Green Book Table 43 test vector when StoC is503677524A323146and Client System Title is4D4D4D0000000001! This proves your client computed CtoS correctly, meaning the meter accepted it and incremented its own Invocation Counter to1for the Server Proof. -
Let's look at the meter's returned Server Proof:
10 00 00 00 01 88 F0 1A 2B 3C 4D 99 11 22 33 44 55 66Dissecting the meter's proof:
10: Server Security Control byte00 00 00 01: Server Invocation Counter (0x00000001)88 F0 1A 2B 3C 4D 99 11 22 33 44 55: 12-byte Server GMAC Tag
Per IEC 62056-53 Section 8.4.2 and DLMS Green Book Section 9.2.5.3.2, to verify this Server Proof, your client must execute:
$$\text{IV} = \text{ServerSystemTitle} (8\text{B}) \parallel \text{ServerIC} (0x00000001)$$
$$\text{AAD} = \text{0x10} \parallel \text{GAK}$$
$$\text{Data} = \text{Client Challenge (CtoS extracted from AARQ)}$$If your client passed
StoCinstead ofCtoSas the data payload during Server Proof verification, it fails immediately! -
There is also the ACSE Service User Calling Authentication wrapper to verify.
Per DLMS Green Book 10th Ed Section 9.3.4 (Association LN Class 15), when HLS 5 is used, the
calling-authentication-valueinAARQmust use thecharstringorbitstringchoice with tag[0](ACSE User Information = 14).If the HES transmits the initial
CtoSchallenge inAARQwith an incorrect length (e.g. 8 bytes vs 16 bytes), the meter's internal RNG pads the challenge with zeros, causing the Server Proof calculation to diverge from the client's expectation. -
David and Marcus, you hit the bullseye! I inspected our HES verification method, and we had two distinct bugs:
- System Title Inversion: Our code was using
client.ciphering.systemTitleto build the IV for both CtoS AND StoC verification, instead of usingserver.systemTitle(4752580000000001/GRX00000001) for the Server Proof! - Challenge Inversion: We were hashing
StoCagainst the server response, rather than our originalCtoSchallenge string.
Once we passed
serverSystemTitleandclientChallengeinto the AES-GCM GMAC validator, the computed tag matched88 F0 1A 2B 3C 4D 99 11 22 33 44 55with 0 bit errors! - System Title Inversion: Our code was using
-
Excellent! Here is the formal mathematical rule for HLS 5 verification symmetry:
$$\text{CtoS Tag} = \text{GMAC}{\text{GAK}}(\text{SystemTitle}{\text{Client}} \parallel IC_{\text{Client}}, \text{AAD}{\text{0x10}}, \text{StoC})$$
$$\text{StoC Tag (Server Proof)} = \text{GMAC}{\text{GAK}}(\text{SystemTitle}{\text{Server}} \parallel IC{\text{Server}}, \text{AAD}_{\text{0x10}}, \text{CtoS})$$Notice the perfect criss-cross symmetry between Challenge Originator and System Title Holder per DLMS Green Book Section 9.2.5.3.
-
From the meter firmware side, there is one more vital rule: Invocation Counter Anti-Replay on HLS.
When the meter receives
Action-RequestMethod 1, it checks:
$$IC_{\text{CtoS}} > IC_{\text{last_stored}}$$
If the client retransmitsCtoSwith an old or duplicate $IC$, the meter returnsAction-Response-With-Service-Error: hardware-fault(0xC7 0x01 C1 01). Always ensure the client increments its $IC$ before building CtoS! -
Here is the verified working trace of the complete HLS 5 handshake exchange:
AARQwith Client Challenge (CtoS
00 01 00 01 00 01 00 3C 60 3A A1 09 06 07 60 85 74 05 08 01 03 A6 0A 04 08 4D 4D 4D 00 00 00 00 01 8A 02 07 80 8B 07 60 85 74 05 08 02 05 AC 0A 80 08 43 74 6F 53 31 32 33 34 BE 10 04 0E 01 00 00 00 06 5F 1F 04 00 00 7E 1F 04 00AAREwith Server Challenge (StoC
00 01 00 01 00 01 00 3C 61 3A A1 09 06 07 60 85 74 05 08 01 03 A2 03 02 01 00 A3 05 A1 03 02 01 00 A6 0A 04 08 47 52 58 00 00 00 00 01 88 02 07 80 89 07 60 85 74 05 08 02 05 AA 0A 80 08 50 36 77 52 4A 32 31 46 BE 10 04 0ECtoSAction Request (Method 1) &StoCServer Proof Action Response succeed cleanly.
-
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 hashesCtoS. - Anti-Replay: Guarantee monotonic $IC$ incrementation before challenge generation.
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