[インデックス 16644] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/sha1
パッケージにトップレベルの Sum
関数を追加するものです。これにより、与えられたデータのSHA-1ハッシュ値を簡単に計算できるようになります。
コミット
commit 4cf73890a2cc4a75cd8cd2ad726690a2ef60cf1d
Author: Rob Pike <r@golang.org>
Date: Tue Jun 25 17:04:18 2013 -0700
crypto/sha1: provide a top-level Sum function
Makes it easy to ask the simple question, what is the hash of this data?
R=golang-dev, rsc, bradfitz
CC=golang-dev
https://golang.org/cl/10571043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4cf73890a2cc4a75cd8cd2ad726690a2ef60cf1d
元コミット内容
crypto/sha1: provide a top-level Sum function
Makes it easy to ask the simple question, what is the hash of this data?
このコミットは、crypto/sha1
パッケージにトップレベルの Sum
関数を提供します。これにより、「このデータのハッシュは何ですか?」という単純な問いに簡単に答えることができるようになります。
変更の背景
Go言語の hash
インターフェース(hash.Hash
)は、ストリーム形式でデータを処理し、ハッシュ値を計算するための柔軟な方法を提供します。通常、ハッシュ値を計算するには、New()
関数でハッシュアルゴリズムのインスタンスを作成し、Write()
メソッドでデータを渡し、最後に Sum()
メソッドを呼び出して最終的なハッシュ値を取得します。
しかし、多くのユースケースでは、単一のバイトスライス全体のハッシュ値を一度に計算したいというシンプルな要求があります。既存の hash.Hash
インターフェースのパターンでは、この単純な操作のためにも複数のステップ(インスタンス作成、書き込み、Sum呼び出し)が必要でした。
このコミットの背景には、このような一般的なユースケースに対して、より簡潔で直感的なAPIを提供したいという意図があります。トップレベルの Sum
関数を導入することで、ユーザーはハッシュオブジェクトのライフサイクルを意識することなく、直接データのハッシュ値を計算できるようになります。これは、特にスクリプトやワンショットのハッシュ計算において、コードの可読性と記述性を向上させます。
前提知識の解説
1. SHA-1 (Secure Hash Algorithm 1)
SHA-1は、アメリカ国家安全保障局(NSA)によって設計された暗号学的ハッシュ関数です。任意の長さの入力データ(メッセージ)を受け取り、160ビット(20バイト)の固定長ハッシュ値(メッセージダイジェスト)を出力します。このハッシュ値は、データの整合性を検証するために使用されます。
SHA-1の主な特性は以下の通りです。
- 一方向性: ハッシュ値から元のデータを復元することは計算上困難です。
- 衝突耐性: 異なる入力データから同じハッシュ値が生成されること(衝突)は計算上困難であるべきです。ただし、SHA-1は現在、理論的および実践的な衝突攻撃に対して脆弱であることが知られており、新しいアプリケーションでの使用は推奨されていません。より強力なSHA-2(SHA-256, SHA-512など)やSHA-3が推奨されます。
- 雪崩効果: 入力データのごくわずかな変更でも、ハッシュ値が大きく変化します。
2. Go言語の crypto/sha1
パッケージ
Go言語の標準ライブラリ crypto/sha1
パッケージは、SHA-1ハッシュアルゴリズムの実装を提供します。このパッケージは、hash.Hash
インターフェースを実装しており、以下のような典型的な使用パターンがあります。
import (
"crypto/sha1"
"fmt"
"io"
)
func main() {
data := []byte("Hello, Go!")
// 1. ハッシュオブジェクトの作成
h := sha1.New()
// 2. データの書き込み
io.WriteString(h, string(data)) // または h.Write(data)
// 3. ハッシュ値の取得
hashValue := h.Sum(nil) // nil を渡すと、ハッシュ値のみが返される
fmt.Printf("%x\n", hashValue) // 16進数文字列で出力
}
3. hash.Hash
インターフェースと Sum
メソッド
hash.Hash
インターフェースは、Go言語におけるハッシュ関数の共通インターフェースを定義しています。主要なメソッドは以下の通りです。
New() hash.Hash
: ハッシュアルゴリズムの新しいインスタンスを返します。Write(p []byte) (n int, err error)
: データをハッシュ計算器に書き込みます。Sum(b []byte) []byte
: 現在のハッシュ計算器の状態に基づいてハッシュ値を計算し、b
に追加して返します。b
がnil
の場合、ハッシュ値のみが返されます。このメソッドはハッシュ計算器の状態をリセットしません。Reset()
: ハッシュ計算器を初期状態にリセットします。Size() int
: ハッシュ値のバイト長を返します。BlockSize() int
: ハッシュアルゴリズムのブロックサイズを返します。
Sum(b []byte) []byte
メソッドは、既存のバイトスライス b
にハッシュ値を追加するという設計になっています。これは、例えば複数のハッシュ値を連結して一つのバイトスライスに格納したい場合などに便利ですが、単にハッシュ値だけが欲しい場合には nil
を渡す必要があります。
4. トップレベルの Sum
関数と Sum
メソッドの違い
このコミットで追加されるトップレベルの Sum
関数は、crypto/sha1.Sum(data []byte) [Size]byte
のようなシグネチャを持ちます。これは、sha1.New().Write(data).Sum(nil)
の一連の操作を内部でカプセル化し、直接ハッシュ値を [Size]byte
型(固定長配列)で返します。
一方、既存の (d *digest) Sum(in []byte) []byte
メソッドは、digest
型のレシーバを持つメソッドであり、hash.Hash
インターフェースの一部として機能します。このメソッドは、ハッシュ計算器の現在の状態に基づいてハッシュ値を計算し、引数 in
にそのハッシュ値を追加して新しいスライスとして返します。
新しいトップレベルの Sum
関数は、より高レベルで使いやすいAPIを提供し、ハッシュオブジェクトの管理をユーザーから隠蔽します。
技術的詳細
このコミットの技術的な核心は、crypto/sha1
パッケージにおけるハッシュ計算の内部構造の整理と、それを利用した簡潔なAPIの提供です。
1. checkSum()
メソッドの導入
コミット前は、*digest
型の Sum
メソッドが、パディング処理、ハッシュ計算、そして結果のバイトスライスへの追加という複数の役割を担っていました。このコミットでは、ハッシュ計算の最終段階(パディングとハッシュ値の生成)を checkSum()
という新しいプライベートメソッドに切り出しています。
func (d *digest) checkSum() [Size]byte
:- このメソッドは、
digest
構造体の現在の状態(内部のハッシュ値h
と処理されたデータの長さlen
)に基づいて、SHA-1の最終的なパディング処理とハッシュ値の計算を行います。 - 計算されたハッシュ値は、
[Size]byte
型(Size
はSHA-1のハッシュ値のバイト長、20バイト)の固定長配列として返されます。これにより、ハッシュ値が常に固定長であることが型レベルで保証されます。 - このメソッドは、ハッシュ計算器の状態を変更しないため、
Sum
メソッドが呼び出された後もdigest
オブジェクトを再利用して追加のデータを処理し、再度Sum
を呼び出すことが可能です(ただし、Sum
メソッド自体はd0
のコピーに対して操作を行うため、元のd0
は変更されません)。
- このメソッドは、
2. (d *digest) Sum(in []byte) []byte
メソッドの変更
既存の Sum
メソッドは、新しく導入された checkSum()
メソッドを利用するように変更されました。
- 変更前:
Sum
メソッド内で直接パディングとハッシュ値の計算を行っていました。 - 変更後:
Sum
メソッドはd.checkSum()
を呼び出してハッシュ値を取得し、その結果を引数in
に追加して返します。これにより、Sum
メソッドのロジックが簡素化され、ハッシュ計算のコアロジックがcheckSum()
に集約されました。
3. トップレベルの Sum(data []byte) [Size]byte
関数の追加
これがこのコミットの主要な変更点です。
func Sum(data []byte) [Size]byte
:- この関数は、
data
という単一のバイトスライスを入力として受け取ります。 - 内部では、新しい
digest
オブジェクトを作成し(var d digest
)、d.Reset()
で初期化します。 - 次に、
d.Write(data)
を呼び出して入力データ全体をハッシュ計算器に書き込みます。 - 最後に、
d.checkSum()
を呼び出して最終的なハッシュ値を計算し、それを[Size]byte
型で直接返します。 - この関数は、ユーザーが
New()
,Write()
,Sum()
といった一連の操作を明示的に行う必要なく、データのハッシュ値を簡単に取得できるようにします。
- この関数は、
4. テストの追加
新しいトップレベルの Sum
関数が正しく機能することを検証するために、sha1_test.go
にテストケースが追加されています。既存の TestGolden
関数内で、Sum([]byte(g.in))
を呼び出し、その結果が期待されるゴールデン値と一致するかどうかを確認しています。これにより、新しいAPIの正確性が保証されます。
これらの変更により、crypto/sha1
パッケージは、ストリーム処理とワンショット処理の両方に対応できる、より柔軟で使いやすいAPIセットを提供することになりました。
コアとなるコードの変更箇所
src/pkg/crypto/sha1/sha1.go
--- a/src/pkg/crypto/sha1/sha1.go
+++ b/src/pkg/crypto/sha1/sha1.go
@@ -90,9 +90,13 @@ 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()
+ return append(in, hash[:]...)\
+}
-\t// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.\
+func (d *digest) checkSum() [Size]byte {
len := d.len
+\t// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
@@ -120,5 +124,13 @@ func (d0 *digest) Sum(in []byte) []byte {
\tdigest[i*4+3] = byte(s)
}
-\treturn append(in, digest[:]...)\
+ return digest
+}\
+
+// Sum returns the SHA1 checksum of the data.
+func Sum(data []byte) [Size]byte {
+ var d digest
+ d.Reset()
+ d.Write(data)
+ return d.checkSum()
}
src/pkg/crypto/sha1/sha1_test.go
--- a/src/pkg/crypto/sha1/sha1_test.go
+++ b/src/pkg/crypto/sha1/sha1_test.go
@@ -54,6 +54,10 @@ var golden = []sha1Test{
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
+ s := fmt.Sprintf("%x", Sum([]byte(g.in)))
+ if s != g.out {
+ t.Fatalf("Sum function: sha1(%s) = %s want %s", g.in, s, g.out)
+ }
c := New()
for j := 0; j < 3; j++ {
if j < 2 {
コアとなるコードの解説
src/pkg/crypto/sha1/sha1.go
の変更点
-
checkSum()
メソッドの追加:func (d *digest) checkSum() [Size]byte
という新しいメソッドが追加されました。- このメソッドは、SHA-1ハッシュ計算の最終段階であるパディング処理と、内部状態
h
から最終的な20バイトのハッシュ値を生成するロジックをカプセル化しています。 - 戻り値の型が
[Size]byte
(固定長配列) であるため、ハッシュ値が常に20バイトであることが明確になります。
-
Sum
メソッドの変更:- 既存の
func (d0 *digest) Sum(in []byte) []byte
メソッドは、checkSum()
を呼び出すように変更されました。 - 変更前は、このメソッド内でパディングとハッシュ値の計算を行っていましたが、変更後は
hash := d.checkSum()
でハッシュ値を取得し、append(in, hash[:]...)
でそのハッシュ値をin
スライスに追加して返します。これにより、Sum
メソッドの役割が「ハッシュ値を計算して既存のスライスに追加する」ことに特化され、コードの責務が明確になりました。
- 既存の
-
トップレベルの
Sum
関数の追加:func Sum(data []byte) [Size]byte
という新しいパッケージレベルの関数が追加されました。- この関数は、ユーザーが最もシンプルにSHA-1ハッシュを計算したい場合に利用されます。
- 内部では、
var d digest
で新しいハッシュ計算器のインスタンスを作成し、d.Reset()
で初期化します。 - 次に、
d.Write(data)
で入力データ全体を一度に書き込みます。 - 最後に、
d.checkSum()
を呼び出して最終的なハッシュ値を計算し、それを直接[Size]byte
型で返します。 - この関数により、
sha1.New().Write(data).Sum(nil)
といった一連の操作を記述することなく、sha1.Sum(data)
だけでハッシュ値を取得できるようになり、APIの使いやすさが大幅に向上しました。
src/pkg/crypto/sha1/sha1_test.go
の変更点
TestGolden
関数へのテストケース追加:- 既存の
TestGolden
関数内に、新しいトップレベルのSum
関数をテストするためのコードが追加されました。 s := fmt.Sprintf("%x", Sum([]byte(g.in)))
の行で、golden
テストデータ (g.in
) を新しいSum
関数に渡し、その結果を16進数文字列にフォーマットしています。if s != g.out
の条件で、計算されたハッシュ値が期待されるゴールデン値 (g.out
) と一致するかどうかを検証しています。- これにより、新しい
Sum
関数が既存のSHA-1実装と互換性があり、正しくハッシュ値を計算できることが保証されます。
- 既存の
これらの変更は、Go言語の標準ライブラリにおけるAPI設計のベストプラクティスを示しています。すなわち、低レベルで柔軟なインターフェース(hash.Hash
)を提供しつつ、一般的なユースケースに対してはより簡潔で使いやすい高レベルな関数(トップレベルの Sum
)も提供するというアプローチです。
関連リンク
- Go言語の
crypto/sha1
パッケージドキュメント (コミット当時のバージョンに近いもの):- https://pkg.go.dev/crypto/sha1@go1.1 (Go 1.1のドキュメント、コミット時期に近い)
- Go言語の
hash
パッケージドキュメント:
参考にした情報源リンク
- Go言語の公式ドキュメント
- SHA-1に関する一般的な情報 (例: Wikipedia)
- Go言語のハッシュ関数の一般的な使用パターンに関する知識