> Source: https://www.ymotongpoo.com/works/goblog-ja/fips140/


# FIPS 140-3 Go暗号モジュール

[The FIPS 140-3 Go Cryptographic Module](https://go.dev/blog/fips140) by Filippo Valsorda (Geomys), Daniel McCarney (Geomys), and Roland Shoemaker (Google)

FIPS 140は暗号実装のための標準規格です。必ずしもセキュリティを向上させるものではありませんが、
FIPS 140への準拠は、Goの採用が進んでいる一部の規制環境において要件となっています。
これまでFIPS 140準拠は、Goユーザーにとって大きな摩擦の種でした。安全性、開発者体験、機能性、
リリース速度、コンプライアンスの面で問題を抱える、非サポートのソリューションを必要としていたからです。

Goはこの高まるニーズに対して、標準ライブラリと `go` コマンドに直接組み込まれたネイティブなFIPS 140
サポートで応えています。これにより、GoはFIPS 140に準拠する最も簡単で安全な方法になりました。
FIPS 140-3の検証を受けたGo Cryptographic Moduleは、今やGo組み込みの暗号ライブラリの基盤となっています。
これは、昨年2月にリリースされたGo 1.24に含まれるGo Cryptographic Module v1.0.0から始まりました。

v1.0.0モジュールは[Cryptographic Algorithm Validation Program(CAVP)証明書A6650](https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/details?validation=39260)を取得し、Cryptographic
Module Validation Program(CMVP)に提出され、5月には[Modules In Processリスト](https://csrc.nist.gov/Projects/cryptographic-module-validation-program/modules-in-process/modules-in-process-list)に掲載されました。
MIPリストに掲載されたモジュールはNISTのレビュー待ちの状態ですが、一部の規制環境では
すでに導入可能です。

[Geomys](https://geomys.org)は、Go Security Teamと協力してこの実装作業を主導し、Goコミュニティ全体の利益のために、
幅広く適用可能なFIPS 140-3検証の取得を進めています。GoogleをはじめとするいくつかのステークホルダーはGeomysと
契約関係を結び、証明書に特定のOperating Environment(動作環境)を含めています。

モジュールの詳細については[ドキュメント](https://go.dev/doc/security/fips140)を参照してください。

一部のGoユーザーは現在、FIPS 140準拠戦略の一環として、[Go+BoringCrypto](https://go.dev/doc/security/fips140#goboringcrypto)というGOEXPERIMENT、
あるいはそのフォークの一つに依存しています。FIPS 140-3対応のGo Cryptographic Moduleとは異なり、
Go+BoringCryptoは公式にサポートされたことは一度もなく、もっぱらGoogle社内のニーズのためだけに
作られたものだったため、開発者体験の面で大きな問題を抱えていました。Googleがネイティブモジュールへの
移行を完了次第、将来のリリースで削除される予定です。

## ネイティブな開発者体験

このモジュールはGoアプリケーションに完全に透過的な形で統合されています。実際、Go 1.24でビルドされた
すべてのGoプログラムは、FIPS 140-3で承認されたすべてのアルゴリズムに対して、すでにこのモジュールを
使用しています!このモジュールは、標準ライブラリの `crypto/internal/fips140/...` パッケージの別名に
すぎません。これらのパッケージは、 `crypto/ecdsa` や `crypto/rand` といったパッケージが公開する
処理の実装を提供しています。

これらのパッケージはcgoを一切使用していません。つまり、他の通常のGoプログラムと同様にクロスコンパイルが
可能で、FFIによるパフォーマンスのオーバーヘッドを払う必要がなく、Go+BoringCryptoやそのフォークとは異なり、
[メモリ管理に起因するセキュリティ上の問題](https://go.dev/blog/tob-crypto-audit#cgo-memory-management)に
悩まされることもありません。

Goのバイナリを起動する際、[GODEBUGオプション](https://go.dev/doc/godebug)である `fips140=on` を使うことで、
モジュールをFIPS 140-3モードにできます。これは環境変数として設定することも、 `go.mod` ファイル経由で
設定することも可能です。FIPS 140-3モードが有効な場合、モジュールは乱数生成にNISTのDRBGを使用し、
`crypto/tls` は自動的にFIPS 140-3で承認されたTLSのバージョンとアルゴリズムのみをネゴシエートするように
なり、初期化時と鍵生成時に必須のセルフテストを実行します。動作の違いはそれだけで、他にはありません。

実験的なより厳格なモードとして `fips140=only` も用意されています。これは承認されていないすべての
アルゴリズムがエラーを返すかパニックを起こすようにするものです。私たちは、これがほとんどのデプロイに
とって柔軟性に欠けすぎる可能性があると理解しており、ポリシー強制の仕組みがどのような形であるべきかについて
[フィードバックを募集しています](https://go.dev/issue/74630)。

最後に、アプリケーションは[`GOFIPS140` 環境変数](https://go.dev/doc/security/fips140#the-gofips140-environment-variable)を
使うことで、より古い、検証済みバージョンの `crypto/internal/fips140/...` パッケージに対してビルドできます。
`GOFIPS140` は `GOOS` や `GOARCH` と同じように動作し、 `GOFIPS140=v1.0.0` に設定すると、CMVPへの検証提出
時点のv1.0.0スナップショットのパッケージに対してプログラムがビルドされます。このスナップショットは、
Go標準ライブラリの他の部分と一緒に `lib/fips140/v1.0.0.zip` として同梱されています。

`GOFIPS140` を使用する場合、 `fips140` のGODEBUGはデフォルトで `on` になります。つまりまとめると、
FIPS 140-3モジュールに対してビルドし、FIPS 140-3モードで実行するために必要なのは
`GOFIPS140=v1.0.0 go build` だけです。それだけです。

ツールチェイン自体が `GOFIPS140` を設定した状態でビルドされている場合、そのツールチェインが生成する
すべてのビルドは、デフォルトでその値を使うようになります。

バイナリのビルドに使われた `GOFIPS140` のバージョンは、 `go version -m` で確認できます。

今後のバージョンのGoも、Geomysによって次のバージョンが完全に認証されるまでは、引き続きGo Cryptographic
Moduleのv1.0.0を同梱し、それと共に動作し続けます。ただし、古いモジュールに対してビルドする場合、
新しい暗号機能の一部が利用できないことがあります。Go 1.24.3からは、 `GOFIPS140=inprocess` を使うことで、
Geomysの検証がIn Processの段階に達している最新のモジュールを動的に選択できます。GeomysはFIPS 140ビルドが
あまりにも古い状態のまま取り残されないよう、少なくとも年に1回、また呼び出し側の標準ライブラリのコード側では
緩和できないモジュール内の脆弱性が見つかるたびに、新しいバージョンのモジュールを検証していく計画です。

## 妥協のないセキュリティ

このモジュールを開発する上で私たちが最優先してきたのは、既存のGo標準ライブラリの暗号パッケージの
セキュリティに匹敵する、あるいはそれを上回ることでした。意外に思われるかもしれませんが、FIPS 140の
セキュリティ要件への準拠を達成し、それを証明する最も簡単な方法は、その要件を上回らないことである場合が
あります。私たちはその道を選びませんでした。

例えば、 `crypto/ecdsa` は[常にヘッジ署名(hedged signatures)を生成していました](https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/crypto/ecdsa/ecdsa.go;l=417)。
ヘッジ署名は、秘密鍵、メッセージ、そしてランダムなバイト列を組み合わせてナンスを生成します。
[決定的ECDSA(deterministic ECDSA)](https://www.rfc-editor.org/rfc/rfc6979)と同様、乱数生成器が故障した
場合の保護になります。そうでなければ秘密鍵が漏洩してしまいます(!)。決定的ECDSAとは異なり、ヘッジ署名は
[APIの問題](https://github.com/MystenLabs/ed25519-unsafe-libs)や[フォールト攻撃](https://en.wikipedia.org/wiki/Differential_fault_analysis)
にも耐性があり、メッセージの一致を漏らすこともありません。FIPS 186-5では[RFC 6979](https://www.rfc-editor.org/rfc/rfc6979)
の決定的ECDSAへのサポートが導入されましたが、ヘッジECDSAへのサポートは導入されませんでした。

FIPS 140-3モードにおいて(あるいはさらに悪いことに、モードをまたいで)通常のランダム化されたECDSA署名や
決定的ECDSA署名にダウングレードする代わりに、私たちは[ヘッジのアルゴリズムを切り替え](https://github.com/golang/go/commit/9776d028f4b99b9a935dae9f63f32871b77c49af)、
半ダースにも及ぶ文書の内容をつなぎ合わせることで、[新しいアルゴリズムがDRBGと従来のECDSAの準拠した組み合わせで
あることを証明しました](https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6#issuecomment-2067819904)。
その過程で、[決定的署名へのオプトイン対応も追加](https://go.dev/doc/go1.24#cryptoecdsapkgcryptoecdsa)しました。

もう一つの例は乱数生成です。FIPS 140-3には暗号学的な乱数の生成方法について厳格な規則があり、実質的に
ユーザー空間の[CSPRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator)
の使用を強制しています。しかし私たちは、安全な乱数バイト列の生成に最も適しているのはカーネルであると
考えています。なぜならカーネルはシステムからエントロピーを収集するのに最も適した立場にあり、プロセスや
仮想マシンがクローンされたとき(これは本来ランダムであるはずのバイト列の再利用につながりかねません)を
検知するのにも最も適しているからです。そのため、[`crypto/rand`](https://pkg.go.dev/crypto/rand)はすべての
読み出し処理をカーネルへとルーティングします。

この矛盾を解消するために、FIPS 140-3モードでは、AES-256-CTRをベースにした準拠のユーザー空間NIST DRBGを
維持しつつ、読み出し操作のたびにカーネル由来の128ビットをそこに注入しています。この追加のエントロピーは
FIPS 140-3の観点では「クレジットされない(uncredited)」追加データとして扱われますが、実際にはこれによって、
多少遅くなるとしても、カーネルから直接読み出すのと同程度に強固になっています。

最後に、Go Cryptographic Module v1.0.0の全体が、[Trail of Bitsによる先日のセキュリティ監査](https://go.dev/blog/tob-crypto-audit)
の対象範囲に含まれており、唯一の非informational(参考情報ではない)所見の影響を受けていませんでした。

Goのコンパイラとランタイムが提供するメモリ安全性の保証と合わせて、これによって、GoをFIPS 140準拠のための
最も簡単で安全なソリューションの一つにするという私たちの目標を達成できたと考えています。

## 幅広いプラットフォームサポート

FIPS 140-3モジュールは、テスト済みまたは「ベンダー確認済み(Vendor Affirmed)」のOperating Environment
(基本的にはオペレーティングシステムとハードウェアプラットフォームの組み合わせ)で運用された場合にのみ
準拠しているとみなされます。できるだけ多くのGoのユースケースに対応するため、Geomysの検証は
[業界でも最も包括的な部類に入るOperating Environmentの集合](https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/details?product=19371&displayMode=Aggregated)
に対してテストされています。

Geomysの検査機関は、さまざまなLinuxディストリビューション(Podman上のAlpine Linux、Amazon Linux、
Google Prodimage、Oracle Linux、Red Hat Enterprise Linux、SUSE Linux Enterprise Server)、macOS、
Windows、FreeBSDを、x86-64(AMDおよびIntel)、ARMv8/9(Ampere Altra、Apple M、AWS Graviton、
Qualcomm Snapdragon)、ARMv7、MIPS、z/Architecture、POWERといった様々なハードウェアの組み合わせで
テストし、合計23のテスト済み環境を実現しました。

これらのうち一部はステークホルダーによって費用が負担され、その他はGoコミュニティの利益のためにGeomysが
資金を提供しました。

さらに、Geomysの検証では、ベンダー確認済みのOperating Environmentとして、幅広い汎用プラットフォームの
集合が挙げられています。

* x86-64およびARMv7/8/9上のLinux 3.10以降
* Apple Mプロセッサ上のmacOS 11〜15
* x86-64上のFreeBSD 12〜14
* x86-64上のWindows 10およびWindows Server 2016〜2022
* x86-64およびARMv8/9上のWindows 11およびWindows Server 2025

## 包括的なアルゴリズムのカバレッジ

意外に思われるかもしれませんが、サポートされたOperating Environment上でFIPS 140-3モジュールによって
実装されたFIPS 140-3承認済みアルゴリズムを使用するだけでは、必ずしも準拠には十分ではありません。
そのアルゴリズムが検証の一環として明確にテストの対象になっている必要があります。そのため、GoでFIPS 140
準拠のアプリケーションをできるだけ簡単に構築できるようにするため、標準ライブラリ内のFIPS 140-3承認済み
アルゴリズムはすべてGo Cryptographic Moduleによって実装されており、デジタル署名からTLSのキースケジュール
に至るまで、検証の一環としてテストされています。

[Go 1.24で導入された](https://go.dev/doc/go1.24#crypto-mlkem)耐量子計算機暗号のML-KEM鍵交換(FIPS 203)も
検証済みです。つまり `crypto/tls` は、X25519MLKEM768を用いたFIPS 140-3準拠の耐量子計算機セキュアな
接続を確立できます。

場合によっては、同じアルゴリズムを異なる複数のNISTの名称の下で検証し、それぞれ異なる用途において完全に
準拠した形で使用できるようにしました。例えば、[HKDFは4つの名称の下でテストおよび検証されています](https://words.filippo.io/dispatches/fips-hkdf/)。
SP 800-108 Feedback KDF、SP 800-56C two-step KDF、Implementation Guidance D.P OneStepNoCounter KDF、
そしてSP 800-133 Section 6.3 KDFです。

最後に、[XAES-256-GCM](https://c2sp.org/XAES-256-GCM)のような将来の機能を公開できるようにするため、
CMAC Counter KDFなどの内部アルゴリズムについても検証を行いました。

全体として、このネイティブなFIPS 140-3モジュールは、Go+BoringCryptoよりも優れたコンプライアンスの
プロファイルを提供しつつ、FIPS 140-3に制約されたアプリケーションでより多くのアルゴリズムを利用できるように
しています。

この新しいネイティブなGo Cryptographic Moduleによって、Goの開発者がFIPS 140準拠のワークロードを
より簡単かつ安全に実行できるようになることを楽しみにしています。

By Filippo Valsorda (Geomys), Daniel McCarney (Geomys), and Roland Shoemaker (Google)

