298 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2014 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 sha3
 | |
| 
 | |
| // Tests include all the ShortMsgKATs provided by the Keccak team at
 | |
| // https://github.com/gvanas/KeccakCodePackage
 | |
| //
 | |
| // They only include the zero-bit case of the bitwise testvectors
 | |
| // published by NIST in the draft of FIPS-202.
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"compress/flate"
 | |
| 	"encoding/hex"
 | |
| 	"encoding/json"
 | |
| 	"hash"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	testString  = "brekeccakkeccak koax koax"
 | |
| 	katFilename = "testdata/keccakKats.json.deflate"
 | |
| )
 | |
| 
 | |
| // Internal-use instances of SHAKE used to test against KATs.
 | |
| func newHashShake128() hash.Hash {
 | |
| 	return &state{rate: 168, dsbyte: 0x1f, outputLen: 512}
 | |
| }
 | |
| func newHashShake256() hash.Hash {
 | |
| 	return &state{rate: 136, dsbyte: 0x1f, outputLen: 512}
 | |
| }
 | |
| 
 | |
| // testDigests contains functions returning hash.Hash instances
 | |
| // with output-length equal to the KAT length for both SHA-3 and
 | |
| // SHAKE instances.
 | |
| var testDigests = map[string]func() hash.Hash{
 | |
| 	"SHA3-224": New224,
 | |
| 	"SHA3-256": New256,
 | |
| 	"SHA3-384": New384,
 | |
| 	"SHA3-512": New512,
 | |
| 	"SHAKE128": newHashShake128,
 | |
| 	"SHAKE256": newHashShake256,
 | |
| }
 | |
| 
 | |
| // testShakes contains functions that return ShakeHash instances for
 | |
| // testing the ShakeHash-specific interface.
 | |
| var testShakes = map[string]func() ShakeHash{
 | |
| 	"SHAKE128": NewShake128,
 | |
| 	"SHAKE256": NewShake256,
 | |
| }
 | |
| 
 | |
| // structs used to marshal JSON test-cases.
 | |
