[インデックス 10668] ファイルの概要
このコミットは、Go言語の crypto/dsa
パッケージにおけるデジタル署名アルゴリズム (DSA) の実装において、入力ハッシュの切り捨て(truncate)を行わないように変更するものです。これにより、FIPS 186-3 標準の規定とは異なる動作をする libgcrypt
のような他のライブラリとの相互運用性が向上します。
コミット
- コミットハッシュ:
60f564fc3759a2d4cb2216ed643a65aa963f06b3
- Author: Adam Langley agl@golang.org
- Date: Thu Dec 8 16:46:19 2011 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/60f564fc3759a2d4cb2216ed643a65aa963f06b3
元コミット内容
crypto/dsa: don't truncate input hashes.
Although FIPS 186-3 says that we should truncate the hashes, at least
one other library (libgcrypt) doesn't. This means that it's impossible
to interoperate with code using gcrypt if we enforce the truncation
inside of crypto/dsa.
This change shouldn't actually affect anything because nearly
everybody pairs DSA with SHA1, which doesn't need to be truncated in
either case.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/5471043
変更の背景
デジタル署名アルゴリズム (DSA) は、メッセージの認証と完全性を保証するために広く使用される暗号技術です。米国国立標準技術研究所 (NIST) が発行する FIPS 186-3 (Digital Signature Standard) は、DSA の実装に関する詳細な仕様を定めています。FIPS 186-3 のセクション 4.6 では、DSA の署名生成プロセスにおいて、入力されるハッシュ値が特定の長さに切り捨てられるべきであると規定されています。
しかし、世の中には libgcrypt
のように、この FIPS 186-3 の規定に従わず、ハッシュの切り捨てを行わない暗号ライブラリも存在します。Go言語の crypto/dsa
パッケージが FIPS 186-3 の規定通りにハッシュの切り捨てを行う場合、libgcrypt
を使用して生成された署名を検証できない、あるいは libgcrypt
が Go で生成された署名を検証できないといった相互運用性の問題が発生します。
このコミットは、このような相互運用性の問題を解決するために行われました。FIPS 186-3 の規定よりも、実際の運用における他の主要なライブラリとの互換性を優先し、crypto/dsa
が入力ハッシュの切り捨てを行わないように変更されました。コミットメッセージにもあるように、DSA は通常 SHA-1 と組み合わせて使用されることが多く、SHA-1 の出力サイズは DSA のサブグループのバイト長と一致するため、実際にはハッシュの切り捨てが不要なケースがほとんどです。このため、今回の変更が既存のシステムに大きな影響を与える可能性は低いと判断されました。
前提知識の解説
DSA (Digital Signature Algorithm)
DSA は、公開鍵暗号方式に基づくデジタル署名スキームです。メッセージの送信者が秘密鍵で署名を生成し、受信者が対応する公開鍵でその署名を検証することで、メッセージの送信者の身元確認(認証)と、メッセージが途中で改ざんされていないこと(完全性)を保証します。DSA は、離散対数問題の困難性に基づいています。
FIPS 186-3 (Digital Signature Standard)
FIPS (Federal Information Processing Standards) は、米国政府が発行する情報処理標準です。FIPS 186-3 は、デジタル署名アルゴリズム (DSA)、楕円曲線デジタル署名アルゴリズム (ECDSA)、RSA 署名アルゴリズムの仕様を定めた標準です。この標準は、政府機関や関連組織が使用する暗号モジュールの要件を定義しており、セキュリティと相互運用性を確保することを目的としています。
特に、FIPS 186-3 のセクション 4.6 は「DSA Signature Generation」について記述しており、署名生成プロセスにおけるハッシュ値の取り扱いについて言及しています。このセクションでは、ハッシュ値が DSA のサブグループのバイト長に切り捨てられるべきであると規定されています。
ハッシュの切り捨て (Hash Truncation)
暗号学的ハッシュ関数は、任意の長さの入力データから固定長のハッシュ値(メッセージダイジェスト)を生成します。DSA のような署名アルゴリズムでは、このハッシュ値が署名生成の入力として使用されます。
FIPS 186-3 がハッシュの切り捨てを規定しているのは、DSA の数学的特性とセキュリティ要件に起因します。DSA の署名生成および検証には、特定のサイズの数値(サブグループの位数 q
のビット長)が使用されます。ハッシュ値がこのサイズよりも大きい場合、そのままでは計算に利用できないため、規定の長さに切り捨てることで、アルゴリズムの入力要件を満たし、セキュリティ上の問題を防ぐことを意図しています。
しかし、ハッシュ関数の出力サイズがすでにサブグループの位数 q
のビット長以下である場合、切り捨ては不要です。例えば、SHA-1 は 160 ビットのハッシュ値を生成し、これは多くの DSA パラメータセットにおける q
のビット長(通常 160 ビット)と一致するため、SHA-1 のハッシュ値は切り捨てなしで直接使用できます。
SHA-1
SHA-1 (Secure Hash Algorithm 1) は、NIST によって開発された暗号学的ハッシュ関数です。入力データから 160 ビット(20 バイト)のハッシュ値を生成します。かつては広く使用されていましたが、衝突攻撃の脆弱性が発見されたため、現在ではセキュリティが重要なアプリケーションでの使用は推奨されていません。しかし、このコミットが作成された 2011 年時点では、DSA と組み合わせて使用されることが一般的でした。
libgcrypt
libgcrypt
は、GNU Privacy Guard (GnuPG) プロジェクトの一部として開発された、汎用的な暗号ライブラリです。対称鍵暗号、公開鍵暗号、ハッシュ関数、乱数生成など、幅広い暗号機能を提供します。C言語で書かれており、多くのアプリケーションで利用されています。このライブラリが FIPS 186-3 のハッシュ切り捨て規定に従わない実装をしていたことが、Go の crypto/dsa
との相互運用性の問題を引き起こす原因となりました。
技術的詳細
このコミットの技術的な核心は、Go の crypto/dsa
パッケージが FIPS 186-3 の規定するハッシュの切り捨てロジックを削除し、入力されたハッシュ値をそのまま使用するように変更した点にあります。
変更前の Sign
関数と Verify
関数では、入力された hash []byte
の長さを n
という変数で調整し、hash[:n]
のようにスライスして使用していました。具体的には、DSA のサブグループのバイト長 n
と入力ハッシュの実際の長さ len(hash)
を比較し、n
が len(hash)
よりも大きい場合は n
を len(hash)
に設定していました。これは、ハッシュがサブグループのバイト長よりも短い場合に、ハッシュ全体を使用することを意味します。しかし、FIPS 186-3 の規定は、ハッシュがサブグループのバイト長よりも長い場合に、その長さに切り捨てることを求めていました。
このコミットでは、以下の行が削除されました。
if n > len(hash) {
n = len(hash)
}
そして、ハッシュ値を使用する箇所が hash[:n]
から hash
全体を使用する hash
に変更されました。
これにより、crypto/dsa
は入力されたハッシュ値をそのまま使用し、FIPS 186-3 が規定する切り捨てを行わなくなりました。この変更は、libgcrypt
のような、ハッシュの切り捨てを行わない他のライブラリとの互換性を確保することを目的としています。
コミットメッセージにもあるように、この変更は「SHA-1 と DSA の組み合わせ」という一般的なユースケースにおいては影響が少ないとされています。なぜなら、SHA-1 の出力サイズ (160ビット) は、DSA のサブグループの位数 q
の一般的なビット長 (160ビット) と一致するため、そもそも切り捨てが不要だからです。しかし、SHA-256 などのより長いハッシュ関数を使用する場合、FIPS 186-3 に厳密に従う実装とは異なる動作をすることになります。
また、Sign
関数と Verify
関数には、この変更を明確にするためのコメントが追加されました。
// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated
// to the byte-length of the subgroup. This function does not perform that
// truncation itself.
このコメントは、crypto/dsa
が FIPS 186-3 の特定の規定に従っていないことを明示し、開発者がこの挙動を認識できるようにしています。
コアとなるコードの変更箇所
diff --git a/src/pkg/crypto/dsa/dsa.go b/src/pkg/crypto/dsa/dsa.go
index a2adc7eb5c..be47846845 100644
--- a/src/pkg/crypto/dsa/dsa.go
+++ b/src/pkg/crypto/dsa/dsa.go
@@ -185,6 +185,10 @@ func GenerateKey(priv *PrivateKey, rand io.Reader) error {
// larger message) using the private key, priv. It returns the signature as a
// pair of integers. The security of the private key depends on the entropy of
// rand.
+//
+// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated
+// to the byte-length of the subgroup. This function does not perform that
+// truncation itself.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
// FIPS 186-3, section 4.6
@@ -218,10 +222,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
\t\tcontinue
\t}\n
-\t\tif n > len(hash) {\n-\t\t\tn = len(hash)\n-\t\t}\n-\t\tz := k.SetBytes(hash[:n])
+\t\tz := k.SetBytes(hash)
\ts := new(big.Int).Mul(priv.X, r)
\ts.Add(s, z)
@@ -238,7 +239,11 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
}\n
// Verify verifies the signature in r, s of hash using the public key, pub. It
-// returns true iff the signature is valid.
+// reports whether the signature is valid.
+//
+// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated
+// to the byte-length of the subgroup. This function does not perform that
+// truncation itself.
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
// FIPS 186-3, section 4.7
@@ -255,12 +260,7 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
\tif n&7 != 0 {\n \t\treturn false
\t}\n-\tn >>= 3\n-\n-\tif n > len(hash) {\n-\t\tn = len(hash)\n-\t}\n-\tz := new(big.Int).SetBytes(hash[:n])
+\t\tz := new(big.Int).SetBytes(hash)
\tu1 := new(big.Int).Mul(z, w)
\tu1.Mod(u1, pub.Q)
コアとなるコードの解説
このコミットでは、src/pkg/crypto/dsa/dsa.go
ファイル内の Sign
関数と Verify
関数が変更されています。
Sign
関数 (署名生成)
変更前:
if n > len(hash) {
n = len(hash)
}
z := k.SetBytes(hash[:n])
変更後:
z := k.SetBytes(hash)
- 削除されたロジック: 以前は、DSA のサブグループのバイト長
n
と入力ハッシュhash
の実際の長さlen(hash)
を比較し、n
がlen(hash)
よりも大きい場合にn
をlen(hash)
に設定していました。これは、ハッシュがサブグループのバイト長よりも短い場合に、ハッシュ全体を使用するためのものでした。しかし、FIPS 186-3 が規定する「ハッシュがサブグループのバイト長よりも長い場合に切り捨てる」というロジックは、この部分では明示的に行われていませんでしたが、hash[:n]
のスライスによって結果的にその効果が得られる可能性がありました。 - 変更されたハッシュの利用:
k.SetBytes(hash[:n])
がk.SetBytes(hash)
に変更されました。これにより、入力されたhash
スライス全体がk
(署名計算に使用されるbig.Int
オブジェクト) に設定されるようになります。つまり、ハッシュの長さに関わらず、切り捨てが行われなくなりました。 - 追加されたコメント:
Sign
関数のドキュメンテーションコメントに以下の注意書きが追加されました。
これは、この関数が FIPS 186-3 のハッシュ切り捨て規定に従わないことを明示しています。// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated // to the byte-length of the subgroup. This function does not perform that // truncation itself.
Verify
関数 (署名検証)
変更前:
n >>= 3
if n > len(hash) {
n = len(hash)
}
z := new(big.Int).SetBytes(hash[:n])
変更後:
z := new(big.Int).SetBytes(hash)
- 削除されたロジック:
Sign
関数と同様に、ハッシュの長さを調整するロジックが削除されました。n >>= 3
はビット長をバイト長に変換する処理ですが、その後のif n > len(hash)
のチェックとhash[:n]
のスライスが削除されました。 - 変更されたハッシュの利用:
new(big.Int).SetBytes(hash[:n])
がnew(big.Int).SetBytes(hash)
に変更されました。これにより、検証時にも入力されたhash
スライス全体がz
(検証計算に使用されるbig.Int
オブジェクト) に設定され、切り捨てが行われなくなります。 - 追加されたコメント:
Verify
関数のドキュメンテーションコメントにも、Sign
関数と同様の注意書きが追加されました。
これにより、検証時にも FIPS 186-3 のハッシュ切り捨て規定に従わないことが明確に示されます。// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated // to the byte-length of the subgroup. This function does not perform that // truncation itself.
これらの変更により、crypto/dsa
パッケージは、入力ハッシュの長さに依存せず、常にハッシュ全体を署名生成および検証の入力として使用するようになります。これは、FIPS 186-3 の厳密な解釈とは異なりますが、libgcrypt
のような他のライブラリとの相互運用性を優先した実用的な選択です。
関連リンク
- Go CL 5471043: https://golang.org/cl/5471043
参考にした情報源リンク
- FIPS 186-3, Digital Signature Standard (DSS) (ubc.ca): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHYL943mVppaxMew72K-akBUqYq1BaIFoI08-ixq-o0shHTnj_9MjdtQp759ZQI2erF56of9cOjY1GCvdhYc6DnqNRPKNeNEk5JKiWc_13bOcTfI2iAS5dsNe_lmNIa9aFw0vTK7yrQO0u0xVCuYZPReKTNuVFnUO5QkBrfHzB0nCuPZbpqKBj5jJaVp_bdR5S8ZpHiZgUY
- FIPS 186-3, Digital Signature Standard (DSS) (nist.gov): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGz7gX-u8CFJSV2Xo18lPixokwO2jxg8c7y6cdqwITE7ez9pkm8iCqHCYj-hjBmEW00xPOsSVsqJghl4WlRFGScWNIFXatff8Yz9zVm72enqXjuEs1Ktg3WGMHhxjDkefpTeOBzFU79I9bJYxdF-8Wt4IWjHWsG-1u_5pAudL8BXQ==