The vector (vx) implementation has been updated to read in the state and update it - as opposed to being a single shot function. This has allowed the new MAC interface can be implemented. For performance reasons s390x uses a larger buffer than the generic implementation. There is a relatively high fixed cost to read the state, calculate the key coefficients and serialize the state, so it makes sense to buffer more blocks before calling it. For now I've had to remove the faster VMSL implementation. It is too complex for me to update in time for Go 1.15. At some point I'd like to revisit it but for now it looks like using the MAC interface is more of a win than using VMSL. The benchmarks show considerable improvements when using the MAC interface. The Sum benchmarks show slowdown due to a combination of the removal of the VMSL implementation and also the added overhead from splitting the summation function into multiple parts. poly1305: name old speed new speed delta 64 1.33GB/s ± 0% 0.80GB/s ± 1% -39.51% (p=0.000 n=16+20) 1K 4.04GB/s ± 0% 2.97GB/s ± 0% -26.46% (p=0.000 n=19+19) 2M 5.32GB/s ± 1% 3.63GB/s ± 0% -31.76% (p=0.000 n=20+19) 64Unaligned 1.33GB/s ± 0% 0.80GB/s ± 0% -39.80% (p=0.000 n=19+18) 1KUnaligned 4.09GB/s ± 1% 2.94GB/s ± 0% -28.23% (p=0.000 n=19+18) 2MUnaligned 5.33GB/s ± 1% 3.52GB/s ± 0% -34.04% (p=0.000 n=20+19) Write64 1.03GB/s ± 1% 1.49GB/s ± 1% +44.34% (p=0.000 n=20+20) Write1K 1.21GB/s ± 0% 3.24GB/s ± 0% +169.02% (p=0.000 n=20+17) Write2M 1.24GB/s ± 1% 3.63GB/s ± 0% +192.36% (p=0.000 n=20+19) Write64Unaligned 1.04GB/s ± 1% 1.50GB/s ± 0% +44.16% (p=0.000 n=19+14) Write1KUnaligned 1.21GB/s ± 0% 3.20GB/s ± 0% +164.55% (p=0.000 n=20+16) Write2MUnaligned 1.24GB/s ± 1% 3.51GB/s ± 0% +183.96% (p=0.000 n=20+19) chacha20poly1305 (this vs. using generic MAC interface - post CL 206977): name old speed new speed delta Open-64 147MB/s ± 2% 156MB/s ± 1% +6.15% (p=0.000 n=20+19) Seal-64 151MB/s ± 0% 164MB/s ± 1% +8.86% (p=0.000 n=19+16) Open-64-X 104MB/s ± 2% 111MB/s ± 1% +6.24% (p=0.000 n=20+20) Seal-64-X 109MB/s ± 2% 111MB/s ± 1% +2.11% (p=0.000 n=20+19) Open-1350 555MB/s ± 0% 751MB/s ± 1% +35.19% (p=0.000 n=20+20) Seal-1350 557MB/s ± 0% 759MB/s ± 0% +36.23% (p=0.000 n=20+20) Open-1350-X 517MB/s ± 1% 683MB/s ± 1% +31.97% (p=0.000 n=20+20) Seal-1350-X 511MB/s ± 0% 683MB/s ± 0% +33.77% (p=0.000 n=18+19) Open-8192 672MB/s ± 0% 1013MB/s ± 0% +50.65% (p=0.000 n=19+19) Seal-8192 674MB/s ± 0% 1018MB/s ± 0% +50.98% (p=0.000 n=18+20) Open-8192-X 663MB/s ± 0% 979MB/s ± 0% +47.57% (p=0.000 n=20+20) Seal-8192-X 658MB/s ± 0% 985MB/s ± 0% +49.62% (p=0.000 n=18+20) name old allocs/op new allocs/op delta Open-64 0.00 0.00 ~ (all equal) Seal-64 0.00 0.00 ~ (all equal) Open-64-X 0.00 0.00 ~ (all equal) Seal-64-X 0.00 0.00 ~ (all equal) Open-1350 0.00 0.00 ~ (all equal) Seal-1350 0.00 0.00 ~ (all equal) Open-1350-X 0.00 0.00 ~ (all equal) Seal-1350-X 0.00 0.00 ~ (all equal) Open-8192 0.00 0.00 ~ (all equal) Seal-8192 0.00 0.00 ~ (all equal) Open-8192-X 0.00 0.00 ~ (all equal) Seal-8192-X 0.00 0.00 ~ (all equal) chacha20poly1305 (this vs. using asm Sum interface - pre CL 206977): name old speed new speed delta Open-64 144MB/s ± 0% 156MB/s ± 1% +8.16% (p=0.000 n=20+19) Seal-64 150MB/s ± 0% 164MB/s ± 1% +9.35% (p=0.000 n=20+16) Open-64-X 104MB/s ± 1% 111MB/s ± 1% +6.15% (p=0.000 n=19+20) Seal-64-X 109MB/s ± 1% 111MB/s ± 1% +1.43% (p=0.000 n=19+19) Open-1350 702MB/s ± 1% 751MB/s ± 1% +6.98% (p=0.000 n=20+20) Seal-1350 715MB/s ± 0% 759MB/s ± 0% +6.09% (p=0.000 n=19+20) Open-1350-X 642MB/s ± 0% 683MB/s ± 1% +6.37% (p=0.000 n=19+20) Seal-1350-X 639MB/s ± 0% 683MB/s ± 0% +6.98% (p=0.000 n=20+19) Open-8192 994MB/s ± 0% 1013MB/s ± 0% +1.85% (p=0.000 n=20+19) Seal-8192 1.00GB/s ± 0% 1.02GB/s ± 0% +1.90% (p=0.000 n=20+20) Open-8192-X 965MB/s ± 0% 979MB/s ± 0% +1.43% (p=0.000 n=19+20) Seal-8192-X 962MB/s ± 0% 985MB/s ± 0% +2.39% (p=0.000 n=20+20) name old allocs/op new allocs/op delta Open-64 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Seal-64 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Open-64-X 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Seal-64-X 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Open-1350 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Seal-1350 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Open-1350-X 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Seal-1350-X 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Open-8192 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Seal-8192 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Open-8192-X 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Seal-8192-X 1.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) Updates golang/go#25219. Change-Id: Ib491e3a47b6b3ec8bbbe1f41f7bf42ad82f5c249 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/219057 Run-TryBot: Michael Munday <mike.munday@ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org>
277 lines
7.0 KiB
Go
277 lines
7.0 KiB
Go
// Copyright 2012 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.
|
|
|
|
package poly1305
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"flag"
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
var stressFlag = flag.Bool("stress", false, "run slow stress tests")
|
|
|
|
type test struct {
|
|
in string
|
|
key string
|
|
tag string
|
|
state string
|
|
}
|
|
|
|
func (t *test) Input() []byte {
|
|
in, err := hex.DecodeString(t.in)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return in
|
|
}
|
|
|
|
func (t *test) Key() [32]byte {
|
|
buf, err := hex.DecodeString(t.key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var key [32]byte
|
|
copy(key[:], buf[:32])
|
|
return key
|
|
}
|
|
|
|
func (t *test) Tag() [16]byte {
|
|
buf, err := hex.DecodeString(t.tag)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var tag [16]byte
|
|
copy(tag[:], buf[:16])
|
|
return tag
|
|
}
|
|
|
|
func (t *test) InitialState() [3]uint64 {
|
|
// state is hex encoded in big-endian byte order
|
|
if t.state == "" {
|
|
return [3]uint64{0, 0, 0}
|
|
}
|
|
buf, err := hex.DecodeString(t.state)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if len(buf) != 3*8 {
|
|
panic("incorrect state length")
|
|
}
|
|
return [3]uint64{
|
|
binary.BigEndian.Uint64(buf[16:24]),
|
|
binary.BigEndian.Uint64(buf[8:16]),
|
|
binary.BigEndian.Uint64(buf[0:8]),
|
|
}
|
|
}
|
|
|
|
func testSum(t *testing.T, unaligned bool, sumImpl func(tag *[TagSize]byte, msg []byte, key *[32]byte)) {
|
|
var tag [16]byte
|
|
for i, v := range testData {
|
|
// cannot set initial state before calling sum, so skip those tests
|
|
if v.InitialState() != [3]uint64{0, 0, 0} {
|
|
continue
|
|
}
|
|
|
|
in := v.Input()
|
|
if unaligned {
|
|
in = unalignBytes(in)
|
|
}
|
|
key := v.Key()
|
|
sumImpl(&tag, in, &key)
|
|
if tag != v.Tag() {
|
|
t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:])
|
|
}
|
|
if !Verify(&tag, in, &key) {
|
|
t.Errorf("%d: tag didn't verify", i)
|
|
}
|
|
// If the key is zero, the tag will always be zero, independent of the input.
|
|
if len(in) > 0 && key != [32]byte{} {
|
|
in[0] ^= 0xff
|
|
if Verify(&tag, in, &key) {
|
|
t.Errorf("%d: tag verified after altering the input", i)
|
|
}
|
|
in[0] ^= 0xff
|
|
}
|
|
// If the input is empty, the tag only depends on the second half of the key.
|
|
if len(in) > 0 {
|
|
key[0] ^= 0xff
|
|
if Verify(&tag, in, &key) {
|
|
t.Errorf("%d: tag verified after altering the key", i)
|
|
}
|
|
key[0] ^= 0xff
|
|
}
|
|
tag[0] ^= 0xff
|
|
if Verify(&tag, in, &key) {
|
|
t.Errorf("%d: tag verified after altering the tag", i)
|
|
}
|
|
tag[0] ^= 0xff
|
|
}
|
|
}
|
|
|
|
func TestBurnin(t *testing.T) {
|
|
// This test can be used to sanity-check significant changes. It can
|
|
// take about many minutes to run, even on fast machines. It's disabled
|
|
// by default.
|
|
if !*stressFlag {
|
|
t.Skip("skipping without -stress")
|
|
}
|
|
|
|
var key [32]byte
|
|
var input [25]byte
|
|
var output [16]byte
|
|
|
|
for i := range key {
|
|
key[i] = 1
|
|
}
|
|
for i := range input {
|
|
input[i] = 2
|
|
}
|
|
|
|
for i := uint64(0); i < 1e10; i++ {
|
|
Sum(&output, input[:], &key)
|
|
copy(key[0:], output[:])
|
|
copy(key[16:], output[:])
|
|
copy(input[:], output[:])
|
|
copy(input[16:], output[:])
|
|
}
|
|
|
|
const expected = "5e3b866aea0b636d240c83c428f84bfa"
|
|
if got := hex.EncodeToString(output[:]); got != expected {
|
|
t.Errorf("expected %s, got %s", expected, got)
|
|
}
|
|
}
|
|
|
|
func TestSum(t *testing.T) { testSum(t, false, Sum) }
|
|
func TestSumUnaligned(t *testing.T) { testSum(t, true, Sum) }
|
|
func TestSumGeneric(t *testing.T) { testSum(t, false, sumGeneric) }
|
|
func TestSumGenericUnaligned(t *testing.T) { testSum(t, true, sumGeneric) }
|
|
|
|
func TestWriteGeneric(t *testing.T) { testWriteGeneric(t, false) }
|
|
func TestWriteGenericUnaligned(t *testing.T) { testWriteGeneric(t, true) }
|
|
func TestWrite(t *testing.T) { testWrite(t, false) }
|
|
func TestWriteUnaligned(t *testing.T) { testWrite(t, true) }
|
|
|
|
func testWriteGeneric(t *testing.T, unaligned bool) {
|
|
for i, v := range testData {
|
|
key := v.Key()
|
|
input := v.Input()
|
|
var out [16]byte
|
|
|
|
if unaligned {
|
|
input = unalignBytes(input)
|
|
}
|
|
h := newMACGeneric(&key)
|
|
if s := v.InitialState(); s != [3]uint64{0, 0, 0} {
|
|
h.macState.h = s
|
|
}
|
|
n, err := h.Write(input[:len(input)/3])
|
|
if err != nil || n != len(input[:len(input)/3]) {
|
|
t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
|
|
}
|
|
n, err = h.Write(input[len(input)/3:])
|
|
if err != nil || n != len(input[len(input)/3:]) {
|
|
t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
|
|
}
|
|
h.Sum(&out)
|
|
if tag := v.Tag(); out != tag {
|
|
t.Errorf("%d: expected %x, got %x", i, tag[:], out[:])
|
|
}
|
|
}
|
|
}
|
|
|
|
func testWrite(t *testing.T, unaligned bool) {
|
|
for i, v := range testData {
|
|
key := v.Key()
|
|
input := v.Input()
|
|
var out [16]byte
|
|
|
|
if unaligned {
|
|
input = unalignBytes(input)
|
|
}
|
|
h := New(&key)
|
|
if s := v.InitialState(); s != [3]uint64{0, 0, 0} {
|
|
h.macState.h = s
|
|
}
|
|
n, err := h.Write(input[:len(input)/3])
|
|
if err != nil || n != len(input[:len(input)/3]) {
|
|
t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
|
|
}
|
|
n, err = h.Write(input[len(input)/3:])
|
|
if err != nil || n != len(input[len(input)/3:]) {
|
|
t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err)
|
|
}
|
|
h.Sum(out[:0])
|
|
tag := v.Tag()
|
|
if out != tag {
|
|
t.Errorf("%d: expected %x, got %x", i, tag[:], out[:])
|
|
}
|
|
if !h.Verify(tag[:]) {
|
|
t.Errorf("%d: Verify failed", i)
|
|
}
|
|
tag[0] ^= 0xff
|
|
if h.Verify(tag[:]) {
|
|
t.Errorf("%d: Verify succeeded after modifying the tag", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func benchmarkSum(b *testing.B, size int, unaligned bool) {
|
|
var out [16]byte
|
|
var key [32]byte
|
|
in := make([]byte, size)
|
|
if unaligned {
|
|
in = unalignBytes(in)
|
|
}
|
|
rand.Read(in)
|
|
b.SetBytes(int64(len(in)))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
Sum(&out, in, &key)
|
|
}
|
|
}
|
|
|
|
func benchmarkWrite(b *testing.B, size int, unaligned bool) {
|
|
var key [32]byte
|
|
h := New(&key)
|
|
in := make([]byte, size)
|
|
if unaligned {
|
|
in = unalignBytes(in)
|
|
}
|
|
rand.Read(in)
|
|
b.SetBytes(int64(len(in)))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
h.Write(in)
|
|
}
|
|
}
|
|
|
|
func Benchmark64(b *testing.B) { benchmarkSum(b, 64, false) }
|
|
func Benchmark1K(b *testing.B) { benchmarkSum(b, 1024, false) }
|
|
func Benchmark2M(b *testing.B) { benchmarkSum(b, 2*1024*1024, false) }
|
|
func Benchmark64Unaligned(b *testing.B) { benchmarkSum(b, 64, true) }
|
|
func Benchmark1KUnaligned(b *testing.B) { benchmarkSum(b, 1024, true) }
|
|
func Benchmark2MUnaligned(b *testing.B) { benchmarkSum(b, 2*1024*1024, true) }
|
|
|
|
func BenchmarkWrite64(b *testing.B) { benchmarkWrite(b, 64, false) }
|
|
func BenchmarkWrite1K(b *testing.B) { benchmarkWrite(b, 1024, false) }
|
|
func BenchmarkWrite2M(b *testing.B) { benchmarkWrite(b, 2*1024*1024, false) }
|
|
func BenchmarkWrite64Unaligned(b *testing.B) { benchmarkWrite(b, 64, true) }
|
|
func BenchmarkWrite1KUnaligned(b *testing.B) { benchmarkWrite(b, 1024, true) }
|
|
func BenchmarkWrite2MUnaligned(b *testing.B) { benchmarkWrite(b, 2*1024*1024, true) }
|
|
|
|
func unalignBytes(in []byte) []byte {
|
|
out := make([]byte, len(in)+1)
|
|
if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
|
|
out = out[1:]
|
|
} else {
|
|
out = out[:len(in)]
|
|
}
|
|
copy(out, in)
|
|
return out
|
|
}
|