ハクソク

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

Libsodiumの脆弱性

概要

  • Libsodiumは13年の歴史を持つ暗号ライブラリ
  • 高レベルAPI提供とAPIの後方互換性維持が特徴
  • Ed25519関連の低レベル関数にバリデーション不備が発覚
  • 影響範囲は一部の低レベル利用者のみ、高レベルAPI利用者は基本的に無関係
  • Ristretto255利用やアップデート推奨、既に修正版が公開

Libsodiumの設計思想とAPIの歴史

  • LibsodiumはDan Bernsteinの「暗号を簡単にする」という理念に基づいた設計
  • 限定的で高レベルな関数・パラメータのみを公開、ユーザー向けの分かりやすいドキュメント作成
  • 内部アルゴリズム非公開、ユーザーはアルゴリズム選択を意識せず利用可能
  • APIの後方互換性を強く重視、API変更によるアプリ修正不要を目指す
  • NaCl APIを継承しつつ、高レベル関数と一部低レベル関数も提供

低レベル関数利用の広がりと課題

  • 年月と共にLibsodiumが低レベル暗号ツールキットとしても利用される傾向
  • --enable-minimalビルドでのみ安定性保証、他の低レベルAPIは保証外
  • カスタムプロトコル構築時、単一ライブラリで一貫したインターフェースが便利
  • 多数のプラットフォーム・機能サポート維持、コミュニティ貢献のための努力

セキュリティ実績と新たなバグ発見

  • 13年間CVEゼロの堅実なセキュリティ実績
  • 最近のバッチ署名対応実験中、Ed25519点バリデーション関数の不備を発見
  • crypto_core_ed25519_is_valid_point()で一部不正な点が許容されていた

技術的詳細とバグの内容

  • Edwards25519曲線には複数の部分群(order)が存在
    • 主部分群(order L)で暗号演算を実施
    • 小さいorderや混合orderの点は排除すべき
  • バリデーションは「点をL倍して単位元(X=0, Y=Z)になればOK」と設計
  • 旧コードではX=0のみ確認し、Y=Zチェックが漏れていた
  • そのため、X=0かつY≠Zな不正点も有効と誤判定

影響範囲

  • 影響を受けるのは以下の場合
    • バージョン1.0.20以前または2025年12月30日より前のリリース利用
    • crypto_core_ed25519_is_valid_point()で外部入力点を検証
    • Edwards25519上で独自暗号処理を実装
  • 高レベルAPI(crypto_sign_*)は無関係
  • crypto_scalarmult_ed25519によるスカラー乗算も安全
  • 通常の鍵ペア生成関数は正しい部分群のみ利用

推奨対応・回避策

  • 2019年導入のRistretto255を推奨、バリデーション不要・高速処理

  • ライブラリアップデートが困難な場合、下記Cコードでアプリ側回避可能

    int is_on_main_subgroup(const unsigned char p[crypto_core_ed25519_BYTES]) {
      static const unsigned char L_1[crypto_core_ed25519_SCALARBYTES] = { ... };
      static const unsigned char ID[crypto_core_ed25519_BYTES] = { ... };
      unsigned char t[crypto_core_ed25519_BYTES];
      unsigned char r[crypto_core_ed25519_BYTES];
      if (crypto_scalarmult_ed25519_noclamp(t, L_1, p) != 0 || crypto_core_ed25519_add(r, t, p) != 0) {
        return 0;
      }
      return sodium_memcmp(r, ID, sizeof ID) == 0;
    }
    

修正版・今後のサポート

  • 2025年12月30日以降の公式配布物・パッケージで修正済み
  • Visual Studio, MingW, NuGet, swift-sodium, rust, libsodium.js等全プラットフォーム対応
  • メンテナンスは個人の善意による無償提供、支援やスポンサー検討の呼びかけ

まとめと今後の利用指針

  • Libsodiumは高い安全性と互換性を維持しつつ進化
  • 低レベルAPI利用時のみ注意、基本的には高レベルAPI利用で安全
  • Ristretto255への移行やライブラリアップデートが最善策
  • オープンソース貢献・継続的なサポートのための支援重要

Hackerたちの意見

