ハクソク

世界を動かす技術を、日本語で。

pyca/cryptographyにおけるOpenSSLの現状

概要

pyca/cryptographyのメンテナーがOpenSSLの方向性に深刻な懸念を表明。
OpenSSL 3でのパフォーマンス低下やAPI複雑化が主要な問題点。
テストやメモリ安全性の不十分さも指摘。
今後はOpenSSL依存度を下げる方針を明確化。
LibreSSL/BoringSSL/AWS-LCなど他実装への移行を検討中。

OpenSSLの現状と問題点

  • pyca/cryptographyは12年間OpenSSLを中核に利用してきた実績。
  • OpenSSL 3以降、パフォーマンス低下APIの複雑化が顕著。
  • pre-Heartbleed時代: メンテナンス不足と技術的停滞。
  • Heartbleed直後: CI導入やリリース体制強化など大幅改善。
  • OpenSSL 3リリース以降: API刷新や内部構造の大規模変更による後退
    • パフォーマンス、複雑性、APIの使い勝手、テスト・検証、メモリ安全性で問題発生。
  • 他のOpenSSLフォーク(LibreSSL/BoringSSL/AWS-LC等)は同様の問題を回避。

パフォーマンスの問題

  • OpenSSL 3OpenSSL 1.1.1と比較してパフォーマンス大幅低下
    • 例: 楕円曲線公開鍵の読み込みが最大8倍遅くなる事例。
  • 開発元から「パフォーマンス低下は想定内」との回答。
  • Rustによる独自実装へ移行した結果、10倍高速化など顕著な改善。
  • 基本的な最適化(コピー・アロケーション・ロック回避等)のみで大幅な性能向上。

複雑化とAPIの問題

  • OpenSSL 3OSSL_PARAM導入、API呼び出しが冗長化・難解化。
    • 通常の引数渡しでなく、キー・バリュー配列でのパラメータ指定。
  • APIの冗長さ: ML-KEMカプセル化でOpenSSLは37行・6回の失敗可能性、BoringSSLは19行・3回。
  • 内部実装も複雑化し、カスタムPerlプリプロセッサ導入など可読性低下。
  • Providers API導入でアルゴリズムの動的差し替えが可能に。
    • 不要な複雑化とパフォーマンス低下、バグの温床。
  • ソースコードの可読性低下: 間接呼び出し・オプション分岐・ifdef乱用などで理解困難。

テストと検証体制の問題

  • OpenSSLテスト自動化の優先度が低い。
  • テストカバレッジ不足: リリース前のバグ検出が不十分、コミュニティ頼り。
  • CIの不安定さ: 重大バグがCIで検出されても「フレーク」として無視。
    • 例: OpenSSL 3.0.4のRSAバッファオーバーフロー。
  • Intel SDE等の先進的なテスト手法未活用。
  • **形式手法(フォーマルベリフィケーション)**の導入遅れ。

メモリ安全性の問題

  • Rust導入でpyca/cryptographyはメモリ安全性を強化。
  • OpenSSLはメモリ安全性向上の取り組みが皆無。
  • セキュリティ重視ライブラリとしては、メモリ安全言語への移行が必要。

根本原因と資金問題

  • 資金不足が主因ではない。OpenSSLは十分な資金と人員を保有。
  • 設計判断の妥当性に疑問。フォーク先(BoringSSL等)は同様の選択をしていない。

今後の方針

  • 新機能におけるOpenSSL実装の必須化を廃止
    • LibreSSL/BoringSSL/AWS-LCのみ対応APIを追加予定(例: ML-KEM, ML-DSA)。
  • バイナリ配布物(wheels)のリンク先をOpenSSLフォークへ切り替え検討。
  • OpenSSL非対応への移行も視野。
  • 非OpenSSL由来の暗号ライブラリも積極的に調査・追跡。

