Develop with pleasure!

福岡でCloudとかBlockchainとか。

2者間の非対話型CoinJoinプロトコルSNICKER

2者間で、同期や対話なくCoinJoinを作成する新しいプロトコルSNICKER(Simple Non-Interactive Coinjoin with Keys for Encryption Reused)が提案されている↓

https://gist.github.com/AdamISZ/2c13fb5819bd469ca318156e2cf25d79

提案者のブログポストは↓

joinmarket.me

CoinJoinとは?

CoinJoinプロトコルはUTXOセットを難読化/ミキシングするのに有名な手法で、古くはShared CoinやDarkWallet、最近だとJoinMarketやWasabi Wallet、Samouraiなどで利用されている。

もともと2013年にGregory Maxwellによって提案されたプロトコルで管理者などの中間者無しでミキシングを行うためのプロトコル。ミキシングの参加者は送金額を満たすインプットとアウトプット(必要に応じてお釣り用のアウトプット)を提供する。この時アウトプットのコインの量は(お釣りを除いて)全て同じ額になる。各自が持ち寄った情報からミキシングトランザクションを作成する。各参加者が所有するインプットへの署名は、このトランザクションに対して行われるので、自身のコインが第三者に盗まれるということはない。理想的なCoinJoinのトランザクションはインプットとアウトプットの金種(コインの量)が全て等しいトランザクションになる。

CoinJoinの課題

↑に理想的なトランザクションと書いたけど、CoinJoinの難点は、参加者間の調整が必要だという点で、参加者が強力な匿名性を確保したい場合は特に難しくなる。この課題のソリューションの1つとして、CoinJoinトランザクションを作成する調整用のサーバーを使用する方法もあるが、ユーザーのプライバシーを低下させるトレードオフになり、攻撃に対して障害点となりやすい。

またそんなサーバがいない場合、シビル攻撃によりプロトコルの妨害をしたり、参加者のプライバシーを損なうような攻撃を効果的に実行できる。

SNICKERプロトコルとは?

SNICKERは、上記の参加者間の調整や同期を不要にすることで、この調整のトレードオフを解消するCoinJoinプロトコル。必要なのは片方のユーザーが暗号化されたデータをブロードキャストし、それをもう1方が受信するだけ。この受信はラジオや衛星を介して行われる可能性もあるため、何が起きたかを全て知ることは物理的に不可能となると(カジュアルにそこまでやることができればだと思うが)。

具体的には、提案者がブロックチェーンの情報のみを使って、別の参加者のアウトプットを推定し、部分的に署名されたCoinJoinトランザクションを作成する。そしてそのトランザクションを受信者が複合できる形で暗号化して何らかの方法でブロードキャスト(公開)する。作成するCoinJoinトランザクションは、ブロックチェーン上で確認できる公開鍵から(アドレスの再利用かもしくはトランザクションインプットの中にある公開鍵から抽出して)作られる。

※ ただし、SNICKERのCoinJoinモデルは2者間のみのCoinJoinであるという意味で非常に制限されたものになる。

現在SNICKERプロトコルにはバージョン0とバージョン1の2つのプロトコルが提案されている。

SNICKER Protocol version 0

バージョン0はアドレスの再利用が前提のプロトコルになる。

提案者がやること

提案者は、再利用されるアドレスのセットを特定する。具体的には、現在未使用のUTXOがあり、それと同じアドレスが以前に少なくとも1回使用済みであるアドレス。このアドレスA毎に、以前そのアドレスのコインが使用されたトランザクションを見つけて、そのアドレスの公開鍵 {P_A}を見つける。続いて、以下の手順を実行する。

  1. 提案者自身のUTXOを1つ見つける。このUTXOのコインの量は↑の {P_A}のUTXOのコインの量と2-of-3のトランザクションの手数料を加えた額以上のコインの量を持っている必要がある。
  2. 1で見つけた提案者のUTXOの公開鍵をQとするとQと {P_A}でECDHしたtweek  {c = ECDH(Q, P_A)}を計算する。
  3. Aの新しいアドレスを公開鍵 {P_A + cG}を使って計算する。
  4. 以下のCoinJoinトランザクションを構築する。
    • Input: {P_A}のUTXOと提案者自身のQのUTXOの2つ。
    • Output: {P_A}から導出した新しい公開鍵 {P_A + cG}から生成したアウトプットと、提案者の新しいアウトプットアドレス2つ(O1、O2)。O1は {P_A + cG}が受け取るのと同額を受け取り、O2はお釣り用のアウトプット。
  5. 提案者は自身のインプットに署名をする。
  6. 提案者は自身の署名を加えたトランザクションとtweakの値cシリアライズし、それをメッセージMとし、 {P_A}を使ってECIESの暗号化スキーム {ECIES-E(P_A, M)}で暗号化したProposalを作成する。
  7. 作成したProposalを掲示板に公開する。

