Moneroでは、送信者の情報を秘匿するためにトランザクションインプットにデコイとなるUTXOを含めて、UTXOセットの中のいずれかが実際にトランザクションで使用されることを証明するためにリング署名を採用している。Moneroが以前採用していたSchnorr署名の変形でもあるリング署名のスキームLSAGの仕組みについては以前ブログに書いた↓
techmedia-think.hatenablog.com
そして、2020年10月17日のネットワークアップグレードで、新しいリング署名スキームConcise Linkable Spontaneous Anonymous Group (CLSAG) がサポートされた。CLSAGの仕組みについて調べようかと思ったけど、既存のMLSAGの仕組みについて書いてなかったので、まずその内容から。
表記
↑のLSAGの投稿と表記を合わせて(インデックスだけ0からではなく1からにしてある)、
- キーベクトルの個数を
d
とする。 - 各キーベクトルのリングメンバーの数を
n
n * d
個の公開鍵のセットを、および- 署名に使用するリング内の鍵(1..n)のインデックスをπとする。
- 楕円曲線上の点を出力するハッシュ関数を
- 署名対象のメッセージを
m
とする。
MLSAG
MoneroでRingCTを導入した際に導入されたリング署名スキームがMultilayer Linkable Spontaneous Anonymous Group (MLSAG)。
LSAGがあるn個の公開鍵セット(リングメンバー)について、その中のいずれかの秘密鍵で署名されたことを証明可能なリング署名スキームなのに対して、MLSAGはd個のサイズnのキーベクトルを持つ場合(d ✕ nのマトリックスに対して)にd個の秘密鍵を使って有効な単一の署名を作ることができる署名スキームとして拡張されたもの。d個分のLSAG署名を作るより、署名データがd - 1
個分のサイズ分削減できる。
MoneroでRingCTを導入する際のリング署名スキームとして開発されたけど、Monero以外でも利用可能。
署名の生成
d
個分対応する秘密鍵を使ってキーイメージを生成する。LSAGだと1つだったけど、dが複数ある前提なのでその数分キーイメージも生成する。d
個分ランダムな数値を生成する。- からπを除いて(つまりデコイの数分)、について、ランダムな数値を生成する。
- まず最初にπの次のインデックス
π + 1
のチャレンジを計算する。 - 以外のチャレンジとして、i = π + 1, π +2, ..., n, 1, 2, .., π -1について、それぞれチャレンジ を生成する。
- を計算する。
- 署名値は
理解しやすいよう具体的に、リングメンバーの数 n = 3
でキーベクトルの個数 d = 2
として、その公開鍵のマトリックスを
- d = 1:
- d = 2:
とし、署名者の秘密鍵のインデックスをπ = 2
とする。
- 生成するキーイメージは以下の2つ。
- 続いてランダムな数値を生成する。
- デコイの数=2つ分のnonceをdの数分生成する。
- 最初のチャレンジ を計算する。
- 残りのチャレンジを計算する。
- 最後に、とを計算する。
- 署名値は となる。
手順6よりαの値はそれぞれ、
となり、これを使って手順4のを置き換えると、
つまり、
となり、LSAGと同様、各チャレンジは循環したデータを含むようになる。
LSAGと比較すると
- キーイメージと、nonceはdの数分増える。
- 計算するチャレンジの数は、dが増えても変わらず、リングメンバーの数だけ。
- ただチャレンジの入力に、全dの全リングメンバーのデータを含め循環するよう拡張してある。ここがポイント!。
署名の検証
生成した署名の検証も手順はLSAGと同じで、違いは、チャレンジの入力となる各データの計算方法だけ。
- キーイメージが楕円曲線のベースポイントの群内の点が検証する。
- と各rの値を使って、を計算する。
- 最終的に計算したが署名値のと一致すれば有効な署名。
データスペースの節約に
↑のようにチャレンジの入力を多重化したのがMLSAGの仕組み。
d ✕ nのセットに対して、LSAGとMLSAGで署名を生成した場合、
- LSAGの署名データのサイズ:(1 + n)* dのデータ
- MLSAGの署名データのサイズ:1 + n * dのデータ
となる。nonceであるrの個数は変わらないけど、チャレンジはdの数によって変わらないので、 d - 1
個で済む分データスペースを節約できる。
RingCTでMLSAGが必要な理由
2017年1月にMoneroに導入されたRingCTでは、このMLSAGが使われている。
最初は、Moneroで複数インプットがある場合に全インプットに渡って単一のMLSAG署名を作ればデータサイズを削減できるからか?と思ったけどどうもそうではなく、トランザクションの各インプットはそれぞれ個別のMLSAG署名を持っている。
RingCTからConfidential Transactionにより量をPedersen Commitmentとして表現されるようになったため、この機能の導入に伴いMLSAGが必要になっている。
CTの仕組みは
インプットのコミットメントの合計 − (アウトプットのコミットメントの合計 + 手数料へのコミットメント)= 0
が成立することでコインのインフレーションがされていないことを証明するようになっているが、Moneroでこれを単純に導入すると、デコイを含むインプットが参照するコミットメントのどれがゼロへのコミットメントかリンクできるとそもそもデコイにならないという問題が発生する。しかし、0へのコミットメントが計算できないと量が検証できない。
この問題に対処するため、実際に使用するインプットと同じ量を持つ疑似コミットメントを作成する。実際に使用するコミットメントをとすると、疑似コミットメントはとなり、ブラインドファクターのみが異なる。そしてc - c' = (r - r')G + (a - a)H = (r - r')G は0へのコミットメントとなる。そしてそれが0へのコミットメントであることを証明するためc - c'に対して有効な署名を対応する秘密鍵(r - r')を使って生成できればいい。つまりこの疑似コミットメントはリングメンバーのいずれかのUTXOと同じ量を持つコミットメントであることが証明できる。
だが、単純に疑似コミットメントと署名をトランザクションインプットにセットしただけだと、リングメンバーのコミットメントと差し引いて計算してどれかが分かってしまうので、ここもリング署名にする必要がある。
RingCTでは、
- コインの所有者であること
- (どのコミットメントがリンクできないようにするための)疑似コミットメントが使用する実際のコミットメントと同じ量を持つこと
の2つを証明するのにリング署名を作成する。つまり、MLSAGの署名を作成するにあたって、
となる。なお、二重使用を防ぐために必要なキーイメージはワンタイムアドレスから作られたものだけで良いので、MoneroのRingCTのトランザクションに添付されるキーイメージはインプット毎に1つだけ。
これがRingCTの導入により、n ✕ 2のマトリックスにおけるリング署名を作らないといけない理由で、そのためにMLSAGが導入されたっぽい。