307 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			307 lines
		
	
	
		
			8.7 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,
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// decodeHex converts a hex-encoded string into a raw byte string.
							 | 
						||
| 
								 | 
							
								func decodeHex(s string) []byte {
							 | 
						||
| 
								 | 
							
									b, err := hex.DecodeString(s)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										panic(err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return b
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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)
							 | 
						||
| 
								 | 
							
								}
							 |