受信者がやること

受信者は再利用したアドレスを記録しておく必要がある。このアドレスを上記と同じようにAと呼ぶ。

受信者は定期的に掲示板をチェックし、暗号化されたBlobを全てダウンロードする。各Blobについて(このバイナリBlobをoとする)、ECIESの復号スキーム {ECIES-D(p_A, o)}を実行し、復号を試みる(ここで {p_A}はアドレスAの秘密鍵)。復号化したデータについて以下をチェックする。

  • 先頭7バイトがSNICKERマジックバイト0x534e49434b4552かどうか。
  • versionが0x00かどうか

チェックをパスするとシリアライズされた値cと部分的に署名されたトランザクションが復号されているので、続いて以下の処理を行う。

  1. トランザクションアウトプットの1つが自身のアドレスAから導出した{P_A + cG}から作られたアドレスであること。
  2. 1の秘密鍵鍵をウォレットにインポートする。
  3. BIP-174ベースの部分的に署名されたトランザクションについて悪意ある脆弱性が無いかチェックする。具体的には、
    • トランザクションのバージョンが02で、ロックタイムは0、インプットのsequenceが0xffffffffであるかチェックする。
    • 署名が1つのインプットに対して有効なものであるか検証する。このインプットと署名は提案者のもので受信者のものではない。
    • 署名されていないインプットの所有権を自身が持っているか確認する。
    • {P_A + cG}を再構築できるか検証する。
    • 自身の使用する金額と新たに受信する金額を確認する。
    • 手数料が現在のネットワークで適切な額が確認する。ブロードキャスト後手数料を上げる場合、RBFは使えないので、CPFPを使うことになる。
  4. 全てのチェックが問題なければ、受信者はPSBTフォーマットのトランザクションに自身の署名を追加し、有効なBitcoinトランザクションを完成させ、Bitcoinネットワークにブロードキャストする。

なお、受信者が↑のトランザクションを受け取った時点で、別の参加者によってConJoinが行われるなどで既に提案者のUTXOが使用済みである可能性はある。そのようなケースがあったとしても受信者が資金を失うようなことは無い。

SNICKER Protocol version 1

バージョン1がほとんどバージョン0と同じプロセスだが、アドレスを再利用しないという点が異なる。

バージョン0では提案者はブロックチェーンをスキャンしてアドレスの再利用を検知して、そこから未使用のUTXOの公開鍵を検知してるため、アドレスの再利用が前提だった(ほとんどのトランザクションは公開鍵のハッシュのコインをロックしてるので、公開鍵が分かるのはそれを使用する時)。しかしBitcoinトランザクションのscriptSigには他にも公開鍵として利用可能なデータがある。

その1つがECDSA署名の署名データだ。ECDSAの署名値(r, s)においてrは署名用に選択したnonce kから導出した点R = kGのx座標だ。このRを↑の{P_A}の代わりに使うことでアドレスの再利用を必要としないSNICKERプロトコルが可能になる。(ただし、そのTxの署名者であることの推定が必要になるが)

基本的にBitcoinの代表的なウォレットであれば、署名生成の際にランダムに選択するnonce kの値はRFC6979に従って署名対象のトランザクションデータから決定論的に導出される*1。つまり、署名をしたウォレットであればkの復元が可能である。

まとめ

CoinJoinを行う際はどうしても対話型になるが、SNICKERは参加者の対話なく2者間のCoinJoinを行う新しいプロトコルの提案だ。

基本的にはCoinJoinを行いたい提案者が適当なUTXO(基本的にはアドレスが再利用されているUTXO)を見つけ、自身のUTXOを加え2者間のCoinJoinトランザクションを構築する。その際、相手のアドレスは、相手の公開鍵と自身の秘密鍵を使ってECDHを実行し共有シークレットを生成し、そのシークレットから生成した点を相手の公開鍵に加算して新たな公開鍵=アドレスとする。自身の署名を加えたら相手の公開鍵を利用してトランザクションデータと共有シークレットをECIESで暗号化して、そのデータを公開する。

公開したデータを発見した受信者は、内容を確認して問題なければ自身の署名を付与してブロードキャストする。

提案者と受信者の間には直接的な通信は発生しない。ブロックチェーンからスキャンしたUTXOに対応する受信者がSNICKER利用者でマッチするかという課題や受信者にインポートが必要な鍵の管理についての課題はある。