MCBE
OverviewPacketsServerToClientHandshake
ID 0x03 · 3Server → ClientConnection

ServerToClientHandshake

Starts encryption. Carries a single ES384-signed JWT whose header holds the server's public key and whose payload holds the salt used to derive the shared AES key.

server-to-client-handshake.bin
JWT header  {"alg":"ES384","x5u":"MHYwEAYHKoZIzj0CAQYF..."}
JWT payload {"salt":"dGhpc2lzYXNhbHQxNjBi="}

Structure

FieldTypeDescription
Handshake Web Tokenstringvaruint32-length-prefixed raw JWT (header.payload.signature).

Notes

  • JWT header: {"alg":"ES384","x5u":"<base64 SPKI DER of the server's ECDSA P-384 key>"}. Payload: {"salt":"<base64 of 16 random bytes>"}.
  • Shared secret = ECDH(server private key, client identity public key) — 48 bytes on secp384r1.
  • Encryption key = SHA-256(salt ‖ sharedSecret). One 32-byte key serves both directions.
  • Cipher: AES-256-CTR with IV = key[0..12] ‖ 00 00 00 02 (a GCM-style counter without the tag).
  • Every batch gains an 8-byte checksum: SHA-256(sendCounter uint64 LE ‖ batch payload ‖ key), truncated to 8 bytes, appended after the payload; then everything after the 0xFE header byte is encrypted.
  • The server enables encryption only after this packet is flushed, so the packet itself travels in plaintext.
  • Over NetherNet this packet is not used — DTLS already encrypts the transport.