準同型暗号化ライブラリHElibのインストールから実行方法まで

# 0. はじめに

本記事では準同型暗号化ライブラリHElibの

1. インストール方法
2. サンプルを用いた動作確認
3. HElibライブラリを用いたコードの実行方法

について説明します。最終的に、HElibを使ったオリジナルのコードを実行できるようになることを目的とします。

なお、以降の文章は**2022/09/04**時点の公式ドキュメント[INSTALL.md](https://github.com/homenc/HElib/blob/master/INSTALL.md)、[examples/READMEmd](https://github.com/homenc/HElib/blob/master/examples/README.md)に基づいて書かれています。そのため、今後のバージョンアップによりインストール方法などが変更される可能性があります。その場合はこれらのドキュメントの参照をお願いします。

## 0.1 環境

著者が使用した環境はWindows10とUbuntu(WSL2)です。以降で説明するインストール方法などはLinuxOSでもmasOSでも共通ですが、macOSは実行できるかどうかの確認がとれていませんので、ご了承ください。

| ソフトウェア名 | バージョン |
|:-----------:|------------:|
| Windows 10 | 21H1 |
| Ubuntu(WSL2) | 20.04.4 LTS |

## 0.2 事前準備

事前準備として、HElibを使う際に必要となる各ソフトウェアについて説明します。

### LinuxOS

LinuxOSで必要となるバージョンについて次の表に示します。

| ソフトウェア名 | バージョン |
|:-----------:|------------:|
| pthreads | インストールされていてばOK |
| git | 2.27 |
| GNU make | 4.2 |
| g++ | 9.3.0 |
| cmake | 3.16 |
| m4 | 1.4.16 |
| patchelf | 0.9 |

次にWSL2上のUbuntuについて説明します。

まず初めに、初期状態のUbuntuではgitをバージョンアップさせる必要があります。この方法については@cointoss1973さんの記事を参考に行います。

https://qiita.com/cointoss1973/items/1c01837e65b937fc0761

```
sudo add-apt-repository ppa:git-core/ppa
sudo apt update
sudo apt upgrade
```

次に、cmake, g++をインストールします。コマンドは次の通りです。

```
sudo apt install cmake
sudo apt install g++
```

次に、m4と呼ばれるC言語などのプリプロセッサとして使われるコマンドをインストールします。この方法は次の記事をもとに実行します。

https://installati.one/ubuntu/20.04/m4/

```
sudo apt-get update
sudo apt-get -y install m4
```

最後に、patchelfというビルドの際に必要となるソフトウェアをインストールします。これは次の記事を参考に行います。

https://zoomadmin.com/HowToInstall/UbuntuPackage/patchelf

```
sudo apt-get update -y
sudo apt-get install -y patchelf
```

他のソフトウェアについては、初期状態でインストール済みかつバージョンを満たしているため問題ありません



### macOS

LinuxOSで必要となるバージョンについて次の表に示します。

| ソフトウェア名 | バージョン |
|:-----------:|------------:|
| pthreads | インストールされていてばOK |
| git | 2.27 |
| Apple clang | 12.0.0 |
| Xcode Command Line Tools | `xcode-select --install`を入力しインストール |
| cmake | 3.20 |
| GNU make | 3.81 |
| m4 | 1.4.16 |
| patchelf | 0.9 |

macOSについては未検証のため、使用している方はご確認をお願いします。

# 1. インストール方法

<ol><li> まず初めにHElibライブラリのインストール先となるディレクトリを作成します。作成場所はホームディレクトリとします。

```
cd
mkdir helib_install
```

<li>次にHElibのリポジトリをクローンします。これも同じくホームディレクトリに作ります。

```
git clone https://github.com/homenc/HElib.git
```

ここまでで、ディレクトリ構成は次のようになります。

```:ディレクトリ構成
~/
├── HElib
└── helib_install
```

<li> リポジトリに移動し、buildディレクトリを作成します。

```
cd HElib
mkdir build
```

<li> buildディレクトリに移動し、cmakeコマンドを実行します。このとき、「-DCMAKE_INSTALL_PREFIX」オプションを、先ほど作成した「helib_install」のパスにします。

```
cd build
cmake -DPACKAGE_BUILD=ON -DCMAKE_INSTALL_PREFIX=~/helib_install ..
```

<li> 次にmakeコマンドを実行します。

```
make
```

<li> 最後にmake installコマンドを実行します。このコマンドを実行することで、「~/helib_install」にHElibに関するライブラリや依存ファイルが作成されます。

```
make install
```

</ol>

以上でインストールは完了です。次にサンプルを用いた動作確認について説明します。

# 2. サンプルを用いた動作確認

<ol><li> 初めにHElibリポジトリのexampleディレクトリに移動し、buildディレクトリを作成します。

```
cd ~/HElib/examples/
mkdir build
```

<li> buildディレクトリに移動し、cmakeコマンドを実行します。このとき、-Dhelib_DIRオプションにはインストール時に作成した「helib_install」ディレクトリのパスを指定します。

```
cd build
cmake -Dhelib_DIR=~/helib_install/helib_pack/share/cmake/helib ..
```

<li> makeコマンドを実行します。

```
make
```

<li> すると、binディレクトリが作成されます。このディレクトリには7つCKKS方式のサンプルと、3つのBGV方式のサンプルがあります。それではこのbinディレクトリに移動し、試しに1つファイルを実行します。実行に1分ほどがかかるのでご注意ください。

```
cd bin
./BGV_binary_arithmetic
```

BGV_binary_arithmeticを実行すると、3つの変数a, b, cの準同型加算・準同型乗算を行います。問題なく実行することができれば、HElibライブラリのインストールは成功です。また、これらの実行ファイルのソースコードは、CKKS方式は「HElib/example/tutorial/」、BGV方式は「HElib/example/」に置かれています。

</ol>

以上で動作確認は完了です。次にHElibライブラリを用いたコードの実行方法について説明します。

# 3. HElibライブラリを用いたコードの実行方法

<ol><li> 初めに、ホームディレクトリにhelib_codeディレクトリを作成し、移動します。

```
cd
mkdir helib_code
cd helib_code
```

<li> HElibライブラリを使ったソースコードを作成します。main.cppを作成し、次のコードをコピペしてください。

```c++:main.cpp
#include <iostream>
#include <helib/helib.h>
using namespace std;

int main() {
	// 前準備として、セットアップに必要な各種パラメータの設定をします。
	unsigned long p = 4999;
	unsigned long m = 32109;
	unsigned long r = 1;
	unsigned long bits = 500;
	unsigned long c = 2;

    // 設定後、各種パラメータをもとにセットアップを行います。
	helib::Context context = helib::ContextBuilder<helib::BGV>()
		.m(m)
		.p(p)
		.r(r)
		.bits(bits)
		.c(c)
		.build();
	
	// 秘密鍵の作成
	helib::SecKey secret_key(context); // 秘密鍵を定義し、各種パラメータを受け取る
	secret_key.GenSecKey(); // 秘密鍵を生成する
	helib::addSome1DMatrices(secret_key); // Key switchingの行列を作成します。これは準同型乗算後のノイズを取り除くために使われます。

	// 公開鍵の設定
	const helib::PubKey& public_key = secret_key; // SecKeyはPubKeyのサブクラスのため、「public_key = secret_key」と記述します。
	const helib::EncryptedArray& ea = context.getEA(); // 各種パラメータの暗号化行列を作成します。

	// 平文の作成
    // HElibではSIMDと呼ばれる、複数の平文を1つにまとめて演算ごとに並列に実行できる機能が備わっています。
    // そのため、1つの平文はptxt[0], ptxt[1], ... というふうに配列となっており、複数の平文を格納することができます。
    // 今回はptxt[0] = 0, ptxt[1] = 1, ..., ptxt[n] = nとし、各要素に0からnまでの値を入れます。
	helib::Ptxt<helib::BGV> ptxt(context);
	
	for (int i = 0; i < ptxt.size(); ++i) {
		ptxt[i] = i;
	}

     // 演算前の平文を出力します。
	cout << ptxt << endl;

	// 暗号文の作成
	helib::Ctxt ctxt(public_key); // 暗号文を作成します。
	public_key.Encrypt(ctxt, ptxt); // 平文ptxtを暗号化します。

	// 演算(自乗)
	ctxt.multiplyBy(ctxt); // ctxt *= ctxt を実行しています。

	// 復号
	helib::Ptxt<helib::BGV> result(context); // 演算後の値を格納する平文を作成します。
	secret_key.Decrypt(result, ctxt); // 自乗の結果を復号します。
	cout << result << endl;
}
```

このプログラムではまず初めにn個分の平文「$0, 1, \cdots , n$」を暗号化します。次にHelibライブラリの準同型乗算を使い、暗号文に対して自乗します。最後に復号し、演算結果の「$0^2, 1^2, \cdots, n^2$」を出力します。

<li> main.cppの実行ファイルを作成するため、CMakeLists.txtを作成します。その後、次のコードをコピペしてください。

```cmake:CMakeLists.txt
set(CMAKE_CXX_STANDARD 17)
find_package(helib)
add_executable(main main.cpp)
target_link_libraries(main helib)
```

このファイルの1行目では、コンパイル時にc++17を使用するよう設定しています。2行目のfind_package(helib)」ではHElibライブラリのヘッダーファイルをインクルードします。3行目の「add_executable(main main.cpp)」ではビルドの対象であるmain.cppを指定し、実行ファイルを定義します。最後の「target_link_libraries(main helib)」では実行ファイルを作成する際に、HElibライブラリとリンクするようにしています。

<li> cmakeコマンドを実行します。

```
cmake -S . -B build -Dhelib_DIR=~/helib_install/helib_pack/share/cmake/helib
```

<li> 最後にbuildディレクトリに移動してmakeコマンドでビルドし、作成されたmainファイルを実行します。実行完了にはしばらく時間がかかります。

```
cd build
make
./main
```

<li> 出力結果が次のようになればOKです。0の2乗から始まり、n = 23の2乗の529が計算できていることが分かります。

```
{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23]]},"serializationVersion":"0.0.1","type":"Ptxt"}
{"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0],[1],[4],[9],[16],[25],[36],[49],[64],[81],[100],[121],[144],[169],[196],[225],[256],[289],[324],[361],[400],[441],[484],[529]]},"serializationVersion":"0.0.1","type":"Ptxt"}
```

</ol>

以上でHElibライブラリを用いたコードの実行方法は完了です。

# 4. おわりに

ここまでくれば、あとはmain.cppをさらにいじったりして、いろいろな処理を実行することができます。

HElibライブラリで提供されている関数等については、「HElib/example/tutorial」にあるCKKS方式のソースコードや「Helib/example/BGV_*」にあるBGV方式のソースコードに書かれたコメントを参考にすると分かりやすいです。本記事の`main.cpp`についても、サンプルの「[BGV_packed_arithmetic.cpp](https://github.com/homenc/HElib/blob/master/examples/BGV_packed_arithmetic/BGV_packed_arithmetic.cpp)」をもとに記述しました。

最後になりますが、本記事の情報が役に立てれば幸いです。なんらかの理由でうまくいかなかった場合は、コメントにて教えていただけるとありがたいです。