IBM FHE Toolkit for Linuxを試してみた

#完全準同型暗号（FHE：Fully Homomorphic Encryption)って何？
みなさま、FHE = 完全準同型暗号（FHE：Fully Homomorphic Encryption)ってご存知でしょうか？
私は恥ずかしながら知りませんでした(汗)。これは暗号の一種なんですが、暗号化したデータを復号しないでも、加算・乗算が可能というものだそうです。つまり、復号せずにデータ処理が可能になります。面白そうじゃないですか！？
これがあれば、暗号化したままデータ処理できるので、クラウド上でデータを処理するようなケースで、データ流出のリスクを防げそうです！調べてみたら、それ以外にも以下のような使い方があるそうです。

利用例|使い方
--|--
データベース検索 | 暗号化したデータをデータベースに入れておけば、復号せずに検索できる
類似データの検索| 自分の持っているデータを開示せずに、類似したデータの存在を検索できる（例：産総研の化合物データベースの秘匿検索技術 https://www.risec.aist.go.jp/achievement/demomovie-ja.html ）
暗号化されたデータから分析結果を得る|例えば、暗号化されたデータを復号せずに推論を行うことができる（例：準同型暗号を用いてAI推論を秘匿計算！GateAIの実力を考察！https://qiita.com/kenmaro/items/80f4c14292b2feb0a491 ）
クラウド上でのデータ処理|クラウド上で復号化しなくて良いので、データ流出のリスクが少ない

ちなみに、「完全」の意味は、加算だけでなく、乗算も復号しないで行うことができるということ。加算と乗算ができれば、その組み合わせで全ての計算が可能ですもんね。
ということで、FHEを実際に試してみたくなったところ、IBMが提供しているIBM FHE Toolkit for Linuxというのを見つけました。
今回は、このツールキットを試してみた顛末を書かせていただきます。
なお、下記の資料を参考にしました。

>Getting Started with the IBM FHE Toolkit for Linux
>https://github.com/IBM/fhe-toolkit-linux/blob/master/GettingStarted.md

# IBM FHE Toolkit for Linux 導入方法

このツールキットは、MacやIBMのメインフレーム(IBM z)でも使うことができるそうですが、今回は手軽なx86のCent OSの仮想サーバーをクラウド(IBM Cloud の　Classic Infrastructure / Virtul Server環境)上に立てて試してみました。以下、わたしが試した手順です。

<ol><li> CentOS 7.xを準備

<li> Gitを導入

````shell:
$ yum install git
````

<li> Dockerを導入 (ver.19以上が必要)

````shell:
$ yum install -y yum-utils
$ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ yum install docker-ce docker-ce-cli containerd.io
$ systemctl start docker
$ systemctl enable docker
```

<li> ツールキットをクローン

````shell:
$ git clone https://github.com/ibm/fhe-toolkit-linux
```

<li> Docker イメージをダウンロード

````shell:
$ cd fhe-toolkit-linux
$ ./FetchDockerImage.sh centos
$ docker images
```

<li> ディレクトリのパーミッションを緩くしないと、エラーになるので、とりあえず以下のように回避

````shell:
$ cd /root/fhe-toolkit-linux/FHE-Toolkit-Workspace
$ chmod a+rwx .
```

<li> IDEを起動

````shell:
$ ./RunToolkit.sh -p centos
```

<li> ブラウザで https://127.0.0.1:8443を開く (リモートで実行している場合には ssh ポート転送などを使う)

<li> チュートリアルに従って、サンプルプログラムをビルドする。

````shell:
（省略）
[build] [100%] Linking CXX executable ../bin/2-CreditCardFraudDetectionInferencing-CKKS
[build] [100%] Built target 2-CreditCardFraudDetectionInferencing-CKKS
[build] Build finished with exit code 0
```

# サンプルを動かしてみる
一番簡単そうな、"Privary Preserving Search Example"というサンプルを動かしてみました。どうやら、ランダムな３つの数を生成して、その数を使った計算をしてみる、というサンプルのようです。

