github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/crypto/internal/poly1305/poly1305_test.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package poly1305 6 7 import ( 8 "crypto/rand" 9 "encoding/binary" 10 "encoding/hex" 11 "flag" 12 "testing" 13 "unsafe" 14 ) 15 16 var stressFlag = flag.Bool("stress", false, "run slow stress tests") 17 18 type test struct { 19 in string 20 key string 21 tag string 22 state string 23 } 24 25 func (t *test) Input() []byte { 26 in, err := hex.DecodeString(t.in) 27 if err != nil { 28 panic(err) 29 } 30 return in 31 } 32 33 func (t *test) Key() [32]byte { 34 buf, err := hex.DecodeString(t.key) 35 if err != nil { 36 panic(err) 37 } 38 var key [32]byte 39 copy(key[:], buf[:32]) 40 return key 41 } 42 43 func (t *test) Tag() [16]byte { 44 buf, err := hex.DecodeString(t.tag) 45 if err != nil { 46 panic(err) 47 } 48 var tag [16]byte 49 copy(tag[:], buf[:16]) 50 return tag 51 } 52 53 func (t *test) InitialState() [3]uint64 { 54 // state is hex encoded in big-endian byte order 55 if t.state == "" { 56 return [3]uint64{0, 0, 0} 57 } 58 buf, err := hex.DecodeString(t.state) 59 if err != nil { 60 panic(err) 61 } 62 if len(buf) != 3*8 { 63 panic("incorrect state length") 64 } 65 return [3]uint64{ 66 binary.BigEndian.Uint64(buf[16:24]), 67 binary.BigEndian.Uint64(buf[8:16]), 68 binary.BigEndian.Uint64(buf[0:8]), 69 } 70 } 71 72 func testSum(t *testing.T, unaligned bool, sumImpl func(tag *[TagSize]byte, msg []byte, key *[32]byte)) { 73 var tag [16]byte 74 for i, v := range testData { 75 // cannot set initial state before calling sum, so skip those tests 76 if v.InitialState() != [3]uint64{0, 0, 0} { 77 continue 78 } 79 80 in := v.Input() 81 if unaligned { 82 in = unalignBytes(in) 83 } 84 key := v.Key() 85 sumImpl(&tag, in, &key) 86 if tag != v.Tag() { 87 t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:]) 88 } 89 if !Verify(&tag, in, &key) { 90 t.Errorf("%d: tag didn't verify", i) 91 } 92 // If the key is zero, the tag will always be zero, independent of the input. 93 if len(in) > 0 && key != [32]byte{} { 94 in[0] ^= 0xff 95 if Verify(&tag, in, &key) { 96 t.Errorf("%d: tag verified after altering the input", i) 97 } 98 in[0] ^= 0xff 99 } 100 // If the input is empty, the tag only depends on the second half of the key. 101 if len(in) > 0 { 102 key[0] ^= 0xff 103 if Verify(&tag, in, &key) { 104 t.Errorf("%d: tag verified after altering the key", i) 105 } 106 key[0] ^= 0xff 107 } 108 tag[0] ^= 0xff 109 if Verify(&tag, in, &key) { 110 t.Errorf("%d: tag verified after altering the tag", i) 111 } 112 tag[0] ^= 0xff 113 } 114 } 115 116 func TestBurnin(t *testing.T) { 117 // This test can be used to sanity-check significant changes. It can 118 // take about many minutes to run, even on fast machines. It's disabled 119 // by default. 120 if !*stressFlag { 121 t.Skip("skipping without -stress") 122 } 123 124 var key [32]byte 125 var input [25]byte 126 var output [16]byte 127 128 for i := range key { 129 key[i] = 1 130 } 131 for i := range input { 132 input[i] = 2 133 } 134 135 for i := uint64(0); i < 1e10; i++ { 136 Sum(&output, input[:], &key) 137 copy(key[0:], output[:]) 138 copy(key[16:], output[:]) 139 copy(input[:], output[:]) 140 copy(input[16:], output[:]) 141 } 142 143 const expected = "5e3b866aea0b636d240c83c428f84bfa" 144 if got := hex.EncodeToString(output[:]); got != expected { 145 t.Errorf("expected %s, got %s", expected, got) 146 } 147 } 148 149 func TestSum(t *testing.T) { testSum(t, false, Sum) } 150 func TestSumUnaligned(t *testing.T) { testSum(t, true, Sum) } 151 func TestSumGeneric(t *testing.T) { testSum(t, false, sumGeneric) } 152 func TestSumGenericUnaligned(t *testing.T) { testSum(t, true, sumGeneric) } 153 154 func TestWriteGeneric(t *testing.T) { testWriteGeneric(t, false) } 155 func TestWriteGenericUnaligned(t *testing.T) { testWriteGeneric(t, true) } 156 func TestWrite(t *testing.T) { testWrite(t, false) } 157 func TestWriteUnaligned(t *testing.T) { testWrite(t, true) } 158 159 func testWriteGeneric(t *testing.T, unaligned bool) { 160 for i, v := range testData { 161 key := v.Key() 162 input := v.Input() 163 var out [16]byte 164 165 if unaligned { 166 input = unalignBytes(input) 167 } 168 h := newMACGeneric(&key) 169 if s := v.InitialState(); s != [3]uint64{0, 0, 0} { 170 h.macState.h = s 171 } 172 n, err := h.Write(input[:len(input)/3]) 173 if err != nil || n != len(input[:len(input)/3]) { 174 t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err) 175 } 176 n, err = h.Write(input[len(input)/3:]) 177 if err != nil || n != len(input[len(input)/3:]) { 178 t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err) 179 } 180 h.Sum(&out) 181 if tag := v.Tag(); out != tag { 182 t.Errorf("%d: expected %x, got %x", i, tag[:], out[:]) 183 } 184 } 185 } 186 187 func testWrite(t *testing.T, unaligned bool) { 188 for i, v := range testData { 189 key := v.Key() 190 input := v.Input() 191 var out [16]byte 192 193 if unaligned { 194 input = unalignBytes(input) 195 } 196 h := New(&key) 197 if s := v.InitialState(); s != [3]uint64{0, 0, 0} { 198 h.macState.h = s 199 } 200 n, err := h.Write(input[:len(input)/3]) 201 if err != nil || n != len(input[:len(input)/3]) { 202 t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err) 203 } 204 n, err = h.Write(input[len(input)/3:]) 205 if err != nil || n != len(input[len(input)/3:]) { 206 t.Errorf("#%d: unexpected Write results: n = %d, err = %v", i, n, err) 207 } 208 h.Sum(out[:0]) 209 tag := v.Tag() 210 if out != tag { 211 t.Errorf("%d: expected %x, got %x", i, tag[:], out[:]) 212 } 213 if !h.Verify(tag[:]) { 214 t.Errorf("%d: Verify failed", i) 215 } 216 tag[0] ^= 0xff 217 if h.Verify(tag[:]) { 218 t.Errorf("%d: Verify succeeded after modifying the tag", i) 219 } 220 } 221 } 222 223 func benchmarkSum(b *testing.B, size int, unaligned bool) { 224 var out [16]byte 225 var key [32]byte 226 in := make([]byte, size) 227 if unaligned { 228 in = unalignBytes(in) 229 } 230 rand.Read(in) 231 b.SetBytes(int64(len(in))) 232 b.ResetTimer() 233 for i := 0; i < b.N; i++ { 234 Sum(&out, in, &key) 235 } 236 } 237 238 func benchmarkWrite(b *testing.B, size int, unaligned bool) { 239 var key [32]byte 240 h := New(&key) 241 in := make([]byte, size) 242 if unaligned { 243 in = unalignBytes(in) 244 } 245 rand.Read(in) 246 b.SetBytes(int64(len(in))) 247 b.ResetTimer() 248 for i := 0; i < b.N; i++ { 249 h.Write(in) 250 } 251 } 252 253 func Benchmark64(b *testing.B) { benchmarkSum(b, 64, false) } 254 func Benchmark1K(b *testing.B) { benchmarkSum(b, 1024, false) } 255 func Benchmark2M(b *testing.B) { benchmarkSum(b, 2*1024*1024, false) } 256 func Benchmark64Unaligned(b *testing.B) { benchmarkSum(b, 64, true) } 257 func Benchmark1KUnaligned(b *testing.B) { benchmarkSum(b, 1024, true) } 258 func Benchmark2MUnaligned(b *testing.B) { benchmarkSum(b, 2*1024*1024, true) } 259 260 func BenchmarkWrite64(b *testing.B) { benchmarkWrite(b, 64, false) } 261 func BenchmarkWrite1K(b *testing.B) { benchmarkWrite(b, 1024, false) } 262 func BenchmarkWrite2M(b *testing.B) { benchmarkWrite(b, 2*1024*1024, false) } 263 func BenchmarkWrite64Unaligned(b *testing.B) { benchmarkWrite(b, 64, true) } 264 func BenchmarkWrite1KUnaligned(b *testing.B) { benchmarkWrite(b, 1024, true) } 265 func BenchmarkWrite2MUnaligned(b *testing.B) { benchmarkWrite(b, 2*1024*1024, true) } 266 267 func unalignBytes(in []byte) []byte { 268 out := make([]byte, len(in)+1) 269 if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 { 270 out = out[1:] 271 } else { 272 out = out[:len(in)] 273 } 274 copy(out, in) 275 return out 276 }