Files
crypto/poly1305/poly1305_test.go
T
Michael Munday 4b2356b1ed poly1305: modify s390x assembly to implement MAC interface
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>
2020-04-29 18:30:12 +00:00

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
}