Skip to main content
This walkthrough takes you from pip install to a working encrypted NAND gate in under five minutes. No account, no API key, all local.

What you’ll build

A program that:
  1. Generates a fresh FHE keypair locally.
  2. Encrypts two boolean bits.
  3. Computes NAND on the encrypted bits.
  4. Decrypts the result and verifies it’s correct.
The key insight: step 3 is the same operation that would happen on api.wavis.xyz. The server (or in this case, your local engine) never sees the plaintext bits — only the ciphertexts.

Prerequisites

  • Python 3.9+
  • ~50 MB free disk
  • A terminal

Step 1: Install

pip install wavis-fhe
This installs the pre-compiled wheel for your platform. No Rust toolchain needed.

Step 2: Generate keys

import wavis_fhe as wv

keys = wv.keygen()
print(f"TLWE dim n = {keys.n()}")
print(f"RLWE ring degree N = {keys.big_n()}")
Output:
TLWE dim n = 636
RLWE ring degree N = 1024
The keys object holds your secret key, public key, and evaluation key. wv.keygen() takes ~5 seconds on the first call (CPU-bound polynomial expansion); subsequent calls are instant due to caching.

Step 3: Encrypt

ct_a = keys.encrypt(False)   # encrypt 0
ct_b = keys.encrypt(True)    # encrypt 1

print(f"Ciphertext size: {len(ct_a.to_bytes())} bytes")
Output:
Ciphertext size: ~2048 bytes
Each Ciphertext is ~2 KB regardless of which bit it encrypts. From this point on, the bits are computationally indistinguishable from random under RLWE hardness.

Step 4: Compute on ciphertexts

ct_result = keys.nand(ct_a, ct_b)
This is the magic. keys.nand() performs a TFHE gate bootstrap on the two ciphertexts. It takes ~14 ms on a modern CPU (fast_128 preset) and returns a fresh ciphertext encrypting NAND(false, true) = true. Crucially, no plaintext was reconstructed during this step. The operation manipulates the ciphertexts directly.

Step 5: Decrypt

result = keys.decrypt(ct_result)
print(f"NAND(False, True) = {result}")
assert result is True
Output:
NAND(False, True) = True

Full program

Save as quickstart.py:
import wavis_fhe as wv

# 1. Keys (5 seconds, done once)
keys = wv.keygen()

# 2. Encrypt
ct_a = keys.encrypt(False)
ct_b = keys.encrypt(True)

# 3. Compute on ciphertexts (~14 ms)
ct_result = keys.nand(ct_a, ct_b)

# 4. Decrypt
result = keys.decrypt(ct_result)
print(f"NAND(False, True) = {result}")
Run it:
$ python quickstart.py
NAND(False, True) = True

What you actually proved

You just executed a computation where:
  • Step 2 (encrypt) used the secret key.
  • Step 3 (compute) did not use the secret key — it used only the evaluation key.
  • Step 4 (decrypt) used the secret key.
In a production deployment, step 3 happens on the server while steps 2 and 4 happen on the client. The server only ever sees ciphertexts.

Try the full gate set

import wavis_fhe as wv

keys = wv.keygen()

t = lambda b: keys.decrypt(b)   # shorthand
e = keys.encrypt

# Truth table verification
for a_bit in [False, True]:
    for b_bit in [False, True]:
        a = e(a_bit); b = e(b_bit)
        assert t(keys.nand(a, b)) == (not (a_bit and b_bit))
        assert t(keys.and_(a, b)) == (a_bit and b_bit)
        assert t(keys.or_(a, b))  == (a_bit or b_bit)
        assert t(keys.xor(a, b))  == (a_bit != b_bit)

print("All gates verified ✓")

Common questions

Q: How do I make this run on a server? A: See Server Mode Example. The pattern is: client keygen() + encrypt(), upload eval-key + ciphertexts, server nand(), return ciphertext, client decrypt(). Q: How fast is this? A: fast_128 ≈ 14 ms/gate, standard_128 ≈ 32 ms/gate on CPU. With batching on a local GPU (BYO GPU): 5.2 ms/gate. See Performance. Q: Is this actually 128-bit secure? A: Yes. The fast_128 preset uses TLWE n=636 with conservative noise parameters. See Security Model for the math. Q: What about more complex circuits? A: NAND is universal — you can build any boolean circuit by composing gates. See Full Adder for a 1-bit adder example.

Next Steps

Full Adder

Build a 1-bit adder from NAND gates

Server Mode

Move computation to api.wavis.xyz