How the fhEVM Brings Together FHE, ZK, and MPC

The fhEVM brings confidentiality to the Ethereum Virtual Machine (EVM). With computable, encrypted data onchain, developers can build a huge number of use cases that were previously not possible, from decentralized poker to confidential onchain payments. The fhEVM enables developers to leverage their existing knowledge of the EVM to build blockchain applications with a layer of confidentiality.

At a high level, the fhEVM is based on the following key cryptographic elements:

  • Fully homomorphic encryption (FHE)
  • Zero-knowledge proofs (ZK)
  • Multi-party computation (MPC)

This articlel provides a high-level introduction to FHE, ZK, and MPC and the crucial role they play in powering the fhEVM.

What is the fhEVM?

The fhEVM is an Ethereum Virtual Machine (EVM) that has been augmented to allow for FHE encrypted types and computation, which are added at the pre-compile level. In traditional EVMs, all the states are completely transparent and cannot be hidden. This means developers can only design and build use cases that make sense on a transparent database, greatly limiting their options. What if you wanted to build a bluffing game? Or hide someone’s voting choice? Neither of these use cases is possible with a conventional EVM.

With the fhEVM, developers can build and deploy a new type of smart contracts where states can be public or private, thereby enabling novel use cases that aren’t feasible on existing public blockchains.

What Does Building on the fhEVM Look Like?

With fhEVMs, there are additional private states that enable you to store encrypted data. Here are the newly added encrypted types:

  • ebool
  • eaddress
  • euint8
  • euint16
  • euint32

In addition to these encrypted types, the TFHE library also offers encrypted operations;

  • TFHE.add()
  • TFHE.sub()
  • TFHE.mul()

It also offers comparisons, such as:

  • TFHE.eq()
  • TFHE.gt()
  • TFHE.max()

These provide a lot of flexibility for a smart contract developer, granting them the ability to store encrypted data and compute on top of them. For example, you can directly add three encrypted integers without needing to decrypt them by using the TFHE.add() method:

1function sum(
2	euint32 x,
3	euint32 y,
4	euint32 z
5) public returns (euint32) {
6	return TFHE.add(TFHE.add(x,y), z);
7}

The support for a wide array of operations and comparisons allows developers to unleash their creativity and build a plethora of new use cases in gaming, DeFi, identity, tooling, and more.

Fully Homomorphic Encryption

The cryptography behind the fhEVM is called fully homomorphic encryption (FHE). FHE has been referred to as the “holy grail” of cryptographic, enabling developers to compute upon encrypted data.

The basic idea behind FHE is to use mathematical structures that allow operations to be performed on encrypted data (ciphertext) in a way that, when decrypted, produce the same result as if the operations were performed on the original unencrypted data (plaintext).

The name comes from a mathematical notion: homomorphism.

Let G and H be groups and f: G → H be a mapping (function). f is called a “group homomorphism” if

$$ f(a \cdot b) = f(a) \text{*} f(b) \forall a, b \in G$$

where $\cdot$ and * denote the group operations of G and H, respectively.

Simply, these are some functions preserving the algebraic structure.

If you need more details, you can take a look at this post on how Zama uses FHE.

What Is Homomorphic Encryption?

Encryption consists of two ingredients: algorithm and key.With homomorphic algorithms, you can perform some operations on encrypted data, without needing to decrypt it.

For example, if you disregard padding, RSA is a homomorphic encryption scheme:

$$c=m^e (mod N)$$

$$Enc(m_1⋅m_2)=(m_1⋅m_2)^e (mod N)$$

$$Enc(m_1⋅m_2)=m_1^e⋅m_2^e (mod N)$$

$$Enc(m_1⋅m_2)=Enc(m_1)⋅Enc(m_2)$$

What’s the Difference Between Homomorphic Encryption and Fully Homomorphic Encryption?

Notice that RSA is homomorphic only with respect to multiplication. With FHE, the goal is to have a homomorphic encryption scheme with respect to addition and multiplication so that all kinds of operations can be performed upon encrypted data.

So, in the simplest way of expression, a fully homomorphic scheme has to satisfy the following:

$$Enc(a+b)=Enc(a)+Enc(b)$$

$$Enc(a⋅b)=Enc(a)⋅Enc(b)$$

