SegwitのOP_CHECKSIGにはバグがある

「バグ」と言っても、勘違いによってセマンティクスを意図せず変更してしまったという話であって、脆弱性とかではないです。

前提

  • 秘密鍵と公開鍵の組(sk,pk)(\mathit{sk},\mathit{pk})があるとする。
  • ある取引tx\mathit{tx}があり、その中のある入力をtxIn\mathit{txIn}とする。
  • tx\mathit{tx}の中にtxIn\mathit{txIn}と同じインデックスをもつ出力は存在しないとする。
  • txIn\mathit{txIn}の親出力はpk\mathit{pk}でロックされているとする。

Pre-segwitの場合

txIn\mathit{txIn}の親出力をアンロックするためには、sk\mathit{sk}を用いて固定値0x00...01の署名bugSig\mathit{bugSig}を生成する必要がある1bugSig\mathit{bugSig}にはこの入力と同じインデックスをもつ出力は存在しないという情報しかコミットされていない。したがってbugSig\mathit{bugSig}を手に入れた攻撃者は、出力数より入力数が多い取引を構築することによって、pk\mathit{pk}でロックされている任意の出力(txIn\mathit{txIn}の親出力以外も)を盗むことができる。

Segwitの場合

txIn\mathit{txIn}の親出力をアンロックするためには、sk\mathit{sk}を用いてoutpointをコミットした署名を生成する必要がある2。ゆえにこの署名は、少なくともoutpointで指定された出力をアンロックするためにしか使えない。

BIP-143の間違い

SegwitにおけるOP_CHECKSIGのアルゴリズムを規定したBIP-143には次のような記述がある3

In the original algorithm, a uint256 of 0x0000......0001 is committed if the input index for a SINGLE signature is greater than or equal to the number of outputs. In this BIP a 0x0000......0000 is committed, without changing the semantics.

著者は”without changing the semantics”と述べているが、上述したようにセマンティクスは変化してしまっている。