crypto/x509 と crypto/tls に耐量子計算機署名アルゴリズムML-DSA(FIPS 204)のサポートを追加した。
crypto/x509
概要
crypto/x509 と crypto/tls に、耐量子計算機署名アルゴリズムML-DSA(Module-Lattice-Based Digital Signature Algorithm、FIPS 204)のサポートが追加された。crypto/x509ではPublicKeyAlgorithmにMLDSA、SignatureAlgorithmにMLDSA44/MLDSA65/MLDSA87が加わり、証明書・証明書署名要求(CSR)・失効リストの生成やPKIX/PKCS#8形式でのシリアライズが*mldsa.PublicKey/*mldsa.PrivateKeyに対応した。crypto/tlsではSignatureSchemeにMLDSA44(0x0904)/MLDSA65(0x0905)/MLDSA87(0x0906)が加わり、ML-DSA証明書を使ったTLSハンドシェイクの署名検証・認証が行えるようになった。対象は純粋なML-DSA署名のみで、コンポジット署名(従来方式との組み合わせ)は対象外である。
導入経緯
量子コンピュータ(CRQC)の実用化タイムラインが従来予測より前倒しになったことを受けた提案。WebPKI全体はMerkle Tree Certificatesへ2027年頃に移行すると見込まれるが、これ自体もML-DSAの葉公開鍵を必要とする上、社内CAなどプライベートPKIの移行には間に合わない。提案者はコンポジット署名よりも純粋なML-DSAを優先する理由をTLS WGメーリングリストで説明している。本提案は#77626で追加されたcrypto/mldsaパッケージのAPIを土台とし、X.509仕様はRFC 9881、TLS仕様はdraft-ietf-tls-mldsa-02に準拠する。詳細は提案本文を参照。
議論タイムライン
- 2026-04-22: レビューグループの「active」カラムに追加され、週次proposal reviewでの審議対象になった(週次ダイジェスト)。
- 2026-04-29: 週次レビューで「likely accept」と判定された(週次ダイジェスト)。
- 2026-05-06: コンセンサスに変化がないことを確認した上で正式にacceptedとなった(週次ダイジェスト)。
議論のハイライト
- コンポジット署名(従来方式との組み合わせ)は本提案の対象外であり、需要がある場合は別提案とするよう明記されている。
- 提案者自身が「表面上はAPIが多く見えるが、実質は既存のenumへの値追加とswitch文の拡張に過ぎない」と説明しており、レビューでも大きな異論は出なかった。
- レビューア(aclements)から「
const MLDSA44 SignatureAlgorithm = iotaを3つ並べると全て0になる」という指摘があったが、これは既存のiota列挙への追加を意図した略記であり、実装上の問題ではないことが確認された。 - TLS仕様(draft-ietf-tls-mldsa-02)はIETF WG内の事情により正式RFCになっていないが、IANAのTLS SignatureSchemeレジストリへのコードポイント登録は完了しており、他実装も存在することから採用の障害にはならないと判断された。
- 秘密鍵は
#77626と同様、32バイトのseed形式のみをサポートする(展開形式は非対応)。
使用例
Before
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
)
func main() {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "example.com"},
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
if err != nil {
panic(err)
}
fmt.Printf("issued %d-byte ECDSA certificate\n", len(der))
}
After
package main
import (
"crypto/mldsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
)
func main() {
priv, err := mldsa.GenerateKey(mldsa.MLDSA65())
if err != nil {
panic(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "example.com"},
SignatureAlgorithm: x509.MLDSA65,
}
der, err := x509.CreateCertificate(rand.Reader, template, template, priv.Public(), priv)
if err != nil {
panic(err)
}
fmt.Printf("issued %d-byte ML-DSA-65 certificate\n", len(der))
}
移行時の注意
- 対応しているのは純粋なML-DSA署名のみであり、コンポジット(ハイブリッド)署名は非対応。ハイブリッド運用が必要な場合は別途対応を待つ必要がある。
- ML-DSA証明書チェーンを検証・発行するには通信相手(クライアント/サーバー)双方がML-DSA対応のTLS実装である必要がある。相手側が未対応の場合はハンドシェイクが成立しない。
crypto/mldsaの秘密鍵はseed形式(32バイト)のみサポートされ、展開済み鍵(semi-expanded key)の読み込みはできない。- TLS側の仕様(draft-ietf-tls-mldsa-02)はまだInternet-Draft段階であり、IANAのコードポイント割り当ては確定しているものの、将来の仕様改定で細部が変わる可能性がある。
実装解説
crypto/x509では、既存のPublicKeyAlgorithm/SignatureAlgorithmのenumにMLDSA系の値を追加し、公開鍵の型スイッチに*mldsa.PublicKeyの分岐を追加する形で実装されている(x509.go#L1115)。署名検証時には公開鍵のParameters()(MLDSA44/65/87のいずれか)と証明書のSignatureAlgorithmが一致するかをチェックしており、不一致の場合はエラーになる。
crypto/tls側では、typeAndHashFromSignatureSchemeでMLDSA44/MLDSA65/MLDSA87を内部のsignatureMLDSA種別にマップし(auth.go#L145)、Ed25519と同様にdirectSigning(事前ハッシュなし)として扱っている。ML-DSAはメッセージを直接署名するアルゴリズムであり、TLS 1.3の署名検証パスをEd25519と共通化できる設計になっている。
crypto/mldsaのGenerateKeyはパラメータセットに応じて内部実装(crypto/internal/fips140/mldsa)のGenerateKey44/65/87に振り分ける薄いラッパーになっている(mldsa_fips140v1.26.go#L27)。*mldsa.PrivateKeyはcrypto.Signer(Public()/Sign())を実装しており、crypto/x509のCreateCertificateなど既存のAPIにそのまま渡せる。