Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/octra-labs/pvac_hfhe_cpp/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers the essential operations you’ll use in every PVAC-HFHE application. You’ll learn how to set up the cryptographic system, encrypt data, and decrypt results.

Overview

Every PVAC-HFHE workflow follows these steps:
1

Generate keys

Create public and secret keys using the keygen function
2

Encrypt data

Convert plaintext values to ciphertexts using enc_value
3

Perform operations

Execute homomorphic operations on encrypted data
4

Decrypt results

Recover plaintext values using dec_value

Setting up the cryptographic context

Every PVAC-HFHE application starts by generating cryptographic keys:
#include <pvac/pvac.hpp>
using namespace pvac;

int main() {
    // Initialize parameters, public key, and secret key
    Params prm;
    PubKey pk;
    SecKey sk;
    
    // Generate keys
    keygen(prm, pk, sk);
    
    return 0;
}
The keygen function takes approximately 859ms according to benchmark data. This is a one-time operation per session.

Encrypting values

Use enc_value to encrypt 64-bit unsigned integers:
// Encrypt a single value
uint64_t plaintext = 42;
Cipher ciphertext = enc_value(pk, sk, plaintext);

Example from source

From examples/basic_usage.cpp:59-63:
uint64_t a = 42, b = 17;
Cipher ca = enc_value(pk, sk, a);
Cipher cb = enc_value(pk, sk, b);
CHECK(dec_value(pk, sk, ca).lo == a, "dec(42) = 42");
CHECK(dec_value(pk, sk, cb).lo == b, "dec(17) = 17");

Decrypting values

The dec_value function returns a field element Fp with two components:
Fp result = dec_value(pk, sk, ciphertext);
uint64_t plaintext_value = result.lo;  // Lower 64 bits
Always use the .lo field of the returned Fp struct to extract the uint64 value. The .hi field contains upper bits of the 127-bit field element.

Testing encryption correctness

Verify your encryption pipeline with special values:
// Test zero
Cipher c0 = enc_value(pk, sk, 0);
assert(dec_value(pk, sk, c0).lo == 0);

// Test one
Cipher c1 = enc_value(pk, sk, 1);
assert(dec_value(pk, sk, c1).lo == 1);

// Test large values
uint64_t large = 123456789;
Cipher c_large = enc_value(pk, sk, large);
assert(dec_value(pk, sk, c_large).lo == large);
From examples/basic_usage.cpp:66-70:
Cipher c0 = enc_value(pk, sk, 0);
Cipher c1 = enc_value(pk, sk, 1);
CHECK(dec_value(pk, sk, c0).lo == 0, "dec(0) = 0");
CHECK(dec_value(pk, sk, c1).lo == 1, "dec(1) = 1");

Performance characteristics

Based on benchmark data:
OperationTimeNotes
keygen859msOne-time setup
enc_value84msPer encryption
dec_value13msPer decryption
Fresh ciphertexts are only 42 KB in size, which is 6-85x smaller than comparable RLWE schemes like BFV and CKKS.

Verifying randomness

Encryptions of the same value produce different ciphertexts due to randomization:
Cipher ca1 = enc_value(pk, sk, 100);
Cipher ca2 = enc_value(pk, sk, 100);

// Same plaintext
assert(dec_value(pk, sk, ca1).lo == dec_value(pk, sk, ca2).lo);

// Different ciphertext representation
assert(ca1.E[0].w[0].lo != ca2.E[0].w[0].lo);
From examples/basic_usage.cpp:216-221:
Cipher ca1 = enc_value(pk, sk, 100);
Cipher ca2 = enc_value(pk, sk, 100);
CHECK(dec_value(pk, sk, ca1).lo == dec_value(pk, sk, ca2).lo, "both = 100");
CHECK(ca1.E[0].w[0].lo != ca2.E[0].w[0].lo, "diff rnd");

Next steps

Key generation

Deep dive into parameters and key structures

Arithmetic operations

Learn ct_add, ct_mul, and ct_sub

Build docs developers (and LLMs) love