Encryption Schemes
This guide explains the three HE schemes supported by HETorch: CKKS, BFV, and BGV.
Overview
| Scheme | Arithmetic | Precision | Use Case | Complexity |
|---|---|---|---|---|
| CKKS | Approximate (float-like) | ~40-60 bits | Neural networks, ML | Medium |
| BFV | Exact (integer) | Exact | Decision trees, counters | Low |
| BGV | Exact (integer) | Exact | Similar to BFV | Medium |
CKKS (Cheon-Kim-Kim-Song)
Overview
CKKS is an approximate HE scheme designed for real/complex number arithmetic. It's the most suitable scheme for neural networks and machine learning.
Key Features
- Approximate Arithmetic: Operations introduce small rounding errors (similar to floating-point)
- Rescaling: Manages scale growth after multiplications
- Leveled: Limited multiplication depth determined by modulus chain
- Bootstrapping: Can refresh ciphertexts to enable deeper computations
When to Use
✓ Good for:
- Neural networks
- Signal processing
- Approximate computations
- Real-valued data
✗ Not good for:
- Exact integer arithmetic
- Comparisons (>, <, ==)
- Branching logic
Parameters
from hetorch import CKKSParameters
params = CKKSParameters(
poly_modulus_degree=8192, # Polynomial degree (power of 2)
coeff_modulus=[60, 40, 40, 60], # Modulus chain
scale=2**40, # Scaling factor
noise_budget=100.0 # Initial noise budget
)
poly_modulus_degree
Controls security, slot count, and performance:
| Value | Security | Slots | Performance | Use Case |
|---|---|---|---|---|
| 4096 | ~100 bits | 2048 | Fast | Development, testing |
| 8192 | ~128 bits | 4096 | Balanced | Recommended |
| 16384 | ~192 bits | 8192 | Slow | High security |
| 32768 | ~256 bits | 16384 | Very slow | Maximum security |
Recommendation: Use 8192 for most applications.
coeff_modulus
Determines multiplication depth and precision:
# Example: [60, 40, 40, 60]
# - First 60: Special prime for encoding
# - Middle 40s: Computation primes (one per multiplication level)
# - Last 60: Special prime for decoding
# Length = max_depth + 1
# 2 multiplications
coeff_modulus=[60, 40, 40, 60] # Length 4 = 2 mults + 2 special
# 4 multiplications
coeff_modulus=[60, 40, 40, 40, 40, 60] # Length 6 = 4 mults + 2 special
# 8 multiplications
coeff_modulus=[60, 40, 40, 40, 40, 40, 40, 40, 40, 60] # Length 10
Guidelines:
- Each multiplication consumes one level
- More levels = deeper computations but larger ciphertexts
- Typical: 2-4 levels for simple models, 6-10 for deep models
scale
Controls precision vs range tradeoff:
| Value | Precision | Range | Use Case |
|---|---|---|---|
| 2^30 | ~9 decimal digits | Larger | Wide range values |
| 2^40 | ~12 decimal digits | Balanced | Recommended |
| 2^50 | ~15 decimal digits | Smaller | High precision |
Recommendation: Use 2^40 for most applications.
noise_budget
Initial noise capacity (used with noise simulation):
params = CKKSParameters(
poly_modulus_degree=8192,
coeff_modulus=[60, 40, 40, 60],
scale=2**40,
noise_budget=100.0 # 100 bits of noise budget
)
CKKS Operations
Supported Operations
# Ciphertext-ciphertext
cadd(ct1, ct2) # Addition
cmult(ct1, ct2) # Multiplication (consumes 1 level)
rotate(ct, steps) # Rotation
# Plaintext-ciphertext
padd(ct, pt) # Addition
pmult(ct, pt) # Multiplication (consumes 1 level)
# Scheme-specific
rescale(ct) # Rescale after multiplication
relinearize(ct) # Reduce ciphertext size
bootstrap(ct) # Refresh noise budget
Rescaling
After multiplication, scale increases. Rescaling brings it back:
# Without rescaling
x = encrypt(1.0, scale=2^40)
y = encrypt(2.0, scale=2^40)
z = cmult(x, y) # scale = 2^80 (too large!)
# With rescaling
x = encrypt(1.0, scale=2^40)
y = encrypt(2.0, scale=2^40)
z = cmult(x, y) # scale = 2^80
z = rescale(z) # scale = 2^40 (back to normal)
HETorch's RescalingInsertionPass handles this automatically.
Example
from hetorch import HEScheme, CKKSParameters, CompilationContext, FakeBackend
context = CompilationContext(
scheme=HEScheme.CKKS,
params=CKKSParameters(
poly_modulus_degree=8192,
coeff_modulus=[60, 40, 40, 60],
scale=2**40,
noise_budget=100.0
),
backend=FakeBackend()
)
BFV (Brakerski-Fan-Vercauteren)
Overview
BFV is an exact HE scheme for integer arithmetic. It provides exact results with no approximation error.
Key Features
- Exact Arithmetic: No rounding errors
- Integer Operations: Works with integers modulo
plain_modulus - Leveled: Limited multiplication depth
- No Rescaling: Simpler than CKKS (no scale management)
When to Use
✓ Good for:
- Exact integer computations
- Counters, voting
- Decision trees
- Boolean circuits
✗ Not good for:
- Floating-point arithmetic
- Neural networks (requires quantization)
Parameters
from hetorch import BFVParameters
params = BFVParameters(
poly_modulus_degree=8192,
coeff_modulus=[60, 60, 60],
plain_modulus=1024
)
plain_modulus
Determines the integer range:
# plain_modulus = 1024
# Values: 0 to 1023
# plain_modulus = 65537 (prime)
# Values: 0 to 65536
Guidelines:
- Must be prime or power of 2
- Larger = more range but slower
- Typical: 1024 - 65537
BFV Operations
# Ciphertext-ciphertext
cadd(ct1, ct2) # Addition (exact)
cmult(ct1, ct2) # Multiplication (exact, consumes 1 level)
rotate(ct, steps) # Rotation
# Plaintext-ciphertext
padd(ct, pt) # Addition (exact)
pmult(ct, pt) # Multiplication (exact)
# Scheme-specific
relinearize(ct) # Reduce ciphertext size
Example
from hetorch import HEScheme, BFVParameters, CompilationContext, FakeBackend
context = CompilationContext(
scheme=HEScheme.BFV,
params=BFVParameters(
poly_modulus_degree=8192,
coeff_modulus=[60, 60, 60],
plain_modulus=1024
),
backend=FakeBackend()
)
BGV (Brakerski-Gentry-Vaikuntanathan)
Overview
BGV is similar to BFV but with different noise management. It's also an exact integer scheme.
Key Features
- Exact Arithmetic: No rounding errors
- Integer Operations: Works with integers modulo
plain_modulus - Leveled: Limited multiplication depth
- Modulus Switching: Different noise management than BFV
When to Use
Similar to BFV. Choice between BFV and BGV depends on specific use case and backend support.
Parameters
from hetorch import BGVParameters
params = BGVParameters(
poly_modulus_degree=8192,
coeff_modulus=[60, 60, 60],
plain_modulus=1024
)
Same parameters as BFV.
Example
from hetorch import HEScheme, BGVParameters, CompilationContext, FakeBackend
context = CompilationContext(
scheme=HEScheme.BGV,
params=BGVParameters(
poly_modulus_degree=8192,
coeff_modulus=[60, 60, 60],
plain_modulus=1024
),
backend=FakeBackend()
)
Scheme Comparison
Arithmetic Type
| Scheme | Type | Example |
|---|---|---|
| CKKS | Approximate | 1.5 + 2.3 ≈ 3.8 (±0.0001) |
| BFV | Exact | 5 + 7 = 12 (exact) |
| BGV | Exact | 5 + 7 = 12 (exact) |
Multiplication Depth
All schemes have limited multiplication depth:
# CKKS with coeff_modulus=[60, 40, 40, 60]
x = encrypt(2.0) # level: 2
y = x * x # level: 1 (consumed 1)
z = y * y # level: 0 (consumed 1)
w = z * z # ERROR: no levels left!
# Solution: Use bootstrapping
z_boot = bootstrap(z) # level: 2 (refreshed)
w = z_boot * z_boot # level: 1 (OK)
Performance
| Scheme | Speed | Ciphertext Size | Complexity |
|---|---|---|---|
| CKKS | Medium | Medium | Medium (rescaling) |
| BFV | Fast | Small | Low (simple) |
| BGV | Medium | Medium | Medium (modulus switching) |
Use Case Matrix
| Application | Recommended Scheme | Why |
|---|---|---|
| Neural networks | CKKS | Approximate arithmetic, rescaling |
| Logistic regression | CKKS | Real-valued weights |
| Decision trees | BFV/BGV | Exact comparisons |
| Voting/counting | BFV/BGV | Exact integers |
| Signal processing | CKKS | Real-valued signals |
| Boolean circuits | BFV/BGV | Exact logic |
Choosing a Scheme
Decision Tree
Do you need exact results?
├─ Yes → Do you need integers?
│ ├─ Yes → BFV or BGV
│ └─ No → Not supported (use CKKS with high precision)
└─ No → CKKS
For Neural Networks
Always use CKKS:
- Supports real-valued weights and activations
- Polynomial approximations work well
- Rescaling manages scale growth
- Bootstrapping enables deep networks
For Other Applications
- Exact integer arithmetic: BFV or BGV
- Approximate arithmetic: CKKS
- Boolean logic: BFV or BGV
- Real-valued data: CKKS
Parameter Selection Guidelines
Security Level
| poly_modulus_degree | Security (bits) | Use Case |
|---|---|---|
| 4096 | ~100 | Development only |
| 8192 | ~128 | Production |
| 16384 | ~192 | High security |
| 32768 | ~256 | Maximum security |
Multiplication Depth
Estimate depth needed:
# Example: 3-layer neural network
# Layer 1: Linear + ReLU (degree 8 polynomial)
# - Linear: 1 multiplication
# - ReLU polynomial: 8 multiplications
# - Total: 9 multiplications
# Layer 2: Same as layer 1
# - Total: 9 multiplications
# Layer 3: Linear only
# - Total: 1 multiplication
# Grand total: 19 multiplications
# Need coeff_modulus length: 19 + 1 = 20
coeff_modulus = [60] + [40] * 18 + [60] # 20 primes
Optimization: Use lazy rescaling and BSGS to reduce depth.
Slot Count
# Slot count = poly_modulus_degree / 2 (for CKKS/BFV)
poly_modulus_degree = 8192
slot_count = 4096 # Can pack 4096 values per ciphertext
Scheme-Specific Passes
CKKS Only
RescalingInsertionPass: Manages scale after multiplications
All Schemes
RelinearizationInsertionPass: Reduces ciphertext sizeBootstrappingInsertionPass: Refreshes noise budget
Example Pipelines
CKKS Pipeline:
from hetorch.passes import PassPipeline
from hetorch.passes.builtin import (
InputPackingPass,
NonlinearToPolynomialPass,
RescalingInsertionPass,
RelinearizationInsertionPass,
BootstrappingInsertionPass,
)
pipeline = PassPipeline([
InputPackingPass(),
NonlinearToPolynomialPass(),
RescalingInsertionPass(strategy="lazy"), # CKKS only
RelinearizationInsertionPass(strategy="lazy"),
BootstrappingInsertionPass(level_threshold=15.0),
])
BFV/BGV Pipeline:
pipeline = PassPipeline([
InputPackingPass(),
# No NonlinearToPolynomialPass (exact arithmetic)
# No RescalingInsertionPass (not needed)
RelinearizationInsertionPass(strategy="lazy"),
BootstrappingInsertionPass(level_threshold=20.0),
])
Next Steps
- Compilation Workflow: Learn the compilation process
- Builtin Passes: Understand transformation passes
- Backends: Choose between fake and real backends