````shell:
[fhe@e86073792fee bin]$ /opt/IBM/FHE-Workspace/build/bin/BGV_binary_arithmetic 

*********************************************************
*            Basic Binary Arithmetic Example            *
*            ===============================            *
*                                                       *
* This is a sample program for education purposes only. *
* It attempts to demonstrate the use of the API for the *
* binary arithmetic operations that can be performed.   *
*                                                       *
*********************************************************
Initialising context object...
m = 4095, p = 2, phi(m) = 1728
  ord(p) = 12
  normBnd = 2.25463
  polyNormBnd = 22.5545
  factors = [3 5 7 13]
  generator 2341 has order (== Z_m^*) of 6
  generator 3277 has order (== Z_m^*) of 4
  generator 911 has order (== Z_m^*) of 6
r = 1
nslots = 144
hwt = 120
ctxtPrimes = [6,7,8,9,10,11,12,13,14]
specialPrimes = [15,16,17,18,19]
number of bits = 798

security level = 24.2499

Security: 24.2499
Creating secret key...
Number of slots: 144
Pre-encryption data:
a = 23141
b = 14533
c = 1137
a*b+c = 336309290
a+b+c = 38811
popcnt(a) = 8
[fhe@e86073792fee bin]$ 
````

これだけの計算ですが、実行には少し時間がかかりました。
いちおう念の為に検算してみると、結果はあっているようです。

# ソースを見てみる

では、実際どんな感じでコードが書かれているか、みてみましょう。
コードの全体像は、コチラにあります。

https://github.com/IBM-HElib/HElib/tree/master/examples/BGV_binary_arithmetic

初期化のために色々やっていますが、計算しているのはここらへんのようです。multTwoNumbersというメソッドを使って、暗号化されている２つの数の積を得ていることがわかります。

````c++:
helib::multTwoNumbers(
      product_wrapper, // 計算結果の出力先
      helib::CtPtrs_vectorCt(encrypted_a), // 暗号化済みの被乗数
      helib::CtPtrs_vectorCt(encrypted_b), // 暗号化済みの乗数
      /*rhsTwosComplement=*/ false, // 乗数がsigned (２の補数) の可能性がなく、unsignedであることを表す
      outSize, // 出力ビット数
      &unpackSlotEncoding); // bootstrappingに必要な情報
````

最後の引数のbootstrappingっていうのがよくわからないですね。そこで、少し調べてみました。bootstrappingとは、「暗号文メンテナンス」のための処理の一つなのだそうです。FHEは格子暗号をもとにしていて、暗号化したまま演算することが可能な性質があります。ただし、演算を繰り返していくとノイズが溜まっていって、一定量以上のノイズが蓄積すると復号できなくなってしまいます。そこで、暗号化状態を維持したままノイズを削減する手法が"bootstrapping"という処理です。この処理をするために、暗号化方式などの情報を与えているのが最後の引数のようです。

# まとめ

FHE Toolkit を使えば、FHEを使ったサービスが作れそうです。本格的に使うには専門家の助けが必要になりそうですが、いろいろなユースケースがありそうで、身近なところでも役立つと思います。もしこの記事を読んで興味が出てきたら、ぜひチュートリアルを試してみてください。
ちなみに、FHEで使われている格子暗号は、量子コンピューターでも解読に時間がかかるといわれており、将来的にも安心して使える暗号方式です（RSAなど因数分解などを元にしている暗号は量子コンピューターで短時間に解読可能と言われています）。

# リファレンス

Fully Homomorphic Encryption content solution
https://www.ibm.com/support/z-content-solutions/fully-homomorphic-encryption/

IBM HElib
https://github.com/IBM-HElib

HElib Documentation
https://ibm.github.io/fhe-toolkit-linux/html/helib/index.html

Examples and Tutorials
https://github.com/IBM-HElib/HElib/blob/master/examples/README.md

(完全)準同型暗号の最前線1（入門編）
https://qiita.com/nindanaoto/items/98335ad4d32b927effa9