20. PhantomFHE 库的安装与使用

前言

在以往的研究中,我通常使用 SEAL 库进行同态加密编码。为了利用 CUDA 加速同态计算过程,本文选择在 WSL2 环境下安装并配置 PhantomFHE。


文章目录

  • 前言
  • 一、PhantomFHE
    • [1. 基本依赖](#1. 基本依赖)
    • [2. 下载 PhantomFHE 库](#2. 下载 PhantomFHE 库)
    • [3. 链接 PhantomFHE 库](#3. 链接 PhantomFHE 库)
  • [二、基于 PhantomFHE 的多项式运算实现](#二、基于 PhantomFHE 的多项式运算实现)
    • [1. 导入依赖](#1. 导入依赖)
    • [2. 参数打印函数](#2. 参数打印函数)
    • [3. 编写代码](#3. 编写代码)
    • [4. 代码执行](#4. 代码执行)
    • Note:加密解密

一、PhantomFHE

PhantomFHE 库的源代码位于 https://github.com/encryptorion-lab/phantom-fhe。但是,如果想把 PhantomFHE 作为插件集成到自己的项目中,可以参考 https://github.com/FLL-Lab/Euston 中的实现方式。下面具体介绍如何以插件的方式使用 PhantomFHE。

1. 基本依赖

bash 复制代码
 sudo apt-get install clang
 sudo apt-get install cmake
 sudo apt-get install gcc
 sudo apt-get install g++
 sudo apt-get install git

2. 下载 PhantomFHE 库

  • 下载并解压 Euston 压缩包,网址:https://github.com/FLL-Lab/Euston 。其中,将文件目录中 Euston/Euston-GPU/thirdparty 这个文件夹整个复制到自己的项目下。比如,我直接将其复制到我的 LearnPhantom 项目文件夹下:

3. 链接 PhantomFHE 库

配置本项目的 CMakeLists.txt 文件。这里注意,Euston 项目已经为我们提供了 PhantomFHE 作为插件的 CMakeLists.txt 配置文件,位于 thirdparty/phantom-fhe/CMakeLists.txt不要 修改此文件。以我的项目为例,只需在自己的项目主 CMakeLists.txt 文件(例如 LearnPhantom/CMakeLists.txt)中链接 PhantomFHE 库即可:

  • 指定CMake版本
cpp 复制代码
cmake_minimum_required(VERSION 3.28)
  • 定义项目名称和使用语言:
cpp 复制代码
project(LearnPhantom LANGUAGES CXX CUDA)
  • 设置 C++ 和 CUDA 标准。这里我使用的标准是 C++17,且要求项目必须使用:
cpp 复制代码
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
  • 配置 CUDA:
cpp 复制代码
#允许 CUDA 代码被拆分到多个 .cu 文件中分别编译,然后再链接
set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)

# 让 CMake 自动检测当前机器的 NVIDIA GPU 架构,并为对应架构生成 CUDA 代码
set(CMAKE_CUDA_ARCHITECTURES native)
  • 链接 PhantomFHE 库:
cpp 复制代码
# 设置 PhantomFHE 根目录变量
set(PHANTOM_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/phantom-fhe)

# 全局加入 PhantomFHE 的头文件目录
include_directories(
        ${PHANTOM_ROOT}/include
)

#把 PhantomFHE 作为子项目加入
add_subdirectory(${PHANTOM_ROOT})

add_executable(LearnPhantom main.cu)

# 给 LearnPhantom 程序单独加入 PhantomFHE 头文件目录
target_include_directories(LearnPhantom PRIVATE
        ${PHANTOM_ROOT}/include
)

target_link_libraries(LearnPhantom PRIVATE Phantom)

最后的配置如下:


二、基于 PhantomFHE 的多项式运算实现

我们使用 PhantomFHE 库的 CKKS 方案,同态地加密计算下面这个方程:
( 1.23 × 4.56 ) 2 + ( 7.89 × 10.11 ) 2 + ( 12.13 × 14.15 ) 2 (1.23 \times 4.56)^2+(7.89 \times 10.11)^2+(12.13 \times 14.15)^2 (1.23×4.56)2+(7.89×10.11)2+(12.13×14.15)2

1. 导入依赖

main.cu 文件中导入必要的依赖库。这里需要注意的是,.cu 文件是 NVIDIA CUDA 程序的源代码文件,用于编写包含 CUDA 相关代码的程序,因此不要将其替换为普通的 .cpp 文件。

cpp 复制代码
#include <iostream>
#include <random>
#include "phantom.h"

using namespace std;
using namespace phantom;
using namespace phantom::arith;
using namespace phantom::util;

2. 参数打印函数

为了打印一些参数信息,我复用了原代码 phantom-fhe 中的两个辅助函数。使用时,直接将这两个函数直接复制到 main 函数之前:

  • 打印密文参数:
cpp 复制代码
void print_parameters(const PhantomContext &context) {
    auto &context_data = context.get_context_data(0);
    string scheme_name;
    switch (context_data.parms().scheme()) {
    case phantom::scheme_type::bfv:
        scheme_name = "BFV";
        break;
    case phantom::scheme_type::ckks:
        scheme_name = "CKKS";
        break;
    case phantom::scheme_type::bgv:
        scheme_name = "BGV";
        break;
    default:
        throw invalid_argument("unsupported scheme");
    }
    cout << "/" << endl;
    cout << "| Encryption parameters :" << endl;
    cout << "|   scheme: " << scheme_name << endl;
    cout << "|   poly_modulus_degree: " << context_data.parms().poly_modulus_degree() << endl;
    cout << "|   coeff_modulus size: ";
    cout << context_data.total_coeff_modulus_bit_count() << " (";
    auto parms = context_data.parms();
    auto &coeff_modulus = parms.coeff_modulus();
    size_t coeff_modulus_size = coeff_modulus.size();
    for (size_t i = 0; i < coeff_modulus_size - 1; i++) {
        cout << coeff_modulus[i].bit_count() << " + ";
    }
    cout << coeff_modulus.back().bit_count();
    cout << ") bits" << endl;

    if (context_data.parms().scheme() == phantom::scheme_type::bfv) {
        cout << "|   plain_modulus: " << context_data.parms().plain_modulus().value() << endl;
    }

    cout << "\\" << endl;
}
  • 打印明文向量:
cpp 复制代码
template<typename T>
void print_vector(vector<T> vec, size_t print_size = 4, int prec = 3) {
    ios old_fmt(nullptr);
    old_fmt.copyfmt(cout);

    size_t slot_count = vec.size();

    cout << fixed << setprecision(prec);
    cout << endl;
    if (slot_count <= 2 * print_size) {
        cout << "    [";
        for (size_t i = 0; i < slot_count; i++) {
            cout << " " << vec[i] << ((i != slot_count - 1) ? "," : " ]\n");
        }
    }
    else {
        vec.resize(max(vec.size(), 2 * print_size));
        cout << "    [";
        for (size_t i = 0; i < print_size; i++) {
            cout << " " << vec[i] << ",";
        }
        if (vec.size() > 2 * print_size) {
            cout << " ...,";
        }
        for (size_t i = slot_count - print_size; i < slot_count; i++) {
            cout << " " << vec[i] << ((i != slot_count - 1) ? "," : " ]\n");
        }
    }
    cout << endl;

    cout.copyfmt(old_fmt);
}

3. 编写代码

  • 参数设置
cpp 复制代码
 	int alpha = 2;
    size_t poly_modulus_degree = 1 << 15;
    double scale = pow(2.0, 40);
    vector<int> modulus_chain = {60, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 60, 60};
  • 创建对象
cpp 复制代码
 	EncryptionParameters parms(scheme_type::ckks); // 参数对象
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, modulus_chain));
    parms.set_special_modulus_size(alpha); // 拿出 modulus_chain 中最后 alpha 个素数作为 key-switching 的模数,其余素数用于普通密文计算

    PhantomContext context(parms); // 上下文对象
    PhantomSecretKey secret_key(context); // 私钥
    PhantomPublicKey public_key = secret_key.gen_publickey(context); // 公钥
    PhantomRelinKey relin_key = secret_key.gen_relinkey(context); // 重线性化密钥
    PhantomGaloisKey galois_key = secret_key.create_galois_keys(context); // 旋转密钥
    PhantomCKKSEncoder encoder(context); // 编码器
    size_t slot_count = encoder.slot_count();
    cout << "密文参数: " << endl;
    print_parameters(context);
  • 设置明文
cpp 复制代码
	vector<double> input1(slot_count, 0.0);
    input1[0] = 1.23;
    input1[1] = 7.89;
    input1[2] = 12.13;
    vector<double> input2(slot_count, 0.0);
    input2[0] = 4.56;
    input2[1] = 10.11;
    input2[2] = 14.15;
  • 编码与加密
cpp 复制代码
 	PhantomPlaintext plain_x1, plain_x2, plain_y; // 编码
    encoder.encode(context, input1, scale, plain_x1);
    encoder.encode(context, input2, scale, plain_x2);

    PhantomCiphertext cipher_x1, cipher_x2; // 加密
    public_key.encrypt_asymmetric(context, plain_x1, cipher_x1);
    public_key.encrypt_asymmetric(context, plain_x2, cipher_x2);
  • 同态计算:其实 PhantomFHE 中的函数与 SEAL 差不多,只不过 SEAL 中要用 evaluator 对象调用这些函数,PhantomFHE 中直接调用即可。
cpp 复制代码
	PhantomCiphertext mult = multiply(context, cipher_x1, cipher_x2); // 同态乘法
    relinearize_inplace(context, mult, relin_key); // 重线性化
    rescale_to_next_inplace(context, mult); // 重缩放

    multiply_inplace(context, mult, mult);
    relinearize_inplace(context, mult, relin_key);
    rescale_to_next_inplace(context, mult);

    PhantomCiphertext total = mult;
    for (int i = 1; i < 3; ++i) {
		    PhantomCiphertext tmp = rotate_vector(context, mult, i, galois_key); // 旋转
		    add_inplace(context, total, tmp); // 相加
	    }
  • 解密与解码:
cpp 复制代码
	cout << "同态计算输出: " << endl;
    secret_key.decrypt(context, total, plain_y);
    vector<double> result;
    encoder.decode(context, plain_y, result);
    print_vector(result, 3, 3);

最后,所有代码如下:

cpp 复制代码
#include <iostream>
#include <random>
#include "phantom.h"

using namespace std;
using namespace phantom;
using namespace phantom::arith;
using namespace phantom::util;

void print_parameters(const PhantomContext &context) {
    auto &context_data = context.get_context_data(0);
    string scheme_name;
    switch (context_data.parms().scheme()) {
    case phantom::scheme_type::bfv:
        scheme_name = "BFV";
        break;
    case phantom::scheme_type::ckks:
        scheme_name = "CKKS";
        break;
    case phantom::scheme_type::bgv:
        scheme_name = "BGV";
        break;
    default:
        throw invalid_argument("unsupported scheme");
    }
    cout << "/" << endl;
    cout << "| Encryption parameters :" << endl;
    cout << "|   scheme: " << scheme_name << endl;
    cout << "|   poly_modulus_degree: " << context_data.parms().poly_modulus_degree() << endl;
    cout << "|   coeff_modulus size: ";
    cout << context_data.total_coeff_modulus_bit_count() << " (";
    auto parms = context_data.parms();
    auto &coeff_modulus = parms.coeff_modulus();
    size_t coeff_modulus_size = coeff_modulus.size();
    for (size_t i = 0; i < coeff_modulus_size - 1; i++) {
        cout << coeff_modulus[i].bit_count() << " + ";
    }
    cout << coeff_modulus.back().bit_count();
    cout << ") bits" << endl;

    if (context_data.parms().scheme() == phantom::scheme_type::bfv) {
        cout << "|   plain_modulus: " << context_data.parms().plain_modulus().value() << endl;
    }

    cout << "\\" << endl;
}


template<typename T>
void print_vector(vector<T> vec, size_t print_size = 4, int prec = 3) {
    ios old_fmt(nullptr);
    old_fmt.copyfmt(cout);

    size_t slot_count = vec.size();

    cout << fixed << setprecision(prec);
    cout << endl;
    if (slot_count <= 2 * print_size) {
        cout << "    [";
        for (size_t i = 0; i < slot_count; i++) {
            cout << " " << vec[i] << ((i != slot_count - 1) ? "," : " ]\n");
        }
    }
    else {
        vec.resize(max(vec.size(), 2 * print_size));
        cout << "    [";
        for (size_t i = 0; i < print_size; i++) {
            cout << " " << vec[i] << ",";
        }
        if (vec.size() > 2 * print_size) {
            cout << " ...,";
        }
        for (size_t i = slot_count - print_size; i < slot_count; i++) {
            cout << " " << vec[i] << ((i != slot_count - 1) ? "," : " ]\n");
        }
    }
    cout << endl;

    cout.copyfmt(old_fmt);
}

int main()
{
    // 设置密文参数
    int alpha = 2;
    size_t poly_modulus_degree = 1 << 15;
    double scale = pow(2.0, 40);
    vector<int> modulus_chain = {60, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 60, 60};

    // 创建对象
    EncryptionParameters parms(scheme_type::ckks); // 参数对象
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, modulus_chain));
    parms.set_special_modulus_size(alpha); // 拿出 modulus_chain 中最后 alpha 个素数作为 key-switching 的模数,其余素数用于普通密文计算

    PhantomContext context(parms); // 上下文对象
    PhantomSecretKey secret_key(context); // 私钥
    PhantomPublicKey public_key = secret_key.gen_publickey(context); // 公钥
    PhantomRelinKey relin_key = secret_key.gen_relinkey(context); // 重线性化密钥
    PhantomGaloisKey galois_key = secret_key.create_galois_keys(context); // 旋转密钥
    PhantomCKKSEncoder encoder(context); // 编码器
    cout << "密文参数: " << endl;
    print_parameters(context);

    // 设置消息
    size_t slot_count = encoder.slot_count();


    // 同态计算
    vector<double> input1(slot_count, 0.0);
    input1[0] = 1.23;
    input1[1] = 7.89;
    input1[2] = 12.13;
    vector<double> input2(slot_count, 0.0);
    input2[0] = 4.56;
    input2[1] = 10.11;
    input2[2] = 14.15;

    PhantomPlaintext plain_x1, plain_x2, plain_y;
    encoder.encode(context, input1, scale, plain_x1);
    encoder.encode(context, input2, scale, plain_x2);

    PhantomCiphertext cipher_x1, cipher_x2;
    public_key.encrypt_asymmetric(context, plain_x1, cipher_x1);
    public_key.encrypt_asymmetric(context, plain_x2, cipher_x2);

    PhantomCiphertext mult = multiply(context, cipher_x1, cipher_x2); // 同态乘法
    relinearize_inplace(context, mult, relin_key); // 重线性化
    rescale_to_next_inplace(context, mult); // 重缩放

    multiply_inplace(context, mult, mult);
    relinearize_inplace(context, mult, relin_key);
    rescale_to_next_inplace(context, mult);

    PhantomCiphertext total = mult;
    for (int i = 1; i < 3; ++i) {
		    PhantomCiphertext tmp = rotate_vector(context, mult, i, galois_key); // 旋转
		    add_inplace(context, total, tmp); // 相加
	    }

    cout << "同态计算输出: " << endl;
    secret_key.decrypt(context, total, plain_y);
    vector<double> result;
    encoder.decode(context, plain_y, result);
    print_vector(result, 3, 3);
    return 0;
}

4. 代码执行

  • 构建项目:在 WSL 终端中 cd 到项目文件夹,并执行以下命令
bash 复制代码
 cmake -S . -B build
 cmake --build build -j
  • 运行项目
bash 复制代码
./build/LearnPhantom
  • 执行结果:注意,我们在同态计算最后一步旋转得到的是一个向量,但只有第0位装载了正确结果。

Note:加密解密

注意,在我给的代码中,使用了非对称加密,但是 PhantomFHE 库还提供了对称加密的实现,二者的主要区别在于加解密所使用的密钥不同,下面举例说明:

  • 设置消息:
cpp 复制代码
	size_t slot_count = encoder.slot_count();
    vector<cuDoubleComplex> input(slot_count);
    double rand_real,rand_imag;
    for (size_t i = 0; i < slot_count; i++) {
        rand_real = (double) rand() / RAND_MAX;
        rand_imag = (double) rand() / RAND_MAX;
        input[i] = make_cuDoubleComplex(rand_real, rand_imag);
    }
    cout << "输入消息: " << endl;
    print_vector(input, 3, 3);
  • 对称加密:用私钥加解密
cpp 复制代码
	PhantomPlaintext x_symmetric_plain;
    encoder.encode(context, input, scale, x_symmetric_plain, 1); // 编码:上下文、要编码的消息、缩放因子、输出的明文、modulus chain level

    PhantomCiphertext x_symmetric_cipher;
    secret_key.encrypt_symmetric(context, x_symmetric_plain, x_symmetric_cipher); // 私钥加密

    secret_key.decrypt(context, x_symmetric_cipher, x_symmetric_plain); // 私钥解密

    vector<cuDoubleComplex> result_symmetric;
    encoder.decode(context, x_symmetric_plain, result_symmetric); // 解码
  • 非对称加密:公钥加密,私钥解密
cpp 复制代码
	PhantomPlaintext x_asymmetric_plain;
    encoder.encode(context, input, scale, x_asymmetric_plain, 1); // 编码

    PhantomCiphertext x_asymmetric_cipher;
    public_key.encrypt_asymmetric(context, x_asymmetric_plain, x_asymmetric_cipher);  // 公钥加密

    secret_key.decrypt(context, x_asymmetric_cipher, x_asymmetric_plain); // 私钥解密

    vector<cuDoubleComplex> result_asymmetric;
    encoder.decode(context, x_asymmetric_plain, result_asymmetric); // 解码

不同加密方式下的结果输出一致:

相关推荐
叁沐9 个月前
论文阅读-PANTHER: Private Approximate Nearest Neighbor Search in the Single Server Setting
隐私保护机器学习
叁沐10 个月前
论文阅读-Cerebro: A Platform for Multi-Party Cryptographic Collaborative Learning
隐私保护机器学习
叁沐1 年前
MD-ML: Super Fast Privacy-Preserving Machine Learning for Malicious Security with a Dishonest Majority
隐私保护机器学习