//+build !noasm,!appengine
/*
* 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 !avx512 {
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 !avx512 {
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 !avx512 {
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 !avx512 {
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 !avx512 {
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 !avx512 {
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 !avx512 {
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 !avx512 {
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)
}
}
}