Re-encryption

Re-encryption is a client-side operation that allows only authorized users to access the underlying plaintext value by making an API call to the Gateway service using the fhevmJs library.

Steps to Perform Re-encryption on Encrypted Values

1. Access Control in Application Smart Contract

Developers need to grant access to various user addresses so that they can access specific ciphertexts. To do this, they must call TFHE.allow() each time a ciphertext changes. This call should include the ciphertext handle and the address of the user who is permitted to access it.

Note: Since the handle for each ciphertext changes whenever the ciphertext is updated, TFHE.allow() must be called after each modification to the value. Failure to do so will result in an unauthorized access error from the Gateway

// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;

import "fhevm/lib/TFHE.sol";

contract EncryptedERC20 {
    //Remaining logic
    mapping(address => euint64) internal balances;

    function mint(uint64 mintedAmount) public virtual {
        balances[owner()] = TFHE.add(balances[owner()], mintedAmount);
        TFHE.allow(balances[owner()], owner());
    }

    // Transfers an encrypted amount between two addresses
    function _transfer(address from, address to, euint64 transferValue) internal virtual {
        euint64 newBalanceTo = TFHE.add(balances[to], transferValue);
        balances[to] = newBalanceTo;

        euint64 newBalanceFrom = TFHE.sub(balances[from], transferValue);
        balances[from] = newBalanceFrom;

        TFHE.allow(balances[to], to);  // Allow 'to' address to access new balance
        TFHE.allow(balances[from], from);  // Allow 'from' address to access updated balance
    }

    // Returns the balance handle for a specified wallet address
    function balanceOf(address wallet) public view virtual returns (euint64) {
        return balances[wallet];
    }
    //Remaining logic
}

2. Guide to Call the Gateway: The next step involves calling the view function from the smart contract to retrieve the unique handle for the ciphertext. Next, generate a keypair for the user and request that the user signs the public key. Finally, make an API call to the Gateway to request re-encryption, providing the ciphertext handle, public key, user address, contract address, and the user's EIP-712 signature. The dApp then decrypts the received value using the private key.

Here’s a small example of how to do this using fhevmJs on the client side for the contract above:

import abi from "./abi.json";
import { Contract, BrowserProvider } from "ethers";
import { createInstance } from "fhevmjs";

const CONTRACT_ADDRESS = "EncryptedERC20ContractAddress";

const provider = new BrowserProvider(window.ethereum);
const accounts = await provider.send("eth_requestAccounts", []);
const USER_ADDRESS = accounts[0];

// Create a fhevmjs instance using Inco's Rivest network and Gateway
const instance = await createInstance({
  chainId: 21097,
  networkUrl: "https://validator.rivest.inco.org/",
  gatewayUrl: "https://gateway.rivest.inco.org/",
  aclAddress: "0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92",
});

// Generate the private and public key pair for re-encryption
const { publicKey, privateKey } = instance.generateKeypair();

// Create an EIP712 object for the user to sign
const eip712 = instance.createEIP712(publicKey, CONTRACT_ADDRESS);

// Request the user's signature on the public key
const params = [USER_ADDRESS, JSON.stringify(eip712)];
const signature = await window.ethereum.request({ method: "eth_signTypedData_v4", params });

// Retrieve the ciphertext to re-encrypt
const encryptedERC20 = new Contract(CONTRACT_ADDRESS, abi, provider.getSigner());
const encryptedBalance = await encryptedERC20.balanceOf(await signer.getAddress());

// Request re-encryption through the Gateway and decrypt the result with the private key
const userBalance = await instance.reencrypt(
  encryptedBalance, // the encrypted balance
  privateKey, // the private key generated by the dApp
  publicKey, // the public key generated by the dApp
  signature, // the user's signature of the public key
  CONTRACT_ADDRESS, // The contract address holding the ciphertext
  USER_ADDRESS // The user address for the ciphertext
);

console.log(userBalance);

Last updated