まとめ

  • OpenSSL 3の方向性に危機感。
  • パフォーマンス・API・テスト・メモリ安全性で他実装に劣後。
  • 依存度低減他実装への移行を進める方針。
  • コミュニティ全体での議論・対応を促す声明。

Hackerたちの意見

> 最終的に、OpenSSLの公開APIを使って実装を追跡しようとするのは、自分を痛めつけるような作業になってしまった。ソースコードを読んで、何がどう動いているのかを理解することは、ソフトウェアエンジニアリングの自己改善の一環としても重要だし、洗練された消費者として、実装の動作について文書化されていないことが必ずあるから、ソースを読むことで真実がわかる。間接呼び出しやオプションのパス、#ifdef、その他の理解を妨げる障害物の数は驚くべきものだ。OpenSSLのソースコードを読むことがどれほど苦痛になったかは、過去にはなかったことで、LibreSSLやBoringSSL、AWS-LCでもそうではない。OpenSSLのコードはv1でも読みやすいとは言えなかったし、例えば多くの最適化された実装が存在する(または生成される予定の巨大なperlスクリプトがある)中で、どの状況でどこに呼び出しが行くのかを理解するのは常に頭痛の種だった。3.0以降はやっていないけど、もしここでもひどく退化しているなら、本当にまずいことになっているはずだ。
OpenSSL 1.xでダイジェストオブジェクトの状態を調べるために使っていたハッキーなコードがあるんだけど、これが3.0で公開APIから削除されちゃった。これを知る過程でダイジェストAPIを深く掘り下げたけど、本当に理解不能だった。間接的な理由があるのかと思ったけど、Cryptographyのメンテナがそう思っていないのは良いことだ。ところで、長年の実績があるCryptography API(例えばx.509パス検証)に依存しているライブラリ開発者として、アレックス・ゲイナーとそのチームがCryptographyを構築し、維持するために素晴らしい仕事をしていると言いたい。私はCryptographyのAPI設計とテスト手法を信頼していて、模範として使っているし、彼らの仕事が多くの脆弱性を防ぎ、Pythonエコシステムを向上させ、そうでなければ不可能だったアプリケーションを可能にしていることを知っている。だから、彼らがこんなに強い意見を表明するときは、彼らの判断を信じたくなる。
3.0の全体的な書き直しは、あらゆる面で大きな後退だよ。古いエンジンを非推奨にしてプロバイダーに置き換えたけど、開発者としてはあまり扱いやすくない(少なくともメンテナンスする側には楽になってほしい)。ライブラリはなんかランタイムでダイナミックになってるし、そのせいでミューテックスが爆発的に増えて、あらゆる面でパフォーマンスが落ちてる。haproxyがこのトピックについて面白い記事を書いてるよ。https://www.haproxy.com/blog/state-of-ssl-stacks 暗号技術が必要だけどOpenSSLのAPIを使いたい人は、aws-lcを使って他のTLSスタックを探すべきだね。
> OpenSSLのコードは読みやすくなかったし、心地よくもなかった。 少し前にSSLについての本を出版したんだけど、最初の計画はOpenSSLのソースコードを順を追って解説することだったんだ。でも、OpenSSLのソースコードがあまりにも複雑で、暗号アルゴリズムについて話すよりもCコードの複雑さについてずっと時間を使うことになりそうだったから、自分でSSLの実装を書いてそれを解説する方が良いと思ったんだ。OpenSSLに対して公平に言うと、なぜこんなに複雑になったのかは理解できる。彼らは後方互換性を保ちながら、すべての人に対応しようとしてるからね。当時、OpenSSLはまだSSLv2をサポートしていて、LibreSSLにはその重荷がないんだ。
ここは本当に注目すべきポイントだと思う: > 後に、公開鍵のパースを自分たちのRustコードに移行したことで、エンドツーエンドのX.509パス検証が60%速くなった。鍵の読み込みを改善するだけで60%のエンドツーエンドの改善が得られるなんて、OpenSSLの鍵パースのオーバーヘッドがどれほど極端だったかがわかる。 > 自分たちのパースを行うことでより良いパフォーマンスを達成できることは、より良いことが実用的であることを明らかにしている。実際、私たちのパフォーマンスは巧妙なSIMDマイクロ最適化の結果ではなく、機能するシンプルなことを行った結果だ。コピーや割り当て、ハッシュテーブル、間接呼び出し、ロックを避けている — 基本的なDER構造をパースするのにこれらは必要ないはずだ。私はPyCAの暗号ライブラリのX.509パス検証ライブラリの設計/実装に関わっていて、OpenSSLによってどれだけのパフォーマンスが無駄にされていたかを見るのは本当に驚きだった。私たちは設計の際に使いやすさと安全性を優先し、PyCAがOpenSSLのAPIにバインドしていた場合よりも速く、より準拠したパス検証の実装を得ることができた。
さて、OpenSSLのコードベースの他の部分でどれだけのパフォーマンスが無駄にされているのか気になるな…
正しい実装が優れたパフォーマンスを持つのは、めっちゃ一般的なことだよね。他の誰かが正しくない方法で速く進めても、間違った答えに何の意味があるの?
LibreSSL覚えてる?確かHeartbleedから生まれたんだよね。OpenSSLにはVAXやAmiga(?)などの古いアーキテクチャをサポートするためのものがあるってプレゼン資料で見たことあるから、そういうのが残ってるのかなって思ったり。
OpenSSLをハード依存から外すことを考えているのは嬉しい。私はpyca/cryptographyの一部をOpenSSLを置き換えたり削除したりしてデバッグしやすくしたことがある。OpenSSLのエラーは本当にひどいからね。全体のパッケージに対してそれを実現するのはそれほど難しくないはずだ。ただ、pyca/cryptographyの一部がPythonの文脈外でも使えるようになるのを見たいな。ここで言及されているX.509パス検証のように。
ちなみに、pyca/cryptographyは本当に優れた暗号ライブラリで、彼らがここで正しい決定を下していると自信を持っている。PythonレベルのAPIはよく考えられていて、文書も充実している。私もいくつかの小さな貢献をしたことがあって、楽しい経験だった。そして私の個人的な「新しいOpenSSL APIはひどい」というエピソード: https://github.com/openssl/openssl/issues/19612(私のGHの問題ではないけど、全く同じことに直面した)> SHA256_xxxの非推奨の呼び出しを削除して、私のコードでEVP_Digestxxxに置き換えようとした。しかし、EVPのコードは遅いようだ。だから、簡単なテスト(下のテストケースB対C)をしたけど、確かに約5倍遅かった。
これ、すごいね。昔はOpenSSLがハードウェア特化で最適化された暗号プリミティブの場所だったけど、その代わりにひどいAPIを使わなきゃいけなかった。今はさらにひどいAPIになって、しかも速くもないの?SHA256はほぼ純粋関数のプロトタイプだよ。「EVP」みたいな概念は必要ない。出力サイズは静的であるべきだし、失敗は完全に不可能であるべき。非同期インターフェースを使うことを選ばない限り、失敗はありえない。唯一の複雑さは、最適な同期実装を選ぶ隠れた部分だけであるべきだね。
正直なところ、OpenSSLがこんなに長い間スタンダードであり続けているのは驚きだ。扱うのがこんなに難しいのにね。バックエンドをRustに移行するのは、長期的な安定性のために正しい選択だと思う。
すべての暗号プリミティブは、しばらくの間はOpenSSLのようなAPIを通じてCで提供されることになるよ。今の提案は、OpenSSLからそのフォークの一つに移行すること。暗号プリミティブじゃないバックエンドのロジック(例えば、パース処理)はRustで書き直されてるし、最後の方でhttps://github.com/ctz/graviolaがRustとアセンブリの組み合わせで暗号プリミティブを実装する可能性があるって言われてるけど、まだ成熟してないみたい。
OpenSSLが他のライブラリよりもこうなりやすかった理由はいくつかあると思うけど、実際には「有名なライブラリにこだわる」って現象は、みんなが思ってるよりずっと一般的だと思う。1. OpenSSLは暗号技術。自分で作るなって明言してたから、Xが面倒だと思ったプログラマーの最初の反応(「自分でXを書こう」)は、これによって賢くないって判断されるか、ユーザーから「自分でTLS実装したってどういうこと?」って反発を受ける。2. 暗号じゃない部分も、OpenSSLを使う著者の本当の興味とは全く関係ないニッチなものが多い。HTTPS POSTが必要なC++プログラマーが、Web PKIやAES、X.500ディレクトリシステム、Distinguished Encodingについて1ヶ月かけて学ぶか、ただOpenSSLを呼び出して気にしないかのどっちかだね。
Heartbleedの頃、他の誰もその作業をやりたがらなかったし、やったとしてももっとひどかった(GNU TLS)。OpenSSLの暗号プリミティブは結構良いけど、プロトコルの部分はあまり良くない。x.509は最悪だし、それに対処するために誰かが書いたものはすごく魅力的。TLSプロトコルはそこまで悪くないけど、長さにどれだけのバイトを使ってるかを見ると頭がおかしくなりそう。OpenSSLは歴史的に開発の互換性がクソだったけど、3.xシリーズのひどいパフォーマンスが多くの人を限界に追い込んだと思う。プロトコルの作業をして、x.509をメモリ安全な言語で実装して、暗号のために(OpenSSLのフォークに)呼び出す感じで。
記事では、実質的に同じ名前のHaproxyのブログ(2025年から)を取り上げている: https://www.haproxy.com/blog/state-of-ssl-stacks Haproxyは実質的にOpenSSLを見捨ててAWS-LCに移行している。パッケージReは両方でビルドされ続けているが、AWS-LCが彼らにとっての道だ。
OpenSSLがそんなにひどい状態だなんて全然知らなかった。
3.0の「書き直し」後でも、まだそんなにひどい状態だなんて驚いたよ。
Rich Salzがプロジェクトを辞めたのは、その未来に対する批判だと思ってる。 https://mta.openssl.org/pipermail/openssl-users/2020-July/01...
OpenSSLのパフォーマンスの低下について、Feisty Duckの暗号ニュースレターの12月号で書いたよ。[1] 最近のOpenSSLカンファレンスでは、AlexとPaulのPython暗号に関するトークに加えて、いくつか他にも見る価値のあるトークがあったよ: - Juniper NetworksのWilliam Bellingrathが、1.1.1から3.4.xまでのバージョンをベンチマークした話。 https://www.youtube.com/watch?v=b01y5FDx-ao - Tomáš Mrázが、パフォーマンスを向上させる方法について書いていて、デフォルトが悪い理由も説明してる。 https://www.youtube.com/watch?v=Cv-43gJJFIs - IBMのMartin Schmatzが、ポスト量子暗号スイートのパフォーマンスに関する非常に詳細な研究を発表した。 https://www.youtube.com/watch?v=69gUVhOEaVM 注意:これらの録画されたトークは、最初にすごく大きなバイオリンの音が入ってるから注意してね。毎回最初の数秒をミュートにしないといけなかったよ。
ED25519やED448、さらに幅広いECキーをサポートしてくれるOpenSSL以外の暗号ライブラリが待ち遠しい。これらは今の仕事に必要なんだけど、OpenSSL 3+が唯一それを実現してくれたんだ。
Ed448は何に使うの?このアルゴリズムの実際の導入例をあまり見たことがないから、すごく気になってる。
> OpenSSLはプログラム実行のどの時点でもアルゴリズムを置き換えることを許可していた。 これ、完全におかしいよ。何のためにこんなことが必要なの?ゼロダウンタイムのためにホットパッチシステムを熱心に支持する人たちのため?ロックでパフォーマンスが左右されるのも無理はないよ、まさに災害のレシピだね。