crypto/mldsa: new package
要約
概要
crypto/mldsaパッケージを新たにGo標準ライブラリに追加するproposalです。Go 1.26で内部実装されたポスト量子署名アルゴリズムML-DSA(FIPS 204規格)を、Go 1.27でパブリックAPIとして公開することを提案しています。
ステータス変更
likely_accept → accepted
2026年4月15日のProposal Review Meetingで「likely accept」に移行し、同22日のミーティングで追加のコンセンサス変更がなかったとして正式にacceptedとなりました。@aclementsがProposal Review Groupを代表して、Issue本文上部のAPIに2つのドキュメントコメントの微修正を加えた形で受理を宣言しています。
技術的背景
現状の問題点
Go 1.26時点でML-DSAの実装はcrypto/internal/fips140/mldsaとして内部パッケージにのみ存在しており、一般の開発者が利用できませんでした。量子コンピュータの脅威に対応するポスト量子暗号(PQC)は業界全体で急速に普及しており、Go標準ライブラリがこれを提供しないことは、サードパーティライブラリへの依存や実装の断片化を招いていました。
ML-DSAはNISTが2024年にFIPS 204として標準化したデジタル署名アルゴリズムで、格子問題(Module Lattice)に基づく安全性を持ち、CRYSTALS-Dilithiumを前身とします。政府・金融など規制産業ではCNSA 2.0(米国国家安全保障システムのアルゴリズム要件)への対応が求められています。
提案された解決策
新パッケージcrypto/mldsaを追加し、以下のAPIを提供します。
パラメータセット(FIPS 204で定義された3種類):
MLDSA44()— 多くのアプリケーションで推奨、公開鍵1312バイト、署名2420バイトMLDSA65()— 公開鍵1952バイト、署名3309バイトMLDSA87()— 公開鍵2592バイト、署名4627バイト
主要な型・関数:GenerateKey(params Parameters) (*PrivateKey, error)— ランダムな秘密鍵生成NewPrivateKey(params Parameters, seed []byte) (*PrivateKey, error)— シードから秘密鍵を復元(32バイトのシードを使用)PrivateKey.Sign()—crypto.Signerインターフェースを実装するランダム化署名PrivateKey.SignDeterministic()— 決定論的署名(同じ入力に対して常に同じ署名を生成)Verify(pk, message, signature, opts)— 署名検証
またcryptoパッケージにMLDSAMuという新しいHash定数を追加し、External μ(事前ハッシュ化メッセージ代表値)をcrypto.Signer経由でハードウェア実装から使用できるようにします。
さらに、crypto/x509のMarshalPKCS8PrivateKey・ParsePKCS8PrivateKey・MarshalPKIXPublicKey・ParsePKIXPublicKeyを拡張し、RFC 9881に従ったPKIX/PKCS#8形式でのML-DSA鍵のパース・シリアライズをサポートします。
これによって何ができるようになるか
- ポスト量子署名の標準的な実装 — サードパーティライブラリ(Cloudflare CIRCL等)に依存せず、Go標準ライブラリだけで量子耐性のあるデジタル署名を実装できます。
- FIPS 140-3準拠システムでの利用 — FIPSモジュールv1.26.0以降を使用する場合に、規制要件を満たす形でML-DSAを使用できます。
- ハードウェアセキュリティモジュール(HSM)との連携 —
crypto.Signerインターフェースを実装しているため、HSMなどのハードウェア実装との統合が可能です。
コード例
// Before: サードパーティライブラリを使用する必要があった
import "github.com/cloudflare/circl/sign/mldsa/mldsa44"
// 各ライブラリで異なるAPIを使用
pk, sk, err := mldsa44.GenerateKey(rand.Reader)
// ...
// After: 標準ライブラリのみで実装可能
import "crypto/mldsa"
// 鍵生成
sk, err := mldsa.GenerateKey(mldsa.MLDSA44())
if err != nil {
return err
}
pk := sk.PublicKey()
// 署名(ランダム化)
opts := &mldsa.Options{Context: "my-app-v1"} // コンテキストでドメイン分離
sig, err := sk.Sign(nil, message, opts)
// 決定論的署名
sig, err = sk.SignDeterministic(message, opts)
// 検証
err = mldsa.Verify(pk, message, sig, opts)
// PKCS#8形式でのシリアライズ(crypto/x509経由)
der, err := x509.MarshalPKCS8PrivateKey(sk)
議論のハイライト
Parameters型のポインタ vs 値渡し:@aclementsの指摘により、MLDSA44()等の戻り値を*ParametersからParameters(値型)に変更。シングルトンを返す関数では*MLDSA44() = MLDSA65()のような誤操作を防ぐため、値型が適切と判断されました。- 秘密鍵フォーマットの選択: NISTが標準化した「セミ展開形式(semi-expanded format)」はサイズが大きく読み込みも遅い上に危険性が高いため、Goはシード形式のみをサポートする方針を採用しました。BoringSSLも同方針を取っています。
HashML-DSA(事前ハッシュ化モード)を採用しない理由: ML-DSAには多数のパラメータセットと組み合わせが存在し、RFC 9881もExternal μによる外部ハッシュ化を推奨する方向でまとまったため、HashML-DSAモードはサポートしないことになりました。NewPrivateKeyの命名問題:@aclementsは「実際にはアンマーシャリング関数なのに名前からわからない」と指摘しました。@FiloSottileはcrypto/ecdhのCurve.NewPrivateKeyやcrypto/mlkemのNewDecapsulationKey768などの先例があることを示し、現在の命名を維持することで決着しました。- 決定論的署名を
Optionsフィールドではなく別メソッドにした理由: 決定論的署名は署名時にのみ関係し、Options構造体はSignとVerifyの両方で共有されるため、SignDeterministicメソッドとして分離しました。 - X.509証明書サポートを今回含めない理由: WebPKIやブラウザを含む主要プレイヤーがMLDSAをX.509証明書でどう扱うか未定であり、標準化の議論が進行中のため、コア機能から始める判断をしました。