ビットコイン・インスティテュート

BIP 66 — 厳密な​DER署名

BIP: 66
  Layer: Consensus (soft fork)
  Title: Strict DER signatures
  Authors: Pieter Wuille <pieter.wuille@gmail.com>
  Status: Deployed
  Type: Specification
  Assigned: 2015-01-10
  License: BSD-2-Clause

概要

本文書は、署名を厳密なDER符号化に制限するという、ビットコインのトランザクション有効性ルールへの変更案を規定する。

著作権

本BIPは2条項BSDライセンスの下でライセンスされる。

動機

ビットコインのリファレンス実装は現在、署名検証をOpenSSLに依存しており、これは事実上OpenSSLがビットコインのブロック有効性ルールを規定していることを意味する。あいにくOpenSSLはコンセンサスに関わる挙動を想定して設計されておらず(バージョン間でのバグ互換性を保証しない)、その変更がビットコインのソフトウェアに影響を与えることがあり、実際に与えてきた。

とりわけ重大な領域が署名の符号化である。つい最近まで、OpenSSLの各リリースはDER標準からのさまざまな逸脱を受け入れ、署名を有効なものとして扱っていた。OpenSSL 1.0.0pと1.0.1kでこれが変更されたとき、一部のノードがチェーンを拒否する事態が生じた。

本文書は、コンセンサスルールがOpenSSLの署名解析に依存しないよう、有効な署名をDERが定める形式に厳密に制限することを提案する。コンセンサスコードからOpenSSLを完全に排除したい実装にとって、こうした変更が必要になる。

仕様

OP_CHECKSIG、OP_CHECKSIGVERIFY、OP_CHECKMULTISIG、OP_CHECKMULTISIGVERIFYに渡され、ECDSA検証の対象となるすべての署名は、厳密なDER符号化で符号化されていなければならない(詳細は後述)。

これらの演算子はいずれも、スタックの最上位から逆順にたどりながら、公開鍵と署名の組に対してECDSA検証を行う。それぞれの検証において、署名が以下のIsValidSignatureEncodingチェックに通らなければ、スクリプト全体が直ちにfalseと評価される。署名が有効なDERであってもECDSA検証に通らない場合は、従来どおりオペコードの実行が継続され、場合によってはオペコードの実行が停止してスタックにfalseが積まれる(ただしスクリプトは直ちには失敗しない)ため、それ以降の署名がスキップされることがある(したがってそれらはIsValidSignatureEncodingの対象にならない)。

DER符号化の参照仕様

以下のコードは、厳密なDERチェックの挙動を規定する。この関数が検証するのは、ビットコインが付加する1バイトのsighashフラグを含んだ署名バイト列であることに注意されたい。このフラグはDER仕様の範囲外であり、本提案の影響を受けない。また、意図的に無効な署名に対してはシンプルかつ短く、効率的に検証できる符号化を用意するため、sigの長さが0の場合にはこの関数は呼び出されない。

DERはhttps://www.itu.int/rec/T-REC-X.690/enで規定されている。

bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) {
    // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
    // * total-length: 1-byte length descriptor of everything that follows,
    //   excluding the sighash byte.
    // * R-length: 1-byte length descriptor of the R value that follows.
    // * R: arbitrary-length big-endian encoded R value. It must use the shortest
    //   possible encoding for a positive integers (which means no null bytes at
    //   the start, except a single one when the next byte has its highest bit set).
    // * S-length: 1-byte length descriptor of the S value that follows.
    // * S: arbitrary-length big-endian encoded S value. The same rules apply.
    // * sighash: 1-byte value indicating what data is hashed (not part of the DER
    //   signature)

    // Minimum and maximum size constraints.
    if (sig.size() < 9) return false;
    if (sig.size() > 73) return false;

    // A signature is of type 0x30 (compound).
    if (sig[0] != 0x30) return false;

    // Make sure the length covers the entire signature.
    if (sig[1] != sig.size() - 3) return false;

    // Extract the length of the R element.
    unsigned int lenR = sig[3];

    // Make sure the length of the S element is still inside the signature.
    if (5 + lenR >= sig.size()) return false;

    // Extract the length of the S element.
    unsigned int lenS = sig[5 + lenR];

    // Verify that the length of the signature matches the sum of the length
    // of the elements.
    if ((size_t)(lenR + lenS + 7) != sig.size()) return false;

    // Check whether the R element is an integer.
    if (sig[2] != 0x02) return false;

    // Zero-length integers are not allowed for R.
    if (lenR == 0) return false;

    // Negative numbers are not allowed for R.
    if (sig[4] & 0x80) return false;

    // Null bytes at the start of R are not allowed, unless R would
    // otherwise be interpreted as a negative number.
    if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false;

    // Check whether the S element is an integer.
    if (sig[lenR + 4] != 0x02) return false;

    // Zero-length integers are not allowed for S.
    if (lenS == 0) return false;

    // Negative numbers are not allowed for S.
    if (sig[lenR + 6] & 0x80) return false;

    // Null bytes at the start of S are not allowed, unless S would otherwise be
    // interpreted as a negative number.
    if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) return false;

    return true;
}

