こんにちは、なんぞーです!
現在大学で、情報セキュリティを専攻しているのですが、その講義内でブロックチェーン界隈でも注目されている秘密分散法について学んだので今回は Golang で実装してみたいと思います!
あくまでも、仕組みを理解するためのコーディングなので、処理速度などは考慮せずに進めていきたいと思います。
コードや内容についてのご指摘やアドバイスなどは Twitter(@nandehu0323)にいただけると非常にありがたいです!
秘密分散(秘密分割)とは一定の参加者で秘密を分散する方法で、参加者にはそれぞれ秘密を分散したカケラ(share)が配布されます。
秘密を消去してもある一定数のカケラを集めることで、元の秘密を復元することができます。
秘密分散法には色々な種類があり、2017 年 10 月に ISO が秘密分散技術の国際標準を発表しました。
今回の sss は、その中でも n 個に分割した分散値を k 個集めると復号できる閾値方式の一種。
RSA 暗号やゼロ知識証明の一種である Feige-Fiat-Shamir Identification Scheme を開発した Adi Shamir 氏が発表した方式です。
まずは簡単に理解するために、Shamir(2,3)閾値秘密分散法を図を使って説明しようと思います!
秘密を 3 個に分割し、そのうちの分割値 2 個から復号します。
はじめに Y 軸との交点が M、傾きが R の直線をグラフ上に引きます。
ここでの M は秘密値を示します。今回は R を 3、M を 5 として考えていくものとします。Step1.直線を引く
次に秘密を 3 個に分割、つまり直線上から 3 個の点を求めます。Step2.点を求める
これで秘密値を 3 個の分散値に分散させることができました。
秘密を分散させたので秘密を含む直線を消します。Step3.秘密を消去する
この分散値を 3 人で保持しておきます。
次に 3 個の分散値から元の M を復号します。
3 個の分散値から 2 つを集め、2 点を通る直線を引くと元の直線を復元することができます。
多項式の補間にはラグランジュ補間を用います。Step4.元の直線を復元する
これで秘密値 5 を復元することができました。
上図のように 3 人に分散して 2 人から復元しようとすると、
1 次式の
、
、
を分散値とします。
5 人に分散して 3 人から復元しようすると、
2 次式の
、
、
、
、
を分散値とします。
このように n 人に分散して、k 人から復元しようとすると k-1 次の多項式が必要となります。
仕組みとしては、このように分散値を使って多項式補間をするという単純なものになっています。
では、早速コーディングに移っていきたいと思います!
今回の実装では、秘密値を複数の分散値に分割する Split と、複数の分散値からある一定(閾値)の分散値を集めると復元できる Combine の 2 つの処理が主な実装となってきます。
実装を進めるにあたってこちらを参考にさせていただきました。
|
|
まず分散値を計算するための多項式を生成します。 この多項式は定数項が secret で、各係数はランダムで大丈夫です。 ただし、次数に関しては閾値-1 でないと復号することができません。
この関数では result 配列の先頭に定数項、次に次数の低い係数から順番に格納していきます。
各係数に関しては上述したようにランダムで係数を決めています。
生成した多項式に値を代入し Y 座標を導出するための関数を作っていきます 。
今回はこの多項式の解を求めるにあたって、ホーナー法と呼ばれる計算法を使っています。
(参考 | Github:codahale/sss)
sss ではガロア体上で計算することが求められています。
理由に関しては調べきれなかったので、自分でも調べてみてますがもしご存知の方がいらっしゃいましたら Twitter の方などでお教えいただけると幸いです。
したがってガロア体上で掛け算、割り算をするための関数も定義しました。
この計算結果を X 座標の値ごとに 配列に格納します。
今回は秘密のデータ”Hello, Shamir!“を分割してみます!
閾値は(3,5)に設定してあります。
上手く分散することができました。しかし、これが復元できなければ元も子もありません。
次は、この分散値を復元する関数を作っていきたいと思います。
|
|
ここまで見ていただいた方はわかると思うのですが、 分散された秘密は平面上の直交座標系の点で表すことができる数値のペアになっています。
先ほど、分割した秘密の Share1 は x=1 上に配置された点の集合を表しています。
5 つに分割された分散値の中から 3 個を元の座標(map)に戻していきます。
各座標が復元できたら、次に元の多項式を復元します。
多項式の復元に成功したら、その多項式の定数項が秘密情報の 1 バイト分ということになります。
多項式のの復元に関してはみんな大好きラグランジュ補間を使っていきます。
ラグランジュ補間の関数に関しましては、参考にさせていただいたリポジトリ内の関数をそのまま使わさせていただいています。
(参考 | Github:codahale/sss)
ラグランジュ補間そのものに関しましてはこのサイトで詳しく説明されています!(大学 1 年の頃にお世話になりました・・・)
(参考 | 補間(ラグランジュの補間公式、ニュートンの補間公式))
今回は分割された分散値を復元してみます!
今回必要な分散値は 3 個です。
無事、分割された分散値から復元することができました!
今回イチから sss を実装してみて、秘密分散の技術って結構単純なロジックなのだなと感じました。 とは言っても「完全に理解した」わけではないので、まだまだ理解の至っていない奥深いところがあるのかもしれません。
また、色々な sss の実装例を見ていて感じたのですが、各々プログラムによって分散値の形式が違ったり、多項式の生成方法が違っていたので、こういう形式の選定によっても秘匿性などが変わってくるのかなと思い、面白かったです。
大学一年生の頃に習ったラグランジュ補間も当時は「何に使うんやこんなの」と思っていましたが、実際に使ってみて、あの勉強も無駄じゃなかったんだなと再確認することができました。
暗号やっぱり面白い!!!!と改めて感じることができました(๑╹ω╹๑ )
最後まで読んで頂きましてありがとうございました!
Twitter(@nandehu0323)もやっておりますのでぜひご感想やアドバイスお待ちしております!