Skip to content

IntegrityPart Verification Failure in S7CommPlus V3 Fragmented Packets #13

@lircy

Description

@lircy

Environment

  • Protocol: S7CommPlus V3 (0x03)
  • Test Tools: TIA Portal V17 + S7-PLCSIM Advanced V5.0
  • Code Platform: .NET/C#
  • Critical Constraint: Max packet size = 1024 bytes (per TCP transmission)

Problem Description

When sending single packets ≤1024 bytes, the calculated IntegrityPart passes PLC verification successfully. However, when sending fragmented packets (>1024 bytes) where:

  1. Each fragment has its own IntegrityPart computed
  2. Fragments are sent sequentially according to protocol

The PLC rejects the IntegrityPart of fragmented packets despite correct session keys and protocol version handling.

Code Analysis Highlight

Key logic in SendS7plusPDUdata method:

private int SendS7plusPDUdata(byte[] sendPduData, int bytesToSend, byte protoVersion, byte[] sessionKey)
{
    m_LastError = 0;

    int curSize;
    int sourcePos = 0;
    int sendLen;
    int NegotiatedIsoPduSize = 1024;// TODO: Respect the negotiated TPDU size

    // 4 Byte TPKT Header
    // 3 Byte ISO-Header
    // 4 Byte S7CommPlus Header
    // 4 Byte S7CommPlus Trailer (must fit into last PDU)
    int MaxSize = NegotiatedIsoPduSize - 4 - 3 - 4 - (protoVersion == 0x03 ? 33 : 0);
    byte[] packet = new byte[MaxSize + 4 + (protoVersion == 0x03 ? 33 : 0)]; //max packet size is always MaxSize + PDU Header

    while (bytesToSend > 0)
    {
        if (bytesToSend > MaxSize)
        {
            curSize = MaxSize;
            bytesToSend -= MaxSize;
        }
        else
        {
            curSize = bytesToSend;
            bytesToSend -= curSize;
        }
        // Header
        packet[0] = 0x72;
        packet[1] = protoVersion;
        packet[2] = (byte)((curSize + (protoVersion == 0x03 ? 33 : 0)) >> 8);
        packet[3] = (byte)((curSize + (protoVersion == 0x03 ? 33 : 0)) & 0x00FF);
        // Data part
        byte[] curData = new byte[curSize];
        Array.Copy(sendPduData, sourcePos, curData, 0, curSize);
        if (protoVersion == 0x03)
        {
            byte[] data = new byte[curData.Length];
            byte[] digestBuffer = new byte[32];
            Buffer.BlockCopy(curData, 0, data, 0, curData.Length);
            CalculateDigest(digestBuffer.AsSpan(), data, sessionKey);
            IntegrityPart integrityPart = new IntegrityPart();
            integrityPart.DigetLength = (byte)digestBuffer.Length;
            integrityPart.PacketDigest = digestBuffer;
            MemoryStream stream1 = new MemoryStream();
            integrityPart.Serialize(stream1);
            Array.Copy(stream1.ToArray(), 0, packet, 4, 33);
        }
        Array.Copy(sendPduData, sourcePos, packet, 4 + (protoVersion == 0x03 ? 33 : 0), curSize);
        sourcePos += curSize;
        sendLen = 4 + curSize + (protoVersion == 0x03 ? 33 : 0);

        // Trailer only in last packet
        if (bytesToSend == 0)
        {
            Array.Resize(ref packet, sendLen + 4); //resize only the last package to sendLen + TrailerLen
            packet[sendLen] = 0x72;
            sendLen++;
            packet[sendLen] = protoVersion;
            sendLen++;
            packet[sendLen] = 0;
            sendLen++;
            packet[sendLen] = 0;
            sendLen++;
        }
        m_client.Send(packet);
    }
    return m_LastError;
}

Suspected Causes

Based on S7CommPlus V3 specification:

  1. Fragment Sequence Handling

    • PLC may require cumulative digest across fragments
    • Current code computes per-fragment digest independently
  2. Header Composition

    • When protoVersion=0x03, the header includes:
      [0x72][ProtoVersion][LengthHi][LengthLo] + 33-byte IntegrityPart
      
    • Trailer (0x72 + ProtoVersion + 00 00) only applied to last packet
  3. Digest Calculation Scope

    • Possible missing elements:
      • Fragment sequence counter
      • Session context persistence between fragments
      • Cumulative payload hash

Request for Assistance

Please help verify:

  1. Does CalculateDigest() need to include fragment sequence metadata?
  2. Is the trailer (0x72...) required for every fragment or only the terminal one?
  3. Should the integrity digest cover all previous fragments in multi-packet transmissions?

Reproduction Steps

  1. Send packet with 900 bytes payload → Verification PASS
  2. Send packet with 1500 bytes payload (split into two fragments):
    • Fragment1: 1009 bytes + IntegrityPart
    • Fragment2: 491 bytes + IntegrityPart + Trailer
  3. Observe PLC rejects both fragments' authentication

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions