[インデックス 17959] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/sha256
パッケージにおけるSHA-256ハッシュアルゴリズムのブロック処理を、AMD64アーキテクチャ向けのアセンブリ言語で実装したものです。これにより、SHA-256ハッシュ計算のパフォーマンスが大幅に向上しました。
コミット
commit 75800a9a2842438c872c6464b3a87b33fedfea27
Author: Joel Sing <jsing@google.com>
Date: Wed Dec 11 11:41:30 2013 -0500
crypto/sha256: block implementation in amd64 assembly
Benchmark on Intel(R) Xeon(R) CPU X5650 @ 2.67GHz
benchmark old ns/op new ns/op delta
BenchmarkHash8Bytes 1259 677 -46.23%
BenchmarkHash1K 14387 6749 -53.09%
BenchmarkHash8K 106006 50107 -52.73%
benchmark old MB/s new MB/s speedup
BenchmarkHash8Bytes 6.35 11.81 1.86x
BenchmarkHash1K 71.17 151.72 2.13x
BenchmarkHash8K 77.28 163.49 2.12x
R=agl
CC=golang-dev
https://golang.org/cl/28460043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/75800a9a2842438c872c6464b3a87b33fedfea27
元コミット内容
このコミットは、crypto/sha256
パッケージにおいて、SHA-256ハッシュアルゴリズムのコアとなるブロック処理をAMD64アーキテクチャ向けのアセンブリ言語で実装したものです。これにより、Intel Xeon CPU X5650 @ 2.67GHzでのベンチマークにおいて、既存のGo言語実装と比較して最大で約53%の処理時間短縮(約2.1倍の高速化)が達成されました。
具体的には、以下のファイルが変更または新規追加されています。
src/pkg/crypto/sha256/sha256.go
: FIPS 180-2からFIPS 180-4への参照更新。src/pkg/crypto/sha256/sha256block.go
: AMD64以外の場合にGo言語実装を使用するためのビルドタグ+build !amd64
が追加。src/pkg/crypto/sha256/sha256block_amd64.s
: SHA-256ブロック処理のAMD64アセンブリ実装が新規追加。src/pkg/crypto/sha256/sha256block_decl.go
: アセンブリ実装の関数シグネチャをGo言語に公開するための宣言ファイルが新規追加。ビルドタグ+build amd64
が付与され、//go:noescape
ディレクティブが使用されています。
変更の背景
ハッシュアルゴリズムのような暗号プリミティブは、多くのアプリケーションで基盤として利用され、その性能はシステム全体のパフォーマンスに大きな影響を与えます。特にSHA-256は、TLS/SSL、ブロックチェーン、デジタル署名など、幅広いセキュリティ関連の用途で広く使われています。
Go言語の初期の実装では、多くの部分が純粋なGo言語で書かれていましたが、パフォーマンスがクリティカルな部分、特にCPU集約的な暗号処理においては、特定アーキテクチャ向けのアセンブリ言語による最適化が求められることがあります。これは、Goコンパイラが生成するコードよりも、手書きのアセンブリコードの方が、レジスタの利用効率、命令の並列性、特定のCPU命令(例: SIMD命令)の活用において優位に立つ場合があるためです。
このコミットの背景には、Go言語のSHA-256実装の性能を向上させ、より高速なハッシュ計算を提供することで、Go言語で構築されるアプリケーション全体のパフォーマンスとスループットを改善するという目的があります。ベンチマーク結果が示すように、このアセンブリ最適化は非常に効果的でした。
また、sha256.go
のFIPS 180-2からFIPS 180-4への参照更新は、SHA-256の標準が改訂されたことに対応するものです。FIPS 180-4は、SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256のセキュアハッシュアルゴリズムを定義しており、FIPS 180-2からの変更点としては、主にSHA-512/tファミリーの追加や、セキュリティ要件の明確化などが挙げられますが、SHA-256のコアアルゴリズム自体に大きな変更はありません。この更新は、実装が最新の標準に準拠していることを示すものです。
前提知識の解説
1. SHA-256ハッシュアルゴリズム
SHA-256(Secure Hash Algorithm 256-bit)は、NIST(アメリカ国立標準技術研究所)によって開発された暗号学的ハッシュ関数の一つです。任意の長さの入力データ(メッセージ)から、256ビット(32バイト)の固定長のハッシュ値(メッセージダイジェスト)を生成します。このハッシュ値は、入力データが少しでも変更されると大きく変化するという「雪崩効果」を持ち、元の入力データをハッシュ値から復元することが非常に困難であるという「一方向性」を持ちます。また、異なる入力から同じハッシュ値が生成される「衝突」を見つけることが計算上困難であるという「衝突耐性」も持っています。
SHA-256の処理は、主に以下のステップで構成されます。
- パディング: 入力メッセージを512ビット(64バイト)のブロックの倍数になるようにパディングします。パディングには、メッセージの最後に'1'ビットを追加し、その後に多数の'0'ビットを続け、最後に元のメッセージの長さを64ビットで表現した値を追加します。
- メッセージブロックの処理: パディングされたメッセージを512ビットのブロックに分割し、各ブロックを順番に処理します。
- 圧縮関数: 各512ビットのブロックは、8つの32ビットワード(A, B, C, D, E, F, G, H)からなる内部状態と、64ラウンドの計算を通じて処理されます。各ラウンドでは、非線形関数(Ch, Maj)、ビット単位の論理演算(AND, OR, XOR, NOT)、ビットシフト(ROTR, SHR)、加算(モジュロ2^32)が組み合わされ、定数(K_t)とメッセージスケジュールされたワード(W_t)が使用されます。
- ハッシュ値の更新: 各ブロックの処理後、内部状態の8つのワードが、初期ハッシュ値(H0~H7)に加算され、次のブロックの処理のための新しいハッシュ値となります。
- 最終ハッシュ値: 全てのブロックが処理された後、最終的な8つのワードが連結され、256ビットのハッシュ値が生成されます。
2. FIPS 180-4
FIPS(Federal Information Processing Standards)は、アメリカ合衆国政府が発行する情報処理標準です。FIPS 180はセキュアハッシュ標準(Secure Hash Standard, SHS)を定義しており、SHA-1、SHA-2(SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256)、SHA-3などのハッシュ関数が含まれます。
FIPS 180-4は、SHA-2ファミリーの最新版の一つであり、SHA-256のアルゴリズム自体はFIPS 180-2から変更されていませんが、標準文書としての記述が更新されています。このコミットでは、Go言語のSHA-256実装がこの最新の標準に準拠していることを明示するために、参照が更新されました。
3. アセンブリ言語による最適化
アセンブリ言語は、CPUが直接実行できる機械語命令を人間が読める形式で記述した低水準プログラミング言語です。Go言語のような高水準言語で書かれたプログラムは、コンパイラによって機械語に変換されますが、特定の部分(特にパフォーマンスがクリティカルな部分)では、手書きのアセンブリ言語を用いることで、コンパイラでは実現が難しいレベルの最適化を行うことができます。
アセンブリ言語による最適化の主な利点は以下の通りです。
- レジスタの効率的な利用: CPUのレジスタは非常に高速な記憶領域ですが、数が限られています。アセンブリ言語では、どのデータをどのレジスタに割り当てるかをプログラマが直接制御できるため、メモリへのアクセスを最小限に抑え、計算を高速化できます。
- 特定のCPU命令の活用: SIMD(Single Instruction, Multiple Data)命令(例: SSE, AVX)や暗号化支援命令(例: AES-NI)など、特定のCPUが持つ特殊な命令は、高水準言語からは直接利用できない場合があります。アセンブリ言語を使えば、これらの命令を直接呼び出し、大量のデータを並列に処理したり、暗号計算をハードウェアレベルで高速化したりできます。
- パイプラインの最適化: 現代のCPUは命令パイプラインを持っており、複数の命令を同時に処理しようとします。アセンブリ言語では、命令の順序を調整することで、パイプラインのストール(停止)を減らし、CPUの利用率を最大化できます。
- メモリレイアウトの最適化: データがメモリにどのように配置されるかを細かく制御し、キャッシュミスを減らすことで、メモリアクセスによる遅延を最小限に抑えることができます。
Go言語では、go:noescape
や go:linkname
といった特別なディレクティブ(プラグマ)を使用して、Goコードからアセンブリ関数を呼び出すことができます。これにより、Go言語の安全性や生産性を保ちつつ、パフォーマンスが要求される部分のみをアセンブリで最適化するというハイブリッドなアプローチが可能です。
4. Go言語のビルドタグ (+build
)
Go言語のソースファイルには、ファイルの先頭に+build
ディレクティブを記述することで、特定のビルド条件を指定できます。これにより、異なるオペレーティングシステム、アーキテクチャ、またはカスタムビルドタグに基づいて、コンパイルするファイルを切り替えることができます。
// +build amd64
: このタグが付いたファイルは、AMD64アーキテクチャ向けにビルドされる場合にのみコンパイルされます。// +build !amd64
: このタグが付いたファイルは、AMD64アーキテクチャ以外の場合にのみコンパイルされます。
このコミットでは、sha256block_amd64.s
(アセンブリ実装)には+build amd64
が、sha256block.go
(Go言語実装)には+build !amd64
がそれぞれ付与されており、これによりAMD64環境ではアセンブリ実装が、それ以外の環境ではGo言語実装が自動的に選択されるようになっています。
5. //go:noescape
ディレクティブ
//go:noescape
はGoコンパイラに対するディレクティブ(プラグマ)の一つで、特定の関数がヒープにメモリを割り当てないことをコンパイラに伝えます。これは、関数が引数として受け取ったポインタを、その関数のスコープ外に「エスケープ」させないことを意味します。このディレクティブは、通常、アセンブリ言語で実装された関数や、非常にパフォーマンスがクリティカルな関数に対して使用され、コンパイラがより積極的な最適化(例: スタック割り当て)を行うことを可能にします。
このコミットでは、sha256block_decl.go
内の block
関数に //go:noescape
が付与されており、アセンブリ実装がヒープ割り当てを行わないことを保証しています。
技術的詳細
このコミットの核心は、SHA-256の圧縮関数をAMD64アセンブリ言語で再実装した点にあります。SHA-256の圧縮関数は、8つの32ビットレジスタ(A, B, C, D, E, F, G, H)と、メッセージスケジュールされたワード(W_t)、そして64個の定数(K_t)を用いて64ラウンドの計算を行います。
アセンブリ実装では、これらの32ビットワードをAMD64の64ビットレジスタ(R8-R15など)に効率的にマッピングし、ビット単位の論理演算(ANDL, ORL, XORL, NOTL)、ビット回転(RORL)、ビットシフト(SHRL)、加算(ADDL)といった命令を直接使用します。
SHA-256のラウンド処理
SHA-256の各ラウンドは、以下の計算を繰り返します。
-
メッセージスケジュールの計算 (W_t):
- 最初の16ワード (W_0からW_15) は、入力メッセージブロックから直接取得されます。
- 残りの48ワード (W_16からW_63) は、以前のワードから以下の式で計算されます。
W_t = SIGMA1(W_{t-2}) + W_{t-7} + SIGMA0(W_{t-15}) + W_{t-16}
ここで、SIGMA0
とSIGMA1
は特定のビット回転とシフトを含む関数です。
-
一時変数T1, T2の計算:
T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + K_t + W_t
T2 = BIGSIGMA0(a) + Maj(a,b,c)
ここで、BIGSIGMA0
,BIGSIGMA1
はビット回転を含む関数、Ch
はChoose関数、Maj
はMajority関数です。
-
ハッシュ値の更新:
h = g
g = f
f = e
e = d + T1
d = c
c = b
b = a
a = T1 + T2
アセンブリ実装の最適化ポイント
sha256block_amd64.s
のアセンブリコードは、上記の計算をAMD64の命令セットに最適化してマッピングしています。
- レジスタ割り当て: 8つのハッシュワード(A-H)をR8からR15までの汎用レジスタに割り当てています。これにより、メモリへのアクセスを最小限に抑え、CPUレジスタの高速性を最大限に活用しています。
- マクロの活用:
MSGSCHEDULE0
,MSGSCHEDULE1
,SHA256T1
,SHA256T2
,SHA256ROUND
などのマクロを定義し、繰り返し現れる計算パターンを抽象化しています。これにより、コードの可読性を保ちつつ、効率的な命令シーケンスを生成しています。MSGSCHEDULE0
: メッセージブロックの最初の16ワードを処理し、バイトオーダーをビッグエンディアンからリトルエンディアンに変換(BSWAPL
)しています。SHA-256はビッグエンディアンを前提としているため、リトルエンディアンのAMD64アーキテクチャではこの変換が必要です。MSGSCHEDULE1
: 残りの48ワードのメッセージスケジュール計算を実装しています。ROTR
(ビット回転)やSHR
(ビットシフト)命令を効率的に使用しています。SHA256T1
,SHA256T2
: T1とT2の計算をそれぞれ実装しています。Ch
関数とMaj
関数は、ビット単位の論理演算(ANDL
,XORL
,NOTL
)で直接表現されています。SHA256ROUND
: T1とT2の計算結果を用いて、ハッシュワードを更新する一連の処理をカプセル化しています。
- ループ処理: 複数のメッセージブロックを処理するために、
loop
ラベルとJEQ
,JB
命令を用いたループ構造が採用されています。 - Goとの連携:
TEXT ·block(SB),0,$264-24
のように、Go言語のリンカが認識できる形式で関数を定義しています。FP
(フレームポインタ)レジスタを用いて、Go言語から渡される引数(dig
とp
)にアクセスしています。
このアセンブリ実装は、Go言語のコンパイラが生成するコードよりも、より密度の高い命令シーケンスと、レジスタのより効率的な利用を実現することで、SHA-256ハッシュ計算のパフォーマンスを大幅に向上させています。特に、ビット回転や論理演算が多用されるSHA-256のようなアルゴリズムでは、アセンブリレベルでの最適化が大きな効果を発揮します。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下の4ファイルです。
-
src/pkg/crypto/sha256/sha256.go
--- a/src/pkg/crypto/sha256/sha256.go +++ b/src/pkg/crypto/sha256/sha256.go @@ -3,7 +3,7 @@ // Package sha256 implements the SHA224 and SHA256 hash algorithms as defined -// in FIPS 180-2. +// in FIPS 180-4. package sha256 import (
- FIPS標準の参照が
FIPS 180-2
からFIPS 180-4
に更新されました。これは、SHA-256アルゴリズムの最新の標準文書への準拠を示すものです。
- FIPS標準の参照が
-
src/pkg/crypto/sha256/sha256block.go
--- a/src/pkg/crypto/sha256/sha256block.go +++ b/src/pkg/crypto/sha256/sha256block.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !amd64 + // SHA256 block step. // In its own file so that a faster assembly or C version // can be substituted easily.
- ファイルの先頭に
// +build !amd64
というビルドタグが追加されました。これは、このGo言語で書かれたblock
関数が、AMD64アーキテクチャ以外の場合にのみコンパイルされることを意味します。AMD64環境では、新しく追加されるアセンブリ実装が優先されます。
- ファイルの先頭に
-
src/pkg/crypto/sha256/sha256block_amd64.s
(新規追加ファイル)// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "../../../cmd/ld/textflag.h" // SHA256 block routine. See sha256block.go for Go equivalent. // // The algorithm is detailed in FIPS 180-4: // // http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf // // Wt = Mt; for 0 <= t <= 15 // Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 // // a = H0 // b = H1 // c = H2 // d = H3 // e = H4 // f = H5 // g = H6 // h = H7 // // for t = 0 to 63 { // T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt // T2 = BIGSIGMA0(a) + Maj(a,b,c) // h = g // g = f // f = e // e = d + T1 // d = c // c = b // b = a // a = T1 + T2 // } // // H0 = a + H0 // H1 = b + H1 // H2 = c + H2 // H3 = d + H3 // H4 = e + H4 // H5 = f + H5 // H6 = g + H6 // H7 = h + H7 // Wt = Mt; for 0 <= t <= 15 #define MSGSCHEDULE0(index) \ MOVL (index*4)(SI), AX; \ BSWAPL AX; \ MOVL AX, (index*4)(BP) // Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63 // SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x) // SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x) #define MSGSCHEDULE1(index) \ MOVL ((index-2)*4)(BP), AX; \ MOVL AX, CX; \ RORL $17, AX; \ MOVL CX, DX; \ RORL $19, CX; \ SHRL $10, DX; \ MOVL ((index-15)*4)(BP), BX; \ XORL CX, AX; \ MOVL BX, CX; \ XORL DX, AX; \ RORL $7, BX; \ MOVL CX, DX; \ SHRL $3, DX; \ RORL $18, CX; \ ADDL ((index-7)*4)(BP), AX; \ XORL CX, BX; \ XORL DX, BX; \ ADDL ((index-16)*4)(BP), BX; \ ADDL BX, AX; \ MOVL AX, ((index)*4)(BP) // Calculate T1 in AX - uses AX, CX and DX registers. // h is also used as an accumulator. Wt is passed in AX. // T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt // BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x) // Ch(x, y, z) = (x AND y) XOR (NOT x AND z) #define SHA256T1(const, e, f, g, h) \ ADDL AX, h; \ MOVL e, AX; \ ADDL $const, h; \ MOVL e, CX; \ RORL $6, AX; \ MOVL e, DX; \ RORL $11, CX; \ XORL CX, AX; \ MOVL e, CX; \ RORL $25, DX; \ ANDL f, CX; \ XORL AX, DX; \ MOVL e, AX; \ NOTL AX; \ ADDL DX, h; \ ANDL g, AX; \ XORL CX, AX; \ ADDL h, AX // Calculate T2 in BX - uses BX, CX, DX and DI registers. // T2 = BIGSIGMA0(a) + Maj(a, b, c) // BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x) // Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) #define SHA256T2(a, b, c) \ MOVL a, DI; \ MOVL c, BX; \ RORL $2, DI; \ MOVL a, DX; \ ANDL b, BX; \ RORL $13, DX; \ MOVL a, CX; \ ANDL c, CX; \ XORL DX, DI; \ XORL CX, BX; \ MOVL a, DX; \ MOVL b, CX; \ RORL $22, DX; \ ANDL a, CX; \ XORL CX, BX; \ XORL DX, DI; \ ADDL DI, BX // Calculate T1 and T2, then e = d + T1 and a = T1 + T2. // The values for e and a are stored in d and h, ready for rotation. #define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \ SHA256T1(const, e, f, g, h); \ SHA256T2(a, b, c); \ MOVL BX, h; \ ADDL AX, d; \ ADDL AX, h #define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \ MSGSCHEDULE0(index); \ SHA256ROUND(index, const, a, b, c, d, e, f, g, h) #define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \ MSGSCHEDULE1(index); \ SHA256ROUND(index, const, a, b, c, d, e, f, g, h) TEXT ·block(SB),0,$264-24 MOVQ p_base+8(FP), SI MOVQ p_len+16(FP), DX SHRQ $6, DX SHLQ $6, DX LEAQ (SI)(DX*1), DI MOVQ DI, 256(SP) CMPQ SI, DI JEQ end MOVQ dig+0(FP), BP MOVL (0*4)(BP), R8 // a = H0 MOVL (1*4)(BP), R9 // b = H1 MOVL (2*4)(BP), R10 // c = H2 MOVL (3*4)(BP), R11 // d = H3 MOVL (4*4)(BP), R12 // e = H4 MOVL (5*4)(BP), R13 // f = H5 MOVL (6*4)(BP), R14 // g = H6 MOVL (7*4)(BP), R15 // h = H7 loop: MOVQ SP, BP // message schedule SHA256ROUND0(0, 0x428a2f98, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND0(1, 0x71374491, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND0(2, 0xb5c0fbcf, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND0(3, 0xe9b5dba5, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND0(4, 0x3956c25b, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND0(5, 0x59f111f1, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND0(6, 0x923f82a4, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND0(7, 0xab1c5ed5, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND0(8, 0xd807aa98, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND0(9, 0x12835b01, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND0(10, 0x243185be, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND0(11, 0x550c7dc3, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND0(12, 0x72be5d74, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND0(13, 0x80deb1fe, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND0(14, 0x9bdc06a7, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND0(15, 0xc19bf174, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND1(16, 0xe49b69c1, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND1(17, 0xefbe4786, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND1(18, 0x0fc19dc6, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND1(19, 0x240ca1cc, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND1(20, 0x2de92c6f, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND1(21, 0x4a7484aa, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND1(22, 0x5cb0a9dc, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND1(23, 0x76f988da, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND1(24, 0x983e5152, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND1(25, 0xa831c66d, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND1(26, 0xb00327c8, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND1(27, 0xbf597fc7, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND1(28, 0xc6e00bf3, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND1(29, 0xd5a79147, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND1(30, 0x06ca6351, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND1(31, 0x14292967, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND1(32, 0x27b70a85, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND1(33, 0x2e1b2138, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND1(34, 0x4d2c6dfc, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND1(35, 0x53380d13, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND1(36, 0x650a7354, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND1(37, 0x766a0abb, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND1(38, 0x81c2c92e, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND1(39, 0x92722c85, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND1(40, 0xa2bfe8a1, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND1(41, 0xa81a664b, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND1(42, 0xc24b8b70, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND1(43, 0xc76c51a3, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND1(44, 0xd192e819, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND1(45, 0xd6990624, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND1(46, 0xf40e3585, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND1(47, 0x106aa070, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND1(48, 0x19a4c116, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND1(49, 0x1e376c08, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND1(50, 0x2748774c, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND1(51, 0x34b0bcb5, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND1(52, 0x391c0cb3, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND1(53, 0x4ed8aa4a, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND1(54, 0x5b9cca4f, R10, R11, R12, R13, R14, R15, R8, R9) SHA26ROUND1(55, 0x682e6ff3, R9, R10, R11, R12, R13, R14, R15, R8) SHA256ROUND1(56, 0x748f82ee, R8, R9, R10, R11, R12, R13, R14, R15) SHA256ROUND1(57, 0x78a5636f, R15, R8, R9, R10, R11, R12, R13, R14) SHA256ROUND1(58, 0x84c87814, R14, R15, R8, R9, R10, R11, R12, R13) SHA256ROUND1(59, 0x8cc70208, R13, R14, R15, R8, R9, R10, R11, R12) SHA256ROUND1(60, 0x90befffa, R12, R13, R14, R15, R8, R9, R10, R11) SHA256ROUND1(61, 0xa4506ceb, R11, R12, R13, R14, R15, R8, R9, R10) SHA256ROUND1(62, 0xbef9a3f7, R10, R11, R12, R13, R14, R15, R8, R9) SHA256ROUND1(63, 0xc67178f2, R9, R10, R11, R12, R13, R14, R15, R8) MOVQ dig+0(FP), BP ADDL (0*4)(BP), R8 // H0 = a + H0 MOVL R8, (0*4)(BP) ADDL (1*4)(BP), R9 // H1 = b + H1 MOVL R9, (1*4)(BP) ADDL (2*4)(BP), R10 // H2 = c + H2 MOVL R10, (2*4)(BP) ADDL (3*4)(BP), R11 // H3 = d + H3 MOVL R11, (3*4)(BP) ADDL (4*4)(BP), R12 // H4 = e + H4 MOVL R12, (4*4)(BP) ADDL (5*4)(BP), R13 // H5 = f + H5 MOVL R13, (5*4)(BP) ADDL (6*4)(BP), R14 // H6 = g + H6 MOVL R14, (6*4)(BP) ADDL (7*4)(BP), R15 // H7 = h + H7 MOVL R15, (7*4)(BP) ADDQ $64, SI CMPQ SI, 256(SP) JB loop end: RET
- このファイルは新規追加され、SHA-256のブロック処理をAMD64アセンブリ言語で実装しています。
- SHA-256のアルゴリズム(メッセージスケジュール、T1/T2計算、ハッシュ値更新)が、AMD64のレジスタと命令(
MOVL
,ADDL
,XORL
,RORL
,SHRL
,BSWAPL
など)を用いて効率的に記述されています。 #define
を用いて、繰り返し現れる計算パターンをマクロとして定義し、コードの可読性と保守性を高めています。- Go言語の関数呼び出し規約に従い、引数(
dig
,p
)へのアクセスや、戻り値の処理が行われています。
-
src/pkg/crypto/sha256/sha256block_decl.go
(新規追加ファイル)// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build amd64 package sha256 //go:noescape func block(dig *digest, p []byte)
- このファイルも新規追加され、
sha256block_amd64.s
で実装されたアセンブリ関数block
のGo言語側の宣言を提供します。 // +build amd64
ビルドタグにより、このファイルはAMD64アーキテクチャの場合にのみコンパイルされます。//go:noescape
ディレクティブは、この関数がヒープにメモリを割り当てないことをGoコンパイラに伝えます。これにより、コンパイラはより積極的な最適化を行うことができます。func block(dig *digest, p []byte)
は、アセンブリ関数がGo言語からどのように呼び出されるかのシグネチャを定義しています。
- このファイルも新規追加され、
コアとなるコードの解説
このコミットのコアとなるコードは、src/pkg/crypto/sha256/sha256block_amd64.s
に記述されたAMD64アセンブリ言語によるSHA-256ブロック処理の実装です。
このアセンブリコードは、SHA-256の圧縮関数を、CPUのレジスタを最大限に活用し、効率的な命令シーケンスで実行するように設計されています。
主要な要素と役割:
-
TEXT ·block(SB),0,$264-24
:- これはGo言語のリンカが認識するアセンブリ関数の宣言です。
·block
は、sha256
パッケージ内のblock
関数を指します。Go言語では、パッケージ内の非エクスポート関数は.
で始まる名前になります。SB
はStatic Baseレジスタを意味し、グローバルシンボルであることを示します。0
は関数がフレームポインタを必要としないことを示します(この場合はスタックフレームサイズが指定されているため、実際にはフレームポインタが使用されます)。$264-24
は、この関数のスタックフレームサイズが264バイトであり、そのうち24バイトが引数領域であることを示します。このスタックフレームは、メッセージスケジュールされたワード(W_t)を格納するために使用されます。
-
引数の取得:
MOVQ p_base+8(FP), SI
: Go言語から渡されたp
スライス(入力メッセージブロック)の基底アドレスをSI
レジスタにロードします。FP
はフレームポインタで、p_base+8
は引数p
の基底アドレスへのオフセットです。MOVQ p_len+16(FP), DX
:p
スライスの長さをDX
レジスタにロードします。SHRQ $6, DX
とSHLQ $6, DX
: これは、入力データの長さを64バイト(512ビット)のブロック数に変換し、再びバイト数に戻すことで、処理すべきブロックの総バイト数を計算しています。SHA-256は512ビット(64バイト)のブロック単位で処理されるため、入力データの長さを64の倍数に切り捨てています。
-
ハッシュ状態の初期化:
MOVQ dig+0(FP), BP
: Go言語から渡されたdigest
構造体(現在のハッシュ状態)へのポインタをBP
レジスタにロードします。MOVL (0*4)(BP), R8
からMOVL (7*4)(BP), R15
:digest
構造体から、現在の8つのハッシュワード(H0~H7)をR8からR15までの汎用レジスタにロードします。これらのレジスタは、SHA-256のラウンド計算中にA~Hの内部状態として使用されます。
-
loop
:- 複数のメッセージブロックを処理するためのループの開始点です。
MOVQ SP, BP
: メッセージスケジュールされたワードを格納するために、スタックポインタSP
をBP
レジスタにコピーします。これにより、スタック上の領域を一時的な作業領域として使用します。
-
SHA256ROUND0
とSHA256ROUND1
マクロ:- これらはSHA-256の64ラウンドの計算を実行するためのマクロです。
SHA256ROUND0
は最初の16ラウンド(メッセージブロックから直接W_tをロード)に使用されます。SHA256ROUND1
は残りの48ラウンド(メッセージスケジュール計算を実行してW_tを生成)に使用されます。- 各マクロは、現在のラウンドの定数(K_t)と、8つのハッシュワード(A, B, C, D, E, F, G, H)を引数として受け取ります。
- 内部的には、
MSGSCHEDULE0
/MSGSCHEDULE1
でW_tを計算し、SHA256T1
/SHA256T2
でT1/T2を計算し、最終的にSHA256ROUND
でハッシュワードを更新します。
-
ハッシュ状態の更新と保存:
- 64ラウンドの計算が終了した後、
ADDL (0*4)(BP), R8
からADDL (7*4)(BP), R15
の命令群で、計算された新しいハッシュワード(R8-R15)を、元のハッシュ状態(dig
構造体)に加算します。これは、SHA-256の仕様に従い、各ブロックの処理後に累積ハッシュ値を更新するステップです。 MOVL R8, (0*4)(BP)
などで、更新されたハッシュ値を再びdigest
構造体のメモリ領域に書き戻します。
- 64ラウンドの計算が終了した後、
-
ループの継続と終了:
ADDQ $64, SI
: 次のメッセージブロックの開始アドレスを計算するために、入力データポインタSI
を64バイト進めます。CMPQ SI, 256(SP)
とJB loop
: 処理すべきブロックが残っているかを確認し、残っていればloop
にジャンプして次のブロックの処理を開始します。256(SP)
は、処理すべき入力データの終了アドレスが格納されているスタック上の場所を指します。JEQ end
: 処理すべきブロックがなければend
にジャンプします。RET
: 関数から戻ります。
このアセンブリコードは、SHA-256の複雑なビット操作と加算を、AMD64の命令セットに直接マッピングすることで、Go言語のコンパイラが生成する汎用的なコードよりも、はるかに高速な実行を実現しています。特に、レジスタの効率的な利用、バイトオーダーの変換(BSWAPL
)、そしてビット回転命令(RORL
)の直接使用が、パフォーマンス向上に大きく貢献しています。
関連リンク
- Go言語の
crypto/sha256
パッケージのドキュメント: https://pkg.go.dev/crypto/sha256 - Go言語のアセンブリについて: https://go.dev/doc/asm
- FIPS 180-4 (Secure Hash Standard): https://csrc.nist.gov/publications/detail/fips/180/4/final
参考にした情報源リンク
- FIPS PUB 180-4: Secure Hash Standard (SHS) - NIST: https://csrc.nist.gov/publications/detail/fips/180/4/final
- Go Assembly Language - The Go Programming Language: https://go.dev/doc/asm
- Go's
+build
directives: https://go.dev/blog/go1.4-custom-builds - Understanding
//go:noescape
in Go: https://medium.com/@valyala/go-tag-go-noescape-and-its-magic-d0f26294046 - SHA-256 Algorithm Explained: https://www.youtube.com/watch?v=f9YQ4_2gB40 (SHA-256のアルゴリズムを視覚的に理解するのに役立つ動画)
- Intel® 64 and IA-32 Architectures Software Developer’s Manuals: https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html (AMD64アセンブリ命令の詳細なリファレンス)