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

[インデックス 16651] ファイルの概要

このコミットは、Go言語の標準ライブラリ crypto/sha256 パッケージに、SHA256およびSHA224ハッシュを計算するためのトップレベルのユーティリティ関数 Sum256Sum224 を追加するものです。これにより、特定のデータに対するハッシュ値を簡単に取得できるようになり、APIの使いやすさが向上しています。

コミット

commit 5cd5d88954a20c2f4792b6010f3ab7b82355e84b
Author: Rob Pike <r@golang.org>
Date:   Wed Jun 26 11:36:18 2013 -0700

    crypto/sha256: provide top-level Sum and Sum224 functions
    Makes it easy to ask the simple question, what is the hash of this data?
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/10629043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5cd5d88954a20c2f4792b6010f3ab7b82355e84b

元コミット内容

crypto/sha256: provide top-level Sum and Sum224 functions
Makes it easy to ask the simple question, what is the hash of this data?

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/10629043

変更の背景

この変更の背景には、Go言語の crypto/sha256 パッケージにおけるハッシュ計算の利便性向上が挙げられます。以前は、あるデータのハッシュ値を計算するためには、以下のような一連のステップを踏む必要がありました。

  1. sha256.New() または sha256.New224() を呼び出してハッシュインターフェースのインスタンスを作成する。
  2. Write() メソッドを呼び出してデータをハッシュ関数に供給する。
  3. Sum(nil) を呼び出して最終的なハッシュ値を取得する。

この一連の操作は、単に特定のバイト列のハッシュ値が欲しいだけのシンプルなユースケースにおいては、やや冗長でした。コミットメッセージにある「Makes it easy to ask the simple question, what is the hash of this data?(このデータのハッシュは何ですか?という単純な質問を簡単にできるようにする)」という言葉が示す通り、開発者がより直感的に、かつ少ないコード量でハッシュ計算を行えるようにすることが目的でした。

新しいトップレベル関数 Sum256Sum224 は、これらのステップを内部でカプセル化し、入力データを受け取って直接ハッシュ値を返すシンプルなインターフェースを提供します。これにより、特にワンショットのハッシュ計算が必要な場合に、コードの記述が簡潔になり、可読性が向上します。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

1. SHA-2 (Secure Hash Algorithm 2)

SHA-2は、アメリカ国家安全保障局(NSA)によって設計された暗号学的ハッシュ関数群です。入力データから固定長のハッシュ値(メッセージダイジェスト)を生成します。主な特徴は以下の通りです。

  • 一方向性: ハッシュ値から元のデータを復元することは計算上困難です。
  • 衝突耐性: 異なる入力データから同じハッシュ値が生成されること(衝突)は計算上困難です。
  • 改ざん検出: データのわずかな変更でもハッシュ値が大きく変化するため、データの改ざんを検出できます。

SHA-2ファミリーには、SHA-256、SHA-224、SHA-512、SHA-384、SHA-512/256、SHA-512/224などがあります。

2. SHA-256

SHA-256は、SHA-2ファミリーの中で最も広く使用されているハッシュ関数の一つです。任意の長さの入力データから256ビット(32バイト)のハッシュ値を生成します。ビットコインなどのブロックチェーン技術や、TLS/SSL証明書、デジタル署名など、幅広いセキュリティアプリケーションで利用されています。

3. SHA-224

SHA-224もSHA-2ファミリーの一つで、SHA-256の切り詰めバージョンです。SHA-256と同じ内部構造を持ちますが、初期ハッシュ値が異なり、最終的なハッシュ値は224ビット(28バイト)に切り詰められます。SHA-256よりも短いハッシュ値が必要な場合や、特定のプロトコル要件がある場合に使用されます。

4. Go言語の crypto/sha256 パッケージ