これ、PHPライブラリのsodium_compatにも影響があったんだよね。https://github.com/FriendsOfPHP/security-advisories/pull/756 今晩は、オープンソースのエコシステムで他にEd25519の実装をチェックして、どこかでこのチェックが抜けてないか確認するつもり。
オープンソースに対するあなたの仕事に感謝します。
いくつかのライブラリでは、チェックが実装されていないものがあったけど、上で話した脆弱性と同じように間違った実装をしているものは見つからなかったよ。もし俺からのメールが届いていないなら、あなたの実装が https://ianix.com/pub/ed25519-deployment.html に載ってないか、俺が見落としたか、もしくは安全ってことだね。
Lean4でsodiumバインディングを4ヶ月くらいやってて、Ristretto255にたどり着いたら、作者がその可能性にワクワクしてる理由がわかった!RistrettoはCurve25519上で任意の多項式を構築できるようにデザインされたAPIで、いじったり実験したりするのがめっちゃ楽しい!もし作者がこれを読んでたら、あなたの仕事に感謝してるって伝えたい!
これの公開リポジトリ持ってる?
すごくいいライブラリだね。ありがとう、フランク・デニス。
大企業で働いてるなら、フランクを会社からスポンサーしてもらうことを考えてみて。
大企業(Apple)で働いてるけど、フランクが誰かもわからないし、どうやってスポンサーすればいいのかもわからない。もし知ってたとしても、スポンサー費用はAppleの口座からじゃなくて、私のポケットから出ていくことになるんだよね。
微妙だけど重要なバグだね。これは「有効かどうか」のチェックが、暗号では思ってるほど単純じゃないことの良い例だよ。素数階のサブグループ外の点を受け入れると、高次の仮定を静かに損なうことがあるんだ。すぐに悪用できるわけじゃなくてもね。それに、低レベルのプリミティブは意図以上に広く再利用される傾向があるから、小さなバリデーションの隙間が意外に大きな影響を及ぼすことがあるってことも思い出させてくれる。
ただ、X25519とEd25519はそもそもチェックが必要ないように設計されてるから、その点は注意してね。Curve25519やEdwards25519の上にもっと複雑なプロトコルを設計しようとすると、サブグループの問題にぶつかることがあるんだ。そういう場合には、できるだけプライムオーダーのサブグループに戻すようにしてるよ。Monocypherには、そんな便利な関数がいくつかあるんだ:crypto_x25519_dirty_fast()、crypto_x25519_dirty_small()、crypto_elligator_map()、crypto_elligator_rev()、crypto_elligator_key_pair()。これらの「ダーティ」関数は、全体の曲線をカバーする公開鍵を明示的に生成するから、ランダムな鍵が `crypto_elligator_rev()` で変換されると、本当にランダムなものと区別がつかなくなるんだ。ただ、単にクランプ操作を外すんじゃなくて、ランダムな低次元ポイントを追加してるんだ。そうすることで、後でそのポイントをX25519の鍵交換に使ったとき、共有秘密が本物のX25519鍵と同じになるんだ。ここで、DJBに感謝したいのは、公開鍵がプライムオーダーのサブグループに処理されていなくても、共有秘密をプライムオーダーのサブグループに投影する鍵交換プロトコルを設計してくれたことだね。元々の意図はチェックを簡単にすることだったかもしれないけど(低次元の鍵は全部ゼロになるから)、いい副作用としてMike HamburgのElligator2のための素晴らしいAPIを実現できたんだ。> プライムオーダーのサブグループの外のポイントを受け入れると、高次の仮定を静かに損なう可能性があるんだ、たとえ即座に悪用できるものが明らかでなくてもね。一方で、計算された結果が低次元成分に依存しないことを証明できれば(X25519の場合がそうだね)、私たちは確実に安全だとわかるんだ。結局、Ristrettoは、プロトコルを安全にプライムオーダーのサブグループに再投影できないときに本当に必要になるんだ。誤解しないでほしいけど、プライムオーダーのグループ抽象化は役に立つよ。でも、もし誰かがこれを必要とするプロトコルを設計する資格があるなら、非自明なコファクターでもうまく機能させる資格もあるはずだし、それができないことを証明することもできるはずだよ。