SST Message Types
SST defines a fixed set of message types used across the three communication planes: entity-to-Auth, entity-to-entity (session key handshake and secure communication), and Auth-to-Auth. Every SST message carries a one-byte type code. The canonical source of truth is MessageType.java.
IoTSP framing
All SST messages — entity-to-Auth, Auth-to-Auth, and entity-to-entity — share the same outer envelope, serialized by serializeIoTSP() in common.js and its Java counterpart IoTSPMessage.java:
┌──────────┬──────────────────────────┬───────────┐
│ msgType │ payloadLen │ payload │
│ (1 byte) │ (variable-length int) │ (N bytes) │
└──────────┴──────────────────────────┴───────────┘
msgType: one of the values in the summary table below.payloadLen: the byte length ofpayload, encoded as a variable-length integer.payload: message-specific content; structure varies by type and is described in each section below.
Summary table
| Code | Name | Plane | Direction |
|---|---|---|---|
| 0 | AUTH_HELLO | Entity ↔ Auth | Auth → Entity |
| 1 | ENTITY_HELLO | Entity ↔ Auth | Entity → Auth |
| 10 | AUTH_SESSION_KEY_REQ | Auth ↔ Auth | Auth → Auth |
| 11 | AUTH_SESSION_KEY_RESP | Auth ↔ Auth | Auth → Auth |
| 20 | SESSION_KEY_REQ_IN_PUB_ENC | Entity ↔ Auth | Entity → Auth |
| 21 | SESSION_KEY_RESP_WITH_DIST_KEY | Entity ↔ Auth | Auth → Entity |
| 22 | SESSION_KEY_REQ | Entity ↔ Auth | Entity → Auth |
| 23 | SESSION_KEY_RESP | Entity ↔ Auth | Auth → Entity |
| 24 | SESSION_KEY_RESP_FOR_DELEGATION | Entity ↔ Auth | Auth → Entity |
| 25 | SESSION_KEY_RESP_FOR_DELEGATION_WITH_DIST_KEY | Entity ↔ Auth | Auth → Entity |
| 30 | SKEY_HANDSHAKE_1 | Entity ↔ Entity | Initiator → Responder |
| 31 | SKEY_HANDSHAKE_2 | Entity ↔ Entity | Responder → Initiator |
| 32 | SKEY_HANDSHAKE_3 | Entity ↔ Entity | Initiator → Responder |
| 33 | SECURE_COMM_MSG | Entity ↔ Entity | Bidirectional |
| 34 | FIN_SECURE_COMM | Entity ↔ Entity | Bidirectional |
| 40 | SECURE_PUB | Entity ↔ Entity | Publisher → Subscriber |
| 50 | MIGRATION_REQ_WITH_SIGN | Entity ↔ Auth | Entity → Auth |
| 51 | MIGRATION_RESP_WITH_SIGN | Entity ↔ Auth | Auth → Entity |
| 52 | MIGRATION_REQ_WITH_MAC | Entity ↔ Auth | Entity → Auth |
| 53 | MIGRATION_RESP_WITH_MAC | Entity ↔ Auth | Auth → Entity |
| 60 | ADD_READER_REQ_IN_PUB_ENC | Entity ↔ Auth | Entity → Auth |
| 61 | ADD_READER_RESP_WITH_DIST_KEY | Entity ↔ Auth | Auth → Entity |
| 62 | ADD_READER_REQ | Entity ↔ Auth | Entity → Auth |
| 63 | ADD_READER_RESP | Entity ↔ Auth | Auth → Entity |
| 70 | DELEGATED_ACCESS_REQ_IN_PUB_ENC | Entity ↔ Auth | Entity → Auth |
| 71 | DELEGATED_ACCESS_RESP_WITH_DIST_KEY | Entity ↔ Auth | Auth → Entity |
| 72 | DELEGATED_ACCESS_REQ | Entity ↔ Auth | Entity → Auth |
| 73 | DELEGATED_ACCESS_RESP | Entity ↔ Auth | Auth → Entity |
| 80 | PRIVILEGED_REQ_IN_PUB_ENC | Entity ↔ Auth | Entity → Auth |
| 81 | PRIVILEGED_RESP_WITH_DIST_KEY | Entity ↔ Auth | Auth → Entity |
| 82 | PRIVILEGED_REQ | Entity ↔ Auth | Entity → Auth |
| 83 | PRIVILEGED_RESP | Entity ↔ Auth | Auth → Entity |
| 100 | AUTH_ALERT | Entity ↔ Auth | Auth → Entity |
Entity ↔ Auth messages
These messages flow between an entity and the Auth it is registered to. They cover the initial greeting, session key acquisition, and error signaling.
AUTH_HELLO (0)
Sent by Auth to open the authentication handshake. The entity must reply with ENTITY_HELLO (or a session key request variant) using the nonce supplied here.
| Field | Type | Description |
|---|---|---|
authId | UInt32BE (4 B) | Identifier of the Auth instance |
authNonce | Buffer (AUTH_NONCE_SIZE) | Random nonce for replay protection |
ENTITY_HELLO (1)
Greeting sent by an entity in response to AUTH_HELLO.
SESSION_KEY_REQ_IN_PUB_ENC (20)
Entity requests session keys. The payload is encrypted with Auth's public key (used before a distribution key has been established).
| Field | Type | Description |
|---|---|---|
entityNonce | Buffer | Entity-generated nonce |
authNonce | Buffer | Echo of the nonce received in AUTH_HELLO |
numKeys | UInt32BE | Number of session keys requested |
senderName | length-prefixed String | Requesting entity's registered name |
purpose | JSON object | Communication purpose (describes the target group or key ID) |
diffieHellmanParam | Buffer (optional) | DH parameter for forward-secrecy key exchange |
SESSION_KEY_RESP_WITH_DIST_KEY (21)
Auth responds with session keys and the entity's distribution key. Used on first contact or after key rotation.
| Field | Type | Description |
|---|---|---|
entityNonce | Buffer | Echo of the entity nonce from the request |
cryptoSpec | JSON | Cipher and MAC algorithm identifiers |
sessionKeyList | length-prefixed list | One or more SessionKey objects |
encryptedDistKey | Buffer | The entity's distribution key, encrypted |
SESSION_KEY_REQ (22)
Standard session key request. The payload is encrypted with the entity's existing distribution key rather than Auth's public key.
Fields are identical to SESSION_KEY_REQ_IN_PUB_ENC.
SESSION_KEY_RESP (23)
Auth responds with session keys only (no distribution key). Used when the entity already holds a valid distribution key.
| Field | Type | Description |
|---|---|---|
entityNonce | Buffer | Echo of the entity nonce |
cryptoSpec | JSON | Cipher and MAC algorithm identifiers |
sessionKeyList | length-prefixed list | One or more SessionKey objects |
SESSION_KEY_RESP_FOR_DELEGATION (24)
Session key response for delegated access. Includes an otherSessionKeyOwnerGroup field identifying the group that owns the returned session keys.
SESSION_KEY_RESP_FOR_DELEGATION_WITH_DIST_KEY (25)
Combines the delegated-access response with a distribution key, analogous to the relationship between (21) and (23).
AUTH_ALERT (100)
Auth sends this message to signal an error during entity communication. The payload is a single-byte alert code.
| Alert code | Value | Meaning |
|---|---|---|
INVALID_DISTRIBUTION_KEY | 0 | The distribution key presented by the entity is not recognized |
INVALID_SESSION_KEY_REQ | 1 | The session key request could not be validated |
UNKNOWN_INTERNAL_ERROR | 2 | An unspecified internal error occurred in Auth |
Auth ↔ Auth messages
These messages flow between two trusted Auth instances, for example when Auth A needs to retrieve session keys on behalf of an entity registered to Auth B.
AUTH_SESSION_KEY_REQ (10)
Sent by one Auth to another to request session keys for a registered entity. Transmitted over HTTP.
| Field | Type | Description |
|---|---|---|
sessionKeyID | Long | ID of the session key being requested |
requestingEntityName | String | Name of the entity on whose behalf the request is made |
requestingEntityGroup | String | Group of the requesting entity |
cachedKeyAuthID | Int | ID of the Auth that cached the key originally |
AUTH_SESSION_KEY_RESP (11)
Response from Auth carrying the requested session keys. Transmitted over HTTP.
| Field | Type | Description |
|---|---|---|
sessionKeyList | JSON array | One or more SessionKey objects serialized as JSON strings |
Entity ↔ Entity messages (session key handshake)
After an entity has obtained session keys from Auth, it establishes a secure channel to a peer using a three-message handshake, then exchanges application data as SECURE_COMM_MSG messages.
State machines
Initiator (client) — implemented in iotSecureClient.js:
IDLE (0) ──send HANDSHAKE_1──► HANDSHAKE_1_SENT (10) ──recv HANDSHAKE_3 ack──► IN_COMM (30)
Responder (server) — implemented in iotSecureServer.js:
IDLE (0) ──recv HANDSHAKE_1──► HANDSHAKE_1_RECEIVED (21) ──send HANDSHAKE_2──► HANDSHAKE_2_SENT (22) ──recv HANDSHAKE_3──► IN_COMM (30)
A WAITING_SESSION_KEY (20) state is entered if the responder needs to request the session key from Auth before proceeding.
Handshake message body format
All three handshake payloads share a common serialized body (from serializeHandshake()) that is then symmetrically encrypted and authenticated:
┌──────────────┬────────────────────────┬────────────────────────┬──────────────────────────┐
│ indicator │ nonce │ replyNonce │ dhParam (optional) │
│ (1 byte) │ (HS_NONCE_SIZE = 8 B) │ (HS_NONCE_SIZE = 8 B) │ (variable) │
└──────────────┴────────────────────────┴────────────────────────┴──────────────────────────┘
The indicator byte is a bitmask controlling which fields carry meaningful data:
| Bit | Mask | Field present |
|---|---|---|
| 0 | 0x01 | nonce |
| 1 | 0x02 | replyNonce |
| 2 | 0x04 | dhParam |
Space for both nonces is always allocated; unused slots are zeroed.
SKEY_HANDSHAKE_1 (30) — Initiator → Responder
Establishes which session key to use and proves the initiator holds it.
Payload layout:
┌──────────────────────────────┬──────────────────────────────────────────────────────────┐
│ sessionKeyId (plaintext) │ Encrypt+MAC( indicator | nonce | replyNonce[zeroed] ) │
│ (SESSION_KEY_ID_SIZE = 8 B) │ │
└──────────────────────────────┴──────────────────────────────────────────────────────────┘
sessionKeyId: identifies which session key both sides will use; sent in plaintext so the responder can look it up.indicator = 0x01— onlynonceis populated.nonce: initiator's freshly generated 8-byte handshake nonce.- The entire handshake body is symmetrically encrypted and authenticated with the session key.
SKEY_HANDSHAKE_2 (31) — Responder → Initiator
Proves the responder holds the same session key by echoing the initiator's nonce, and introduces its own nonce for the initiator to echo back.
Payload layout (entirely encrypted + MACed):
┌──────────┬───────────────────────┬───────────────────────┬──────────────────────────┐
│ indicator│ nonce (responder's) │ replyNonce (echo of │ dhParam (if DH enabled) │
│ (1 B) │ (8 B) │ initiator's nonce, 8B)│ (variable) │
└──────────┴───────────────────────┴───────────────────────┴──────────────────────────┘
indicator = 0x03(nonce + replyNonce), or0x07if a DH parameter is appended.nonce: responder's freshly generated handshake nonce.replyNonce: echo of the initiator's nonce fromSKEY_HANDSHAKE_1; proves the responder decrypted it correctly.dhParam(optional): responder's DH public key when Diffie-Hellman forward secrecy is configured.
SKEY_HANDSHAKE_3 (32) — Initiator → Responder
Completes mutual authentication by echoing the responder's nonce. After this message both sides transition to IN_COMM.
Payload layout (entirely encrypted + MACed):
┌──────────┬───────────────────────┬────────────────────────────────┬──────────────────────────┐
│ indicator│ nonce (zeroed) │ replyNonce (echo of │ dhParam (if DH enabled) │
│ (1 B) │ (8 B, unused) │ responder's nonce, 8 B) │ (variable) │
└──────────┴───────────────────────┴────────────────────────────────┴──────────────────────────┘
indicator = 0x02(replyNonce only), or0x06if a DH parameter is appended.replyNonce: echo of the responder's nonce fromSKEY_HANDSHAKE_2; proves the initiator decrypted it correctly.dhParam(optional): initiator's DH public key. After this message both sides compute the shared DH secret and update the session key viaupdateSymmetricKeyWithKeyFromDH().
SECURE_COMM_MSG (33) — Bidirectional
Carries application data after the handshake completes. The payload is the symmetric-encryption-plus-authentication output of:
┌───────────────────────────────────────────────────────────────────────────┐
│ Encrypt+MAC( seqNum (SEQ_NUM_SIZE = 8 B, big-endian) | data (variable) ) │
└───────────────────────────────────────────────────────────────────────────┘
seqNum: monotonically increasing 64-bit counter, tracked separately per direction (writeSeqNum/readSeqNum) to detect replays.data: raw application payload bytes.
FIN_SECURE_COMM (34) — Bidirectional
Signals session teardown.
- TCP: handled implicitly by the socket
endevent; no explicitFIN_SECURE_COMMmessage is sent over the wire. - UDP: explicit message with an empty payload is required because UDP has no connection state. The responder transitions back to
IDLEand the socket can be reused for a new session.
SECURE_PUB (40) — Publisher → Subscriber
Used in publish/subscribe scenarios (MQTT or UDP broadcast). Because there is no prior handshake, the key ID must be included in plaintext so subscribers can look up the correct session key.
Payload layout:
┌────────────────────────────────┬─────────────────────────────────────────────────────────┐
│ keyId (plaintext) │ Encrypt+MAC( seqNum (8 B, big-endian) | data (variable))│
│ (SESSION_KEY_ID_SIZE = 8 B) │ │
└────────────────────────────────┴─────────────────────────────────────────────────────────┘
keyId: identifies which session key from the subscriber'scurrentSessionKeyListto use for decryption; in plaintext.seqNum: 64-bit monotonically increasing counter for replay detection.data: encrypted application payload.
If a subscriber receives a SECURE_PUB for an unknown keyId, it requests the missing session key from Auth and retries decryption on arrival.
Transport: MQTT topic Ptopic (TCP) or UDP broadcast to 255.255.255.255:8088.
Migration messages
Migration lets a registered entity move its credentials from one Auth to another. Two authentication variants are supported: signature-based (using Auth certificates) and MAC-based (using distribution keys).
MIGRATION_REQ_WITH_SIGN (50) / MIGRATION_REQ_WITH_MAC (52)
Sent by the entity to initiate migration.
| Field | Type | Description |
|---|---|---|
entityNonce | Buffer (ENTITY_NONCE_SIZE) | Entity-generated nonce |
authNonce | Buffer (AUTH_NONCE_SIZE) | Nonce from the target Auth's hello |
senderName | UInt8-length-prefixed String | Registered name of the migrating entity |
WITH_SIGN authenticates via an Auth certificate and signature; WITH_MAC authenticates using a distribution key and MAC.
MIGRATION_RESP_WITH_SIGN (51) / MIGRATION_RESP_WITH_MAC (53)
Sent by the target Auth to confirm migration.
| Field | Type | Description |
|---|---|---|
authID | UInt32 | ID of the responding Auth |
entityNonce | Buffer | Echo of the entity nonce from the request |
authCertificate | X.509 certificate (WITH_SIGN) | Auth's certificate for signature verification |
encryptedNewDistributionKey | Buffer (WITH_MAC) | New distribution key encrypted for the entity |
Add-reader messages (file sharing)
These messages support the file-sharing use case, allowing an entity (a writer) to register additional readers that may decrypt shared content.
ADD_READER_REQ_IN_PUB_ENC (60) / ADD_READER_REQ (62)
Entity requests that a new reader be granted access to shared content. Fields mirror SESSION_KEY_REQ_IN_PUB_ENC / SESSION_KEY_REQ: entity nonce, auth nonce, number of keys, sender name, purpose JSON, and optional DH parameter.
IN_PUB_ENC encrypts the request with Auth's public key; the plain variant uses the distribution key.
ADD_READER_RESP_WITH_DIST_KEY (61) / ADD_READER_RESP (63)
Auth confirms that the reader has been added and returns session keys. Fields mirror SESSION_KEY_RESP_WITH_DIST_KEY / SESSION_KEY_RESP.
Delegated access messages
Delegated access allows an entity to act on behalf of another entity or group, typically to access resources it would not directly be permitted to access.
DELEGATED_ACCESS_REQ_IN_PUB_ENC (70) / DELEGATED_ACCESS_REQ (72)
Entity requests delegated session keys. Fields are structurally identical to the session key request messages: entity nonce, auth nonce, number of keys, sender name, purpose, and optional DH parameter.
DELEGATED_ACCESS_RESP_WITH_DIST_KEY (71) / DELEGATED_ACCESS_RESP (73)
Auth returns session keys for delegated access, with or without a distribution key.
Privilege messages
Privilege messages establish an entity's authority to grant delegated access to others. An entity must first prove it holds the privilege before it can issue delegation.
PRIVILEGED_REQ_IN_PUB_ENC (80) / PRIVILEGED_REQ (82)
Entity requests a privilege credential that it can later present when issuing delegated access. Payload structure is identical to the session key request messages.
PRIVILEGED_RESP_WITH_DIST_KEY (81) / PRIVILEGED_RESP (83)
Auth responds with the privilege credential (encoded as session keys), optionally including the distribution key.