Skip to content

Encryption

For sensitive payloads (e.g., scheduling details, personal context), INK supports ECIES encryption.

Encryption Flow

Diagram

Encryption Procedure

  1. Sender generates an ephemeral X25519 key pair for this message. The sender’s long-term encryptionKeyMultibase is NOT used for ECDH — ephemeral keys provide forward secrecy.
  2. Sender performs ECDH using the ephemeral private key and the recipient’s encryptionKeyMultibase (from their agentLink).
  3. Derives a symmetric key via HKDF-SHA256:
    • IKM: The raw ECDH shared secret (32 bytes).
    • Salt: "ink/0.1" (UTF-8 encoded, 6 bytes).
    • Info: "ink/0.1/encrypt" (UTF-8 encoded).
    • Output length: 32 bytes.
  4. Encrypts the plaintext envelope with AES-256-GCM using a random 12-byte nonce.
  5. Wraps the result in a InkEncryptedPayload outer envelope.

Outer Envelope

{
"protocol": "ink/0.1",
"type": "network.tulpa.encrypted",
"from": "did:plc:sender",
"ephemeralKey": "<base64url-encoded ephemeral X25519 public key>",
"nonce": "<base64url-encoded 12-byte AES-GCM nonce>",
"ciphertext": "<base64url-encoded AES-GCM ciphertext + 16-byte auth tag>",
"timestamp": "2026-03-18T12:00:00Z",
"messageNonce": "<base64url-encoded 128-bit replay-protection nonce>"
}

The outer envelope is not encryptedfrom, ephemeralKey, timestamp and messageNonce are plaintext. This is necessary so the recipient can identify the sender and apply replay protection before decryption.

Plaintext Envelope

The ciphertext, when decrypted, yields a JSON object identical to an unencrypted INK message:

{
"protocol": "ink/0.1",
"type": "network.tulpa.intent",
"from": "did:plc:sender",
"to": "did:plc:recipient",
"intentType": "scheduling",
"purpose": "Discuss partnership opportunity",
"urgency": "normal",
"nonce": "<base64url>",
"timestamp": "2026-03-18T12:00:00Z"
}

The recipient MUST verify that from and to in the plaintext envelope match the outer envelope’s from and the recipient’s own DID. A mismatch indicates tampering and MUST be rejected.

Decryption Procedure

  1. Parse the outer envelope. Verify the Authorization header signature. Reject before decryption if invalid.
  2. Check timestamp and messageNonce against replay protection rules. Reject before decryption if replayed.
  3. Perform ECDH using the recipient’s own X25519 private key and the ephemeralKey.
  4. Derive the symmetric key via HKDF-SHA256.
  5. Decrypt ciphertext using AES-256-GCM.
  6. Parse the plaintext envelope. Verify from matches outer from and to matches recipient’s DID.
  7. Process the inner message normally.

Envelope Anatomy: Plaintext vs Encrypted

A side-by-side comparison of what is visible on the wire in each mode.

Diagram

Key differences:

  • Encrypted envelopes expose only: from, ephemeralKey, nonce, timestamp, messageNonce
  • The to field, intent type, purpose and all payload data are inside the ciphertext
  • Both modes carry Ed25519 signatures and replay protection
  • The messageNonce (replay protection) is on the outer envelope; the inner nonce is the AES-GCM IV

Encryption Requirements by Intent Type

Intent TypeSenderReceiverRationale
schedulingMUST encryptMUST reject plaintextContains availability windows, calendar data
context_shareMUST encryptMUST reject plaintextContains personal/professional context
intro_requestMAY encryptMUST accept bothLow sensitivity
opportunityMAY encryptMUST accept bothLow sensitivity
follow_upSHOULD encryptMUST accept bothMay reference prior conversations
askMAY encryptMUST accept bothGeneral-purpose