Goの暗号セキュリティ監査
Go Cryptography Security Audit by Roland Shoemaker and Filippo Valsorda
Goは、開発者が安全なアプリケーションを構築できるように、標準ライブラリに一式揃った暗号パッケージを同梱しています。Googleは最近、独立したセキュリティ企業であるTrail of Bitsと契約を結び、新しいネイティブFIPS 140-3モジュールの一部としても検証されている中核パッケージ群の監査を実施しました。この監査では、レガシーでサポート対象外のGo+BoringCrypto連携において低深刻度の指摘が1件、そしていくつかの情報提供目的の指摘が見つかりました。監査報告書の全文はこちらで確認できます。
監査の対象範囲には、鍵交換(ECDHおよびポスト量子のML-KEM)、デジタル署名(ECDSA、RSA、Ed25519)、暗号化(AES-GCM、AES-CBC、AES-CTR)、ハッシュ(SHA-1、SHA-2、SHA-3)、鍵導出(HKDFおよびPBKDF2)、認証(HMAC)の各実装、そして暗号論的乱数生成器が含まれていました。繊細なアセンブリのコアを持つ低レベルな多倍長整数と楕円曲線の実装も対象に含まれています。TLSやX.509といったより高レベルなプロトコルは対象外でした。3名のTrail of Bitsのエンジニアが1か月にわたってこの監査に取り組みました。
私たちはGoの暗号パッケージのセキュリティの実績、そして今回の監査結果を誇りに思っています。この監査は、パッケージの正しさを保証するための多くの手段の一つに過ぎません。第一に、Cryptography Principles(暗号に関する原則)に導かれ、パフォーマンスよりセキュリティを優先するなどして、複雑さを徹底的に抑えています。さらに、さまざまな手法を組み合わせて入念にテストしています。内部パッケージであっても安全なAPIを活用することを徹底しており、当然ながらメモリ管理の問題を避けるためにGo言語の性質にも頼れます。最後に、保守をしやすくし、コードレビューや監査をより効果的にするために、読みやすさを重視しています。
低深刻度の指摘が1件
唯一悪用の可能性がある問題であるTOB-GOCL-3は、_低深刻度_です。つまり影響が軽微で、引き起こすことが困難だったということです。この問題はGo 1.24で修正済みです。
ただし、TOB-GOCL-3(詳細は後述します)は、デフォルトでは有効になっておらず、Google以外での利用はサポート対象外であるレガシーなGo+BoringCrypto GOEXPERIMENTに関するメモリ管理の問題です。
情報提供目的の指摘が5件
残りの指摘は_情報提供目的_のものです。つまり、直ちにリスクをもたらすものではありませんが、セキュリティのベストプラクティスに関連するものです。私たちはこれらについて、現在開発中のGo 1.25のツリーで対応しました。
指摘TOB-GOCL-1、TOB-GOCL-2、TOB-GOCL-6は、さまざまな暗号操作におけるタイミングサイドチャネルの可能性に関するものです。この3件のうち、秘密の値を扱うために定数時間であることが期待されている操作に影響するのはTOB-GOCL-2だけですが、これはPower ISAのターゲット(GOARCHのppc64およびppc64le)にのみ影響します。TOB-GOCL-4は、内部APIが現在の用途を超えて再利用された場合の誤用のリスクを指摘しています。TOB-GOCL-5は、到達することが現実的ではない上限に対するチェックが欠けている点を指摘しています。
タイミングサイドチャネル
指摘TOB-GOCL-1、TOB-GOCL-2、TOB-GOCL-6は、軽微なタイミングサイドチャネルに関するものです。TOB-GOCL-1とTOB-GOCL-6は、現時点では機微な値には使用していないものの、将来そうした値に使われる可能性がある関数に関連するものです。TOB-GOCL-2は、Power ISA向けのP-256 ECDSAのアセンブリ実装に関連するものです。
crypto/ecdh、crypto/ecdsa: バイト列からフィールド要素への変換が定数時間ではない(TOB-GOCL-1)
NIST楕円曲線の内部実装には、内部表現と外部表現の間でフィールド要素を変換するメソッドが用意されていましたが、これは可変時間で動作していました。
このメソッドの利用箇所はすべて、秘密とはみなされない公開の入力値(公開のECDH値やECDSAの公開鍵)を対象としていたため、私たちはこれをセキュリティ上の問題ではないと判断しました。とはいえ、将来誤ってこのメソッドを秘密の値に対して使ってしまうことを防ぐため、そしてこれが問題かどうかを考える必要をなくすために、念のためこのメソッドを定数時間にすることにしました。
crypto/ecdsa: Power ISAアセンブリにおけるP-256の条件付き符号反転が定数時間ではない(TOB-GOCL-2、CVE-2025-22866)
Goはファーストクラスのプラットフォームに加えて、一般的ではないアーキテクチャを含む、いくつかの追加のプラットフォームもサポートしています。基盤となるさまざまな暗号プリミティブのアセンブリ実装をレビューする過程で、Trail of Bitsのチームは、ppc64およびppc64leアーキテクチャ上のECDSA実装に影響する問題を1件発見しました。
P-256の点の条件付き符号反転の実装で条件分岐命令を使用していたため、この関数は期待されていた定数時間ではなく、可変時間で動作していました。この修正は比較的単純で、条件分岐命令を、他の場所ですでに使用している定数時間で正しい結果を条件付きで選択するパターンに置き換えました。私たちはこの問題にCVE-2025-22866を割り当てました。
大多数のユーザーに届くコードを優先することと、特定のISAをターゲットにするには専門知識が必要であることから、ファーストクラスではないプラットフォーム向けのアセンブリの保守は、基本的にはコミュニティからの貢献に頼っています。修正のレビューに協力してくれたIBMのパートナーに感謝します。
crypto/ed25519: Scalar.SetCanonicalBytesが定数時間ではない(TOB-GOCL-6)
内部のedwards25519パッケージには、スカラーの内部表現と外部表現を変換するメソッドが用意されていましたが、これは可変時間で動作していました。
このメソッドはed25519.Verifyへの署名の入力に対してのみ使われており、これは秘密とはみなされないため、私たちはこれをセキュリティ上の問題ではないと判断しました。とはいえ、TOB-GOCL-1の指摘と同様に、将来誤ってこのメソッドを秘密の値に対して使ってしまうことを防ぐため、また、このコードを標準ライブラリの外でフォークして秘密の値に使っている人がいる可能性を認識しているため、念のためこのメソッドを定数時間にすることにしました。
Cgoのメモリ管理
指摘TOB-GOCL-3は、Go+BoringCrypto連携におけるメモリ管理の問題に関するものです。
crypto/ecdh: カスタムファイナライザが、このメモリを使用するC関数呼び出しの開始時にメモリを解放してしまう可能性がある(TOB-GOCL-3)
レビューの過程で、私たちのcgoベースのGo+BoringCrypto連携について多くの疑問が挙がりました。これはGoogle内部での利用向けにFIPS 140-2準拠の暗号モードを提供するものです。Go+BoringCryptoのコードはGoチームによって外部利用向けにサポートされているものではありませんが、Googleの内部でのGoの利用にとっては重要なものでした。
Trail of Bitsのチームは、Cライブラリと連携するために必要な手動のメモリ管理に起因する脆弱性を1件と、セキュリティには関係のないバグを1件発見しました。GoチームはこのコードのGoogle外部での利用をサポートしていないため、この問題に対してCVEやGo脆弱性データベースのエントリを発行しないことにしましたが、Go 1.24で修正しました。
このような落とし穴は、私たちがGo+BoringCrypto連携から移行することを決めた理由の一つです。私たちは、複雑なcgoのセマンティクスを避けて従来のGoのメモリモデルを使えるようにするため、通常の純粋なGoの暗号パッケージを使うネイティブFIPS 140-3モードの開発を進めてきました。
実装の網羅性
指摘TOB-GOCL-4とTOB-GOCL-5は、NIST SP 800-90AとRFC 8018という2つの仕様の限定的な実装に関するものです。
crypto/internal/fips140/drbg: CTR_DRBG APIには複数の誤用のリスクがある(TOB-GOCL-4)
私たちが導入するネイティブFIPS 140-3モードの一部として、準拠した乱数を提供するために、NISTのCTR_DRBG(AES-CTRベースの決定的乱数ビット生成器)の実装が必要でした。
私たちの用途で必要なのはNIST SP 800-90A Rev. 1のCTR_DRBGの機能のうちごく一部だけだったため、私たちは仕様全体を実装せず、特に導出関数とパーソナライゼーション文字列を省略しました。これらの機能は、一般的な文脈でDRBGを安全に使用するうえで重要となりうるものです。
私たちの実装は必要な特定のユースケースに厳密に絞り込まれており、また外部にエクスポートされているものでもないため、これは許容範囲であり、実装の複雑さを減らす価値があると判断しました。この実装が内部の他の用途に使われることは想定しておらず、こうした制限を詳しく説明する警告をドキュメントに追加しました。
crypto/pbkdf2: PBKDF2が出力長の制限を強制していない(TOB-GOCL-5)
Go 1.24で、私たちはgolang.org/x/cryptoから標準ライブラリへパッケージを移す作業を始めました。これによって、ファーストパーティで高品質かつ安定したGoの暗号パッケージが、特に理由もなく標準ライブラリの外に置かれているという紛らわしいパターンに終止符を打ちました。
この作業の一環として、私たちはgolang.org/x/crypto/pbkdf2パッケージを標準ライブラリへcrypto/pbkdf2として移しました。このパッケージをレビューする過程で、Trail of Bitsのチームは、RFC 8018で定義されている導出鍵のサイズの上限を私たちが強制していないことに気付きました。
この上限は(2^32 - 1) * <ハッシュ長>であり、これを超えると鍵がループしてしまいます。SHA-256を使用する場合、この上限を超えるには137GBを超える鍵が必要になります。これほど巨大な鍵を生成するためにPBKDF2を使った人はこれまでいないだろうと私たちは考えていますが、特にPBKDF2はすべてのブロックでイテレーションを実行するため、正しさのために、現在では標準で定義されている上限を強制するようにしています。
今後について
この監査の結果は、高品質で使いやすい暗号ライブラリを開発するためにGoチームが注いできた努力を裏付けるものであり、安全なソフトウェアを構築するためにこれらのライブラリに頼っているユーザーに安心感を提供するはずです。
とはいえ、私たちはこれに満足して立ち止まるつもりはありません。Goのコントリビューターは、提供している暗号ライブラリの開発と改善を続けています。
Go 1.24には、純粋なGoで書かれたFIPS 140-3モードが含まれており、現在CMVPのテストが進行中です。これによって、現在サポート対象外のGo+BoringCrypto連携に代わる、すべてのGoユーザー向けにサポートされるFIPS 140-3準拠モードが提供されることになります。
私たちはまた、最新のポスト量子暗号の実装にも取り組んでおり、crypto/mlkemパッケージにGo 1.24でML-KEM-768とML-KEM-1024の実装を導入し、crypto/tlsパッケージにハイブリッドのX25519MLKEM768鍵交換のサポートを追加しています。
最後に、私たちは基本的なユースケースに対して高品質なアルゴリズムを選び使用する際のハードルを下げるために設計された、より使いやすい高レベルの暗号APIの導入を計画しています。まずは、ユーザーが無数にある候補アルゴリズムのうちどれに頼るべきかを決める必要をなくすパスワードハッシュ化のシンプルなAPIの提供から始め、最先端の技術が変化するのに合わせて新しいアルゴリズムへ自動的に移行できる仕組みを備える予定です。
By Roland Shoemaker and Filippo Valsorda