PALISADEで準同型暗号を試す

EAGLYSアドベントカレンダー２３日目の記事です。この記事では格子暗号ライブラリ「PALISADE」を用いて、準同型暗号を試します。準同型暗号についてはこちらなどを参考になります。https://qiita.com/herumi/items/d8645efe2cc5be2e7ee3

#PALISADEとは？

https://palisade-crypto.org/software-library/

で公開されているライブラリ。CKKSやBGVなどの格子スキームに加え、IDベース暗号や属性ベース暗号を備えています。FHEを使ったマルチパーティ計算用のしきい値暗号やビット演算を高速に行えるTFHEもサポートされています。

PALISADEを使った事例として、次の論文があります。
http://ogl.is.ocha.ac.jp/Publications/paper2019/dicomo2019_yamada.pdf


#インストール方法

以下がインストールされていることは確認しておきます。

```
$ sudo apt install g++ autoconf build-essential cmake
```

あとは、PLISADEのセットアップ手順に書いてあるとおりのコマンドを実行すれば、ビルドが完了します。ビルドには最初、３０分弱かかった。

```
$ git clone https://gitlab.com/palisade/palisade-development.git
$ git submodule sync --recursive
$ git submodule update --init  --recursive
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
```

これらが終わったら、以下のテストを実行します。

```
$ make testall
```

セットアップ完了です。

あとは`/build/bin/`ディレクトリ下にあるバイナリを実行することで様々なテストを実行できます。もし自分でテストを作りたければ、`/src`下にあるディレクトリ(`pke`とか`binfhe`)の`examples`下にファイルを配置します。では、使い方とともに実例を紹介しましょう。

#　基本的な使い方

クラス`CryｐtoContext`を指定する必要があります。これは

- 格子点の表現方法
- 暗号化方式
- 方式に必要なパラメタ

を指定することで初期化ができます。

PALISADEではすべての機能が`CryptoContext`から生えたメソッドに定義されていて、それを通した操作以外は禁止されています。また、異なるCryptoContextで作られた暗号文で計算ができません。

1. 格子点の表現方法。　表現に使う多項式は`Poly`と`NativePoly`と`DCRTPoly`が存在します。DCRTというのは、double-CRTのことで、大きなモジュラスの多項式環を小さなモジュラスの多項式環の直積に分解することで計算量を削減したもののようです。
2. 暗号化方式　幅広い方式が利用できます。詳細はPALISADEのリポジトリにあるwikiを確認するとよいです。https://gitlab.com/palisade/palisade-development/-/wikis/Home
3. 方式に必要なパラメタ　方式によって異なります。`example`ディレクトリにあるファイルから指定するべきパラメタの詳細は確認できます。


`CryptoContext`では`Enable`を使って、オブジェクトの各操作を可能にします。

```c++
cc->Enable(ENCRYPTION)
```

- ENCRYPTION 鍵生成、暗号化、復号の暗号アルゴリズムをすべて有効にする
- PRE プロキシ再暗号化を有効にする
- SHE somewhat準同型操作を有効にする
- MULTIPARTY しきい値FHEを有効にする

例えば、SHEを有効にしないと、、、

```
EvalMultKeyGen operation has not been enabled
```

relinearization keyを生成した時点で怒られます。relinearizationとは乗算によって次元が増大した暗号文を元のサイズに戻すのに使いますが、そもそもSHEを有効にしないと乗算ができないですね。

# BFVtranの例

`src/pke/examples/simple_integer.cpp`で使われているので、これを参考に簡単に加算と乗算を試してみます。

- 参考にしたソース

https://gitlab.com/palisade/palisade-development/-/blob/master/src/pke/examples/simple-integers.cpp


