メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

新機能

post-quantum署名アルゴリズムML-DSA(FIPS 204)を実装するcrypto/mldsaパッケージを新設し、crypto/x509・crypto/tlsにもML-DSA鍵・証明書・TLS 1.3署名のサポートを追加する。

New crypto/mldsa package

この項目の注釈は AI により生成されており、誤りを含む場合があります。
使用例のコンパイル検証: 検証済み

概要

新しいcrypto/mldsaパッケージが、FIPS 204で規定されたpost-quantum署名アルゴリズムML-DSAを実装する。Go 1.26で内部実装(crypto/internal/fips140/mldsa)として追加されていたML-DSAが、Go 1.27でようやく公開APIとして利用可能になる。

あわせてcrypto/x509がML-DSAの秘密鍵・公開鍵・署名に対応し、crypto/tlsもTLS 1.3のハンドシェイク署名としてMLDSA44MLDSA65MLDSA87の各SignatureScheme値をサポートする。これにより、量子コンピュータ耐性を持つデジタル署名を標準ライブラリのみで生成・検証でき、証明書の発行やTLS通信にも組み込めるようになる。

導入経緯

本機能は2件のproposalの合意内容を統合したものである。

  • #77626「crypto/mldsa: new package」は、Go 1.26で内部実装のみだったML-DSAを公開APIとしてcrypto/mldsaに切り出す提案である。提案者はGoのセキュリティチームの一員で、事前にfilippo.io/mldsaとしてプレビュー実装を公開し動作検証を済ませていた。
  • #78888「crypto/x509,crypto/tls: add ML-DSA support」は、#77626のAPIを前提としてcrypto/x509crypto/tlsにML-DSA対応を追加する提案である。量子コンピュータ(CRQC)の実用化タイムラインが前倒しになっていること、WebPKIが検討するMerkle Tree Certificatesへの移行が2027年頃になる見込みで、プライベートPKIの移行はそれより早急に必要であることが動機として挙げられている。コンポジット署名(従来署名との組み合わせ)は意図的にスコープ外とされた。

議論タイムライン

  • 2026-02-25: #77626がproposal reviewの active リストに追加される。
  • 2026-04-15: #77626が、ドキュメントコメントの修正を条件に likely accept と判定される。
  • 2026-04-22: #77626がコンセンサス変更なしとして accepted となる。同日、#78888が active リストに追加される。
  • 2026-04-29: #78888が likely accept と判定される。
  • 2026-05-06: #78888がコンセンサス変更なしとして accepted となる。

議論のハイライト

  • Parameters型はポインタでなく値型: MLDSA44()等がシングルトンを返す設計であることから、aclementsの指摘により*Parametersではなく値型Parametersを返すよう変更された。*MLDSA44() = MLDSA65()のような誤操作を防ぐためである。
  • 秘密鍵はseed形式のみサポート: NISTが規定した「semi-expanded形式」はサイズが大きく読み込みも遅い上に危険性が高いとして、Goは32バイトのseedのみをサポートする方針を採った。BoringSSLも同方針である。
  • HashML-DSAは非サポート: 外部ハッシュ済みメッセージの利用は、RFC 9881が推奨する「External μ」方式で代替する。これを表現するためcrypto.Hashに実装を持たないセンチネル値crypto.MLDSAMuを追加する設計とし、crypto.MD5SHA1に前例があるとされた。
  • SignDeterministicを独立メソッドにした理由: 決定論的署名はSignにのみ関係しVerifyとは無関係なため、両者で共有するOptions構造体のフィールドにはせず、専用メソッドとして分離した。
  • NewPrivateKeyの命名: 実体は seed のアンマーシャリング関数であり名前から分かりにくいという指摘があったが、crypto/ecdhCurve.NewPrivateKeycrypto/mlkemNewDecapsulationKey768に前例があるとして現状の名前を維持した。
  • X.509証明書サポートの分離: #77626の時点ではWebPKI・ブラウザ側のポスト量子署名の扱いが未定だったためコア機能のみとし、X.509対応は#78888として別提案にした。

使用例

Before

package main

import (
	"crypto/ed25519"
	"crypto/rand"
	"fmt"
	"log"
)

func main() {
	pub, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		log.Fatal(err)
	}

	message := []byte("hello, world")
	sig := ed25519.Sign(priv, message)

	if !ed25519.Verify(pub, message, sig) {
		log.Fatal("invalid signature")
	}
	fmt.Printf("public key: %d bytes, signature: %d bytes\n", len(pub), len(sig))
}

After

package main

import (
	"crypto/mldsa"
	"fmt"
	"log"
)

func main() {
	sk, err := mldsa.GenerateKey(mldsa.MLDSA44())
	if err != nil {
		log.Fatal(err)
	}

	message := []byte("hello, world")
	opts := &mldsa.Options{Context: "example"}
	sig, err := sk.Sign(nil, message, opts)
	if err != nil {
		log.Fatal(err)
	}

	pk := sk.PublicKey()
	if err := mldsa.Verify(pk, message, sig, opts); err != nil {
		log.Fatal("invalid signature: ", err)
	}
	fmt.Printf("public key: %d bytes, signature: %d bytes\n", len(pk.Bytes()), len(sig))
}

移行時の注意

  • 秘密鍵は32バイトのseedのみサポートする。semi-expanded形式には対応しない。
  • 署名時と検証時でOptions.Context(最大255バイト)を一致させる必要がある。
  • FIPS 140-3 Go Cryptographic Moduleのv1.0.0を使用している場合、crypto/mldsaは利用できず、GenerateKeyNewPrivateKeyNewPublicKeyVerifyはエラーを返す。v1.26.0以降であれば利用可能である。
  • crypto/x509crypto/tlsが対応するのはpure ML-DSA署名のみで、コンポジット署名(従来アルゴリズムとの組み合わせ)は対象外である。
  • crypto/tlsMLDSA44/MLDSA65/MLDSA870x09040x0906)はIANAのTLS SignatureSchemeレジストリに登録済みのコードポイントだが、対応するTLS仕様のdraft-ietf-tls-mldsa-02自体はまだドラフト段階である。

実装解説

crypto/mldsaの公開APIは、パラメータ表現と鍵・署名処理の2つのファイルに分かれている。

関連リンク