NINE65 Functional Walkthrough

A hands-on guide to homomorphic encryption: from environment setup to deep circuit computation without bootstrapping.

1. Introduction

This document provides a comprehensive, hands-on walkthrough of the NINE65 Fully Homomorphic Encryption (FHE) system, with a specific focus on its fully functional capabilities. The goal is to equip developers with the practical knowledge needed to set up the FHE environment, perform core cryptographic operations, and leverage the library's unique features, such as its deep circuit capacity.

We will proceed step-by-step, from initial configuration to executing complex homomorphic computations.

2. Core Functional Components

The following components are fully implemented, tested, and ready for use:

Component Functionality Status
Parameter Configuration SecureConfig for 128-bit and 192-bit security levels Fully Functional
Key Generation Secure (CSPRNG) and deterministic key generation for SecretKey, PublicKey, and EvaluationKey Fully Functional
Encoding/Decoding BFVEncoder for converting integer messages into plaintext polynomials Fully Functional
Encryption/Decryption BFVEncryptor and BFVDecryptor for both symmetric and asymmetric encryption Fully Functional
Error Handling Robust Result-based error handling (Nine65Error) Fully Functional
Homomorphic Addition BFVEvaluator::add for ciphertext-ciphertext addition Fully Functional
Homomorphic Subtraction BFVEvaluator::sub for ciphertext-ciphertext subtraction Fully Functional
Homomorphic Multiplication RNSFHEContext::mul_dual_public (standard) or mul_dual_symmetric (single-party) with auto mod-switch at depth 3+ Fully Functional
Deep Circuits (Public + Symmetric) Multi-level consecutive multiplications without bootstrapping; auto mod-switch enables deeper public-mode circuits Fully Functional

3. Environment Setup

First, we must configure the FHE environment. This involves selecting a security level and initializing the necessary cryptographic engines and context objects.

Security Note: For any production use, always start with a SecureConfig. The secure_128() preset provides a 128-bit classical security level, compliant with the Homomorphic Encryption Standard.
use nine65::prelude::*;

// 1. Select a production-ready, 128-bit secure parameter set.
let config = SecureConfig::secure_128().into_config();

// 2. Initialize the NTT (Number Theoretic Transform) engine, which is crucial
//    for efficient polynomial multiplication.
let ntt = NTTEngine::new(config.q, config.n);

// 3. For this walkthrough, we use a deterministic Random Number Generator (RNG)
//    to ensure the examples are reproducible. In production, you MUST use
//    `KeySet::generate_secure(&config, &ntt)` which uses the OS's secure RNG.
let mut rng = ShadowHarvester::with_seed(42);

// 4. Generate the complete set of keys: secret, public, and evaluation keys.
let keys = KeySet::generate(&config, &ntt, &mut rng);

// 5. Instantiate the core FHE components using the generated keys and config.
let encoder = BFVEncoder::new(&config);
let encryptor = BFVEncryptor::new(&keys.public_key, &encoder, &ntt, config.eta);
let decryptor = BFVDecryptor::new(&keys.secret_key, &encoder, &ntt);
let evaluator = BFVEvaluator::new(&ntt, &encoder, Some(&keys.eval_key));

println!("FHE Environment Initialized Successfully!");

4. Basic Encryption & Decryption

The most fundamental workflow is the encryption of a plaintext message, followed by its decryption. This confirms that the keys and parameters are configured correctly.

// Assume the setup from Step 1 is complete.
let message = 12345u64;

// Encrypt the message using the public key and the deterministic RNG.
let ciphertext = encryptor.encrypt(message, &mut rng);

// Decrypt the resulting ciphertext using the secret key.
let decrypted_message = decryptor.decrypt(&ciphertext);

println!("Original Message: {}", message);
println!("Decrypted Message: {}", decrypted_message);

assert_eq!(message, decrypted_message);
println!("Encryption/Decryption Roundtrip Successful!");

5. Homomorphic Operations

With the setup confirmed, we can now perform computations directly on encrypted data.

Addition and Subtraction

These are the simplest homomorphic operations and have a very low impact on the noise within the ciphertext.

// Encrypt two separate messages.
let m1 = 100u64;
let m2 = 25u64;
let ct1 = encryptor.encrypt(m1, &mut rng);
let ct2 = encryptor.encrypt(m2, &mut rng);

// Perform homomorphic addition.
let ct_sum = evaluator.add(&ct1, &ct2);
let sum_result = decryptor.decrypt(&ct_sum);
println!("Homomorphic Addition: {} + {} = {}", m1, m2, sum_result);
assert_eq!(sum_result, m1 + m2);

// Perform homomorphic subtraction.
let ct_diff = evaluator.sub(&ct1, &ct2);
let diff_result = decryptor.decrypt(&ct_diff);
println!("Homomorphic Subtraction: {} - {} = {}", m1, m2, diff_result);
assert_eq!(diff_result, m1 - m2);