Go言語の crypto/sha256 パッケージは、SHA-256およびSHA-224ハッシュ関数を実装しています。このパッケージは、hash.Hash インターフェースを実装しており、ストリーミング方式でデータをハッシュ関数に供給し、最終的にハッシュ値を取得する一般的なパターンを提供します。

  • sha256.New(): SHA-256ハッシュを計算するための hash.Hash インターフェースを返すコンストラクタ。
  • sha256.New224(): SHA-224ハッシュを計算するための hash.Hash インターフェースを返すコンストラクタ。
  • hash.Hash インターフェースの主要メソッド:
    • Write(p []byte) (n int, err error): データをハッシュ関数に書き込む。
    • Sum(b []byte) []byte: 現在のハッシュ状態から最終的なハッシュ値を計算し、b に追加して返す。bnil の場合、新しいスライスを割り当てて返す。
    • Reset(): ハッシュ状態を初期化する。
    • Size(): ハッシュ値のバイト長を返す。
    • BlockSize(): ハッシュ関数のブロックサイズを返す。

このコミット以前は、Sum メソッドは hash.Hash インターフェースの一部として存在していましたが、これはハッシュ計算の「最終化」ステップであり、通常は Write を複数回呼び出した後に一度だけ呼び出されるものでした。今回の変更は、この WriteSum の組み合わせを、単一の関数呼び出しで完結させるためのものです。

技術的詳細

このコミットの技術的な変更は、主に src/pkg/crypto/sha256/sha256.gosrc/pkg/crypto/sha256/sha256_test.go の2つのファイルに集中しています。

sha256.go の変更点

  1. digest.Sum メソッドの変更:

    • 既存の digest 型の Sum メソッドは、ハッシュ計算の最終処理(パディング、最終ブロックの処理、ハッシュ値の生成)を直接行っていた部分が、新しく導入されたプライベートメソッド checkSum() に委譲されるようになりました。
    • Sum メソッドの役割は、checkSum() で計算されたハッシュ値を、引数 in で渡されたバイトスライスに追加して返すことに限定されました。これにより、Sum メソッドは hash.Hash インターフェースの要件を満たしつつ、内部の実装詳細を checkSum() に隠蔽できるようになりました。
  2. digest.checkSum() メソッドの導入:

    • この新しいプライベートメソッド checkSum() は、digest 型の現在の状態(内部ハッシュ値 d.h、処理済みバイト長 d.len、バッファ d.nx、バッファ内容 d.buf)に基づいて、最終的なSHA256またはSHA224ハッシュ値を計算するロジックをカプセル化します。
    • 具体的には、SHA-2の仕様に従って、メッセージのパディング(1ビットと0ビットの追加、元のメッセージ長の追加)を行い、最終ブロックを処理し、内部ハッシュ状態 d.h から最終的なハッシュバイト列を生成します。
    • このメソッドは常に [Size]byte 型(SHA256のハッシュ値のバイト長、32バイト)の配列を返します。SHA224の場合でも32バイトの配列を返し、呼び出し側で必要に応じて28バイトに切り詰める形になります。
  3. トップレベル関数 Sum256 の追加:

    • func Sum256(data []byte) [Size]byte という新しい関数が追加されました。
    • この関数は、入力データ data を受け取り、内部で新しい digest インスタンスを作成し、Reset() で初期化し、Write(data) でデータを供給し、最後に checkSum() を呼び出してSHA256ハッシュ値を計算し、[Size]byte 型の配列として直接返します。
    • これにより、ユーザーは sha256.Sum256(myBytes) のように、一行でハッシュ計算を行えるようになりました。
  4. トップレベル関数 Sum224 の追加:

    • func Sum224(data []byte) (sum224 [Size224]byte) という新しい関数が追加されました。
    • Sum256 と同様に、入力データ data を受け取りますが、digest インスタンスの is224 フラグを true に設定してSHA224モードで初期化します。
    • データを書き込み、checkSum() を呼び出して32バイトのハッシュ値を取得した後、その先頭28バイト(Size224)を sum224 配列にコピーして返します。

これらの変更により、digest 型の Sum メソッドは hash.Hash インターフェースのセマンティクス(入力スライスにハッシュを追加する)を維持しつつ、実際のハッシュ計算ロジックは checkSum() に集約され、さらに Sum256Sum224 という便利なワンショット関数が提供されることになりました。

