Home docs pia PIA Packet Format
Post

PIA Packet Format

These packets are usually sent directly from one console to another through UDP, with no server in between. Everything is encoded in big-endian byte order.

All packets consist of an unencrypted header, which is followed by one or more messages, and sometimes an unencrypted footer.

Up to 5.6:

OffsetSizeDescription
0x04Magic number: 32 AB 98 64
0x41Encrypted (1=No 2=Yes)
0x51Connection id
0x62Packet id
0x82Session timer
0xA2RTT timer

5.7 - 5.10:

OffsetSizeDescription
0x04Magic number: 32 AB 98 64
0x41Encrypted (1=No 2=Yes)
0x51Connection id
0x62Packet id
0x82Session timer
0xA2RTT timer
0xC8AES-GCM nonce
0x1416AES-GCM authentication tag

5.11 - 5.21:

OffsetSizeDescription
0x04Magic number: 32 AB 98 64
0x41This byte consists of two parts:
0x80: Encryption enabled
0x7F: Version number (3 or 4)
0x51Connection id
0x62Packet id
0x88AES-GCM nonce
0x1016AES-GCM authentication tag

5.23 - 5.26:

OffsetSizeDescription
0x04Magic number: 32 AB 98 64
0x41This byte consists of two parts:
0x80: Encryption enabled
0x7F: Version number (5)
0x51Connection id
0x62Packet id
0x88AES-GCM nonce
0x108AES-GCM authentication tag (first 8 bytes)

5.27 - 5.44:

OffsetSizeDescription
0x04Magic number: 32 AB 98 64
0x41This byte consists of two parts:
0x80: Encryption enabled
0x7F: Version number (9)
0x54Destination variable id
0x94Source variable id
0xD2Packet id
0xF1Footer size
0x108AES-GCM nonce
0x188AES-GCM authentication tag (first 8 bytes)

6.16 - 6.30:

OffsetSizeDescription
0x04Magic number: 32 AB 98 64
0x41This byte consists of two parts:
0x80: Encryption enabled
0x7F: Version number (11, 12 or 13)
0x52Destination variable id
0x72Source variable id
0x92Packet id
0xB1Footer size
0xC8AES-GCM nonce
0x148AES-GCM authentication tag (first 8 bytes)

Version

Pia VersionHeader version
5.11 - 5.173
5.18 - 5.214
5.23 - 5.265
5.27 - 5.449
6.16 - 6.2311
6.25 - 6.2612
6.29 - 6.3013

Connection ID

During connection establishment, both consoles generate a random number between 2 and 255. This is the connection id. If packets are sent to a specific address, rather than station index, the connection id is set to 0.

Packet ID

If the connection id is 0, the packet id is also 0. If the connection id is not 0, the packet id is an incrementing number starting at 1.

RTT Calculation

The session timer contains the number of milliseconds since the start of the session. Every client has its own session timer (they are independent from each other). Aside from its own session timer, every client also keeps track of the session timers of all other clients. When A sends a packet to B the RTT timer is what A belives the session timer of B to be. Hopefully, an example will make this clear:

Let’s say the session timer of A is at 234 when A sends a packet to B. It takes 2 milliseconds until the packet arrives at B. B receives 234 from A even though the session timer of A is now at 236. 10 milliseconds later, B sends a packet to A with 244 (234 + 10) in the RTT timer field. Again, it takes 2 milliseconds until the packet arrives at A. At this point, the session timer of A is at 248, but it receives 244 in the RTT timer field, so it knows that it takes 4 milliseconds for a packet to travel back and forth between A and B.

The footer is only used in LDN mode when a packet is sent to more than one console. It contains the variable id of all receiving consoles as 16-bit integers.

Messages

This part of the packet may be encrypted. A packet may contain more than one message (the number of messages is determined from the size of packet).

All messages are padded such that their size is a multiple of 4 bytes.

Up to 5.4:

OffsetSizeDescription
0x01Message flags
0x11Source station index
0x22Payload size
0x44Destination
0x84Source constant id
0xC2Protocol type
0xE2Protocol port (protocol-specific)
0x104Reserved (always 0)
0x14 Payload (protocol-specific)
  Padding

5.6 - 5.10:

OffsetSizeDescription
0x01Message flags
0x12Payload size
0x38Destination
0xB8Source constant id
0x131Protocol type
0x141Protocol port (protocol-specific)
0x153Padding (always 0)
0x18 Payload (protocol-specific)
  Padding

5.11 - 5.12:

