[インデックス 16651] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/sha256
パッケージに、SHA256およびSHA224ハッシュを計算するためのトップレベルのユーティリティ関数 Sum256
と Sum224
を追加するものです。これにより、特定のデータに対するハッシュ値を簡単に取得できるようになり、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
パッケージにおけるハッシュ計算の利便性向上が挙げられます。以前は、あるデータのハッシュ値を計算するためには、以下のような一連のステップを踏む必要がありました。
sha256.New()
またはsha256.New224()
を呼び出してハッシュインターフェースのインスタンスを作成する。Write()
メソッドを呼び出してデータをハッシュ関数に供給する。Sum(nil)
を呼び出して最終的なハッシュ値を取得する。
この一連の操作は、単に特定のバイト列のハッシュ値が欲しいだけのシンプルなユースケースにおいては、やや冗長でした。コミットメッセージにある「Makes it easy to ask the simple question, what is the hash of this data?(このデータのハッシュは何ですか?という単純な質問を簡単にできるようにする)」という言葉が示す通り、開発者がより直感的に、かつ少ないコード量でハッシュ計算を行えるようにすることが目的でした。
新しいトップレベル関数 Sum256
と Sum224
は、これらのステップを内部でカプセル化し、入力データを受け取って直接ハッシュ値を返すシンプルなインターフェースを提供します。これにより、特にワンショットのハッシュ計算が必要な場合に、コードの記述が簡潔になり、可読性が向上します。
前提知識の解説
このコミットを理解するためには、以下の前提知識が役立ちます。
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
に追加して返す。b
がnil
の場合、新しいスライスを割り当てて返す。Reset()
: ハッシュ状態を初期化する。Size()
: ハッシュ値のバイト長を返す。BlockSize()
: ハッシュ関数のブロックサイズを返す。
このコミット以前は、Sum
メソッドは hash.Hash
インターフェースの一部として存在していましたが、これはハッシュ計算の「最終化」ステップであり、通常は Write
を複数回呼び出した後に一度だけ呼び出されるものでした。今回の変更は、この Write
と Sum
の組み合わせを、単一の関数呼び出しで完結させるためのものです。
技術的詳細
このコミットの技術的な変更は、主に src/pkg/crypto/sha256/sha256.go
と src/pkg/crypto/sha256/sha256_test.go
の2つのファイルに集中しています。
sha256.go
の変更点
-
digest.Sum
メソッドの変更:- 既存の
digest
型のSum
メソッドは、ハッシュ計算の最終処理(パディング、最終ブロックの処理、ハッシュ値の生成)を直接行っていた部分が、新しく導入されたプライベートメソッドcheckSum()
に委譲されるようになりました。 Sum
メソッドの役割は、checkSum()
で計算されたハッシュ値を、引数in
で渡されたバイトスライスに追加して返すことに限定されました。これにより、Sum
メソッドはhash.Hash
インターフェースの要件を満たしつつ、内部の実装詳細をcheckSum()
に隠蔽できるようになりました。
- 既存の
-
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バイトに切り詰める形になります。
- この新しいプライベートメソッド
-
トップレベル関数
Sum256
の追加:func Sum256(data []byte) [Size]byte
という新しい関数が追加されました。- この関数は、入力データ
data
を受け取り、内部で新しいdigest
インスタンスを作成し、Reset()
で初期化し、Write(data)
でデータを供給し、最後にcheckSum()
を呼び出してSHA256ハッシュ値を計算し、[Size]byte
型の配列として直接返します。 - これにより、ユーザーは
sha256.Sum256(myBytes)
のように、一行でハッシュ計算を行えるようになりました。
-
トップレベル関数
Sum224
の追加:func Sum224(data []byte) (sum224 [Size224]byte)
という新しい関数が追加されました。Sum256
と同様に、入力データdata
を受け取りますが、digest
インスタンスのis224
フラグをtrue
に設定してSHA224モードで初期化します。- データを書き込み、
checkSum()
を呼び出して32バイトのハッシュ値を取得した後、その先頭28バイト(Size224
)をsum224
配列にコピーして返します。
これらの変更により、digest
型の Sum
メソッドは hash.Hash
インターフェースのセマンティクス(入力スライスにハッシュを追加する)を維持しつつ、実際のハッシュ計算ロジックは checkSum()
に集約され、さらに Sum256
と Sum224
という便利なワンショット関数が提供されることになりました。
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
の変更点
-
func (d0 *digest) Sum(in []byte) []byte
:- このメソッドは
hash.Hash
インターフェースの一部であり、ハッシュ計算の最終結果をin
スライスに追加して返します。 - 変更前は、パディングや最終ブロック処理のロジックがこのメソッド内に直接記述されていました。
- 変更後は、
d.checkSum()
を呼び出して実際のハッシュ計算を行い、その結果をhash
変数に格納します。 d.is224
フラグに基づいて、SHA224の場合はhash
の先頭Size224
バイト(28バイト)を、SHA256の場合はhash
全体(32バイト)をin
スライスに追加して返します。これにより、Sum
メソッドはインターフェースの契約を維持しつつ、内部実装をよりクリーンに保っています。
- このメソッドは
-
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の場合は呼び出し側で適切な長さに切り詰める必要があります。
- このプライベートメソッドは、
-
func Sum256(data []byte) [Size]byte
の新規追加:- この関数は、ユーザーが最も簡単にSHA256ハッシュを計算できるようにするためのものです。
var d digest
で新しいdigest
インスタンスをスタック上に作成します。d.Reset()
でハッシュ状態を初期化します。d.Write(data)
で入力データ全体を一度にハッシュ関数に供給します。return d.checkSum()
で、checkSum()
メソッドを呼び出して最終的なSHA256ハッシュ値を取得し、そのまま返します。
-
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のドキュメントが最も近い公式情報です)
- Go 1.1
参考にした情報源リンク
- Secure Hash Algorithm (SHA) Family: https://en.wikipedia.org/wiki/Secure_Hash_Algorithm
- SHA-256: https://en.wikipedia.org/wiki/SHA-2
- Go言語の
hash.Hash
インターフェース: https://pkg.go.dev/hash#Hash - Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/