Comment on page
📪

Encrypted Types

Explaining how encryption & decryption work, and how our apps interact with encrypted data

Encrypted Uint

Encrypted data types we can work with are encrypted unsigned integers of multiple bit lengths - euint8, euint16, euint32 as well as ebool (encrypted boolean). These types are encrypted version of the standard uint type.
No Shit? The prefix "e" stands for "encrypted", indicating that the value of these variables is encrypted and stored on-chain in an obfuscated form
Being able to use different sizes of integers can be important. In the world of FHE each computation is significantly more expensive than in the cleartext world. If you want to minimize gas costs & computation time, selecting the correct integer type can be very helpful!

Sending an euint

To send these encrypted variables to the chain, the data must first be encrypted off-chain using FhenixJS/fhevmJS. Once encrypted, these values are typically represented as byte arrays and sent to the contract in this format.
Consider an example of a contract function that receives encrypted data:
function receiveEncrypted(bytes calldata encryptedData) public {
euint32 data = TFHE.asEuint32(encryptedData);
// Now, you can use 'data' in computations.
}
In this function, encryptedData is received as a byte array. It's then converted to an 'euint32' using 'TFHE.asEuint32'.

Operations On euints

Interaction with these encrypted types is made possible through various operations, such as addition, subtraction, and comparison. Importantly, these operations are performed in the encrypted domain using specific TFHE functions, without revealing the underlying values. Here's a simple demonstration:
euint32 a = TFHE.asEuint32(encryptedA);
euint32 b = TFHE.asEuint32(encryptedB);
​
euint32 sum = TFHE.add(a, b); // Encrypted addition
euint32 diff = TFHE.sub(a, b); // Encrypted subtraction
bool isLess = TFHE.le(a, b); // Encrypted less-than-or-equal-to comparison
While standard casting operations are possible for encrypted types, remember that these values are meant to remain encrypted. Casting them back to normal integer types to reveal their values is not intended and not allowed until they are being revealed to a user. For example, an encrypted ERC20 token will hide the user's balance during all calculations, and only reveal it when he queries it.
Keep in mind: Operations on encrypted numbers will always overflow if they go past the integer size limits.

Returning euints

To return encrypted data to a user, you'll typically use a function that includes some form of access control, ensuring that only the intended recipient can access the information. The specifics can depend on your application, but in general, you'll want to verify that the caller is authorized to view the data they're requesting.
Once you've determined that the user is authorized to access the data, you'll prepare it for return using the TFHE.reencrypt function. This function encrypts the data in a special way that only the specified user can decrypt. In effect, it "re-encrypts" the data using the user's public key, making sure that only the recipient (who holds the corresponding private key) can decipher the data.
Here's an example of a function that securely returns user data:
function getSecureData(
bytes32 publicKey,
bytes calldata signature
)
public
view
onlyAuthorizedUser(publicKey, signature) // hypothetical access control
returns (bytes memory)
{
euint32 secureData = balances[msg.sender]// fetch the secure data based on some condition
return TFHE.reencrypt(secureData, publicKey);
}
​
In this function, onlyAuthorizedUser is a hypothetical modifier ensuring access control. The getSecureData function then returns the secure data, but only after reencrypting it using TFHE.reencrypt with the user's public key.