```cpp:test_bfv.cpp
#include "palisade.h"

using namespace lbcrypto;
using namespace std;

int main(){
    int plaintextModulus = 65537;
    double sigma = 3.2;
    SecurityLevel securityLevel = HEStd_128_classic;
    uint32_t depth = 2;

    CryptoContext<DCRTPoly> cc =
        CryptoContextFactory<DCRTPoly>::genCryptoContextBFVrns(
        plaintextModulus, securityLevel, sigma, 0, depth, 0, OPTIMIZED);
        
    cc->Enable(ENCRYPTION);
    cc->Enable(SHE);

    auto keys = cc->KeyGen();
    cc->EvalMultKeyGen(keys.secretKey);

    vector<int64_t> x1 = {1,2,3,4,5,6,7,8};
    vector<int64_t> x2 = {8,7,6,5,4,3,2,1};   

    Plaintext ptxt1 = cc->MakePackedPlaintext(x1);
    Plaintext ptxt2 = cc->MakePackedPlaintext(x2);

    auto c1 = cc->Encrypt(keys.publicKey, ptxt1);
    auto c2 = cc->Encrypt(keys.publicKey, ptxt2);

    auto cAdd = cc->EvalAdd(c1, c2);
    auto cMul = cc->EvalMult(c1, c2);

    Plaintext resultAdd;
    Plaintext resultMul;

    cout << "Plaintext x1: " << x1 << endl;
    cout << "Plaintext x2: " << x2 << endl;

    cc->Decrypt(keys.secretKey, cAdd, &resultAdd);
    resultAdd->SetLength(x1.size());

    cc->Decrypt(keys.secretKey, cMul, &resultMul);
    resultMul->SetLength(x1.size());

    cout << "x1 + x2 = " << resultAdd << endl;
    cout << "x1 * x2 = " << resultMul << endl;
}
```

まずは`CryptoContext`を指定します。


```c++
int plaintextModulus = 65537;
double sigma = 3.2;
SecurityLevel securityLevel = HEStd_128_classic;
uint32_t depth = 2;

CryptoContext<DCRTPoly> cc =
    CryptoContextFactory<DCRTPoly>::genCryptoContextBFVrns(
    plaintextModulus, securityLevel, sigma, 0, depth, 0, OPTIMIZED);
```

次に機能を有効にする。ここでは暗号化と準同型計算を有効にしています。

```c++
cc->Enable(ENCRYPTION);
cc->Enable(SHE);
```

鍵を生成します。一行目では公開鍵、秘密鍵の鍵ペアを生成します。二行目ではrelinearization keyを生成します。relinearization keyがないと、乗算ができません。

```c++
auto keys = cc->KeyGen();
cc->EvalMultKeyGen(keys.secretKey);
```

平文をベクトルで宣言し、それをエンコードします。

```c++
vector<int64_t> x1 = {1,2,3,4,5,6,7,8};
vector<int64_t> x2 = {8,7,6,5,4,3,2,1};   
Plaintext ptxt1 = cc->MakePackedPlaintext(x1);
Plaintext ptxt2 = cc->MakePackedPlaintext(x2);
```

暗号化して加算します。

```c++
auto c1 = cc->Encrypt(keys.publicKey, ptxt1);
auto c2 = cc->Encrypt(keys.publicKey, ptxt2);
auto cAdd = cc->EvalAdd(c1, c2);
auto cMul = cc->EvalMult(c1, c2);
```

復号して、それを表示します。

```c++
Plaintext resultAdd;
Plaintext resultMul;

cout << "Plaintext x1: " << x1 << endl;
cout << "Plaintext x2: " << x2 << endl;

cc->Decrypt(keys.secretKey, cAdd, &resultAdd);
resultAdd->SetLength(x1.size());

cc->Decrypt(keys.secretKey, cMul, &resultMul);
resultMul->SetLength(x1.size());

cout << "x1 + x2 = " << resultAdd << endl;
cout << "x1 * x2 = " << resultMul << endl;
```

このファイル`test_bfv.cpp`という名前にして`src/pke/examples/test_bfv.cpp`として配置してみます。
配置してもmakeし直す必要はなく、make installだけすればいいです。`/build`に移動して、次のコマンド`test_bfv.cpp`の実行ができます。

```shell
$ make install
$ ./bin/examples/pke/test_bfv.cpp
```


もし、`simple_integer.cpp`を試してみたい場合は、`build/bin/examples/pke/simple_integer`を実行すればいいです。

実行してみると、実際に暗号文状態での加算・乗算がそのまま平文の加算・乗算になっていることが確認できます。


```
Plaintext x1: [ 1 2 3 4 5 6 7 8 ]
Plaintext x2: [ 8 7 6 5 4 3 2 1 ]
x1 + x2 = ( 9 9 9 9 9 9 9 9 ... )
x1 * x2 = ( 8 14 18 20 20 18 14 8 ... )
```

# まとめ

PALISADEで準同型暗号を試しました。PALISADE以外にもHElibやSEALがあるので、遊んでみてください。

これが今年最後の記事になります。来年も暗号関連の記事を書いていこうと思いますのでよろしくおねがいします。