Skip to main content

🔄 Rotate Encryption Keys

Rotating encryption keys allows you to replace the encrypted copy of a file without changing its FileAllowlist object or access permissions.

Why Rotate Keys?​

Key rotation is useful when:

  • A user is removed from the allowlist.
  • You want to periodically rotate encryption keys.
  • You want to replace the encrypted copy of a file while keeping the same access policy.

During rotation:

  1. Read the current nonce
  2. Download and Decrypt the Current File
  3. Encrypt the File Again
  4. Upload the New Encrypted File
  5. Update the on-chain allowlist by calling rotate() with the new Walrus CID.

The allowlist object remains the same, but its internal nonce is incremented. Future decryption requests must use the latest version of the file.


Step 0: Read the current nonce​

const currentNonce = await getAllowlistNonce(fileAllowlistId);

Step 1: Download and Decrypt the Current File​

Decrypt the existing encrypted file using the normal decryption flow described above.

const plaintext = await decryptEncryptedFile({
cid: oldCid,
identity: buildIdentity(fileAllowlistId, currentNonce),
keypair,
});

At this point you have the original file contents as a Uint8Array.


Step 2: Encrypt the File Again​

Encrypt the plaintext again using the same FileAllowlist object.

const nextNonce = currentNonce + 1n;
const identity = buildIdentity(fileAllowlistId, nextNonce);

const { encryptedObject } = await sealClient.encrypt({
packageId: ORIGINAL_PACKAGE_ID,
id: toHex(identity),
threshold: THRESHOLD,
data: plaintext,
});

Although the allowlist object is the same, SEAL generates a new random symmetric encryption key every time encryption is performed.


Step 3: Upload the New Encrypted File​

Upload the new encrypted file to Walrus.

const upload = await lighthouse.upload(encryptedObject, API_KEY, {
storageType: "walrus",
});

const cid = upload.data.Hash;

Save the returned blob ID.


Step 4: Update the Allowlist​

Call the rotate function with the new Walrus blob ID.

const tx = new Transaction();

tx.moveCall({
target: `${LATEST_PACKAGE_ID}::allowlist::rotate`,
arguments: [
tx.object(fileAllowlistId),
tx.object(capId),
tx.pure.vector("u8", Array.from(fromBase64(cid))),
],
});

await suiClient.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});

This transaction:

  • increments the allowlist nonce,
  • stores the new Walrus blob ID,
  • emits a Rotated event.

What Happens After Rotation?​

After rotation:

  • Existing allowlist members can decrypt the newly uploaded file.
  • The previous encrypted copy is no longer approved by seal_approve.
  • New decryption requests must use the latest uploaded file.

The FileAllowlist object ID does not change, so access permissions remain the same. Only the encrypted file and its associated nonce are updated.