Access Control List

Access Control List (ACL) in FHEVM

The FHEVM includes an Access Control List (ACL) contract that tracks which addresses have permission to interact with encrypted data (ciphertexts). This ensures that only authorized addresses can view or manipulate specific ciphertext handles.. Note that these are not actual ciphertexts but unique handles to ciphertexts, and the ACL stores the relationship between which address can access, view, or modify each ciphertext handle.

How ACL Works

The ACL manages permissions by storing a list of addresses allowed to interact with each ciphertext handle. When a ciphertext handle is created or modified, ACL permissions are defined to control access. There are two types of permissions:

  1. Permanent Permissions: Stored directly on the blockchain, allowing a specified address to interact with the ciphertext handle at any time.

  2. Temporary Permissions: Used only for the current transaction and stored in transient storage, which helps save gas. Temporary permissions are especially useful when allowing external function calls to access or modify a ciphertext handle.

These permissions can be assigned as follows:

  • Permanent Allowance: TFHE.allow(ciphertext_handle, address) stores permanent access permissions on the blockchain.

Note: Since the unique handle for a ciphertext updates with any change to its value, you must call TFHE.allow() after each update. Failing to specify TFHE.allow() will result in an unauthorized access error from the Gateway.

  • Transient Allowance: TFHE.allowTransient(ciphertext_handle, address) provides temporary access, valid only for the duration of the transaction. Temporary permissions are stored in transient storage, while permanent permissions are recorded in a dedicated contract on the blockchain. This makes transient permissions more gas-efficient for short-term access needs.

Code Example: Setting Temporary and Permanent Permissions

In this example, we demonstrate how temporary and permanent permissions work with the ACL in FHEVM. The generateHiddenRandomNumber contract creates a secret (a hidden random number) and temporarily shares it with another contract, GameContract, while maintaining permanent access to the hidden random number itself, enabling secure function calls using encrypted data..

import "fhevm/lib/TFHE.sol";

contract generateHiddenRandomNumber {
  // Stores an encrypted random number
  euint32 private hiddenRandomNumber;
  GameContract _gameContract;

  constructor() {
    // Generate a random encrypted 32-bit integer upon contract creation
    hiddenRandomNumber = TFHE.randEuint32();
    // contract can always access hidden random number
    TFHE.allow(hiddenRandomNumber, address(this));
    _gameContract = new GameContract();
  }

  function giveMySecret(address externalContractAddress) public {
    // Temporarily allow the specified contract access to `hiddenRandomNumber` for this transaction
    TFHE.allowTransient(hiddenRandomNumber, externalContractAddress);

    // Pass `hiddenRandomNumber` to the external contract for use
    _gameContract.spinLottery(hiddenRandomNumber);
  }
}

contract GameContract {
  function spinLottery(euint32 hiddenRandomNumber) public {
    // Verify that the caller has permission to use this ciphertext
    require(TFHE.isSenderAllowed(hiddenRandomNumber), "The caller is not authorized to access the hidden random number.");

    // Perform computations with `hiddenRandomNumber` for the game's logic
    // (e.g., use it in lottery or game outcomes)
  }
}

Explanation of Code:

  • Temporary Access: TFHE.allowTransient(hiddenRandomNumber, externalContractAddress); temporarily allows the GameContract (or any specified external contract) to access hiddenRandomNumber. This access is valid only for the duration of this transaction.

  • Permanent Access: TFHE.allow(hiddenRandomNumber, address(this)) provides permanent access to a ciphertext handle to the generateHiddenRandomNumber contract. Once set, the permission remains on-chain and allows future interactions without requiring reauthorization.

Automatic Transient Permissions

Certain functions in FHEVM automatically provide temporary access (transient allowance) to make development simpler. These functions include:

  • TFHE.asEuintXX(), TFHE.asEaddress(), and TFHE.asEbool()

  • TFHE.randXX()

  • Operations like TFHE.add(), TFHE.select(), and other computation functions

However, if you want permanent access to a created encrypted random number, you can specify it as shown in the following example:

function randomize() public {
  // Generate a random encrypted value with transient access
  euint64 random = TFHE.randEuint64();

  // Make the access to `random` permanent for contract reuse
  TFHE.allow(random, address(this));
}

Security Best Practice: isSenderAllowed()

When a function receives an encrypted input, it’s important to verify that the sender has permission to use that ciphertext. This can be done with TFHE.isSenderAllowed(ciphertext). This check prevents unauthorized access and reduces the risk of exploitation by unauthorized parties.

function spinLottery(euint32 hiddenRandomNumber) public {
  // Verify that the caller has permission to use this ciphertext
  require(TFHE.isSenderAllowed(hiddenRandomNumber), "The caller is not authorized to access the hidden random number.");

  // Perform computations with `hiddenRandomNumber` for the game's logic
  // (e.g., use it in lottery or game outcomes)
}

ACL in Re-encryption and Retrieval

The ACL system also manages access during re-encryption. When a user requests re-encryption, the gateway retrieves the ACL information to determine if the requester has the appropriate access rights. This ensures that only authorized users can initiate re-encryption and view the data. When creating an fhevmInstance, you need to provide the ACL address, as detailed in the fhevmJS documentation.


Last updated