intermediate
+300 XP

Encryption with Seal

Implement end-to-end encryption using Sui Seal for private messaging channels.

Lesson Syllabus

Threshold Encryption Fundamentals
🔐

What is Sui Seal?

Sui Seal is a threshold encryption framework built into the Sui ecosystem. Unlike traditional encryption where a single key encrypts and decrypts data, Seal uses **threshold encryption** (t-of-n) -- meaning a message is encrypted so that at least `t` out of `n` key shares are needed to decrypt it. This eliminates single points of failure and ensures that no single party can unilaterally access private messages. The `@mysten/seal` SDK provides a TypeScript-native interface to Seal's on-chain encryption primitives.

🧩

How Threshold Encryption Works

In a t-of-n scheme, the encryption key is split into `n` **key shares** distributed to different validators or participants. To decrypt, at least `t` shares must be combined. Here's the flow: (1) A **policy object** on-chain defines who can request decryption. (2) The sender encrypts a message with the policy's public key using `SealClient.encrypt()`. (3) Authorized recipients request partial decryptions from key servers. (4) Once `t` partial decryptions are gathered, the SDK combines them to reveal the plaintext. The beauty is that the full decryption key is **never reconstructed** on any single machine.

🏗️

Seal and Sui Objects

Seal policies are themselves **Sui Move objects**. When you create an encrypted messaging channel, the channel object acts as the encryption policy: only members of the channel can request decryption. The `SealClient` takes a reference to this policy object when encrypting. This tight coupling between object ownership and encryption access means that Sui's permission model directly controls who can read messages -- no separate access control layer needed.

Session Key Management
🔧

SealClient Setup and Configuration

Before encrypting or decrypting, you must configure `SealClient` with the correct key server objects. Key servers are on-chain objects that hold their portion of the threshold key. You pass their object IDs during initialization. The `SealClient` communicates with these servers when decrypting -- it sends each server a request and collects partial decryptions. The `verifyKeyServers` flag ensures the SDK validates server authenticity before trusting their responses.

🗝️

Creating Session Keys

A **session key** is a short-lived keypair used to sign decryption requests without exposing the user's main wallet key. You create one with `SealSessionKey.create()`, specifying an address, a `maxEpoch` (expiry), and the `personalMessage` the user signs to authorize the session. The session key can then be used for multiple decrypt calls within its validity window. This pattern keeps the user's main private key safe while enabling repeated decryptions.

🔄

Session Key Lifecycle

Session keys have a clear lifecycle: **create** them when the user opens an encrypted channel, **use** them for every decrypt operation during the session, and let them **expire** at `maxEpoch`. If the session key is compromised, damage is limited because it can only decrypt (not sign transactions) and it expires soon. Best practice: create a new session key each time the user opens the app, with a maxEpoch a few epochs in the future.

Key Rotation
🔑

Why Key Rotation Matters

Key rotation is the practice of periodically changing encryption keys. Even with threshold encryption, long-lived keys accumulate risk: over time, more key shares could be compromised or leaked. By rotating keys, you ensure that even if old shares are exposed, messages encrypted with newer keys remain secure. Seal supports key rotation at the policy level -- when a channel's encryption key is rotated, new messages use the new key while old messages remain decryptable with the old key (backward compatibility).

🔄

The rotateKeys() API and Migration

Seal exposes key rotation through a Move transaction that updates the channel's policy object. On the TypeScript side, you build and execute this transaction, then update your local `SealClient` references. The pattern: (1) Call a Move function to rotate the on-chain policy key. (2) New messages are encrypted with the fresh key. (3) Decryption of old messages still works because key servers retain historical shares. Always rotate keys when removing channel members to maintain privacy guarantees.