Secret Store and Access Control in Encrypted Smart Contracts

This section focuses on access control and secure data management in encrypted smart contracts using the TFHE library within the fhEVM framework. It explains how:

  1. Secrets are stored, verified, and processed securely with Fully Homomorphic Encryption (FHE).

  2. Access Control Lists (ACLs) are used to manage permissions for manipulating ciphertexts, ensuring only authorized entities can access or modify encrypted data.

  3. Automatic Temporary Allowances simplify contract development by granting transient permissions for operations like type conversions, random value generation, and computation results.

  4. Best Practices prevent unauthorized access by requiring sender authorization checks before ciphertext processing.

  5. Encrypted Transfers (e.g., in ERC20 token contracts) securely update balances and manage permissions without revealing sensitive values.

This section is critical for developers building privacy-preserving decentralized applications, ensuring encrypted operations remain secure, efficient, and robust.

Key Features Demonstrated

  1. Secret Management: Ensures only authorized entities can access and manipulate secrets.

  2. Automatic Transient Allowance: Simplifies operations by providing temporary access for computations, type conversions, and random value generation.

  3. Access Control Verification: Protects sensitive operations by verifying sender permissions.

  4. Re-encryption Support: Enables secure transfer of ciphertext between users and contracts.

  5. Secure Transfer in ERC20 Tokens: Demonstrates an encrypted transfer function with homomorphic operations, maintaining data privacy and security.

This documentation is designed to provide developers with a comprehensive guide to implementing access control and secure computation workflows in encrypted smart contracts using TFHE and fhEVM.

Secret Store and Access Control

The following example illustrates how encrypted smart contracts manage secrets and ensure access control using TFHE:

Contract: SecretStore

contract SecretStore {
    euint16 public secretResult;

    function storeSecret(euint16 callerSecret) public {
        // Verify that the caller has access to this ciphertext
        require(
            TFHE.isSenderAllowed(callerSecret),
            "The caller is not authorized to access this secret."
        );

        // Perform some FHE computation (result is automatically given transient allowance)
        euint16 computationResult = TFHE.add(callerSecret, 3);

        // Store the resulting ciphertext handle in contract storage
        secretResult = computationResult;

        // Make the temporary allowance permanent for future use or decryption requests
        TFHE.allow(secretResult, address(this));
    }
}

Explanation:

  1. Creation and Temporary Allowance: The SecretGiver contract generates a secret and temporarily allows the SecretStore contract to manipulate it.

  2. Verification: The SecretStore contract verifies the caller's access to the secret using TFHE.isSenderAllowed.

  3. FHE Computation: The secret is processed (e.g., TFHE.add operation) to produce a computation result.

  4. Storage and Permanent Allowance: The computed result is stored in the contract with permanent access granted for future operations.

Automatic (Transient) Allowance

To streamline development, certain operations in TFHE automatically grant temporary access (using TFHE.allowTransient) for the contract performing the operation. This applies to:

Categories of Automatic Allowance

Type Conversion Functions:

  • TFHE.asEuintXX()

  • TFHE.asEaddress()

  • TFHE.asEbool()

Random Value Generation:

  • TFHE.randXX()

Computation Results

  • Operations such as TFHE.add() and TFHE.select().

Example: Generating and Storing a Random Value

function randomize() public {
    // Generate a random value; this value has temporary allowance
    euint64 random = TFHE.randEuint64();

    // Permanently store the temporary access for this ciphertext
    TFHE.allow(random, address(this));
}

Explanation:

  • A random encrypted value is generated with temporary allowance.

  • Permanent allowance is then granted to allow future operations or decryption.

Security Best Practices: Verifying Sender Access

It is crucial to verify the sender's access to any ciphertext before processing. This ensures that unauthorized users cannot exploit the function.

Verification Example:

Here is a simple code snippet:

require(
    TFHE.isSenderAllowed(ciphertext),
    "The caller is not authorized to access this ciphertext."
);

Rationale:

  • Prevents attackers from passing unauthorized ciphertexts to exploit functions.

  • Secures contract behavior by ensuring only authorized senders can access sensitive data.

ACL for Re-encryption

If ciphertext needs to be re-encrypted for a user, explicit access must be granted. This is necessary for the user to request re-encryption securely.

Process:

  1. A user signs a public key associated with a specific contract.

  2. The ciphertext must be allowed for both the user and the contract.

Example: Transfer Function in an ERC20 Token Contract

function transfer(address to, euint64 encryptedAmount) public {
    // Verify that the caller is authorized to access this encrypted amount
    require(
        TFHE.isSenderAllowed(encryptedAmount),
        "The caller is not authorized to access this encrypted amount."
    );

    euint64 amount = TFHE.asEuint64(encryptedAmount);
    ebool canTransfer = TFHE.le(amount, balances[msg.sender]);

    // Update the recipient's balance
    euint64 newBalanceTo = TFHE.add(
        balances[to],
        TFHE.select(canTransfer, amount, TFHE.asEuint64(0))
    );
    balances[to] = newBalanceTo;

    // Allow the new balance for both the contract and the recipient
    TFHE.allow(newBalanceTo, address(this));
    TFHE.allow(newBalanceTo, to);

    // Update the sender's balance
    euint64 newBalanceFrom = TFHE.sub(
        balances[msg.sender],
        TFHE.select(canTransfer, amount, TFHE.asEuint64(0))
    );
    balances[msg.sender] = newBalanceFrom;

    // Allow the new balance for both the contract and the sender
    TFHE.allow(newBalanceFrom, address(this));
    TFHE.allow(newBalanceFrom, msg.sender);
}

Last updated