The main challenge here is that almost all encryption algorithms include some randomized noise terms. But when you perform operations on encrypted data over and over again, the noise growth exceeds the threshold and the ciphertext becomes impossible to decrypt.

FHE solves this. In 2008, cryptographer Craig Gentry proposed a technique called bootstrapping, which allows ciphertexts to be reencrypted with less noise.This made fully homomorphic encryption possible.

Threshold Cryptography

The specific FHE scheme powering the fhEVM is called TFHE (Fully Homomorphic Encryption Over the Torus), which is based on programmable bootstrapping. To be exact, the fhEVM leverages TTFHE (Threshold Fully Homomorphic Encryption Over the Torus). Let’s take a deeper look at what this means.

Threshold cryptography is a cryptographic technique designed to secure a cryptographic process (such as encryption or decryption) across multiple parties.The main idea is to distribute both power and risk across multiple participants of the system.

Threshold cryptography has a couple of key principles:

  1. Secret sharing: A secret is divided into several parts and distributed to the participants. The secret can be rebuilt when a sufficient number of participants agrees on the rebuilding process.
  2. Fault tolerance: As threshold cryptography requires only a subset of participants to be available for a cryptographic operation, it provides resilience against unavailability of some participants.

When the threshold is reached, the computation can be executed. But how can we know that this threshold has been reached without a trusted party? With multi-party computation (MPC).

Multi-Party Computation

In fhEVMs, trusted managers are replaced by an MPC protocol where all input parties need to be online to participate. No single entity knows the secret key, so the process of generating a shared secret key is done through an MPC protocol.

Secure MPC can be defined as the problem of n players to compute an agreed function of their inputs in a secure way, where security means guaranteeing the correctness of the output as well as the privacy of the players’ inputs.

Concretely, we assume that we have inputs

$$x_1,...,x_n $$

where player $i$ knows $x_i$, and we want to compute

$$ f(x_1,...,x_n)=(y_1,...,y_n) $$

such that player $i$ is guaranteed to learn $y_i$.

Yao's Millionaires Problem

The most basic example here is Yao's Millionaires Problem. Two millionaires meet in the street and want to find out who is richer without revealing how much money they have. The function computed in this case is a simple comparison between two integers:

$$ f(x_1,x_2)=x_1 <_? x_2 $$

where $ f(x_1, x_2) = 1 $, if $ x_1 < x_2 $; otherwise, it is 0.

If the result is that the first millionaire is richer, then they know that the second has less money—but this is the only information that’s revealed. Each millionaire doesn't know how much money the other has.

In fhEVM, MPC is only used for decrypting the ciphertext.

What Does Encryption and Decryption Look Like?

Encryption and decryption processes are performed by a global key that is distributed among the validators of the network.

But this distribution must ensure that any individual party cannot decrypt the state on its own. To solve this issue, fhEVMs utilize threshold cryptography.

The global key is shared among validators. When there's a request for decryption, a specific percentage of validators must agree that the data to be decrypted belongs to the user asking for it. The limit percentage here is the threshold.

Image

Reencryption

TFHE.reencrypt() enables confidential value decryption, granting access exclusively to the user. This method involves transforming the ciphertext encrypted under the global FHE key into another encrypted ciphertext. This transformation utilizes a temporary public key provided by the user. Subsequently, the user can decrypt the new ciphertext using the corresponding temporary private key, all of which occurs on the client side.

1function balanceOf(
2	bytes32 publicKey
3) public view returns (bytes memory) {
4	return TFHE.reencrypt(balances[msg.sender], publicKey, 0);
5}

Decryption

In order to decrypt encrypted data, one can use TFHE.decrypt() method from the TFHE library. For example:

1function getTotalSupply() public view returns (uint32) {
2	return TFHE.decrypt(totalSupply);
3}

In such a case, the encrypted total supply info is decrypted with the global private key if the threshold is exceeded.

Zero-Knowledge Proofs

In cryptography, a zero-knowledge protocol is a way for one person (the prover) to show another person (the verifier) that a certain statement is true, without giving away any information other than the fact that the statement is indeed true.

One example use case for this is age verification. Say you want to get into a bar that requires you to prove that you’re over 21, the only way to do this would be to show a form of identification. Your ID document would not just show that you’re over 21, but also your date of birth, your name, your address, and perhaps your nationality. This is a lot of information to reveal, and requires you to trust that the person checking will not use this information when all they actually need to know is that you’re over 21. Zero-knowledge proofs offer a solution.

