Motivation
Consider the following scenario: Your contract receives an encrypted input that should remain confidential.How access control works
In practice, the code above will revert with anACLNotAllowed error because the calling contract doesn’t have permission for that ciphertext handle. Any FHE operation will fail if the caller lacks permission for all input handles.
Unauthorized operations fail
What the ACL does not gate
The ACL governs operation creation. Computing on a handle, and requesting its decryption. It does not gate the onchain publication of a decrypt result:publishDecryptResult (and verifyDecryptResult) only check the signature. The TaskManager recovers the signer of an ECDSA signature over that exact (result, encType, chainId, ctHash) message, and accepts the result only if the recovered address equals the registered decryptResultSigner. That address belongs to Teecryptor. Publication is deliberately permissionless so clients and relayers can settle results onchain.
What protects the value is that an attacker cannot obtain that signature. Only Teecryptor can produce it. Its signing key never leaves the enclave, so no other party can sign a result the TaskManager will accept. Teecryptor signs only in response to a decryption request, and that request is ACL-gated. The requester must hold permission on the handle and present a valid ACP. The one exception is a handle marked with FHE.allowPublic(), which anyone can request. Without permission, Teecryptor denies the request and produces no signature, so there is nothing valid to publish.
Granting access
CoFHE provides six methods to grant access to ciphertext handles:FHE.allowThis()
FHE.allowThis()
FHE.allowThis(CIPHERTEXT_HANDLE)Allows the current contract access to the handle. Use this when you want the contract itself to retain access to a ciphertext beyond the current transaction.FHE.allowSender()
FHE.allowSender()
FHE.allowSender(CIPHERTEXT_HANDLE)Allows the transaction sender (msg.sender) access to the handle. Use this when you want to grant the caller of the function access to the ciphertext.FHE.allow()
FHE.allow()
FHE.allow(CIPHERTEXT_HANDLE, ADDRESS)Allows a specific address persistent access to the handle. Use this when you want to grant permanent access to another contract or user.FHE.allowTransient()
FHE.allowTransient()
FHE.allowTransient(CIPHERTEXT_HANDLE, ADDRESS)Allows a specific address temporary access to the handle for the duration of the transaction only. Use this for cross-contract calls within the same transaction.FHE.allowPublic()
FHE.allowPublic()
FHE.allowPublic(CIPHERTEXT_HANDLE)Marks a ciphertext handle as eligible for public decryption. Anyone can then request decryption of this value offchain via decryptForTx and publish or verify the result onchain.Use this when a value is intended to become public, for example, the amount burned when you unshield an FHERC20 token.allowPublic does not reveal the value immediately. It only grants permission for anyone to request decryption. The value is revealed only when someone submits the plaintext and signature onchain via FHE.publishDecryptResult or FHE.verifyDecryptResult.Who you trust when you grant access
Access to a handle is full access. A contract that holds it, including the contract that created it, can compute on the value, pass access on to others, or allow public decryption.Decryption and access control
Decryption is a multi-step process: a client requests the plaintext and its signature offchain viadecryptForTx, then publishes or verifies the result onchain.
Access control governs who can request decryption:
- If the ciphertext was marked with
FHE.allowPublic(), anyone can request decryption without an ACP (.withoutACP()). - Otherwise, only addresses with explicit permission on the handle can request decryption, and must provide a valid ACP (
.withACP()).
If the requester does not have permission on the ciphertext handle, the decryption request will be denied by the access control system. Grant appropriate permissions before attempting to decrypt, use
FHE.allowPublic() for values intended to become public, or FHE.allow() / FHE.allowSender() for restricted access.How the ACL is stored
Every blockchain integrating CoFHE includes a deployedACL.sol contract. This contract manages ownership records for each ciphertext, ensuring that only authorized owners can perform operations on their encrypted data.
ACL storage structure
The ACL contract contains the following mapping which tracks the ownership of each ciphertext handle:Best practices
Principle of Least Privilege
Only grant access to addresses that genuinely need it. Avoid using
allowPublic() unless the data is truly meant to be public.Use Transient for Cross-Contract Calls
When calling other contracts within a transaction, use
allowTransient() instead of permanent access to limit exposure.Track Your Permissions
Keep track of which addresses have access to which ciphertexts, especially in complex multi-contract systems.
Grant Only to Trusted Contracts
A grant cannot be revoked, and the receiver can pass access on. See who you trust when you grant access.