Corino Key Management System Deep Dive
Introduction
Trusted execution environments (TEEs) provide confidential computation, but constructing shared private state across a network of TEEs requires expressive and careful key management across the TEEs in the network. Corino is Synchronicity’s key management system (KMS) designed specifically for this purpose.
Corino coordinates key generation, configuration, distribution, rotation and recovery across all nodes in a Synchronicity Protostar deployment. This document provides a technical deep dive into Corino’s architecture, covering how different key types are used, how keys are generated and provisioned via the Lit Protocol, and how Corino integrates with the dstack KMS and Sovereign SDK.
Keys in Corino
There are two types of keys in a standard Protostar deployment, data sealing keys and forced inclusion keys. Data sealing keys are used to share access to private state across all nodes in the network. Forced inclusion keys are used to encrypt and decrypt transactions that bypass the sequencer to ensure the network’s censorship resistance.
Corino is designed to be flexible such that it can support additional types of keys should a bespoke Protostar deployment or other system integrating with Corino require it.
Data Sealing Keys
Corino supports Protostar deployments which consist of many different nodes with different roles (e.g., sequencers, indexers, replica nodes, provers). These nodes perform their functions within a TDX enclave but store encrypted data outside of the enclave on a public data availability (DA) layer. This encrypted storage outside of the enclave is known as sealed storage.
Sealed storage is necessary for:
-
Ordinary application functionality: the application’s full state/history may be too large to practically store in TDX memory.
-
Dynamic node participation: new nodes joining the network should have access to rich historical state data rather than just previous block headers or previously generated proofs.
-
Verification: because Synchronicity Protostar deployments use ZK proofs to guarantee state integrity, the data being proven must be available to all nodes on a shared DA layer.
-
Availability and liveness: sharing data across nodes is necessary to ensure the network is always accessible and able to make progress.
-
Persistent backup and recovery mechanisms: in the event of system-wide failures, new nodes need to be able to come online and access the entire historical state to continue advancing state from the most recent state snapshot posted to the DA.
Data sealing keys are symmetric AEAD keys that are used only inside of each TEE to encrypt and decrypt application state data before it is written to or read from off-enclave storage. Because these keys never leave the enclave without being encrypted beforehand, they are never exposed to the host operating system, hypervisor or node operator. These keys are rotated regularly for forward secrecy, to reduce the risk of side-channel leakage over time and minimize the amount of data that can be learned from the compromise of any given key. Corino enables sealing keys to be rotated transparently without affecting the user experience or network availability or liveness whatsoever.
Forced Inclusion Keys
The censorship resistance of a Protostar deployment is dependent on its sequencer. The sequencer is the node that collects transactions from the network’s mempool, determines if they are valid or not, orders them into batches, and broadcasts those batches of transactions to the data availability (DA) layer.
If users believe they are being censored by the sequencer (i.e., their transactions have not been included or are only included after unusual delays), Protostar deployments support forced inclusion. This feature allows users to ensure that their transaction will be executed by the network, albeit after some time delay, regardless of the sequencer’s behavior. This is done by posting these “forced” transactions directly to the DA. Forced inclusion gives very strong censorship resistance guarantees even in the case of sequencer downtime, capture, or mistake. If transactions are forcibly included, the Protostar deployment rollup is forced to process the next DA block for force included transactions every X batches (currently hardcoded at 1). Also, the rollup can only be X blocks behind. Both create a very fast forced inclusion mechanism.
Forced inclusion keys are a public-private key pair. The public key is available to everyone, enabling anyone to force transactions to be included by the network. The associated private key is provisioned to the network’s nodes so they can process these forcibly included transactions. These keys are rotated less frequently to avoid breaking client tooling that relies on a stable public key and because of the limited data encrypted under these keys: if a forced inclusion key pair is compromised, an attacker could only read transactions that were forcibly included under this particular key.
Corino KMS Overview
Shared private state requires coordinating and syncing sealed storage across all nodes in a Protostar deployment. Additionally, for network-wide censorship resistance, the entire network needs a common forced inclusion public-private key pair. These requirements demand that each Protostar deployment must generate, provision, rotate, manage, store, and backup keys. Each of these actions presents different challenges.
Here’s (briefly) how Corino solves them.
-
Generation: At some fixed interval, an on-chain-governance-determined key generation node will locally (within a TEE) generate a new key. The key generation service uses cryptographically secure random number generators from industry-standard libraries to generate high-entropy keys. No key derivation functions (KDFs) are used; all keys are generated purely from entropy. Keys for different cryptographic schemes are generated via their own modular adapter, but all of these ultimately call the
getrandom()system call. Additionally, dstack deployment on Intel TDX configures this syscall to use Intel’s RDRAND which is a NIST SP 800-90A compliant hardware entropy source outside the host/virtual machine manager control. A TDX instance cannot boot unless 256 bits of RDRAND output is mixed into the entropy pool early during the boot process, so a malicious host cannot manipulate entropy used for the key generation. Key material is never logged, the key generation program is written in memory-safe Rust, and all cryptographic operations are implemented in constant-time. -
Provisioning: Once a new sealing key is generated, it needs to be distributed to the other nodes on the network. Key access is permissioned by integrating with Lit Protocol . The sealing key is “wrapped” under Lit’s identity-based encryption scheme which allows for the sealing key to be decrypted only upon the fulfillment of a predetermined expressive set of access control conditions governed by an EVM smart contract.
After the sealing key is generated and wrapped under these access control conditions, it is posted publicly on the DA layer. Then, any node in the Protostar deployment can query the Lit network to request decryption keys for the wrapped key. If the requesting node meets the conditions (e.g., is on the public whitelist of permissioned nodes, provides a valid attestation that is verified on-chain, has some value staked), then it receives decryption key shards from the Lit network, constructs the decryption key from the shards and decrypts the sealing key locally (within a TEE), at which point the TEE can use the key to read and compute over the data that was stored outside of the enclave.
-
Rotation & Management: The key generation service submits a
KeyGenerationtransaction containing the encrypted key to the network. Only nodes that are permissioned during the initial rollup configuration can submit these transactions. These permissions can be added or removed by the static rollup admin. When the sequencer processes this transaction, it emits aKeyGenerationevent which contains everything required (i.e., the key ciphertext, access control conditions under which it was encrypted and the key hash) to provision the new key to the rest of the nodes in the network. -
Backup & Recovery: When a new node joins the network, it will request Lit for all of the decryption keys it needs to catch up to the current state. The Lit network provides new copies of previous sealing keys provided that the requesting node meets the access control conditions and provides the wrapped keys from where they are stored on the DA layer.
Corino KMS Step By Step
Generation
Keys are generated locally and retrieved by lightweight, modular, Rust-based key generation and retrieval services. These services are only ever deployed in a TEE and can be configured to run as a sidecar container on any node that has already been permissioned to join a given Protostar deployment.
The key generation service:
-
Securely generates various encryption keys at configurable intervals;
-
Encrypts newly generated keys under some access control conditions via the Lit Protocol ;
-
Distributes encrypted keys and access control metadata to configurable endpoints; and
-
Notifies any listening key retrieval services of new keys.
The Corino key generation service is meant to be run as a standalone application in its own confidential virtual machine similar to the dstack KMS service.
The key retrieval service is a companion service to the key generation service and is responsible for listening for new keys, fetching them, and decrypting them via the Lit Protocol.
Each key is generated by a single key generation node, which presents a centralization risk. Corino takes several steps to ensure that keys are being generated correctly and securely.
Key Generation On TEEs
Keys are only generated on TEEs that have already been permissioned to join a given Protostar deployment. This means that each key generation is performed only upon a successful attestation check. In other words, before keys are generated, the node operator has to submit public evidence that they’re running the right key generation program in a secure enclave. This evidence can be checked publicly, and the key generation program can be audited. This provides high quality guarantees that keys are only minted by enclaves that are running the expected code in an approved configuration and no plaintext keys ever exit the enclave.
High Quality Salt
The generation of keys relies on a source of randomness, called salt. Even if the key generation function itself is performed correctly, sealing keys may be compromised or influenced by altering, spoofing, or reading the salt. To protect against this risk, Corino sources salt by combining hardware randomness from the node’s hardware with verifiable randomness from a decentralized source . The combination of hardware randomness with decentralized randomness means no single party can guess, control, or bias the salt.
Future Improvements for Key Generation
Distributed Key Generation
A truly decentralized key-generation solution via threshold distributed key generation (DKG) is currently under development. Here, instead of each sealing key being generated by a single key generation node, a network of key generation nodes would run a multi-party computation (MPC) algorithm to generate a key as a collective. No single key generation node would ever assemble the entire sealing key, which eliminates any single point of trust or exfiltration. As long as a threshold of the key generation network participated honestly, the sealing key would be secure.
There is a nuanced tradeoff space here between key generation performance, the cryptographic security of the key generated, network incentives, and the collusion-resistance of the MPC procedure. Improving the honest threshold size and participant diversity strengthens security and collusion resistance but raises latency and coordination costs; smaller committees generate keys faster but rely on fewer honest participants and have weaker fault tolerance. Designing a DKG system thus involves balancing throughput, decentralization, and the assumed honesty of participants. There is exciting work being done in this area and by upgrading to an MPC-based solution, Corino will further eliminate points of centralization in the network.
Quantum Resistance
AES has withstood nearly two decades of intense cryptanalysis, has battle-tested secure implementations and has wide hardware support. Today, AES-256 is widely believed to be quantum-resistant: it is not known to be broken by currently available quantum algorithms. The best known attack on AES is Grover’s algorithm , which is able to reduce the brute force attack on symmetric ciphers by an order of , thereby reducing the security of AES-256 to that of AES-128. This is still widely considered extremely secure.
Nonetheless, in the future, Corino will likely transition to provably quantum secure encryption techniques. There has been some recent work on establishing post-quantum encryption standards, and Corino will keep with best-practice for key management as material developments occur.
Access-Controlled Provisioning via Lit
Corino leverages Lit Protocols threshold identity-based encryption and programmable access control conditions (ACCs) to publish wrapped encryption keys publicly while restricting decryption to attested, whitelisted TEEs. This section details Lit’s encryption scheme and how Corino integrates with Lit.
Lit Encryption Scheme
Lit uses a threshold identity-based encryption scheme with the following algorithms:
-
The different Lit DKG nodes create a -threshold public-private key pair via:
-
The parties run the -threshold distributed key generation protocol of Gennaro et al .
-
Beforehand, the parties agree on the threshold , a prime for which there exists a large prime dividing .
-
“Large” is defined by fixing a security parameter and choosing such that the lengths of both and grow polynomially with .
-
Denote by the subgroup of elements of order in . The parties also agree on a choice of generator .
-
-
After setup, each party has a private share of the secret key .
-
The network public key is
-
Each party’s public key is
-
-
To perform id-based encryption:
- Users first create an locally. They hash their message and then create an which is just a hash of and a collection of access control conditions:
- Now, anyone can encrypt the message under the given :
-
To decrypt, a user requests a decryption key by sending , , and evidence that they meet this access control to the Lit network.
-
Each Lit node checks the access control condition.
-
If this passes, the Lit node locally hashes the information presented to them to reconstruct , and computes a BLS12-381 signature over the using their share of the secret . That is, they hash the to a point on the curve and then raise to their secret:
- Any single Lit node never reconstructs the entire decryption key for a given , rather they send these shares to the requesting user who can then reassemble the full id-key using Lagrange interpolation from any -sized collection of shares:
- Using the full ID key , which is an aggregated BLS signature over the parameter, anyone can locally decrypt a ciphertext encrypted under that same parameter:
-
Lit HSM-Model KMS Integration
The Lit Protocol network provides functionality to Corino analogous to a distributed hardware-security module layer. Once keys are generated, they are wrapped under programmable access control conditions such that only attested, whitelisted TEEs can reconstruct these keys within their hardware trust boundaries.