Image

A zero-knowledge proof of a statement must satisfy the following properties:

  1. Completeness: If the statement is true, an honest verifier will be convinced of this fact by an honest prover.
  2. Soundness: If the statement is false, no cheating prover can convince an honest verifier that it is true, except with some negligible probability.
  3. Zero-knowledge: If the statement is true, no verifier learns anything other than the fact that the statement is true.

fhEVM, Revisited

Now we are more familiar with FHE, ZKP, and MPC concepts, let's get back to fhEVMs. To recap, FHE is used for confidential compute, MPC for decryption, and ZK to prove the knowledge of the plaintext.

Let us assume there is a private smart contract running on our fhEVM and a user wants to provide an encrypted input to a transaction or a view function. They are required to submit two things to the contract:

  1. ciphertext: Intended input value encrypted using the global public key
  2. ZKPoK (zero-knowledge proof of knowledge): Corresponding zero-knowledge proof of the plaintext knowledge

We need a ZKPOK because users have to prove they actually know the underlying plaintext message. Otherwise, another person would be able to use the ciphertext as if it was theirs. Thanks to the ZKPoK, produced ciphertext cannot be used in another context. The second reason is to prove that the ciphertext was formed correctly, and under the correct global FHE key, else there wouldn’t be composability.

$(Encpk(m),ZKPoK(M))$ pair is called certified ciphertext.

Encryption With fhevmjs

fhevmjs is a JavaScript library that enables developers to interact with the fhEVM protocol. Take a look at Inco docs to see the installation guide. In order to submit a ciphertext to the blockchain, we perform the encryption of the plaintext data on the client side under the global FHE key, which can be retrieved by connecting to our RPC. You can find the details here.

The functions encrypt8, encrypt16 and encrypt32 are available to encrypt your plaintext into a ciphertext. For example:

This is when a ZKPoK of the ciphertext will be generated. Note that in the current version, the ZKPoK isn’t available yet, but will be released in the near future.

1const { createInstance } = require("fhevmjs");
2const { JsonRpcProvider } = require("ethers");
3
4const provider = new JsonRpcProvider(`https://testnet.inco.org`);
5
6// Contract address of TFHE.sol
7// From https://github.com/zama-ai/fhevmjs/blob/c4b8a80a8783ef965973283362221e365a193b76/bin/fhevm.js#L9
8const FHE_LIB_ADDRESS = "0x000000000000000000000000000000000000005d";
9
10let _instance;
11
12const getInstance = async () => {
13	if (_instance) return _instance;
14
15	const network = await provider.getNetwork();
16	const chainId = +network.chainId.toString(); // chainId: 9090
17
18	console.log("network", network);
19	console.log("chainId", chainId);
20  
21	// Get blockchain public key
22	const ret = await provider.call({
23		to: FHE_LIB_ADDRESS,
24		// first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library
25		data: "0xd9d47bb001",
26	});
27	const decoded = AbiCoder.defaultAbiCoder().decode(["bytes"], ret);
28	const publicKey = decoded[0];
29
30	_instance = await createInstance({ chainId, publicKey });
31};
32const encryptedUint32 = instance.encrypt32(10);

Verification

When a smart contract receives a certified ciphertext from a user, it must verify its corresponding proof. To do so, it converts the input into a usable euint by calling TFHE.asEuint. For example:

  • TFHE.asEuint8(bytes ciphertext) method verified the provided ciphertext and returns an euint8
  • TFHE.asEbool(bytes ciphertext) verifies the provided ciphertext and returns an ebool
1function verify(
2	bytes calldata _amount
3) public returns (euint32) {
4	euint32 amount = TFHE.asEuint32(_amount);
5    
6	return amount;
7}

Conclusion

In this post, we’ve explored the fhEVM concept as a fusion of three different and significant cryptographic notions: FHE, ZK, and MPC. For more on the fhEVM, check out the Inco docs.

Image Reference

  1. https://www.inco.org/
  2. https://www.scienceabc.com/eyeopeners/can-you-prove-that-you-know-something-without-revealing-what-it-is.html

Subscribe to our newsletter

Top industry insights on FHE.

By clicking Sign Up you're confirming that you agree with our Terms and Conditions.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.