You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
545 lines
12 KiB
545 lines
12 KiB
//go:build !noasm && !appengine && gc |
|
// +build !noasm,!appengine,gc |
|
|
|
/* |
|
* Minio Cloud Storage, (C) 2017 Minio, Inc. |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
package sha256 |
|
|
|
import ( |
|
"bytes" |
|
"encoding/binary" |
|
"encoding/hex" |
|
"fmt" |
|
"hash" |
|
"reflect" |
|
"sync" |
|
"testing" |
|
) |
|
|
|
func TestGoldenAVX512(t *testing.T) { |
|
|
|
if !hasAvx512 { |
|
// t.SkipNow() |
|
return |
|
} |
|
|
|
server := NewAvx512Server() |
|
h512 := NewAvx512(server) |
|
|
|
for _, g := range golden { |
|
h512.Reset() |
|
h512.Write([]byte(g.in)) |
|
digest := h512.Sum([]byte{}) |
|
s := fmt.Sprintf("%x", digest) |
|
if !reflect.DeepEqual(digest, g.out[:]) { |
|
t.Fatalf( |
|
"Sum256 function: sha256(%s) = %s want %s", g.in, s, |
|
hex.EncodeToString(g.out[:]), |
|
) |
|
} |
|
} |
|
} |
|
|
|
func createInputs(size int) [16][]byte { |
|
input := [16][]byte{} |
|
for i := 0; i < 16; i++ { |
|
input[i] = make([]byte, size) |
|
} |
|
return input |
|
} |
|
|
|
func initDigests() *[512]byte { |
|
digests := [512]byte{} |
|
for i := 0; i < 16; i++ { |
|
binary.LittleEndian.PutUint32(digests[(i+0*16)*4:], init0) |
|
binary.LittleEndian.PutUint32(digests[(i+1*16)*4:], init1) |
|
binary.LittleEndian.PutUint32(digests[(i+2*16)*4:], init2) |
|
binary.LittleEndian.PutUint32(digests[(i+3*16)*4:], init3) |
|
binary.LittleEndian.PutUint32(digests[(i+4*16)*4:], init4) |
|
binary.LittleEndian.PutUint32(digests[(i+5*16)*4:], init5) |
|
binary.LittleEndian.PutUint32(digests[(i+6*16)*4:], init6) |
|
binary.LittleEndian.PutUint32(digests[(i+7*16)*4:], init7) |
|
} |
|
return &digests |
|
} |
|
|
|
func testSha256Avx512(t *testing.T, offset, padding int) [16][]byte { |
|
|
|
if !hasAvx512 { |
|
// t.SkipNow() |
|
return [16][]byte{} |
|
} |
|
|
|
l := uint(len(golden[offset].in)) |
|
extraBlock := uint(0) |
|
if padding == 0 { |
|
extraBlock += 9 |
|
} else { |
|
extraBlock += 64 |
|
} |
|
input := createInputs(int(l + extraBlock)) |
|
for i := 0; i < 16; i++ { |
|
copy(input[i], golden[offset+i].in) |
|
input[i][l] = 0x80 |
|
copy(input[i][l+1:], bytes.Repeat([]byte{0}, padding)) |
|
|
|
// Length in bits. |
|
len := uint64(l) |
|
len <<= 3 |
|
for ii := uint(0); ii < 8; ii++ { |
|
input[i][l+1+uint(padding)+ii] = byte(len >> (56 - 8*ii)) |
|
} |
|
} |
|
mask := make([]uint64, len(input[0])>>6) |
|
for m := range mask { |
|
mask[m] = 0xffff |
|
} |
|
output := blockAvx512(initDigests(), input, mask) |
|
for i := 0; i < 16; i++ { |
|
if bytes.Compare(output[i][:], golden[offset+i].out[:]) != 0 { |
|
t.Fatalf( |
|
"Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, |
|
hex.EncodeToString(output[i][:]), |
|
hex.EncodeToString(golden[offset+i].out[:]), |
|
) |
|
} |
|
} |
|
return input |
|
} |
|
|
|
func TestAvx512_1Block(t *testing.T) { testSha256Avx512(t, 31, 0) } |
|
func TestAvx512_3Blocks(t *testing.T) { testSha256Avx512(t, 47, 55) } |
|
|
|
func TestAvx512_MixedBlocks(t *testing.T) { |
|
|
|
if !hasAvx512 { |
|
// t.SkipNow() |
|
return |
|
} |
|
|
|
inputSingleBlock := testSha256Avx512(t, 31, 0) |
|
inputMultiBlock := testSha256Avx512(t, 47, 55) |
|
|
|
input := [16][]byte{} |
|
|
|
for i := range input { |
|
if i%2 == 0 { |
|
input[i] = inputMultiBlock[i] |
|
} else { |
|
input[i] = inputSingleBlock[i] |
|
} |
|
} |
|
|
|
mask := [3]uint64{0xffff, 0x5555, 0x5555} |
|
output := blockAvx512(initDigests(), input, mask[:]) |
|
var offset int |
|
for i := 0; i < len(output); i++ { |
|
if i%2 == 0 { |
|
offset = 47 |
|
} else { |
|
offset = 31 |
|
} |
|
if bytes.Compare(output[i][:], golden[offset+i].out[:]) != 0 { |
|
t.Fatalf( |
|
"Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, |
|
hex.EncodeToString(output[i][:]), |
|
hex.EncodeToString(golden[offset+i].out[:]), |
|
) |
|
} |
|
} |
|
} |
|
|
|
func TestAvx512_MixedWithNilBlocks(t *testing.T) { |
|
|
|
if !hasAvx512 { |
|
// t.SkipNow() |
|
return |
|
} |
|
|
|
inputSingleBlock := testSha256Avx512(t, 31, 0) |
|
inputMultiBlock := testSha256Avx512(t, 47, 55) |
|
|
|
input := [16][]byte{} |
|
|
|
for i := range input { |
|
if i%3 == 0 { |
|
input[i] = inputMultiBlock[i] |
|
} else if i%3 == 1 { |
|
input[i] = inputSingleBlock[i] |
|
} else { |
|
input[i] = nil |
|
} |
|
} |
|
|
|
mask := [3]uint64{0xb6db, 0x9249, 0x9249} |
|
output := blockAvx512(initDigests(), input, mask[:]) |
|
var offset int |
|
for i := 0; i < len(output); i++ { |
|
if i%3 == 2 { // for nil inputs |
|
initvec := [32]byte{ |
|
0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85, |
|
0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5, 0x3a, |
|
0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c, |
|
0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0, 0xcd, 0x19, |
|
} |
|
if bytes.Compare(output[i][:], initvec[:]) != 0 { |
|
t.Fatalf( |
|
"Sum256 function: sha256 for nil vector = %s want %s", |
|
hex.EncodeToString(output[i][:]), |
|
hex.EncodeToString(initvec[:]), |
|
) |
|
} |
|
continue |
|
} |
|
if i%3 == 0 { |
|
offset = 47 |
|
} else { |
|
offset = 31 |
|
} |
|
if bytes.Compare(output[i][:], golden[offset+i].out[:]) != 0 { |
|
t.Fatalf( |
|
"Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, |
|
hex.EncodeToString(output[i][:]), |
|
hex.EncodeToString(golden[offset+i].out[:]), |
|
) |
|
} |
|
} |
|
} |
|
|
|
func TestAvx512Server(t *testing.T) { |
|
|
|
if !hasAvx512 { |
|
// t.SkipNow() |
|
return |
|
} |
|
|
|
const offset = 31 + 16 |
|
server := NewAvx512Server() |
|
|
|
// First block of 64 bytes |
|
for i := 0; i < 16; i++ { |
|
input := make([]byte, 64) |
|
copy(input, golden[offset+i].in) |
|
server.Write(uint64(Avx512ServerUID+i), input) |
|
} |
|
|
|
// Second block of 64 bytes |
|
for i := 0; i < 16; i++ { |
|
input := make([]byte, 64) |
|
copy(input, golden[offset+i].in[64:]) |
|
server.Write(uint64(Avx512ServerUID+i), input) |
|
} |
|
|
|
wg := sync.WaitGroup{} |
|
wg.Add(16) |
|
|
|
// Third and final block |
|
for i := 0; i < 16; i++ { |
|
input := make([]byte, 64) |
|
input[0] = 0x80 |
|
copy(input[1:], bytes.Repeat([]byte{0}, 63-8)) |
|
|
|
// Length in bits. |
|
len := uint64(128) |
|
len <<= 3 |
|
for ii := uint(0); ii < 8; ii++ { |
|
input[63-8+1+ii] = byte(len >> (56 - 8*ii)) |
|
} |
|
go func(i int, uid uint64, input []byte) { |
|
output := server.Sum(uid, input) |
|
if bytes.Compare(output[:], golden[offset+i].out[:]) != 0 { |
|
t.Fatalf( |
|
"Sum256 function: sha256(%s) = %s want %s", |
|
golden[offset+i].in, |
|
hex.EncodeToString(output[:]), |
|
hex.EncodeToString(golden[offset+i].out[:]), |
|
) |
|
} |
|
wg.Done() |
|
}(i, uint64(Avx512ServerUID+i), input) |
|
} |
|
|
|
wg.Wait() |
|
} |
|
|
|
func TestAvx512Digest(t *testing.T) { |
|
|
|
if !hasAvx512 { |
|
// t.SkipNow() |
|
return |
|
} |
|
|
|
server := NewAvx512Server() |
|
|
|
const tests = 16 |
|
h512 := [16]hash.Hash{} |
|
for i := 0; i < tests; i++ { |
|
h512[i] = NewAvx512(server) |
|
} |
|
|
|
const offset = 31 + 16 |
|
for i := 0; i < tests; i++ { |
|
input := make([]byte, 64) |
|
copy(input, golden[offset+i].in) |
|
h512[i].Write(input) |
|
} |
|
for i := 0; i < tests; i++ { |
|
input := make([]byte, 64) |
|
copy(input, golden[offset+i].in[64:]) |
|
h512[i].Write(input) |
|
} |
|
for i := 0; i < tests; i++ { |
|
output := h512[i].Sum([]byte{}) |
|
if bytes.Compare(output[:], golden[offset+i].out[:]) != 0 { |
|
t.Fatalf( |
|
"Sum256 function: sha256(%s) = %s want %s", golden[offset+i].in, |
|
hex.EncodeToString(output[:]), |
|
hex.EncodeToString(golden[offset+i].out[:]), |
|
) |
|
} |
|
} |
|
} |
|
|
|
func benchmarkAvx512SingleCore(h512 []hash.Hash, body []byte) { |
|
|
|
for i := 0; i < len(h512); i++ { |
|
h512[i].Write(body) |
|
} |
|
for i := 0; i < len(h512); i++ { |
|
_ = h512[i].Sum([]byte{}) |
|
} |
|
} |
|
|
|
func benchmarkAvx512(b *testing.B, size int) { |
|
|
|
if !hasAvx512 { |
|
b.SkipNow() |
|
return |
|
} |
|
|
|
server := NewAvx512Server() |
|
|
|
const tests = 16 |
|
body := make([]byte, size) |
|
|
|
b.SetBytes(int64(len(body) * tests)) |
|
b.ResetTimer() |
|
|
|
for i := 0; i < b.N; i++ { |
|
h512 := make([]hash.Hash, tests) |
|
for i := 0; i < tests; i++ { |
|
h512[i] = NewAvx512(server) |
|
} |
|
|
|
benchmarkAvx512SingleCore(h512, body) |
|
} |
|
} |
|
|
|
func BenchmarkAvx512_05M(b *testing.B) { benchmarkAvx512(b, 512*1024) } |
|
func BenchmarkAvx512_1M(b *testing.B) { benchmarkAvx512(b, 1*1024*1024) } |
|
func BenchmarkAvx512_5M(b *testing.B) { benchmarkAvx512(b, 5*1024*1024) } |
|
func BenchmarkAvx512_10M(b *testing.B) { benchmarkAvx512(b, 10*1024*1024) } |
|
|
|
func benchmarkAvx512MultiCore(b *testing.B, size, cores int) { |
|
|
|
if !hasAvx512 { |
|
b.SkipNow() |
|
return |
|
} |
|
|
|
servers := make([]*Avx512Server, cores) |
|
for c := 0; c < cores; c++ { |
|
servers[c] = NewAvx512Server() |
|
} |
|
|
|
const tests = 16 |
|
|
|
body := make([]byte, size) |
|
|
|
h512 := make([]hash.Hash, tests*cores) |
|
for i := 0; i < tests*cores; i++ { |
|
h512[i] = NewAvx512(servers[i>>4]) |
|
} |
|
|
|
b.SetBytes(int64(size * 16 * cores)) |
|
b.ResetTimer() |
|
|
|
var wg sync.WaitGroup |
|
|
|
for i := 0; i < b.N; i++ { |
|
wg.Add(cores) |
|
for c := 0; c < cores; c++ { |
|
go func(c int) { |
|
benchmarkAvx512SingleCore( |
|
h512[c*tests:(c+1)*tests], |
|
body, |
|
) |
|
wg.Done() |
|
}(c) |
|
} |
|
wg.Wait() |
|
} |
|
} |
|
|
|
func BenchmarkAvx512_5M_2Cores(b *testing.B) { |
|
benchmarkAvx512MultiCore( |
|
b, 5*1024*1024, 2, |
|
) |
|
} |
|
func BenchmarkAvx512_5M_4Cores(b *testing.B) { |
|
benchmarkAvx512MultiCore( |
|
b, 5*1024*1024, 4, |
|
) |
|
} |
|
func BenchmarkAvx512_5M_6Cores(b *testing.B) { |
|
benchmarkAvx512MultiCore( |
|
b, 5*1024*1024, 6, |
|
) |
|
} |
|
|
|
type maskTest struct { |
|
in [16]int |
|
out [16]maskRounds |
|
} |
|
|
|
var goldenMask = []maskTest{ |
|
{[16]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, [16]maskRounds{}}, |
|
{ |
|
[16]int{64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0}, |
|
[16]maskRounds{{0x5555, 1}}, |
|
}, |
|
{ |
|
[16]int{0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64}, |
|
[16]maskRounds{{0xaaaa, 1}}, |
|
}, |
|
{ |
|
[16]int{64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}, |
|
[16]maskRounds{{0xffff, 1}}, |
|
}, |
|
{ |
|
[16]int{ |
|
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, |
|
128, 128, 128, |
|
}, |
|
[16]maskRounds{{0xffff, 2}}, |
|
}, |
|
{ |
|
[16]int{ |
|
64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, |
|
128, |
|
}, |
|
[16]maskRounds{{0xffff, 1}, {0xaaaa, 1}}, |
|
}, |
|
{ |
|
[16]int{ |
|
128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, 64, 128, |
|
64, |
|
}, |
|
[16]maskRounds{{0xffff, 1}, {0x5555, 1}}, |
|
}, |
|
{ |
|
[16]int{ |
|
64, 192, 64, 192, 64, 192, 64, 192, 64, 192, 64, 192, 64, 192, 64, |
|
192, |
|
}, |
|
[16]maskRounds{{0xffff, 1}, {0xaaaa, 2}}, |
|
}, |
|
// |
|
// >= 64 0110=6 1011=b 1101=d 0110=6 |
|
// >=128 0100=4 0010=2 1001=9 0100=4 |
|
{ |
|
[16]int{0, 64, 128, 0, 64, 128, 0, 64, 128, 0, 64, 128, 0, 64, 128, 0}, |
|
[16]maskRounds{{0x6db6, 1}, {0x4924, 1}}, |
|
}, |
|
{ |
|
[16]int{ |
|
1 * 64, 2 * 64, 3 * 64, 4 * 64, 5 * 64, 6 * 64, 7 * 64, 8 * 64, |
|
9 * 64, 10 * 64, |
|
11 * 64, 12 * 64, 13 * 64, 14 * 64, 15 * 64, 16 * 64, |
|
}, |
|
[16]maskRounds{ |
|
{0xffff, 1}, {0xfffe, 1}, {0xfffc, 1}, {0xfff8, 1}, {0xfff0, 1}, |
|
{0xffe0, 1}, {0xffc0, 1}, {0xff80, 1}, |
|
{0xff00, 1}, {0xfe00, 1}, {0xfc00, 1}, {0xf800, 1}, {0xf000, 1}, |
|
{0xe000, 1}, |
|
{0xc000, 1}, {0x8000, 1}, |
|
}, |
|
}, |
|
{ |
|
[16]int{ |
|
2 * 64, 1 * 64, 3 * 64, 4 * 64, 5 * 64, 6 * 64, 7 * 64, 8 * 64, |
|
9 * 64, 10 * 64, |
|
11 * 64, 12 * 64, 13 * 64, 14 * 64, 15 * 64, 16 * 64, |
|
}, |
|
[16]maskRounds{ |
|
{0xffff, 1}, {0xfffd, 1}, {0xfffc, 1}, {0xfff8, 1}, {0xfff0, 1}, |
|
{0xffe0, 1}, {0xffc0, 1}, {0xff80, 1}, |
|
{0xff00, 1}, {0xfe00, 1}, {0xfc00, 1}, {0xf800, 1}, {0xf000, 1}, |
|
{0xe000, 1}, |
|
{0xc000, 1}, {0x8000, 1}, |
|
}, |
|
}, |
|
{ |
|
[16]int{ |
|
10 * 64, 20 * 64, 30 * 64, 40 * 64, 50 * 64, 60 * 64, 70 * 64, |
|
80 * 64, 90 * 64, |
|
100 * 64, 110 * 64, 120 * 64, 130 * 64, 140 * 64, 150 * 64, |
|
160 * 64, |
|
}, |
|
[16]maskRounds{ |
|
{0xffff, 10}, {0xfffe, 10}, {0xfffc, 10}, {0xfff8, 10}, |
|
{0xfff0, 10}, |
|
{0xffe0, 10}, {0xffc0, 10}, {0xff80, 10}, |
|
{0xff00, 10}, {0xfe00, 10}, {0xfc00, 10}, {0xf800, 10}, |
|
{0xf000, 10}, {0xe000, 10}, |
|
{0xc000, 10}, {0x8000, 10}, |
|
}, |
|
}, |
|
{ |
|
[16]int{ |
|
10 * 64, 19 * 64, 27 * 64, 34 * 64, 40 * 64, 45 * 64, 49 * 64, |
|
52 * 64, 54 * 64, |
|
55 * 64, 57 * 64, 60 * 64, 64 * 64, 69 * 64, 75 * 64, 82 * 64, |
|
}, |
|
[16]maskRounds{ |
|
{0xffff, 10}, {0xfffe, 9}, {0xfffc, 8}, {0xfff8, 7}, {0xfff0, 6}, |
|
{0xffe0, 5}, {0xffc0, 4}, {0xff80, 3}, |
|
{0xff00, 2}, {0xfe00, 1}, {0xfc00, 2}, {0xf800, 3}, {0xf000, 4}, |
|
{0xe000, 5}, |
|
{0xc000, 6}, {0x8000, 7}, |
|
}, |
|
}, |
|
} |
|
|
|
func TestMaskGen(t *testing.T) { |
|
input := [16][]byte{} |
|
for gcase, g := range goldenMask { |
|
for i, l := range g.in { |
|
buf := make([]byte, l) |
|
input[i] = buf[:] |
|
} |
|
|
|
mr := genMask(input) |
|
|
|
if !reflect.DeepEqual(mr, g.out) { |
|
t.Fatalf( |
|
"case %d: got %04x\n want %04x", gcase, mr, |
|
g.out, |
|
) |
|
} |
|
} |
|
}
|
|
|