Multiplication

Homomorphic multiplication is the most powerful and noise-intensive operation. The recommended API is RNSFHEContext::mul_dual_public (for public-key mode) or mul_dual_symmetric (for single-party mode). These handle relinearization and K-Elimination rescaling internally, and auto-apply modulus switching at depth 3+ for deeper circuits.

let m3 = 7u64;
let m4 = 8u64;
let ct3 = encryptor.encrypt(m3, &mut rng);
let ct4 = encryptor.encrypt(m4, &mut rng);

// Perform homomorphic multiplication via the Dual-RNS API.
// mul_dual_public handles rescaling and auto mod-switch internally.
let ct_prod = ctx.mul_dual_public(&ct3, &ct4).unwrap();

let prod_result = decryptor.decrypt(&ct_prod);
println!("Homomorphic Multiplication: {} * {} = {}", m3, m4, prod_result);
assert_eq!(prod_result, m3 * m4);

6. Deep Circuits (Symmetric Mode)

NINE65's flagship capability is executing deep computational graphs without bootstrapping. With the auto mod-switch enhancement, deep circuits now work in both symmetric and public-key modes. Symmetric mode uses the SecretKey for both encryption and decryption (ideal when the data owner performs the computation), while public mode supports multi-party scenarios with auto mod-switch enabling deeper circuits at level 3+.

This example calculates 316 homomorphically, a task that requires a multiplicative depth of 4 and would necessitate bootstrapping in most other FHE libraries.

// 1. Create a new encryptor that uses the secret key (symmetric mode).
let symmetric_encryptor = BFVEncryptor::new_symmetric(
    &keys.secret_key, &encoder, &ntt, config.eta
);

// 2. Encrypt the base number.
let base = 3u64;
let mut ct_power = symmetric_encryptor.encrypt(base, &mut rng);

// 3. Perform 4 consecutive squaring operations (multiplications).
// mul_dual_symmetric handles rescaling and auto mod-switch internally.
println!("Calculating 3^16 homomorphically...");
for i in 1..=4 {
    ct_power = ctx.mul_dual_symmetric(&ct_power, &ct_power).unwrap();

    let power = 2u64.pow(i + 1);
    let current_val = decryptor.decrypt(&ct_power);
    println!("  Depth {}: Homomorphic result for 3^{} = {}", i, power, current_val);
}

// 4. Verify the final result.
let final_result = decryptor.decrypt(&ct_power);
let expected_result = (3u128.pow(16) % config.t as u128) as u64;

println!("Final Homomorphic Result: {}", final_result);
println!("Expected Result: {}", expected_result);

assert_eq!(final_result, expected_result);
println!("Deep circuit (depth 4) computed successfully!");
Key Insight: Traditional FHE libraries require costly bootstrapping operations (100-1000ms each) to handle deep circuits. NINE65's exact arithmetic approach allows depth-4 computations to complete without any bootstrapping overhead.

7. Security & Error Handling

NINE65's API is designed to guide developers toward secure and robust implementations.

Secure Methods

For production, always use methods with a _secure suffix (e.g., encrypt_secure, generate_secure). These methods are guaranteed to use a cryptographically secure random number generator (CSPRNG) from the underlying operating system.

Fallible Operations

Any operation that can potentially fail (e.g., encoding a message that is too large) returns a Nine65Result<T>. This enforces explicit error handling and prevents unexpected panics.

// Attempt to encode a message that is larger than the plaintext modulus `t`.
let large_message = config.t + 1;
let encode_result = encoder.try_encode(large_message);

match encode_result {
    Ok(_) => panic!("This should have failed!"),
    Err(Nine65Error::MessageOutOfBounds { message, modulus }) => {
        println!("Correctly caught expected error: Message {} is out of bounds for modulus {}.",
                 message, modulus);
    }
    Err(e) => panic!("Caught an unexpected error type: {:?}", e),
}

8. Summary

This walkthrough has demonstrated the core, fully functional capabilities of the NINE65 FHE library. Developers can confidently use the system for:

  • Setting up a secure FHE environment with 128-bit+ security
  • Performing basic encryption, decryption, addition, subtraction, and multiplication
  • Executing deep, multi-level homomorphic computations in symmetric mode without bootstrapping
  • Implementing robust applications with built-in secure defaults and comprehensive error handling

With the auto mod-switch enhancement (February 2026), deep circuits now work in both symmetric and public-key modes. Auto modulus switching at level 3+ enables deeper public-mode circuits without manual noise management.

NINE65 provides a powerful and efficient toolset for privacy-preserving computation, offering a unique and practical solution for deep circuit evaluation on standard CPU hardware.

Top