For the complete documentation index, see llms.txt
For a comprehensive technical analysis and formal security proofs, see the Seal Whitepaper. The latest version (v2) incorporates the design and analysis of MPC committees for the decentralized mode key server.
Seal uses a cryptographic primitive called Identity-Based Encryption (IBE) to encrypt stored data. This design detail is abstracted away from both developers and users, as Seal does not have visibility into the data it helps secure.
An IBE scheme consists of the following algorithms:
Setup: Generates a master secret key msk and a master public key mpk.Derive(msk, id): Given a master secret key and an identity id (string or byte array), generates a derived secret key sk for that identity.Encrypt(mpk, id, m): Given a public key, an identity, and a message, returns an encryption c.Decrypt(sk, c): Given a derived secret key and a ciphertext, compute the message m.Such a scheme is correct if for any id and m, (msk, mpk) ← Setup() and c ← Encrypt(mpk, id, m) we have Decrypt(Derive(msk, id), c) = m.
The domain of identities is not fixed and can be any string or byte array. Seal uses this property to bound onchain strings to IBE identities.
Seal consists of two main components:
PkgId controls the subdomain of IBE identities that starts with [PkgId] (that is, all strings of the form [PkgId]*). You can think of [PkgId] as an identity namespace. The package defines, through Move code, who is authorized to access the keys associated with its identity subdomain.Consider the following basic example for realizing time-lock encryption:
module patterns::tle;
use sui::bcs;
use sui::clock;
const ENoAccess : u64 = 1;
/////////////////////////////////////////////
/// Access control
/// The IBE identity being used: [pkg id][bcs::to_bytes(time)]
/// The following function accepts only the inner identity, i.e., [bcs::to_bytes(time)], and Seal extends it with the namespace.
entry fun seal_approve(id: vector<u8>, c: &clock::Clock) {
// Convert the identity to u64.
let mut prepared: BCS = bcs::new(id);
let t = prepared.peel_u64();
let leftovers = prepared.into_remainder_bytes();
// Check that the time has passed and the entire identity is consumed.
assert!((leftovers.length() == 0) && (c.timestamp_ms() >= t), ENoAccess);
}
The module controls all IBE identities that begin with its package ID PkgId. To encrypt data with a time-lock T, a user selects a key server and encrypts the data using the identity [PkgId][bcs::to_bytes(T)] and the server’s IBE master public key. Once the onchain time on Sui exceeds T, anyone can request the decryption key for the identity [PkgId][bcs::to_bytes(T)] from the Seal key server. Access control is enforced by the seal_approve function defined in the module. This function receives the requested identity (excluding the PkgId prefix) and a Clock as arguments. It returns success only if the current time is greater than or equal to T. The key server evaluates seal_approve to determine whether the derived key can be returned.
Time-lock encryption can be applied to a variety of onchain use cases, including MEV-resistant trading, secure voting, and more. For additional examples and useful implementation patterns, see Example patterns.
The framework is fully generic. Developers can define custom authorization logic within seal_approve* functions and choose which key servers to use based on their application’s needs. For example, they can use a fixed set of trusted key servers or allow users to select their preferred servers.
When you upgrade a package, it retains the same identity subdomain. To support secure upgrades, follow the recommended best practices for versioned shared objects. Specifically, version your shared objects, or create a global versioned shared object for your package. For examples, see the allowlist and subscription patterns. Keep in mind that if a package is upgradeable, the access control policy can be changed at any time by the package owner. These changes are transparent and publicly visible onchain.
Seal is designed to reduce centralization using a couple of mechanisms.
First, users can choose any combination of one or more key servers and use their master public keys to encrypt data. This setup supports t-out-of-n threshold encryption, which ensures:
t key servers are compromised.t key servers are available.Seal does not mandate the use of any specific key server. Instead, users can select key servers based on their own trust assumptions. Key servers can vary in security characteristics, such as running within secure enclaves or being air-gapped, and can operate across different locations and jurisdictions.
:::info
The set of key servers is not dynamic once the data is encrypted, and encrypted data cannot be changed to use a different set of servers.
:::
Second, a decentralized key server can also be implemented using an MPC committee in a t-out-of-n configuration. This committee can consist of Sui validators or any other group of participants. Users can choose to use decentralized key servers in addition to independent key servers. In this setup, the participants in the MPC committee can change over time, allowing for dynamic membership.
The security of encrypted data relies on the following assumptions:
A light server is initialized with an identity-based encryption (IBE) master secret key and has access to a trusted full node. In simple deployments, the server runs as a backend service with the secret key stored in protected storage, optionally secured using a software or hardware vault. More advanced deployments can use secure enclaves, MPC committees, or even air-gapped environments to enhance security.
The server exposes only two APIs:
/v1/service - Returns information about the service’s onchain registered information./v1/fetch_key - Handles a request for one or more derived keys and returns them if access is permitted by the associated package or policies. Each request must meet the following requirements:
signPersonalMessage. For details, see the signed_message format.seal_approve* rules. For PTB construction guidelines, see valid_ptb.See crates/key-server for the implementation of the key server.
Decryption keys returned from the key server are returned directly to the caller, which is typically the dApp’s web page. To ensure that dApps can access only keys explicitly approved by the user, the user must approve the key access request in their wallet. This approval is granted once per package and authorizes a session key. The session key allows the dApp to retrieve associated decryption keys for a limited time without requiring repeated user confirmations.
Seal is designed to support multiple identity-based encryption (IBE) schemes as Key Encapsulation Mechanisms (KEMs) and various symmetric encryption schemes as Data Encapsulation Mechanisms (DEMs). Currently supported primitives include:
Prefer AES-256-GCM for most use cases as it is faster. Use HMAC-CTR only when you require onchain decryption.
Post-quantum primitives are planned to be added in the future.
For advanced encryption schemes, use Seal as a KMS to protect the scheme’s secret key. This approach enables streaming, hardware-assisted, or chunked decryption while keeping keys out of application code.