Encryption
For sensitive payloads (e.g., scheduling details, personal context), INK supports ECIES encryption.
Encryption Flow
Encryption Procedure
- Sender generates an ephemeral X25519 key pair for this message. The sender’s long-term
encryptionKeyMultibaseis NOT used for ECDH — ephemeral keys provide forward secrecy. - Sender performs ECDH using the ephemeral private key and the recipient’s
encryptionKeyMultibase(from theiragentLink). - 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.
- Encrypts the plaintext envelope with AES-256-GCM using a random 12-byte nonce.
- Wraps the result in a
InkEncryptedPayloadouter 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 encrypted — from, 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
- Parse the outer envelope. Verify the
Authorizationheader signature. Reject before decryption if invalid. - Check
timestampandmessageNonceagainst replay protection rules. Reject before decryption if replayed. - Perform ECDH using the recipient’s own X25519 private key and the
ephemeralKey. - Derive the symmetric key via HKDF-SHA256.
- Decrypt
ciphertextusing AES-256-GCM. - Parse the plaintext envelope. Verify
frommatches outerfromandtomatches recipient’s DID. - 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.
Key differences:
- Encrypted envelopes expose only:
from,ephemeralKey,nonce,timestamp,messageNonce - The
tofield, 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 innernonceis the AES-GCM IV
Encryption Requirements by Intent Type
| Intent Type | Sender | Receiver | Rationale |
|---|---|---|---|
scheduling | MUST encrypt | MUST reject plaintext | Contains availability windows, calendar data |
context_share | MUST encrypt | MUST reject plaintext | Contains personal/professional context |
intro_request | MAY encrypt | MUST accept both | Low sensitivity |
opportunity | MAY encrypt | MUST accept both | Low sensitivity |
follow_up | SHOULD encrypt | MUST accept both | May reference prior conversations |
ask | MAY encrypt | MUST accept both | General-purpose |