-
Generation: At fixed intervals, high-entropy 256-bit AEAD sealing keys are generated locally by a key generation node.
-
Rotation/Management: After key generation, the sealing key must be distributed to other nodes in the rollup network. Access to the key is permissioned via Lit Protocol.
- The sealing key is hashed to create , and create a new associated to this sealing key and a strict set of access control conditions:
-
The access control conditions check:
-
whether a wallet address is included in a whitelist posted on a network-permissioning EVM smart contract; and,
-
for an attestation quote that is then verified on-chain via a combination of Automata and deployment-specific contracts. This check ensures that at the time of the decryption request the node requesting the key is:
-
running on the right hardware;
-
appropriately configured for confidentiality; and,
-
running the expected version of the Protostar deployment’s code.
-
-
-
The key generation node locally encrypts the sealing key via the Lit protocol encryption described above:
-
The lead sequencer publishes and publicly.
-
The other rollup full nodes can read and from some DA layer and request a decryption key by sending , , and evidence that they meet this access control to the Lit network.
- Lit nodes each check the access control conditions, hash the information presented to them to reconstruct , and generate a partial decryption key:
- Lit nodes send shares to the requesting rollup full node. Once the full node has a threshold of the key shares, they can locally assemble the id-bound decryption key inside the TEE:
- Finally, rollup full nodes can decrypt the sealing key for internal use:
Rotation & Management
Purpose of Key Rotation
Corino supports a robust key rotation mechanism and recommends that Protostar deployments rotate their sealing keys frequently in order to limit exposure per key, preserve forward secrecy and reduce side-channel risk. Before application state data is posted to the DA layer or sent to the indexer, it is encrypted under the newest sealing key and tagged by key version.
Limiting Blast Radius & Forward Secrecy
Regular rotation ensures that private data is encrypted under short-lived sealing keys, each scoped to a particular epoch, thus limiting data per key. This provides forward secrecy : even if a key is later compromised, the attacker cannot decrypt data from other epochs. The blast radius from the compromise of any single key is bounded to the data sealed while that key was active. In Corino, rotation is transparent to users and all network roles. New data is always sealed under the most current key version, old keys can be provably deleted from enclave local memory, and incident response can quarantine a suspect key by rotating immediately. The result is that even if a side-channel or operational lapse reveals information about a given key, historical and subsequent epochs remain confidential, and any attacker’s payoff is capped by the system’s rotation cadence.
Mitigate Side-Channel Attack Surface
TEEs are generally not fully resistant to physical attacks. Various side-channel attacks may leak partial information about a Protostar’s state or private keys. To minimize this attack surface, Protostar developers should take best-practice measures . Still, the more a key is used, the more information is passively leaked by normal node operations to a would-be attacker. Even if a node is performing its network role perfectly, node operators themselves or attackers with physical access to the machine running an enclave may, over time, be able to learn information about the keys in use. The risk of passive side-channel key exfiltration grows over time, but rotating keys and deleting old keys from the enclave’s memory puts a hard cap on the amount of data passively leaked to physical side-channels. This shifts an attacker’s problem from “slowly accumulate leakage until the key is recoverable” to “there is never enough time or reuse under a single key to reconstruct it from passive leakage alone.” If you can’t extract critical levels of secret information by passively observing the data leaked by the enclave, then in practice, this means that physical side channel attacks require manipulating the operations performed by the enclave, and these “active” side-channel attacks are much more detectable since they involve node operations that are outside the enclave’s normal patterns of operation. Corino’s protocol-level enforcement of rotation cadence ensures that this security posture is maintained across the entire deployment. Enforceable network-wide key rotation thus forms a crucial component in Corino’s defense-in-depth side-channel mitigation strategy by heading off an entire class of passive key-recovery attacks and shifting the attack surface to an area that is more easily monitored.
Recovery and Attack Mitigation
In the event of a corrupted, compromised, or malicious node, the Protostar deployment can update the access control conditions and rotate to a new sealing key in order to quarantine the breach and block the node’s decryption of new writes to the shared private state. In such an instance, the node’s operator would only be able to read data stored under the old key version, not any subsequent data.
Upgrading Keys
In the future, developers may elect to change the underlying encryption scheme that is used for sealing keys. To make this possible and thereby future-proof Corino, the system must support key rotation.
Triggering Key Rotation
Key rotations can be triggered in several ways, and developers can choose which they want to use. At minimum, it is recommended that applications implement routine, fixed cadence rotation. Additionally, key rotations can be triggered “manually” in a process governed by EVM smart contracts. These serve different purposes.
Fixed Cadence Rotation
Fixed cadence key rotation is used for predictable forward secrecy. Keys can be rotated every N blocks, M DA slots, or T minutes/hours.
Manual Rotation
Manual key rotation is used to quarantine compromise, respond to misconfiguration or roll out urgent policy changes. Typical triggers include suspicious attestation failures or quote measurements, anomalous decryption/error rates, side-channel concerns, incident reports or governance policy updates.
Manual key rotation is managed via the Corino EVM smart contract associated with the Protostar deployment.
EVM Smart Contract
The Corino governance protocol is governed via a suite of EVM smart contracts that acts as the verifiable source of truth for valid TEE network roles.
A TEE looking to join the network sends its attestation quote to the smart contract which checks that it is running allowed software and has enough funds staked for its role. The developers of a Protostar deployment can whitelist code hashes for arbitrarily many roles and assign different layers of permissions for key access to those roles via the Lit Protocol.
For more information on the Corino EVM smart contracts, see:
Backup & Recovery
Corino is designed to support total recovery of shared private state and the continuation of progress, even in the case of a complete network outage or coordinated attack. The system for backing up and recovering historical sealing keys is pretty straightforward under this model; however, it requires some persistent data storage of historical key information and a resilient system for updating the EVM smart contract to ensure that even after a full network outage or coordinated attack, authorized operators can securely modify or restore the whitelist and access control configurations without exposing the system to governance capture or malicious updates.
When a new node spins up, it will request Lit for all of the decryption keys it needs to catch up to the current state. The Lit network is expected to always provide new copies of previous provided that the requesting node meets the access control conditions, and provides the . In particular, this means that these historical ‘s need to be stored in perpetuity and made accessible by Lit.
New nodes will also need access to the encrypted versions of historical sealing keys from the DA layer.
In the case of a complete outage or coordinated attack, recovery proceeds as follows.
-
Configure a new TEE to run the latest node software;
-
Add this TEE to the Corino EVM governance contract’s whitelist;
-
Request all historical decryption keys from Lit;
-
Decrypt and catch up to current state; and
-
Generate a new key and begin processing transactions under that key.
Overview of dstack KMS
Synchronicity uses the open-source dstack SDK to deploy dockerized workloads to TEEs. By itself, dstack is already quite complicated. Critically, dstack has its own internal KMS that is used to manage the following keys.
Root Keys (Hardware-Bound)
The KMS maintains two root keys that are bound to the TDX hardware measurements during boot:
1. Root CA Key (P-256 ECDSA)
-
Purpose: Trust anchor for all TLS certificates in the dstack system
-
Generation:
KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)(onboard_service.rs:116 ) -
Hardware Binding: TDX quote embedded in self-signed certificate during bootstrap
-
Storage:
/etc/kms/certs/root-ca.{crt,key} -
Usage: Parent key for disk encryption, environment encryption, and app CA keys
2. K256 Root Key (secp256k1 ECDSA)
-
Purpose: Ethereum-compatible signing operations and app key derivation
-
Generation:
SigningKey::random(&mut rand::rngs::OsRng)(onboard_service.rs:118 ) -
Hardware Binding: Public key registered in DstackKms contract with TDX quote proof
-
Storage:
/etc/kms/certs/root-k256.key -
Usage: Parent key for all application K256 signing keys
3. Temp CA Key (P-256 ECDSA)
-
Purpose: Issues client certificates for TLS authentication during KMS replication
-
Generation:
KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)(onboard_service.rs:115 ) -
Storage:
/etc/kms/certs/tmp-ca.{crt,key}
Derived Application Keys
For each application, the KMS derives multiple keys using HKDF-SHA256 with specific context data:
1. App Disk Encryption Key
-
Parent: Root CA private key
-
Derivation Context:
[app_id, instance_id, "app-disk-crypt-key"] -
Algorithm:
kdf::derive_dh_secret()(main_service.rs:517 ) -
Purpose: Full disk encryption passphrase for the app’s persistent storage
-
Format: 32-byte symmetric key
2. Environment Encryption Key (X25519)
-
Parent: Root CA private key
-
Derivation Context:
[app_id, "env-encrypt-key"] -
Algorithm:
kdf::derive_dh_secret()→x25519_dalek::StaticSecret(main_service.rs:521 ) -
Purpose: Decrypt secret environment variables passed to containers
-
Format: X25519 private key (32 bytes)
-
Public Key: Available via
GetAppEnvEncryptPubKeyRPC (no attestation required)
3. App K256 Signing Key
-
Parent: K256 Root Key
-
Derivation Context:
[app_id, "app-key"] -
Algorithm:
kdf::derive_ecdsa_key()(crypto.rs:15 ) -
Purpose: Ethereum-compatible signing for blockchain operations
-
Format: secp256k1 private key (32 bytes)
-
Signature Proof: Includes signature from parent key for chain-of-trust verification
4. App CA Key (P-256 ECDSA)
-
Parent: Root CA private key
-
Derivation Context:
[app_id, "app-ca"] -
Algorithm:
kdf::derive_ecdsa_key_pair()(main_service.rs:481 ) -
Purpose: App-specific certificate authority for custom TLS certificates
-
Format: P-256 ECDSA key pair
-
Usage: Signs certificates via
SignCertRPC
Key Derivation Hierarchy
TDX Hardware Measurements
├── Root CA Key (P-256) ────┬── App Disk Encryption Key
│ ├── Environment Encryption Key (X25519)
│ └── App CA Key (P-256)
│
└── K256 Root Key ──────────── App K256 Signing Key (secp256k1)For more information on how the dstack KMS works, see the official dstack KMS documentation .
Corino’s Partition From dstack
Corino and dstack are architecturally independent systems by design. Corino is a standalone key management system using Lit Protocol for access control. The dstack KMS is an intra TEE-specific key provisioning system.
Their only integration is that Corino’s key retrieval service optionally uses the dstack SDK to derive a wallet used for Lit Protocol authentication. This works by getting a signing key from the dstack KMS, deriving an Ethereum address from that key and then signing SIWE messages for Lit Protocol authentication as follows.
let key_response = self
.dstack_client
.get_key(
Some(self.config.key_id.clone()),
Some("signing".to_string()),
)
.await?;
let account = to_account(&key_response)?;
let address = format!("0x{:x}", account.address());Corino and the dstack KMS are intentionally kept architecturally independent to provide significant security, flexibility, and operational benefits.
Corino uses the dstack KMS to manage node-scoped keys, such as those used for signing TLS certificates and blockchain transactions, local disk encryption, or decryption of secret environment variables passed to the container in deployment. These keys belong to a single TEE and are not provisioned to other nodes in a given Protostar deployment.
Corino uses the Lit network to handle network-wide scoped keys, such as sealing keys or forced inclusion keys. These keys need to be provisioned across the network according to node roles and other Protostar deployment-specific conditions, rotated regularly, and backed up to maintain a Protostar deployment’s liveness and recoverability.
This separation enforces least-privilege boundaries; node identities stay local under dstack while Corino provides the secure network-wide KMS required to maintain a trust-minimized shared private state. Different lifecycles require different systems, and by making the Corino KMS stack as modular as possible, it is able to maintain independent development flexibility into the future.
Appendix: Integration with Sovereign SDK
Integration Points
Corino integrates with the Sovereign SDK in two places.
-
sync-key-managementmodule: processes transactions submitted by the key generation service containing encrypted sealing and forced inclusion keys. -
sov-encryption-layerutil: exposes encryption and decryption capability to both the preferred sequencer and STF runner using keys sent by the Corino key retrieval service.
Runtime Use
-
Each Sovereign rollup node is instantiated with a global
sov-encryption-layerthat contains encryption keys and exposes encryption/decryption operations to various components of the Protostar deployment. -
When the Corino key generation service creates and encrypts a new key, it sends a
RotateKeytransaction to the rollup which contains the encrypted key and the metadata required to decrypt the key via the Lit Protocol. -
The lead sequencer rollup emits a websocket event with the transaction which the key retrieval service is listening for.
-
The key retrieval service decrypts the key via the Lit Protocol and sends it back to the rollup node via a Unix Socket connection.
-
The
sov-encryption-layerreceives and stores the new key.
Rotation
Key rotation in this system is automatic, slot-based, and seamless – enabling keys to rotate without requiring rollup restarts or service interruptions. Below is the complete flow.
1. Periodic Key Generation (key-generation service)
Timer-Driven Generation:
// Every KEY_GENERATION_INTERVAL_MS (e.g., 60 seconds)
async fn timer_task(sender, interval_duration, is_running) {
let mut timer = interval(interval_duration);
timer.set_missed_tick_behavior(MissedTickBehavior::Skip);
loop {
timer.tick().await;
sender.send(ServiceMessage::GenerateKey).await;
}
}Process:
-
Timer fires every interval (configurable, min 30 seconds);
-
Key generation service generates new key;
-
Key generation service encrypts new key with Lit Protocol under access control conditions; and
-
Key generation service sends a transaction containing the encrypted key and its associated metadata to the rollup;
-
The lead sequencer processes this new key transaction and emits an associated event Broadcasts to configured webhooks (including key-retrieval service).
2. Key Distribution with Slot Delay (key-retrieval service)
Critical Timing Logic
async fn forward_key_to_websocket(&self, key_gen: &KeyGenerationPayload) -> Result<()> {
// Calculate the target slot number: current slot + delay
let target_slot_number = if let Some(current_slot) = key_gen.slot_number {
current_slot + self.config.slot_delay // e.g., 1000 + 10 = 1010
} else {
self.config.slot_delay
};
// Send to rollup with future activation slot
let key_message = json!({
"method": "submit_transaction",
"params": {
"transaction": {
"key_generation": {
"key_id": key_gen.key_id,
"encrypted_key": key_gen.encrypted_key,
"slot_number": target_slot_number, // Key activates in the future
"timestamp": chrono::Utc::now()
}
}
}
});
}3. Key Reception in Rollup (EncryptionLayer)
Unix Socket Listener:
async fn handle_key_connection(stream: UnixStream, cache: Arc<KeyCache>) {
let mut buffer = vec![0u8; 4096];
while let Ok(n) = stream.read(&mut buffer).await {
// Deserialize bincode message
let key_update = deserialize_key_service_message(&buffer[..n])?;
match key_update {
KeyUpdate::NewKey(key) => {
info!("KEY RECEIVED: '{}' for slot {}", key.id, key.slot_number);
let internal_key = InternalKey {
id: key.id.clone(),
material: key.key_data.clone(), // Raw AES-256 key
};
// Store key indexed by slot_number
cache.set_key_for_slot(key.slot_number, internal_key);
info!("KEY STORED: Key '{}' for slot {}", key.id, key.slot_number);
}
}
}
}KeyCache Storage:
pub struct KeyCache {
// BTreeMap for historical lookup: slot → key
slot_key_map: Arc<RwLock<BTreeMap<u64, InternalKey>>>,
// Fast O(1) cache for most recent key
current_key_cache: Arc<RwLock<Option<(u64, InternalKey)>>>,
}
fn set_key_for_slot(&self, slot_number: u64, key: InternalKey) {
// Store in main map
self.slot_key_map.write().unwrap().insert(slot_number, key.clone());
// Update current cache if this is the newest key
let mut current_cache = self.current_key_cache.write().unwrap();
if slot_number >= current_cache.slot_number {
*current_cache = Some((slot_number, key)); // Cache for fast access
}
}4. Automatic Key Selection During Encryption
Sequencer Batch Encryption:
fn batch_bytes(batch: PreferredSequencerReadBatch, encryption_layer: Option<&EncryptionLayer>) {
if let Some(encryptor) = encryption_layer {
// Get the slot number for this batch
let slot_number = batch.visible_slot_number_after_increase.as_true().get();
// Automatically select key for this slot
let slot_key = encryptor.get_key_for_slot(slot_number)
.ok_or_else(|| anyhow!("No encryption key for slot {}", slot_number))?;
info!("🔐 SEQUENCER: Using key '{}' for slot {}", slot_key.id, slot_number);
// Encrypt transactions
let txs_serialized = borsh::to_vec(&*batch.txs)?;
let encrypted_txs = encryptor.encrypt_with_key(&slot_key.material, &txs_serialized)?;
// Store which key was used
EncryptedPreferredBatchData {
encrypted_txs_data: encrypted_txs,
encryption_slot: slot_number, // Critical: records which key to use for decryption
// ...
}
}
}Key Lookup Algorithm:
fn get_key_for_slot(&self, slot_number: u64) -> Option<InternalKey> {
// Fast path: Check current key cache (O(1))
if let Some((cached_slot, cached_key)) = self.current_key_cache.read().unwrap().as_ref() {
if slot_number >= *cached_slot {
return Some(cached_key.clone()); // Use current key
}
}
// Slow path: Historical lookup (O(log n))
let map = self.slot_key_map.read().unwrap();
// Find highest slot ≤ requested slot
map.range(..=slot_number).next_back().map(|(_, key)| key.clone())
}Configuration & Deployment
| Parameter | Location | Default | Purpose |
|---|---|---|---|
KEY_GENERATION_INTERVAL_MS | key-generation | 60000 | How often to generate new keys |
SLOT_DELAY | key-retrieval | 10 | How many slots in future to activate keys |
key_rotation_interval | sovereign config | Optional | Reference |
socket_path | sovereign config | Required | Where to receive keys via Unix socket |