OffsetSizeDescription
0x01Message flags
0x11Version (always 1)
0x22Payload size
0x41Protocol type
0x51Protocol port (protocol-specific)
0x68Destination
0xE8Source constant id
0x16 Payload (protocol-specific)
  Padding

5.14 - 5.17:

OffsetSizeDescription
0x01Message flags
0x11Version (always 2)
0x22Payload size
0x41Protocol type
0x53Protocol port (protocol-specific)
0x88Destination
0x108Source constant id
0x18 Payload (protocol-specific)
  Padding

5.18 - 5.26:

Fields that are not present are copied from the previous message.

TypeDescription
Uint8Flags indicating which of the following fields are present.
Uint8Message flags. Only present if flags & 1.
Uint16Payload size. Only present if flags & 2.
Uint8Protocol type. Only present if flags & 4.
Uint24Protocol port (protocol-specific). Only present if flags & 4.
Uint64Destination. Only present if flags & 8.
Uint64Source constant id. Only present if flags & 16.
BytesPayload (protocol-specific)
 Padding

5.27 - 6.30:

Fields that are not present are copied from the previous message.

TypeDescription
Uint8Flags indicating which of the following fields are present.
Uint8Message flags. Only present if flags & 1.
Uint16Payload size. Only present if flags & 2.
Uint8Protocol type. Only present if flags & 4.
Uint24Protocol port (protocol-specific). Only present if flags & 4.
Uint64Destination. Only present if flags & 8.
BytesPayload (protocol-specific)
 Padding

Message flags

MaskDescription
0x1The message is sent to multiple consoles (multicast)
0x2The message should be relayed to another console
0x4The message was relayed through another console
0x8The message may not be bundled with other messages in a single packet
0x10The message payload is zlib compressed. This was introduced around Pia version 5.14 and is only supported by some specific protocols.

Note: it seems like later pia versions use 0x20 for zlib compression instead.

Station index

Every console in a mesh gets its own station index. The following station index values are special:

  • 253: Invalid. Used for consoles that haven’t joined a mesh yet.
  • 254: Represents the host of the mesh.
  • 255: Used for broadcast messages.

Destination

The content of this field depends on the multicast bit. If the multicast bit is cleared, this field contains the constant id of the destination console. If the multicast bit is set, this field contains a bitmap where each bit represents one destination console (the bit number of a console is its station index: 1 << station_index).

Encryption

Packets are encrypted and signed with the session key. The messages are padded with 0xFF before encryption such that their combined size is a multiple of 16 bytes.

Up to 5.6:

If encryption is enabled, the messages are encrypted with AES-ECB. The HMAC-MD5 of the whole packet (both header and encrypted payload) is appended to the packet.

5.7 - 6.30:

If encryption is enabled, the messages are encrypted with AES-GCM. The authentication tag is stored in the header. No other signature is appended to the packet.

Nonce

The AES-GCM nonce depends on the network type and is generated as follows:

NEX (up to 5.44):

OffsetSizeDescription
0x01Connection id
0x13gathering_id & 0xFFFFFF
0x48Nonce from header

LDN (up to 5.44):

OffsetSizeDescription
0x03First 3 bytes of CRC32 hash
0x31Connection id
0x48Nonce from header

The CRC32 hash is calculated over the following data:

OffsetSizeDescription
0x04Session id (see application data)
0x46MAC address of source

LDN (6.16 - 6.30):

OffsetSizeDescription
0x04XOR of network id and IP address of source
0x48Nonce from header

LAN (up to 5.44):

OffsetSizeDescription
0x04IP address of source
0x41Connection id
0x57Last 7 bytes of nonce from header

LAN (6.16 - 6.30):

OffsetSizeDescription
0x04IP address of source (CRC-32 hash if IPv6 is used)
0x48Nonce from header

NPLN (6.16 - 6.30):

OffsetSizeDescription
0x04Network id
0x48Nonce from header

Session Key

The session key is used for packet encryption and signature calculation.

NEX (Up to 5.44):

The session key is obtained from the game server during matchmaking.

LDN (Up to 5.44):

A random number generator is constructed using the session param as seed. Four random 32-bit integers are generated and appended to each other in little-endian byte order. The session key is generated by encrypting this buffer with AES, using a game-specific key.

LDN (6.16 - 6.30):

The session key is generated by encrypting the network SSID with AES, using a game-specific-key.

LAN (Up to 6.30):

First, the last byte of the session key param is incremented by 1. The session key is generated by taking the first 16 bytes of the HMAC-SHA256 of the incremented session key param, using a game-specific key.

Contents