sha256_test.go の変更点

  • TestGolden 関数内に、新しく追加された Sum256 および Sum224 関数を検証するためのテストケースが追加されました。
  • 既存のゴールデンテストデータ (golden および golden224) を利用して、Sum256([]byte(g.in)) および Sum224([]byte(g.in)) の結果が期待されるハッシュ値 (g.out) と一致するかどうかを確認しています。これにより、新しいトップレベル関数が正しく機能することが保証されます。

コアとなるコードの変更箇所

src/pkg/crypto/sha256/sha256.go

--- a/src/pkg/crypto/sha256/sha256.go
+++ b/src/pkg/crypto/sha256/sha256.go
@@ -134,9 +134,16 @@ func (d *digest) Write(p []byte) (nn int, err error) {
 func (d0 *digest) Sum(in []byte) []byte {
 	// Make a copy of d0 so that caller can keep writing and summing.
 	d := *d0
+	hash := d.checkSum()
+	if d.is224 {
+		return append(in, hash[:Size224]...)
+	}
+	return append(in, hash[:]...)
+}
 
-	// Padding.  Add a 1 bit and 0 bits until 56 bytes mod 64.
+func (d *digest) checkSum() [Size]byte {
 	len := d.len
+	// Padding.  Add a 1 bit and 0 bits until 56 bytes mod 64.
 	var tmp [64]byte
 	tmp[0] = 0x80
 	if len%64 < 56 {
@@ -157,10 +164,8 @@ func (d0 *digest) Sum(in []byte) []byte {
 	}
 
 	h := d.h[:]
-	size := Size
 	if d.is224 {
 		h = d.h[:7]
-		size = Size224
 	}
 
 	var digest [Size]byte
@@ -171,5 +176,24 @@ func (d0 *digest) Sum(in []byte) []byte {
 		digest[i*4+3] = byte(s)
 	}
 
-	return append(in, digest[:size]...)\n+\treturn digest
+}\n+\n+// Sum returns the SHA256 checksum of the data.
+func Sum256(data []byte) [Size]byte {
+	var d digest
+	d.Reset()
+	d.Write(data)
+	return d.checkSum()
+}\n+\n+// Sum224 returns the SHA224 checksum of the data.
+func Sum224(data []byte) (sum224 [Size224]byte) {
+	var d digest
+	d.is224 = true
+	d.Reset()
+	d.Write(data)
+	sum := d.checkSum()
+	copy(sum224[:], sum[:Size224])
+	return
 }

src/pkg/crypto/sha256/sha256_test.go

--- a/src/pkg/crypto/sha256/sha256_test.go
+++ b/src/pkg/crypto/sha256/sha256_test.go
@@ -88,6 +88,10 @@ var golden224 = []sha256Test{
 func TestGolden(t *testing.T) {
 	for i := 0; i < len(golden); i++ {
 		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)
+		}
 		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(golden224); i++ {
 		g := golden224[i]
+		s := fmt.Sprintf("%x", Sum224([]byte(g.in)))
+		if s != g.out {
+			t.Fatalf("Sum224 function: sha224(%s) = %s want %s", g.in, s, g.out)
+		}
 		c := New224()
 		for j := 0; j < 3; j++ {
 			if j < 2 {

コアとなるコードの解説

sha256.go の変更点

  1. func (d0 *digest) Sum(in []byte) []byte:

    • このメソッドは hash.Hash インターフェースの一部であり、ハッシュ計算の最終結果を in スライスに追加して返します。
    • 変更前は、パディングや最終ブロック処理のロジックがこのメソッド内に直接記述されていました。
    • 変更後は、d.checkSum() を呼び出して実際のハッシュ計算を行い、その結果を hash 変数に格納します。
    • d.is224 フラグに基づいて、SHA224の場合は hash の先頭 Size224 バイト(28バイト)を、SHA256の場合は hash 全体(32バイト)を in スライスに追加して返します。これにより、Sum メソッドはインターフェースの契約を維持しつつ、内部実装をよりクリーンに保っています。
  2. func (d *digest) checkSum() [Size]byte の新規追加:

    • このプライベートメソッドは、digest の現在の状態から最終的なハッシュ値を計算する「コアロジック」を担います。
    • len := d.len で現在の処理済みバイト長を取得します。
    • tmp[0] = 0x80 から始まる部分は、SHA-2のパディング処理です。メッセージの最後に1ビットの'1'と、それに続く'0'ビットを追加し、メッセージ長が512ビット(64バイト)の倍数になるように調整します。具体的には、メッセージ長を64で割った余りが56バイト未満であれば、残りのスペースを0で埋め、最後に元のメッセージ長(ビット単位)を64ビットで追加します。
    • block(d, tmp[:]) は、パディングされた最終ブロックを処理し、内部ハッシュ状態 d.h を更新します。
    • h := d.h[:] は、内部ハッシュ状態 d.h[8]uint32 型)をバイトスライスとして取得します。SHA224の場合、h = d.h[:7] となり、先頭7つの uint32 値(28バイト)のみが考慮されます。
    • var digest [Size]byte を宣言し、for ループで d.h の各 uint32 値をビッグエンディアン形式で digest バイト配列に変換して格納します。
    • 最終的に、計算された32バイトの digest 配列を返します。このメソッドは常に32バイトの配列を返すため、SHA224の場合は呼び出し側で適切な長さに切り詰める必要があります。
  3. func Sum256(data []byte) [Size]byte の新規追加:

    • この関数は、ユーザーが最も簡単にSHA256ハッシュを計算できるようにするためのものです。
    • var d digest で新しい digest インスタンスをスタック上に作成します。
    • d.Reset() でハッシュ状態を初期化します。
    • d.Write(data) で入力データ全体を一度にハッシュ関数に供給します。
    • return d.checkSum() で、checkSum() メソッドを呼び出して最終的なSHA256ハッシュ値を取得し、そのまま返します。
  4. func Sum224(data []byte) (sum224 [Size224]byte) の新規追加:

    • Sum256 と同様に、SHA224ハッシュを簡単に計算するための関数です。
    • d.is224 = true を設定することで、digest インスタンスがSHA224モードで動作するようにします。これにより、checkSum() 内部でのハッシュ値の扱い(特に h = d.h[:7] の部分)がSHA224の仕様に沿うようになります。
    • sum := d.checkSum() で32バイトのハッシュ値を取得します。
    • copy(sum224[:], sum[:Size224]) で、取得した32バイトのハッシュ値の先頭28バイト(Size224)を、戻り値の sum224 配列にコピーします。
    • return で、28バイトのSHA224ハッシュ値を返します。

sha256_test.go の変更点

  • TestGolden 関数内の for ループの冒頭に、Sum256 および Sum224 のテストコードが追加されています。
  • s := fmt.Sprintf("%x", Sum256([]byte(g.in))) のように、新しいトップレベル関数を直接呼び出し、その結果を16進数文字列にフォーマットしています。
  • if s != g.out で、計算されたハッシュ値が期待されるゴールデン値 (g.out) と一致するかを検証しています。一致しない場合は t.Fatalf でテストを失敗させ、詳細なエラーメッセージを出力します。
  • これにより、新しいAPIが既存のハッシュ計算ロジックと整合性が取れていること、および正しく機能することが保証されます。

これらの変更は、Go言語の標準ライブラリにおけるAPI設計の哲学、すなわち「シンプルで使いやすいインターフェースを提供する」という原則を反映しています。

関連リンク

  • Go CL 10629043: https://golang.org/cl/10629043
  • Go言語 crypto/sha256 パッケージのドキュメント (コミット当時のバージョンに近いもの):
    • Go 1.1 crypto/sha256 ドキュメント: https://pkg.go.dev/crypto/sha256@go1.1 (このコミットはGo 1.1リリース前に行われたものですが、Go 1.1のドキュメントが最も近い公式情報です)

参考にした情報源リンク