Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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)
}

このコミットで追加される Sum512Sum384 のようなトップレベル関数は、上記の New(), Write(), Sum() の一連の操作を単一の関数呼び出しで完結させるための「コンビニエンス関数」として提供されます。

技術的詳細

このコミットの主要な変更点は、crypto/sha512 パッケージに Sum512Sum384 という2つの新しいトップレベル関数が追加されたことです。これらの関数は、入力されたバイトスライス全体のハッシュ値を直接計算して返します。

crypto/sha512 の変更点

  1. 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)を満たしつつ、内部的なハッシュ計算ロジックを再利用可能にしています。

  2. Sum512(data []byte) [Size]byte 関数の追加: この関数は、入力 data のSHA-512ハッシュ値を計算し、[Size]byte (64バイト配列) として返します。 内部的には、新しい digest オブジェクトを作成し、Reset() で初期化し、Write(data) でデータを書き込み、最後に checkSum() を呼び出してハッシュ値を取得します。

  3. 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 の変更点

  1. Sum 関数のコメント修正: sha256.go 内の Sum 関数のコメントが // Sum returns the SHA256 checksum of the data. から // Sum256 returns the SHA256 checksum of the data. に変更されました。これは、関数名が Sum256 であることに合わせて、コメントもより具体的に記述することで、APIの明確性を高めるためです。

  2. テスト出力の修正: 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 の変更

  1. func (d *digest) checkSum() [Size]byte の追加と Sum メソッドからの分離:

    • 以前の Sum メソッドは、ハッシュ計算の最終処理(パディング、最終ブロックの処理、ハッシュ値の生成)と、結果を in スライスに追加して返すという2つの役割を持っていました。
    • この変更では、ハッシュ計算の最終処理部分が checkSum() という新しいメソッドとして切り出されました。このメソッドは、digest 構造体の現在の状態に基づいて最終的な512ビットのハッシュ値([Size]byte 型)を計算し、それを返します。
    • 元の Sum メソッドは、checkSum() を呼び出してハッシュ値を取得し、そのハッシュ値を in スライスに追加して返すという、hash.Hash インターフェースの Sum メソッドの契約に沿った役割に特化しました。これにより、コードの関心事が分離され、再利用性が向上しています。
  2. func Sum512(data []byte) [Size]byte の追加:

    • この関数は、data というバイトスライスを引数に取り、そのSHA-512ハッシュ値を [Size]byte 型(64バイト配列)で直接返します。
    • 内部では、digest 型の変数 d を宣言し、d.Reset() で初期状態に戻します。
    • 次に、d.Write(data) を呼び出して、入力データ全体をハッシュ計算器に供給します。
    • 最後に、d.checkSum() を呼び出して最終的なハッシュ値を取得し、それを関数の戻り値としています。
    • この関数は、単一のデータブロックのハッシュ値を簡単に計算したい場合に非常に便利です。
  3. 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をより使いやすく、一貫性のあるものにするための重要なステップです。

関連リンク

参考にした情報源リンク