[インデックス 16652] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/sha512 パッケージに、トップレベルの Sum512 および Sum384 関数を追加し、ハッシュ計算をより簡潔に行えるようにすることを目的としています。また、crypto/sha256 パッケージの Sum256 関数のコメントとテスト出力の修正も含まれています。これにより、ユーザーは特定のデータに対するハッシュ値を簡単に取得できるようになります。
コミット
commit fa7e46c88481b06420191460e47d9c9c512a1f94
Author: Rob Pike <r@golang.org>
Date: Wed Jun 26 13:14:11 2013 -0700
crypto/sha512: provide top-level Sum512 and Sum384 functions
Makes it easy to ask the simple question, what is the hash of this data?
Also fix the commentary and prints in Sum256.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/10630043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fa7e46c88481b06420191460e47d9c9c512a1f94
元コミット内容
crypto/sha512: トップレベルの Sum512 および Sum384 関数を提供する。
これにより、このデータのハッシュは何ですか?という単純な質問を簡単に尋ねられるようになる。
また、Sum256 のコメントと出力も修正する。
変更の背景
Go言語の crypto パッケージにおけるハッシュ関数の一般的な利用パターンは、hash.Hash インターフェースを実装した型(例: sha256.New(), sha512.New()) を作成し、Write メソッドでデータを供給し、最後に Sum メソッドでハッシュ値を取得するというものです。この方法は、ストリーミングデータや大量のデータを段階的にハッシュ化する場合に非常に柔軟で強力です。
しかし、コミットメッセージにあるように、「このデータのハッシュは何ですか?」というような、単一のバイトスライス全体を一度にハッシュ化したいという単純なユースケースにおいては、New() でハッシュオブジェクトを作成し、Write() でデータを書き込み、Sum() で結果を取得するという一連のステップは、冗長に感じられることがあります。
このコミットの背景には、このような一般的な、かつ簡潔なハッシュ計算のニーズに応えるため、sha256 パッケージに既に存在していた Sum256 関数と同様の、トップレベルのコンビニエンス関数 (Sum512, Sum384) を sha512 パッケージにも提供することで、APIの使いやすさを向上させるという意図があります。これにより、ユーザーはより直感的にハッシュ計算を実行できるようになります。
また、sha256 パッケージにおける Sum 関数のコメントが Sum256 に変更されたこと、およびテスト出力の修正は、APIの一貫性と明確性を高めるためのクリーンアップ作業の一環です。
前提知識の解説
1. 暗号学的ハッシュ関数 (Cryptographic Hash Function)
暗号学的ハッシュ関数は、任意のサイズの入力データ(メッセージ)を受け取り、固定サイズの出力(ハッシュ値、メッセージダイジェスト、フィンガープリントとも呼ばれる)を生成する数学的なアルゴリズムです。主な特性は以下の通りです。
- 決定性: 同じ入力に対しては常に同じハッシュ値が生成されます。
- 計算効率: ハッシュ値の計算は高速です。
- 原像計算困難性 (Pre-image Resistance): ハッシュ値から元の入力データを復元することは計算上不可能です。
- 第二原像計算困難性 (Second Pre-image Resistance): ある入力データと同じハッシュ値を持つ別の入力データを見つけることは計算上不可能です。
- 衝突耐性 (Collision Resistance): 異なる2つの入力データが同じハッシュ値を持つこと(衝突)は計算上不可能です。
これらの特性により、暗号学的ハッシュ関数は、データの完全性検証、デジタル署名、パスワードの保存など、様々なセキュリティ関連の用途で利用されます。
2. SHA-2 (Secure Hash Algorithm 2) ファミリー
SHA-2は、アメリカ国家安全保障局(NSA)によって設計された暗号学的ハッシュ関数のファミリーです。このファミリーには、SHA-256、SHA-384、SHA-512など、異なるハッシュ値の長さを生成する複数のバージョンが含まれます。
- SHA-256: 256ビット(32バイト)のハッシュ値を生成します。ビットコインなどの多くのブロックチェーン技術で利用されています。
- SHA-512: 512ビット(64バイト)のハッシュ値を生成します。SHA-256よりも長いハッシュ値を生成するため、より高いセキュリティレベルが求められる場合に利用されます。
- SHA-384: SHA-512と同じアルゴリズムを使用しますが、出力されるハッシュ値は384ビット(48バイト)に切り詰められます。これは、SHA-512のセキュリティ強度を維持しつつ、より短いハッシュ値が必要な場合に選択されます。
3. Go言語の crypto パッケージと hash.Hash インターフェース
Go言語の標準ライブラリには、暗号学的ハッシュ関数を実装した crypto パッケージ群(例: crypto/sha256, crypto/sha512)が含まれています。これらのパッケージは、hash パッケージで定義されている hash.Hash インターフェースを実装しています。
hash.Hash インターフェースは以下のメソッドを定義しています。
type Hash interface {
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.
io.Writer
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
Sum(b []byte) []byte
// Reset resets the Hash to its initial state.
Reset()
// Size returns the the number of bytes Sum will return.
Size() int
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount of data, but it may operate more efficiently if all writes are a multiple of the BlockSize.
BlockSize() int
}
このインターフェースにより、Goのハッシュ関数は統一された方法で利用できます。通常、ハッシュ計算は以下のように行われます。
import (
"crypto/sha256"
"fmt"
)
func main() {
h := sha256.New() // ハッシュオブジェクトの作成
h.Write([]byte("Hello, world!")) // データの書き込み
hashValue := h.Sum(nil) // ハッシュ値の取得
fmt.Printf("%x\n", hashValue)
}
このコミットで追加される Sum512 や Sum384 のようなトップレベル関数は、上記の New(), Write(), Sum() の一連の操作を単一の関数呼び出しで完結させるための「コンビニエンス関数」として提供されます。
技術的詳細
このコミットの主要な変更点は、crypto/sha512 パッケージに Sum512 と Sum384 という2つの新しいトップレベル関数が追加されたことです。これらの関数は、入力されたバイトスライス全体のハッシュ値を直接計算して返します。
crypto/sha512 の変更点
-
checkSum()メソッドの分離と再利用: 以前のdigest型のSumメソッドは、ハッシュ計算の最終処理と結果の整形を同時に行っていました。このコミットでは、ハッシュ計算のコアロジック(パディング、最終ブロック処理、ハッシュ値の生成)をcheckSum()という新しいプライベートメソッドとして分離しました。 これにより、SumメソッドはcheckSum()を呼び出してハッシュ値を取得し、その結果をinスライスに追加して返すという役割に特化しました。 新しいSum512およびSum384関数も、このcheckSum()メソッドを内部的に利用することで、コードの重複を避け、ロジックの一貫性を保っています。- 変更前 (
Sumメソッドの一部):func (d0 *digest) Sum(in []byte) []byte { // ... (padding and final block processing) ... // ... (hash value calculation into 'digest' array) ... return append(in, digest[:size]...) } - 変更後 (
Sumメソッドと新しいcheckSumメソッド):func (d0 *digest) Sum(in []byte) []byte { // ... (copy d0 to d) ... hash := d.checkSum() // 新しいcheckSum()を呼び出し if d.is384 { return append(in, hash[:Size384]...) } return append(in, hash[:]...) } func (d *digest) checkSum() [Size]byte { // ... (padding and final block processing) ... // ... (hash value calculation into 'digest' array) ... return digest // 計算されたハッシュ値を返す }
このリファクタリングにより、
Sumメソッドはhash.Hashインターフェースの要件(Sum(b []byte) []byte)を満たしつつ、内部的なハッシュ計算ロジックを再利用可能にしています。 - 変更前 (
-
Sum512(data []byte) [Size]byte関数の追加: この関数は、入力dataのSHA-512ハッシュ値を計算し、[Size]byte(64バイト配列) として返します。 内部的には、新しいdigestオブジェクトを作成し、Reset()で初期化し、Write(data)でデータを書き込み、最後にcheckSum()を呼び出してハッシュ値を取得します。 -
Sum384(data []byte) (sum384 [Size384]byte)関数の追加: この関数は、入力dataのSHA-384ハッシュ値を計算し、[Size384]byte(48バイト配列) として返します。Sum512と同様にdigestオブジェクトを作成しますが、d.is384 = trueを設定してSHA-384モードであることを指定します。その後、Reset()、Write(data)を行い、checkSum()で512ビットのハッシュ値を取得した後、copy関数を使って先頭の384ビット(Size384バイト)をsum384配列にコピーして返します。
crypto/sha256 の変更点
-
Sum関数のコメント修正:sha256.go内のSum関数のコメントが// Sum returns the SHA256 checksum of the data.から// Sum256 returns the SHA256 checksum of the data.に変更されました。これは、関数名がSum256であることに合わせて、コメントもより具体的に記述することで、APIの明確性を高めるためです。 -
テスト出力の修正:
sha256_test.go内のテストコードで、エラーメッセージ内の関数名がSum functionからSum256 functionに変更されました。これにより、テスト失敗時のメッセージが実際の関数名と一致し、デバッグが容易になります。
これらの変更により、Goのハッシュパッケージは、ストリーミング処理のための New().Write().Sum() パターンと、単一のデータブロックのための SumXXX() パターンの両方を提供し、ユーザーの多様なニーズに対応できるようになりました。
コアとなるコードの変更箇所
src/pkg/crypto/sha256/sha256.go
--- a/src/pkg/crypto/sha256/sha256.go
+++ b/src/pkg/crypto/sha256/sha256.go
@@ -179,7 +179,7 @@ func (d *digest) checkSum() [Size]byte {
return digest
}
-// Sum returns the SHA256 checksum of the data.
+// Sum256 returns the SHA256 checksum of the data.
func Sum256(data []byte) [Size]byte {
var d digest
d.Reset()
src/pkg/crypto/sha256/sha256_test.go
--- a/src/pkg/crypto/sha256/sha256_test.go
+++ b/src/pkg/crypto/sha256/sha256_test.go
@@ -90,7 +90,7 @@ func TestGolden(t *testing.T) {
g := golden[i]
s := fmt.Sprintf("%x", Sum256([]byte(g.in)))
if s != g.out {
- t.Fatalf("Sum function: sha256(%s) = %s want %s", g.in, s, g.out)
+ t.Fatalf("Sum256 function: sha256(%s) = %s want %s", g.in, s, g.out)
}
c := New()
for j := 0; j < 3; j++ {
src/pkg/crypto/sha512/sha512.go
--- a/src/pkg/crypto/sha512/sha512.go
+++ b/src/pkg/crypto/sha512/sha512.go
@@ -135,7 +135,14 @@ func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := new(digest)
*d = *d0
+ hash := d.checkSum()
+ if d.is384 {
+ return append(in, hash[:Size384]...)
+ }
+ return append(in, hash[:]...)
+}
+func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 112 bytes mod 128.
len := d.len
var tmp [128]byte
@@ -158,10 +165,8 @@ func (d0 *digest) Sum(in []byte) []byte {
}
h := d.h[:]
- size := Size
if d.is384 {
h = d.h[:6]
- size = Size384
}
var digest [Size]byte
@@ -176,5 +181,24 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*8+7] = byte(s)
}
- return append(in, digest[:size]...)\n
+ return digest
+}
+
+// Sum returns the SHA512 checksum of the data.
+func Sum512(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
+}
+
+// Sum384 returns the SHA384 checksum of the data.
+func Sum384(data []byte) (sum384 [Size384]byte) {
+ var d digest
+ d.is384 = true
+ d.Reset()
+ d.Write(data)
+ sum := d.checkSum()
+ copy(sum384[:], sum[:Size384])
+ return
}
src/pkg/crypto/sha512/sha512_test.go
--- a/src/pkg/crypto/sha512/sha512_test.go
+++ b/src/pkg/crypto/sha512/sha512_test.go
@@ -88,6 +88,10 @@ var golden384 = []sha512Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum512([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum512 function: sha512(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
@@ -106,6 +110,10 @@ func TestGolden(t *testing.T) {
}
for i := 0; i < len(golden384); i++ {
g := golden384[i]
+ s := fmt.Sprintf("%x", Sum384([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum384 function: sha384(%s) = %s want %s", g.in, s, g.out)
+ }
c := New384()
for j := 0; j < 3; j++ {
if j < 2 {
コアとなるコードの解説
crypto/sha512/sha512.go の変更
-
func (d *digest) checkSum() [Size]byteの追加とSumメソッドからの分離:- 以前の
Sumメソッドは、ハッシュ計算の最終処理(パディング、最終ブロックの処理、ハッシュ値の生成)と、結果をinスライスに追加して返すという2つの役割を持っていました。 - この変更では、ハッシュ計算の最終処理部分が
checkSum()という新しいメソッドとして切り出されました。このメソッドは、digest構造体の現在の状態に基づいて最終的な512ビットのハッシュ値([Size]byte型)を計算し、それを返します。 - 元の
Sumメソッドは、checkSum()を呼び出してハッシュ値を取得し、そのハッシュ値をinスライスに追加して返すという、hash.HashインターフェースのSumメソッドの契約に沿った役割に特化しました。これにより、コードの関心事が分離され、再利用性が向上しています。
- 以前の
-
func Sum512(data []byte) [Size]byteの追加:- この関数は、
dataというバイトスライスを引数に取り、そのSHA-512ハッシュ値を[Size]byte型(64バイト配列)で直接返します。 - 内部では、
digest型の変数dを宣言し、d.Reset()で初期状態に戻します。 - 次に、
d.Write(data)を呼び出して、入力データ全体をハッシュ計算器に供給します。 - 最後に、
d.checkSum()を呼び出して最終的なハッシュ値を取得し、それを関数の戻り値としています。 - この関数は、単一のデータブロックのハッシュ値を簡単に計算したい場合に非常に便利です。
- この関数は、
-
func Sum384(data []byte) (sum384 [Size384]byte)の追加:- この関数は、
dataというバイトスライスを引数に取り、そのSHA-384ハッシュ値を[Size384]byte型(48バイト配列)で直接返します。 Sum512と同様にdigest型の変数dを宣言しますが、SHA-384モードで計算を行うためにd.is384 = trueを設定します。- その後、
d.Reset()、d.Write(data)を実行し、d.checkSum()を呼び出します。checkSum()は常に512ビットのハッシュ値を生成しますが、SHA-384はSHA-512のアルゴリズムの出力の一部を切り詰めたものであるため、この512ビットのハッシュ値の先頭Size384バイト(48バイト)のみをcopy関数でsum384配列にコピーして返します。 - これにより、SHA-384ハッシュ値も単一の関数呼び出しで簡単に取得できるようになりました。
- この関数は、
crypto/sha256/sha256.go および crypto/sha256/sha256_test.go の変更
sha256.goでは、Sum256関数のコメントがより正確に「SHA256チェックサムを返す」と記述されるように修正されました。sha256_test.goでは、テスト失敗時のエラーメッセージが、Sum functionではなくSum256 functionと表示されるように修正されました。これは、ユーザーがどの関数でエラーが発生したかをより明確に理解できるようにするための、小さな改善です。
これらの変更は、Goの暗号ライブラリのAPIをより使いやすく、一貫性のあるものにするための重要なステップです。
関連リンク
- Go言語の
crypto/sha512パッケージのドキュメント: https://pkg.go.dev/crypto/sha512 - Go言語の
crypto/sha256パッケージのドキュメント: https://pkg.go.dev/crypto/sha256 - Go言語の
hashパッケージのドキュメント: https://pkg.go.dev/hash - SHA-2 (Wikipedia): https://ja.wikipedia.org/wiki/SHA-2
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go Code Review Comments (golang.org/cl/10630043): https://golang.org/cl/10630043 (コミットメッセージに記載されているChange-ID)
- Go言語の公式ドキュメント
- 暗号学的ハッシュ関数に関する一般的な知識