Reference · Wire Format · v1

Envelope Wire Format Specification

A HexaEight envelope is a single UTF-8 string. There are two variants. A decoder is a one-line dispatch on the prefix.

Variant B — Sessioned (supported)

"hsha:" <session-hash> "|" <ciphertext>
TokenTypeConstraints
hsha:literalExactly the 5-byte ASCII sequence 0x68 0x73 0x68 0x61 0x3A.
session-hashhexSHA-256 of the session identifier, lowercase hex. Exactly 64 chars.
ciphertextBase64URL (RFC 4648 §5)MQ-V4 ciphertext with embedded HMAC-SHA256 tag. No padding.

The sender's identity is not on the wire. It is carried (cryptographically asserted) inside the encrypted JSON payload as a SENDER field and exposed to receivers as DecryptedEnvelope.Sender.

Variant A — Standard (preview)

<source-id> "|" <kgt> "|" <ciphertext>
TokenTypeConstraints
source-idASCIISender's login-token prefix. Implementation-dependent length.
kgtdecimal integerUnix minute, floored to nearest 15. Fits in int64.
ciphertextBase64URLMQ-V4 ciphertext with embedded HMAC-SHA256 tag.
Variant A is in the wire format but not yet recommended for production round-trips. The source-id token depends on the sender's login-token format. An anonymous-sender mode using this wire shape (no inner sender claim) is planned. For now, use Variant B.

Variant detection

if envelope.startsWith("hsha:"):
    parse as Variant B (Sessioned)
else:
    parse as Variant A (Standard)

No identity prefix is allowed to be exactly hsha. The platform reserves this 4-byte prefix.

Encoding rules

Ciphertext internals

The ciphertext blob is the output of the HexaEight MQ-V4 trapdoor encryption mode. Its internal layout is:

OffsetLengthField
02 bytesVersion tag (V3 or V39 mode flag)
232 bytesHMAC-SHA256 integrity tag
34n bytesEncrypted block stream

Inside the decrypted ciphertext sits a JSON object with at minimum the fields SENDER, RECEIVER, BODY. The Bridge surfaces SENDER as DecryptedEnvelope.Sender — the cryptographically verified sender identity.

Reference parser (pseudocode)

function parse(envelope: string): ParsedEnvelope {
  if envelope.length < 6 throw FormatError
  if envelope.startsWith("hsha:") {
    let rest = envelope.slice(5)
    let pipe = rest.indexOf("|")
    if pipe != 64 throw FormatError
    return {
      kind: Sessioned,
      sessionHash: rest.slice(0, 64),
      ciphertext:  base64UrlDecode(rest.slice(65))
    }
  } else {
    let p1 = envelope.indexOf("|")
    let p2 = envelope.indexOf("|", p1 + 1)
    if p1 < 1 || p2 < p1 + 2 throw FormatError
    return {
      kind: Standard,
      sourceId:   envelope.slice(0, p1),
      kgt:        parseInt(envelope.slice(p1 + 1, p2)),
      ciphertext: base64UrlDecode(envelope.slice(p2 + 1))
    }
  }
}

Versioning

The format described here is wire format v1. The variant prefix serves as the discriminator — there is no separate version byte for the envelope structure. Future variants will introduce new prefixes rather than mutate the existing two. Decoders SHOULD reject unknown prefixes loudly.

Conformance for new SDK implementations

An SDK is conformant if it can:

  1. Encrypt a Variant B envelope and have the .NET Bridge decrypt it given the matching session.
  2. Decrypt a Variant B envelope produced by the .NET Bridge given the matching session.
  3. Surface SENDER from inside the encrypted JSON as a top-level field on the decrypted result.
  4. Reject envelopes with leading/trailing whitespace, NUL bytes, embedded newlines, or unknown variant prefixes.

A test vector pack will be published at github.com/HexaEightTeam/pqc-review.