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

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)