Bitcoin Core v0.12 の合意検証で libsecp256k1 が OpenSSL を置換

2016年1月15日、Bitcoin Core v0.12 が合意クリティカルな ECDSA 署名検証の標準バックエンドとして libsecp256k1 を採用し、OpenSSL を置き換えた。OpenSSL はサトシのオリジナル v0.1 リリース以来 7年間、ビットコインの依存ライブラリーであり続けていた。

背景

libsecp256k1 プロジェクトは 2013年3月5日、ピーター・ウィーユによって開始された。当初の動機は性能で、ウィーユは GLV-method endomorphism によって OpenSSL の汎用楕円曲線コードを上回る速度向上が得られるか確かめたかった。1 週間でライブラリーはビットコインの全ブロックチェーンを検証可能になった(当時のブロック高は約 225,000)。

グレゴリー・マクスウェルが参加し、プロジェクトは性能実験から、OpenSSL の secp256k1 実装を完全に置き換えるビットコイン専用ライブラリーへと拡大していった。

OpenSSL を置換する理由

2014年までに、合意クリティカルなコードで OpenSSL を使用することの具体的な問題が複数特定されていた:

  1. 署名解析の不整合が予期せぬチェーン分裂を引き起こす可能性。OpenSSL のバージョンが異なると、同じ署名が有効か無効かで判定が割れる場合があり、全ノードが同じ結論に達する必要がある合意システムでは許容できない。
  2. 性能 — libsecp256k1 は最終的に署名検証で 2.5〜5.5倍高速。署名検証は新ブロック検証コストの大部分を占める。
  3. 監査可能性 — 単一の曲線とビットコインが必要とする操作のみに焦点を絞ることで、ライブラリーは深く査読可能なサイズに収まり、サイドチャネル攻撃に対する定数時間実装も実現できた。

2014年11月、ウィーユは libsecp256k1 のテストを書きながら CVE-2014-3570 を発見・報告した。これは OpenSSL の BN_sqr(二乗)ルーチンの重大なバグで、長年 OpenSSL に潜在していた。

マクスウェルは Bitcoin Magazine の記事で結論をこうまとめた。「OpenSSL はビットコインのような合意クリティカルなシステムには適さないライブラリーだ」

展開

  • Bitcoin Core v0.10(2015年2月): ウォレット署名の標準として libsecp256k1 を採用。
  • Bitcoin Core v0.12(2016年1月15日): 合意クリティカルな ECDSA 署名検証の標準として libsecp256k1 を採用。

意義

libsecp256k1 が OpenSSL を置き換えたのは、合意の署名検証だった。すべてのノードが同じ判定に達しなければチェーンが分裂する経路である。サトシの v0.1 が OpenSSL を採用したのは、2008年当時としては自明な選択だった——Windows 上の C++ プロジェクト向け標準暗号ライブラリーだったからだ。2016年までに、Bitcoin Core 開発者たちは、合意システムにとって「自明」と「正しい」は同じではないと結論し、3年かけてビットコイン専用の置換実装を作り上げた。

このパターン——コードベースが成熟するにつれてサトシの設計選択がビットコイン固有の実装に段階的に置き換えられていくこと——は、Bitcoin Core の進化における繰り返し現れるテーマである。PR #4641(laanwj、2014)も同様で、サトシのハンガリアン記法による変数命名規則を新しい Bitcoin Core コードから体系的に削除する動きの始まりだった。

libsecp256k1 移行は、複数の参加者記録と依存関係記録において根拠的事件として読まれる。 ウラジミール・ファン・デル・ラーン伝記は v0.12 のリリース日をリードメンテナーとしての在任期間における定義的な業績として記録する。 ピーター・ウィーユ伝記は本ライブラリ ― ウィーユが 2013 年に開始した ― を貢献記録の根本的な柱の一つとして扱う。 グレゴリー・マクスウェル伝記も libsecp256k1 の共同著作をマクスウェル記録の二本柱のうちの一つとして据える (もう一本は CoinJoin / Confidential Transactions)。そして 2011 年のビットコイン v0.5 Crypto++ 依存除去エントリは同じ依存置換の弧をより早い端点から読む ― 2011 年の Crypto++ から OpenSSL へ、 2016 年の OpenSSL から libsecp256k1 へ ― v0.12 のリリース日をその連鎖の終端行として扱う。