Skip to main content
ZK Verifier checks every encrypted input before it can enter a confidential smart contract.

Why ZKPoK?

A Zero-Knowledge Proof of Knowledge (ZKPoK) lets a user prove they know the plaintext behind an encrypted input, without revealing that plaintext. ZKPoKs protect against potential malicious vectors, including:
  1. Malleability attacks: Without ZKPoK protection, attackers could transform observed ciphertexts into new valid-looking ones without knowing their contents, for example by combining them with encrypted zeros.
  2. Chosen ciphertext attacks (CCAs): Attackers submit modified ciphertexts and observe the results, potentially exploiting homomorphic operations to infer sensitive information or even recover the secret key.
Requiring a ZKPoK for each encrypted input is what makes it safe to run an encryption system in a public runtime like a blockchain. Only someone who knows the original plaintext can produce a valid proof.

Sending encrypted inputs

When providing ciphertexts as an input to a smart contract, users have to generate a ZKPoK and get a verification approval first. The Client SDK (@cofhe/sdk) and FHE.sol handle most of this work; the mechanism is described here end to end (also in the diagram below).
ZK Proof of Knowledge Flow
The process of sending inputs to a smart contract:
  1. The user encrypts the inputs and generates a ZK proof of knowledge for them.
  2. The user sends the ciphertexts and proofs to the ZK Verifier, as one batch.
  3. The ZK Verifier verifies each proof. If all are valid, it stores the encrypted values in the ciphertext database and signs one message approving the whole batch.
  4. The ZK Verifier returns the handles and the batch approval to the user.
  5. The user sends the handles and the approval as inputs to a contract call.
  6. The contract verifies the batch signature, approving the inputs and emitting InputVerified per input, which anchors a commitment so each input becomes decryptable later.
  7. The contract performs the actual logic.
Most of this process is abstracted away. Steps 1 to 6 all happen behind the SDK and library calls, while step 7 (the actual logic) is up to you to write.

Trust model

The ZK Verifier runs inside a hardware-attested TEE (Intel TDX). Its signing key is held as Shamir shares by independent partners and released only to the exact attested code image. Neither the operator nor anyone else can sign approvals outside the reviewed program. See Key Management for how shares are created and released. After a successful verification, the service stores the ciphertext bytes in the CT Server and archives the inputs and proofs for auditability. The signed message is verified onchain by the TaskManager using ecrecover; the verifier’s signer address is registered there as verifierSigner.

Signature format

For developers integrating without the SDK, the signature covers a batch digest. Each input is hashed, the hashes are concatenated in input order, and the digest of that concatenation is signed:
The digest binds the sender and the consuming contract, so a verified batch cannot be replayed by another account or into another contract. The recid value the service returns (0 or 1) must be adjusted to 27 or 28 for Solidity’s ecrecover.