| type KeccakKats struct {
 | |
| 	Kats map[string][]struct {
 | |
| 		Digest  string `json:"digest"`
 | |
| 		Length  int64  `json:"length"`
 | |
| 		Message string `json:"message"`
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) {
 | |
| 	xorInOrig, copyOutOrig := xorIn, copyOut
 | |
| 	xorIn, copyOut = xorInGeneric, copyOutGeneric
 | |
| 	testf("generic")
 | |
| 	if xorImplementationUnaligned != "generic" {
 | |
| 		xorIn, copyOut = xorInUnaligned, copyOutUnaligned
 | |
| 		testf("unaligned")
 | |
| 	}
 | |
| 	xorIn, copyOut = xorInOrig, copyOutOrig
 | |
| }
 | |
| 
 | |
| // TestKeccakKats tests the SHA-3 and Shake implementations against all the
 | |
| // ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage
 | |
| // (The testvectors are stored in keccakKats.json.deflate due to their length.)
 | |
| func TestKeccakKats(t *testing.T) {
 | |
| 	testUnalignedAndGeneric(t, func(impl string) {
 | |
| 		// Read the KATs.
 | |
| 		deflated, err := os.Open(katFilename)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("error opening %s: %s", katFilename, err)
 | |
| 		}
 | |
| 		file := flate.NewReader(deflated)
 | |
| 		dec := json.NewDecoder(file)
 | |
| 		var katSet KeccakKats
 | |
| 		err = dec.Decode(&katSet)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("error decoding KATs: %s", err)
 | |
| 		}
 | |
| 
 | |
| 		// Do the KATs.
 | |
| 		for functionName, kats := range katSet.Kats {
 | |
| 			d := testDigests[functionName]()
 | |
| 			for _, kat := range kats {
 | |
| 				d.Reset()
 | |
| 				in, err := hex.DecodeString(kat.Message)
 | |
| 				if err != nil {
 | |
| 					t.Errorf("error decoding KAT: %s", err)
 | |
| 				}
 | |
| 				d.Write(in[:kat.Length/8])
 | |
| 				got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
 | |
| 				if got != kat.Digest {
 | |
| 					t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n  %s\ngot:\n  %s\nwanted:\n %s",
 | |
| 						functionName, impl, kat.Length, kat.Message, got, kat.Digest)
 | |
| 					t.Logf("wanted %+v", kat)
 | |
| 					t.FailNow()
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // TestUnalignedWrite tests that writing data in an arbitrary pattern with
 | |
| // small input buffers.
 | |
| func TestUnalignedWrite(t *testing.T) {
 | |
| 	testUnalignedAndGeneric(t, func(impl string) {
 | |
| 		buf := sequentialBytes(0x10000)
 | |
| 		for alg, df := range testDigests {
 | |
| 			d := df()
 | |
| 			d.Reset()
 | |
| 			d.Write(buf)
 | |
| 			want := d.Sum(nil)
 | |
| 			d.Reset()
 | |
| 			for i := 0; i < len(buf); {
 | |
| 				// Cycle through offsets which make a 137 byte sequence.
 | |
| 				// Because 137 is prime this sequence should exercise all corner cases.
 | |
| 				offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
 | |
| 				for _, j := range offsets {
 | |
| 					if v := len(buf) - i; v < j {
 | |
| 						j = v
 | |
| 					}
 | |
| 					d.Write(buf[i : i+j])
 | |
| 					i += j
 | |
| 				}
 | |
| 			}
 | |
| 			got := d.Sum(nil)
 | |
| 			if !bytes.Equal(got, want) {
 | |
| 				t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // TestAppend checks that appending works when reallocation is necessary.
 | |
| func TestAppend(t *testing.T) {
 | |
| 	testUnalignedAndGeneric(t, func(impl string) {
 | |
| 		d := New224()
 | |
| 
 | |
| 		for capacity := 2; capacity <= 66; capacity += 64 {
 | |
| 			// The first time around the loop, Sum will have to reallocate.
 | |
| 			// The second time, it will not.
 | |
| 			buf := make([]byte, 2, capacity)
 | |
| 			d.Reset()
 | |
| 			d.Write([]byte{0xcc})
 | |
| 			buf = d.Sum(buf)
 | |
| 			expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
 | |
| 			if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
 | |
| 				t.Errorf("got %s, want %s", got, expected)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // TestAppendNoRealloc tests that appending works when no reallocation is necessary.
 | |
| func TestAppendNoRealloc(t *testing.T) {
 | |
| 	testUnalignedAndGeneric(t, func(impl string) {
 | |
| 		buf := make([]byte, 1, 200)
 | |
| 		d := New224()
 | |
| 		d.Write([]byte{0xcc})
 | |
| 		buf = d.Sum(buf)
 | |
| 		expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
 | |
| 		if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
 | |
| 			t.Errorf("%s: got %s, want %s", impl, got, expected)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // TestSqueezing checks that squeezing the full output a single time produces
 | |
| // the same output as repeatedly squeezing the instance.
 | |
| func TestSqueezing(t *testing.T) {
 | |
| 	testUnalignedAndGeneric(t, func(impl string) {
 | |
| 		for functionName, newShakeHash := range testShakes {
 | |
| 			d0 := newShakeHash()
 | |
| 			d0.Write([]byte(testString))
 | |
| 			ref := make([]byte, 32)
 | |
| 			d0.Read(ref)
 | |
| 
 | |
| 			d1 := newShakeHash()
 | |
| 			d1.Write([]byte(testString))
 | |
| 			var multiple []byte
 | |
| 			for range ref {
 | |
| 				one := make([]byte, 1)
 | |
| 				d1.Read(one)
 | |
| 				multiple = append(multiple, one...)
 | |
| 			}
 | |
| 			if !bytes.Equal(ref, multiple) {
 | |
| 				t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref))
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
 | |
| func sequentialBytes(size int) []byte {
 | |
| 	result := make([]byte, size)
 | |
| 	for i := range result {
 | |
| 		result[i] = byte(i)
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| // BenchmarkPermutationFunction measures the speed of the permutation function
 | |
| // with no input data.
 | |
| func BenchmarkPermutationFunction(b *testing.B) {
 | |
| 	b.SetBytes(int64(200))
 | |
| 	var lanes [25]uint64
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		keccakF1600(&lanes)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // benchmarkHash tests the speed to hash num buffers of buflen each.
 | |
| func benchmarkHash(b *testing.B, h hash.Hash, size, num int) {
 | |
| 	b.StopTimer()
 | |
| 	h.Reset()
 | |
| 	data := sequentialBytes(size)
 | |
| 	b.SetBytes(int64(size * num))
 | |
| 	b.StartTimer()
 | |
| 
 | |
| 	var state []byte
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		for j := 0; j < num; j++ {
 | |
| 			h.Write(data)
 | |
| 		}
 | |
| 		state = h.Sum(state[:0])
 | |
| 	}
 | |
| 	b.StopTimer()
 | |
| 	h.Reset()
 | |
| }
 | |
| 
 | |
| // benchmarkShake is specialized to the Shake instances, which don't
 | |
| // require a copy on reading output.
 | |
| func benchmarkShake(b *testing.B, h ShakeHash, size, num int) {
 | |
| 	b.StopTimer()
 | |
| 	h.Reset()
 | |
| 	data := sequentialBytes(size)
 | |
| 	d := make([]byte, 32)
 | |
| 
 | |
| 	b.SetBytes(int64(size * num))
 | |
| 	b.StartTimer()
 | |
| 
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		h.Reset()
 | |
| 		for j := 0; j < num; j++ {
 | |
| 			h.Write(data)
 | |
| 		}
 | |
| 		h.Read(d)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) }
 | |
| func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) }
 | |
| func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) }
 | |
| func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) }
 | |
| 
 | |
| func BenchmarkShake128_MTU(b *testing.B)  { benchmarkShake(b, NewShake128(), 1350, 1) }
 | |
| func BenchmarkShake256_MTU(b *testing.B)  { benchmarkShake(b, NewShake256(), 1350, 1) }
 | |
| func BenchmarkShake256_16x(b *testing.B)  { benchmarkShake(b, NewShake256(), 16, 1024) }
 | |
| func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) }
 | |
| 
 | |
| func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) }
 | |
| 
 | |
| func Example_sum() {
 | |
| 	buf := []byte("some data to hash")
 | |
| 	// A hash needs to be 64 bytes long to have 256-bit collision resistance.
 | |
| 	h := make([]byte, 64)
 | |
| 	// Compute a 64-byte hash of buf and put it in h.
 | |
| 	ShakeSum256(h, buf)
 | |
| }
 | |
| 
 | |
| func Example_mac() {
 | |
| 	k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long")
 | |
| 	buf := []byte("and this is some data to authenticate")
 | |
| 	// A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key.
 | |
| 	h := make([]byte, 32)
 | |
| 	d := NewShake256()
 | |
| 	// Write the key into the hash.
 | |
| 	d.Write(k)
 | |
| 	// Now write the data.
 | |
| 	d.Write(buf)
 | |
| 	// Read 32 bytes of output from the hash into h.
 | |
| 	d.Read(h)
 | |
| }
 |