Develop with pleasure!

福岡でCloudとかBlockchainとか。

ECDSA署名のエンコーディング関連のmalleabilityを解消するルールを追加するBIP-146

Segwitのトランザクションで署名が入力から分離されたことで、署名スクリプトに細工してtxidを変更することはできなくなったけど、witnessに移動した署名データの改変自体は可能。依然として可能なECDSA署名のエンコーディング関連の改変を解消するためにトランザクションの有効性を検証する際にルールを追加しようというのがBIP-146↓

https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki

追加のルール自体は以前からCoreのリレーポリシーとして適用されているので実質有効化されているようなものかな。

動機

署名のmalleabilityとは、ネットワーク上のリレーノードが(リレーノード自体はブロードキャストされたトランザクションの署名に使われた秘密鍵にアクセスすることはできない)、トランザクションの署名を改変する能力のことを指す。非Segregated Witnessのトランザクションでは、署名のmalleabilityによりtxidの変更が可能で、もしそのtxidを参照する子トランザクションが存在した場合、その子トランザクションを無効にする。Segregated Witness(BIP-141)のトランザクションであれば第三者がtxidを変更するようなことはできないが、malleabilityによりwtxidを改変することは可能で、Compact Block(BIP-152)のリレーの効率を低下させる可能性がある。

BIP-66で署名のフォーマットに厳密なDER形式を適用して以降、ECDSAの署名には既知の2つのmalleabilityの要因が残っている↓

  1. ECDSA署名固有のmalleability
    ECDSA固有の問題で、署名データ(r, s)があった場合に、sの値をマイナスにし(r, -s(mod n))したデータも署名として有効と判断される。
  2. 失敗した署名のmalleability
    OP_CHECKSIGまたはOP_CHECKMULTISIGで署名の検証に失敗した場合、FALSEがスタックに戻され、スクリプトの評価は続く。失敗した署名でも、BIP-66の規則に従う署名である限りそれには意味がある。

このBIPでは、これらのmalleabilityを解消するための新しいルールを定義する。

仕様

署名エンコーディングのmalleabilityを解消するため、以下の新しいルールをpre-segregated Witness及びsegregated witness scriptに適用する。

LOW_S

ECDSAの署名のSの値は最大でも曲線の位数を2で割った値以下でないといけない。OP_CHECKSIG*1, OP_CHECKSIGVERIFY, OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFYをパスする各ECDSA署名には、このルールが適用され、S0x1から0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0楕円曲線のベースポイントGの位数を2で割った値)の範囲の値で、厳密なDERフォーマットを適用した署名でなくてはならない。

ECDSAの署名検証の際、署名が空のバイト列ではなく、LOW_Sチェックに合格しない場合、スクリプト全体がfalseと判定される。

署名値にHIGH_Sの値が使われている場合は、以下を計算することで簡単にLOW_Sに置き換えることができる。

S' = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 - S

NULLFAIL

OP_CHECKSIGがスタックにfalseを返す際は、関連する署名は空のバイト配列でなければならない。

OP_CHECKMULTISIGがスタックにfalseを返す際は、OP_CHECKMULTISIGに渡す全ての署名は空のバイト配列でなければならない。この時、マルチシグ内の一部の署名の不正などで署名検証が早期に終了し一部の署名がスキップされたとしても、全署名についてこのNULLFAILチェックをしなければならない。

それ以外の場合は、スクリプト全体がすぐにfalseと判定される。

サンプル

以下の例はLOW_SルールとNULLFAILルールを組み合わせた結果*2

注釈

CO       : 曲線の位数(curve order) = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141
HCO      : 曲線の位数を2で割った値(half curve order) = CO / 2 = 0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0
P1, P2   : シリアライズされた有効な公開鍵
S1L, S2L : P1とP2に対応した有効な low S 値を持つ署名(1 ≤ S ≤ HCO)
S1H, S2H : P1とP2に対応したhigh S 値を持つ署名 (HCO < S < CO)
F        : BIP-66に準拠しているが無効な署名(空ではない)

以下のスクリプトはこれまで通りスタックにTRUEを返す。

S1L P1 CHECKSIG
0 S1L S2L 2 P1 P2 2 CHECKMULTISIG

以下のスクリプトはこれまで通りスタックにFALSEを返す。

0 P1 CHECKSIG
0 0 0 2 P1 P2 2 CHECKMULTISIG

以下のスクリプトはこれまではTRUEだったが、新しいルールではスクリプト自体がすぐに失敗する。

S1H P1 CHECKSIG
0 S1H S2L 2 P1 P2 2 CHECKMULTISIG
0 S1L S2H 2 P1 P2 2 CHECKMULTISIG
0 S1H S2H 2 P1 P2 2 CHECKMULTISIG

以下のスクリプトはこれまではFALSEだったが、新しいルールではスクリプト自体がすぐに失敗する。

F P1 CHECKSIG
0 S2L S1L 2 P1 P2 2 CHECKMULTISIG
0 S1L F   2 P1 P2 2 CHECKMULTISIG
0 F   S2L 2 P1 P2 2 CHECKMULTISIG
0 S1L 0   2 P1 P2 2 CHECKMULTISIG
0 0   S2L 2 P1 P2 2 CHECKMULTISIG
0 F   0   2 P1 P2 2 CHECKMULTISIG
0 0   F   2 P1 P2 2 CHECKMULTISIG

デプロイメント

このBIPはBIP-9のversion bitsを利用してデプロイされるが詳細は未定。

互換性

参照クライアントはv0.9.0以降LOW_S互換の署名を生成しており、LOW_Sのルールはv0.11.0以降の参照クライアントでリレーポリシーとして適用されている。2016年8月時点で、ルールに違反するトランザクションはごくわずかである。実際に使われている全scriptPubKeyにおいて、準拠していない署名を準拠した署名に変換するのは非常に簡単なことであるため、このルールによって機能が損なわれることはない。

OP_CHECKSIGOP_CHECKMULTISIGに失敗するスクリプトがチェーン上に記録されるのは稀で、NULLFAILルールは参照クライアントのv0.13.1以降リレーポリシーとして適用されている。

ただ、ユーザーは変わったスクリプトを設計する際は、このルールに特別な注意を払うこと。

実装

参照クライアントの実装は↓

https://github.com/bitcoin/bitcoin/blob/35fe0393f216aa6020fc929272118eade5628636/src/script/interpreter.cpp#L185

で、そのプルリクは↓

github.com

*1:BIP-141のP2WPKHも含む

*2:参照クライアントv0.13.1の実装により、曲線の位数の半分の値より大きなS値を持つ署名がLOW_Sチェックをパスする可能性があることに注意が必要。ただそのような署名は無効かつ、その後のNULLFAILルールのチェックで失敗する。