例

表記法: P1とP2は有効な、シリアライズ済みの公開鍵である。S1とS2は、それぞれ鍵P1とP2を用いた有効な署名である。S1’ とS2’ は、同じ鍵を用いた、DER準拠ではないがそれ以外は有効な署名である。Fは、DERには準拠しているが無効な任意の署名(0、すなわち空文字列も含む)である。F’ は、DERに準拠しておらず無効な任意の署名である。

  1. S1' P1 CHECKSIGは失敗する(変更)

  2. S1' P1 CHECKSIG NOTは失敗する(変更なし)

  3. F P1 CHECKSIGは失敗する(変更なし)

  4. F P1 CHECKSIG NOTは成功しうる(変更なし)

  5. F' P1 CHECKSIGは失敗する(変更なし)

  6. F' P1 CHECKSIG NOTは失敗する(変更)

  7. 0 S1' S2 2 P1 P2 2 CHECKMULTISIGは失敗する(変更)

  8. 0 S1' S2 2 P1 P2 2 CHECKMULTISIG NOTは失敗する(変更なし)

  9. 0 F S2' 2 P1 P2 2 CHECKMULTISIGは失敗する(変更なし)

  10. 0 F S2' 2 P1 P2 2 CHECKMULTISIG NOTは失敗する(変更)

  11. 0 S1' F 2 P1 P2 2 CHECKMULTISIGは失敗する(変更なし)

  12. 0 S1' F 2 P1 P2 2 CHECKMULTISIG NOTは成功しうる(変更なし)

上記の例が示すとおり、この変更で必要になるのは追加の失敗ケースのみであり、これはソフトフォークとしての変更に求められる性質と一致する。

展開

本BIPでは、BIP 34の二重しきい値切り替え機構を、同じしきい値のままnVersion = 3に対して再利用する。新しいルールは、nVersion = 3であるブロック(高さH)について、その直前の1000ブロック(高さH-1000からH-1)のうち少なくとも750ブロックもnVersion = 3である場合に有効となる。さらに、あるブロックの直前1000ブロックのうち950ブロックがnVersion = 3になると、nVersion = 2のブロックは無効となり、以降のすべてのブロックが新しいルールを強制する。

互換性

署名が厳密にDERに準拠していることを要求するルールは、v0.8.0以降、リファレンスクライアントのリレーポリシーとして既に強制されており、2015年1月時点でこれに違反するトランザクションがチェーンに追加される例はごくわずかである。また、DERに準拠しない署名はいずれも自明な手順で準拠する署名に変換できるため、この要求によって機能が失われることはない。本提案には、トランザクション展性を軽減するという副次的な利点もある(BIP 62を参照)。

実装

リファレンスクライアント向けの実装はhttps://github.com/bitcoin/bitcoin/pull/5713で入手できる。

謝辞

本文書は、以前のBIP62提案から抜き出したものである。BIP62にはさまざまな人々の意見が反映されており、なかでもグレゴリー・マクスウェルとピーター・トッドは、本文書についてもフィードバックを寄せてくれた。

開示

  • 本BIPがネットワーク全体で採用・強制された後、著者は、厳密なDER署名が自身が以前に発見していたコンセンサスバグへの間接的な解決策になっていたことを明らかにした (https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-July/009697.html)。