Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Twirling Keys

Copyright ©️ 2025 CryptoLab, Inc. All rights reserved.

Table of Contents:

Even if the ciphertext—produced by encrypting cleartext with the client’s secret key—is exposed to third parties during transmission to the computation server, there is no risk of compromising sensitive information, as the original data cannot be recovered without the secret key.

However, the ciphertext alone cannot support operations such as multiplication, rotation, or conjugation on the encrypted data. To enable these types of homomorphic operations, specific auxiliary keys corresponding to each operation are required. These are referred to as evaluation keys. Evaluation keys can be safely shared and used publicly, as they do not reveal any information about the underlying secret key.

That said, key management remains a critical component of any homomorphic encryption system. Even when built upon robust cryptographic principles, improper handling or design of key management mechanisms can introduce significant security vulnerabilities.

To mitigate such risks, it is essential to explicitly define the data owner and to strictly ensure that the secret (private) key is never disclosed to any untrusted entity under any circumstances.

In this chapter, we provide a detailed walkthrough on how to use the KeyGenerator and KeyPack classes to generate the necessary public keys—such as encryption and evaluation keys—and how to serialize them for secure transfer.

Week 3 at SuperSecure, Inc.

In the earlier scenario, the computation server was unable to fully leverage the capabilities of the homomorphic encryption (HE) system—at least not until the necessary evaluation keys had been provided.

Now it’s your turn to extend this setup. Your objective is to design a key management workflow in which the data owner (the client) generates all required public keys and securely transfers them to the computation server.

To simplify the implementation, both the client-side and server-side logic will be contained within a single example.

TASK: In this scenario, the client is responsible for generating the appropriate public keys and exporting them—either to files or directly to I/O streams. These exported keys are then transferred to the computation server. On the server side, the keys must be imported from the received sources before they can be used to perform homomorphic operations.

In particular, rotation keys should be generated for all power-of-two indices. This ensures that the server can efficiently perform arbitrary rotation operations during computation.

Code

open code
#include "HEaaN/HEaaN.hpp"

#include <iostream>
#include <filesystem>

int main() {
    const auto preset{HEaaN::ParameterPreset::SD3};
    const auto context = HEaaN::makeContext(preset);

    const auto sk = HEaaN::SecretKey(context);
    const auto pack = HEaaN::KeyPack(context);
    const auto keygen = HEaaN::KeyGenerator(context, sk, pack);

    std::cout << "Generating evaluation keys ... \n";
    keygen.genEncKey();
    keygen.genMultKey();
    keygen.genRotKeyBundle();
    keygen.genConjKey();
    std::cout << "Key generation complete.\n";

    std::string key_dir_path = "./keys";
    if (!std::filesystem::exists(key_dir_path)) {
        std::filesystem::create_directory(key_dir_path);
    }
    sk.save(key_dir_path + "/SecretKey.bin");
    pack.save(key_dir_path);

     /// ---- On the computation server side ----

    auto pack_server = HEaaN::KeyPack(context, key_dir_path);
}

Explanation

Let’s walk through the source code step by step.

KeyPack and KeyGenerator Classes

const auto sk = HEaaN::SecretKey(context);

This line creates a secret key, just as in earlier examples. What’s different here is the purpose: we’re generating evaluation keys from the secret key. In HEaaN, two core classes are involved in this process:

  • KeyPack: a container for storing evaluation keys, with built-in support for saving and loading keys.

  • KeyGenerator: responsible for generating the evaluation keys from the secret key.

Let’s first examine KeyPack.

const auto pack = HEaaN::KeyPack(context);

Here, a KeyPack object is instantiated using a Context object. In homomorphic encryption, all keys and ciphertexts must be bound to a specific cryptographic context, which encapsulates critical scheme parameters. At this point, the pack object is empty and must be populated with evaluation keys.

There is also an alternative constructor:

explicit HEaaN::KeyPack(const Context &context, const std::string &key_dir_path);

This version creates a KeyPack by loading keys from the specified path. We’ll explore this server-side use case in the next chapter.

Additional constructors exist for advanced use cases like sparse secret encapsulation, but those are beyond our current scope and will be covered separately.

Next, let’s turn to KeyGenerator.

const auto keygen = HEaaN::KeyGenerator(context, sk, pack);

This creates a KeyGenerator object, which requires:

  • a Context (FHE parameter configuration),

  • a SecretKey (used to derive evaluation keys),

  • and a KeyPack (to store the generated keys).

Note that passing an existing KeyPack is optional. If not provided, KeyGenerator will internally create one, which can be accessed later using keygen.getKeyPack(). However, for clarity and explicit control, we choose to supply pack directly.

Generating Evaluation Keys

keygen.genEncKey();
keygen.genMultKey();
keygen.genRotKeyBundle();
keygen.genConjKey();

These lines generate the required public and evaluation keys:

  • genEncKey(): creates a public encryption key for encrypting data without the secret key.

  • genMultKey(): generates the multiplication key (also called a relinearization key) used when multiplying ciphertexts by another ciphertexts.

  • genRotKeyBundle(): automatically generates rotation keys for all power-of-two indices, enabling efficient vector rotations.

  • genConjKey(): generates the conjugation key, which supports complex conjugation operations( $\overline{a+bi}= a-bi$ ) in CKKS.

About Rotation Keys

Rotation in the CKKS scheme refers to cyclically shifting the slots of a cleartext vector in the encrypted state. For example, a left rotation by $k$ would map: $$ (x_0, x_1, \cdots, x_{n-1}) \mapsto (x_k, x_{k+1}, \cdots, x_{n-1}, x_{0} \cdots, x_{k-1}) $$ This operation is essential for tasks like matrix–vector multiplication, convolutions, and other linear algebraic computations.

You can generate rotation keys for specific indices using:

void KeyGenerator::genLeftRotKey(u64 rot) const;
void KeyGenerator::genRightRotKey(u64 rot) const;

However, calling genRotKeyBundle() simplifies the process by generating both left and right rotation keys for all power-of-two indices. For example, using the SD3 preset—which provides up to \(2^{12}=4096\) slots—genRotKeyBundle() will create rotation keys for indices \(1, 2, 4, 8, \cdots, 2048\)1.

Save and Load

sk.save(key_dir_path + "/SecretKey.bin");
pack.save(key_dir_path);
  • sk.save(...): saves the secret key to a file or stream (must remain private).

  • pack.save(...): saves the key pack (including encryption and evaluation keys) to the specified directory or stream.

HEaaN provides flexible serialization support for saving keys and other data containers. Secret keys, key packs, and related objects such as ciphertext and plaintext can be saved either to files or directly to output streams (e.g., std::ostream). Corresponding load functions are available for deserialization. Notably, when constructing a KeyPack with a path argument, the stored keys are automatically loaded from the specified directory. This design simplifies key management and enables seamless integration into various I/O workflows. However, if the specified directory does not contain all the required key files in the expected format, a runtime error occurs. It is therefore important to ensure that the key files are properly saved and verified before attempting to load them via path-based initialization.

Remark : Encryption Key

Consider a scenario where the server is the data owner and wants to encrypt incoming client queries using its own secret key to protect data during transmission. Naturally, the server should never expose its secret key.

Instead, the server can provide its public encryption key to clients. This allows clients to perform encryption securely, while the server retains sole authority to decrypt using its private key.


Copyright ©️ 2025 CryptoLab, Inc. All rights reserved.

No portion of this book may be reproduced in any form without written permission from CryptoLab, Inc.


  1. Note that the left and right rotation with index 4096 gives the original ciphertext.