Source file src/crypto/sha3/sha3_test.go

     1  // Copyright 2014 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 sha3_test
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/internal/cryptotest"
    10  	"crypto/internal/fips140"
    11  	. "crypto/sha3"
    12  	"encoding/hex"
    13  	"io"
    14  	"math/rand"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  const testString = "brekeccakkeccak koax koax"
    20  
    21  // testDigests contains functions returning hash.Hash instances
    22  // with output-length equal to the KAT length for SHA-3, Keccak
    23  // and SHAKE instances.
    24  var testDigests = map[string]func() *SHA3{
    25  	"SHA3-224": New224,
    26  	"SHA3-256": New256,
    27  	"SHA3-384": New384,
    28  	"SHA3-512": New512,
    29  }
    30  
    31  // testShakes contains functions that return *sha3.SHAKE instances for
    32  // with output-length equal to the KAT length.
    33  var testShakes = map[string]struct {
    34  	constructor  func(N []byte, S []byte) *SHAKE
    35  	defAlgoName  string
    36  	defCustomStr string
    37  }{
    38  	// NewCSHAKE without customization produces same result as SHAKE
    39  	"SHAKE128":  {NewCSHAKE128, "", ""},
    40  	"SHAKE256":  {NewCSHAKE256, "", ""},
    41  	"cSHAKE128": {NewCSHAKE128, "CSHAKE128", "CustomString"},
    42  	"cSHAKE256": {NewCSHAKE256, "CSHAKE256", "CustomString"},
    43  }
    44  
    45  // decodeHex converts a hex-encoded string into a raw byte string.
    46  func decodeHex(s string) []byte {
    47  	b, err := hex.DecodeString(s)
    48  	if err != nil {
    49  		panic(err)
    50  	}
    51  	return b
    52  }
    53  
    54  // TestUnalignedWrite tests that writing data in an arbitrary pattern with
    55  // small input buffers.
    56  func TestUnalignedWrite(t *testing.T) {
    57  	cryptotest.TestAllImplementations(t, "sha3", testUnalignedWrite)
    58  }
    59  
    60  func testUnalignedWrite(t *testing.T) {
    61  	buf := sequentialBytes(0x10000)
    62  	for alg, df := range testDigests {
    63  		d := df()
    64  		d.Reset()
    65  		d.Write(buf)
    66  		want := d.Sum(nil)
    67  		d.Reset()
    68  		for i := 0; i < len(buf); {
    69  			// Cycle through offsets which make a 137 byte sequence.
    70  			// Because 137 is prime this sequence should exercise all corner cases.
    71  			offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
    72  			for _, j := range offsets {
    73  				if v := len(buf) - i; v < j {
    74  					j = v
    75  				}
    76  				d.Write(buf[i : i+j])
    77  				i += j
    78  			}
    79  		}
    80  		got := d.Sum(nil)
    81  		if !bytes.Equal(got, want) {
    82  			t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want)
    83  		}
    84  	}
    85  
    86  	// Same for SHAKE
    87  	for alg, df := range testShakes {
    88  		want := make([]byte, 16)
    89  		got := make([]byte, 16)
    90  		d := df.constructor([]byte(df.defAlgoName), []byte(df.defCustomStr))
    91  
    92  		d.Reset()
    93  		d.Write(buf)
    94  		d.Read(want)
    95  		d.Reset()
    96  		for i := 0; i < len(buf); {
    97  			// Cycle through offsets which make a 137 byte sequence.
    98  			// Because 137 is prime this sequence should exercise all corner cases.
    99  			offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
   100  			for _, j := range offsets {
   101  				if v := len(buf) - i; v < j {
   102  					j = v
   103  				}
   104  				d.Write(buf[i : i+j])
   105  				i += j
   106  			}
   107  		}
   108  		d.Read(got)
   109  		if !bytes.Equal(got, want) {
   110  			t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want)
   111  		}
   112  	}
   113  }
   114  
   115  // TestAppend checks that appending works when reallocation is necessary.
   116  func TestAppend(t *testing.T) {
   117  	cryptotest.TestAllImplementations(t, "sha3", testAppend)
   118  }
   119  
   120  func testAppend(t *testing.T) {
   121  	d := New224()
   122  
   123  	for capacity := 2; capacity <= 66; capacity += 64 {
   124  		// The first time around the loop, Sum will have to reallocate.
   125  		// The second time, it will not.
   126  		buf := make([]byte, 2, capacity)
   127  		d.Reset()
   128  		d.Write([]byte{0xcc})
   129  		buf = d.Sum(buf)
   130  		expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
   131  		if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
   132  			t.Errorf("got %s, want %s", got, expected)
   133  		}
   134  	}
   135  }
   136  
   137  // TestAppendNoRealloc tests that appending works when no reallocation is necessary.
   138  func TestAppendNoRealloc(t *testing.T) {
   139  	cryptotest.TestAllImplementations(t, "sha3", testAppendNoRealloc)
   140  }
   141  
   142  func testAppendNoRealloc(t *testing.T) {
   143  	buf := make([]byte, 1, 200)
   144  	d := New224()
   145  	d.Write([]byte{0xcc})
   146  	buf = d.Sum(buf)
   147  	expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
   148  	if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
   149  		t.Errorf("got %s, want %s", got, expected)
   150  	}
   151  }
   152  
   153  // TestSqueezing checks that squeezing the full output a single time produces
   154  // the same output as repeatedly squeezing the instance.
   155  func TestSqueezing(t *testing.T) {
   156  	cryptotest.TestAllImplementations(t, "sha3", testSqueezing)
   157  }
   158  
   159  func testSqueezing(t *testing.T) {
   160  	for algo, v := range testShakes {
   161  		d0 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
   162  		d0.Write([]byte(testString))
   163  		ref := make([]byte, 32)
   164  		d0.Read(ref)
   165  
   166  		d1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
   167  		d1.Write([]byte(testString))
   168  		var multiple []byte
   169  		for range ref {
   170  			d1.Read(make([]byte, 0))
   171  			one := make([]byte, 1)
   172  			d1.Read(one)
   173  			multiple = append(multiple, one...)
   174  		}
   175  		if !bytes.Equal(ref, multiple) {
   176  			t.Errorf("%s: squeezing %d bytes one at a time failed", algo, len(ref))
   177  		}
   178  	}
   179  }
   180  
   181  // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
   182  //
   183  // The alignment of each slice is intentionally randomized to detect alignment
   184  // issues in the implementation. See https://golang.org/issue/37644.
   185  // Ideally, the compiler should fuzz the alignment itself.
   186  // (See https://golang.org/issue/35128.)
   187  func sequentialBytes(size int) []byte {
   188  	alignmentOffset := rand.Intn(8)
   189  	result := make([]byte, size+alignmentOffset)[alignmentOffset:]
   190  	for i := range result {
   191  		result[i] = byte(i)
   192  	}
   193  	return result
   194  }
   195  
   196  func TestReset(t *testing.T) {
   197  	cryptotest.TestAllImplementations(t, "sha3", testReset)
   198  }
   199  
   200  func testReset(t *testing.T) {
   201  	out1 := make([]byte, 32)
   202  	out2 := make([]byte, 32)
   203  
   204  	for _, v := range testShakes {
   205  		// Calculate hash for the first time
   206  		c := v.constructor(nil, []byte{0x99, 0x98})
   207  		c.Write(sequentialBytes(0x100))
   208  		c.Read(out1)
   209  
   210  		// Calculate hash again
   211  		c.Reset()
   212  		c.Write(sequentialBytes(0x100))
   213  		c.Read(out2)
   214  
   215  		if !bytes.Equal(out1, out2) {
   216  			t.Error("\nExpected:\n", out1, "\ngot:\n", out2)
   217  		}
   218  	}
   219  }
   220  
   221  var sinkSHA3 byte
   222  
   223  func TestAllocations(t *testing.T) {
   224  	cryptotest.SkipTestAllocations(t)
   225  	t.Run("New", func(t *testing.T) {
   226  		if allocs := testing.AllocsPerRun(10, func() {
   227  			h := New256()
   228  			b := []byte("ABC")
   229  			h.Write(b)
   230  			out := make([]byte, 0, 32)
   231  			out = h.Sum(out)
   232  			sinkSHA3 ^= out[0]
   233  		}); allocs > 0 {
   234  			t.Errorf("expected zero allocations, got %0.1f", allocs)
   235  		}
   236  	})
   237  	t.Run("NewSHAKE", func(t *testing.T) {
   238  		if allocs := testing.AllocsPerRun(10, func() {
   239  			h := NewSHAKE128()
   240  			b := []byte("ABC")
   241  			h.Write(b)
   242  			out := make([]byte, 32)
   243  			h.Read(out)
   244  			sinkSHA3 ^= out[0]
   245  		}); allocs > 0 {
   246  			t.Errorf("expected zero allocations, got %0.1f", allocs)
   247  		}
   248  	})
   249  	t.Run("Sum", func(t *testing.T) {
   250  		if allocs := testing.AllocsPerRun(10, func() {
   251  			b := []byte("ABC")
   252  			out := Sum256(b)
   253  			sinkSHA3 ^= out[0]
   254  		}); allocs > 0 {
   255  			t.Errorf("expected zero allocations, got %0.1f", allocs)
   256  		}
   257  	})
   258  	t.Run("SumSHAKE", func(t *testing.T) {
   259  		if allocs := testing.AllocsPerRun(10, func() {
   260  			b := []byte("ABC")
   261  			out := SumSHAKE128(b, 10)
   262  			sinkSHA3 ^= out[0]
   263  		}); allocs > 0 {
   264  			t.Errorf("expected zero allocations, got %0.1f", allocs)
   265  		}
   266  	})
   267  }
   268  
   269  func TestCSHAKEAccumulated(t *testing.T) {
   270  	// Generated with pycryptodome@3.20.0
   271  	//
   272  	//    from Crypto.Hash import cSHAKE128
   273  	//    rng = cSHAKE128.new()
   274  	//    acc = cSHAKE128.new()
   275  	//    for n in range(200):
   276  	//        N = rng.read(n)
   277  	//        for s in range(200):
   278  	//            S = rng.read(s)
   279  	//            c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
   280  	//            c.update(rng.read(100))
   281  	//            acc.update(c.read(200))
   282  	//            c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
   283  	//            c.update(rng.read(168))
   284  	//            acc.update(c.read(200))
   285  	//            c = cSHAKE128.cSHAKE_XOF(data=None, custom=S, capacity=256, function=N)
   286  	//            c.update(rng.read(200))
   287  	//            acc.update(c.read(200))
   288  	//    print(acc.read(32).hex())
   289  	//
   290  	// and with @noble/hashes@v1.5.0
   291  	//
   292  	//    import { bytesToHex } from "@noble/hashes/utils";
   293  	//    import { cshake128 } from "@noble/hashes/sha3-addons";
   294  	//    const rng = cshake128.create();
   295  	//    const acc = cshake128.create();
   296  	//    for (let n = 0; n < 200; n++) {
   297  	//        const N = rng.xof(n);
   298  	//        for (let s = 0; s < 200; s++) {
   299  	//            const S = rng.xof(s);
   300  	//            let c = cshake128.create({ NISTfn: N, personalization: S });
   301  	//            c.update(rng.xof(100));
   302  	//            acc.update(c.xof(200));
   303  	//            c = cshake128.create({ NISTfn: N, personalization: S });
   304  	//            c.update(rng.xof(168));
   305  	//            acc.update(c.xof(200));
   306  	//            c = cshake128.create({ NISTfn: N, personalization: S });
   307  	//            c.update(rng.xof(200));
   308  	//            acc.update(c.xof(200));
   309  	//        }
   310  	//    }
   311  	//    console.log(bytesToHex(acc.xof(32)));
   312  	//
   313  	cryptotest.TestAllImplementations(t, "sha3", func(t *testing.T) {
   314  		t.Run("cSHAKE128", func(t *testing.T) {
   315  			testCSHAKEAccumulated(t, NewCSHAKE128, (1600-256)/8,
   316  				"bb14f8657c6ec5403d0b0e2ef3d3393497e9d3b1a9a9e8e6c81dbaa5fd809252")
   317  		})
   318  		t.Run("cSHAKE256", func(t *testing.T) {
   319  			testCSHAKEAccumulated(t, NewCSHAKE256, (1600-512)/8,
   320  				"0baaf9250c6e25f0c14ea5c7f9bfde54c8a922c8276437db28f3895bdf6eeeef")
   321  		})
   322  	})
   323  }
   324  
   325  func testCSHAKEAccumulated(t *testing.T, newCSHAKE func(N, S []byte) *SHAKE, rate int64, exp string) {
   326  	rnd := newCSHAKE(nil, nil)
   327  	acc := newCSHAKE(nil, nil)
   328  	for n := 0; n < 200; n++ {
   329  		N := make([]byte, n)
   330  		rnd.Read(N)
   331  		for s := 0; s < 200; s++ {
   332  			S := make([]byte, s)
   333  			rnd.Read(S)
   334  
   335  			c := newCSHAKE(N, S)
   336  			io.CopyN(c, rnd, 100 /* < rate */)
   337  			io.CopyN(acc, c, 200)
   338  
   339  			c.Reset()
   340  			io.CopyN(c, rnd, rate)
   341  			io.CopyN(acc, c, 200)
   342  
   343  			c.Reset()
   344  			io.CopyN(c, rnd, 200 /* > rate */)
   345  			io.CopyN(acc, c, 200)
   346  		}
   347  	}
   348  	out := make([]byte, 32)
   349  	acc.Read(out)
   350  	if got := hex.EncodeToString(out); got != exp {
   351  		t.Errorf("got %s, want %s", got, exp)
   352  	}
   353  }
   354  
   355  func TestCSHAKELargeS(t *testing.T) {
   356  	cryptotest.TestAllImplementations(t, "sha3", testCSHAKELargeS)
   357  }
   358  
   359  func testCSHAKELargeS(t *testing.T) {
   360  	if testing.Short() {
   361  		t.Skip("skipping test in short mode.")
   362  	}
   363  
   364  	// See https://go.dev/issue/66232.
   365  	const s = (1<<32)/8 + 1000 // s * 8 > 2^32
   366  	S := make([]byte, s)
   367  	rnd := NewSHAKE128()
   368  	rnd.Read(S)
   369  	c := NewCSHAKE128(nil, S)
   370  	io.CopyN(c, rnd, 1000)
   371  	out := make([]byte, 32)
   372  	c.Read(out)
   373  
   374  	// Generated with pycryptodome@3.20.0
   375  	//
   376  	//    from Crypto.Hash import cSHAKE128
   377  	//    rng = cSHAKE128.new()
   378  	//    S = rng.read(536871912)
   379  	//    c = cSHAKE128.new(custom=S)
   380  	//    c.update(rng.read(1000))
   381  	//    print(c.read(32).hex())
   382  	//
   383  	exp := "2cb9f237767e98f2614b8779cf096a52da9b3a849280bbddec820771ae529cf0"
   384  	if got := hex.EncodeToString(out); got != exp {
   385  		t.Errorf("got %s, want %s", got, exp)
   386  	}
   387  }
   388  
   389  func TestMarshalUnmarshal(t *testing.T) {
   390  	cryptotest.TestAllImplementations(t, "sha3", func(t *testing.T) {
   391  		t.Run("SHA3-224", func(t *testing.T) { testMarshalUnmarshal(t, New224()) })
   392  		t.Run("SHA3-256", func(t *testing.T) { testMarshalUnmarshal(t, New256()) })
   393  		t.Run("SHA3-384", func(t *testing.T) { testMarshalUnmarshal(t, New384()) })
   394  		t.Run("SHA3-512", func(t *testing.T) { testMarshalUnmarshal(t, New512()) })
   395  		t.Run("SHAKE128", func(t *testing.T) { testMarshalUnmarshalSHAKE(t, NewSHAKE128()) })
   396  		t.Run("SHAKE256", func(t *testing.T) { testMarshalUnmarshalSHAKE(t, NewSHAKE256()) })
   397  		t.Run("cSHAKE128", func(t *testing.T) { testMarshalUnmarshalSHAKE(t, NewCSHAKE128([]byte("N"), []byte("S"))) })
   398  		t.Run("cSHAKE256", func(t *testing.T) { testMarshalUnmarshalSHAKE(t, NewCSHAKE256([]byte("N"), []byte("S"))) })
   399  	})
   400  }
   401  
   402  // TODO(filippo): move this to crypto/internal/cryptotest.
   403  func testMarshalUnmarshal(t *testing.T, h *SHA3) {
   404  	buf := make([]byte, 200)
   405  	rand.Read(buf)
   406  	n := rand.Intn(200)
   407  	h.Write(buf)
   408  	want := h.Sum(nil)
   409  	h.Reset()
   410  	h.Write(buf[:n])
   411  	b, err := h.MarshalBinary()
   412  	if err != nil {
   413  		t.Errorf("MarshalBinary: %v", err)
   414  	}
   415  	h.Write(bytes.Repeat([]byte{0}, 200))
   416  	if err := h.UnmarshalBinary(b); err != nil {
   417  		t.Errorf("UnmarshalBinary: %v", err)
   418  	}
   419  	h.Write(buf[n:])
   420  	got := h.Sum(nil)
   421  	if !bytes.Equal(got, want) {
   422  		t.Errorf("got %x, want %x", got, want)
   423  	}
   424  }
   425  
   426  // TODO(filippo): move this to crypto/internal/cryptotest.
   427  func testMarshalUnmarshalSHAKE(t *testing.T, h *SHAKE) {
   428  	buf := make([]byte, 200)
   429  	rand.Read(buf)
   430  	n := rand.Intn(200)
   431  	h.Write(buf)
   432  	want := make([]byte, 32)
   433  	h.Read(want)
   434  	h.Reset()
   435  	h.Write(buf[:n])
   436  	b, err := h.MarshalBinary()
   437  	if err != nil {
   438  		t.Errorf("MarshalBinary: %v", err)
   439  	}
   440  	h.Write(bytes.Repeat([]byte{0}, 200))
   441  	if err := h.UnmarshalBinary(b); err != nil {
   442  		t.Errorf("UnmarshalBinary: %v", err)
   443  	}
   444  	h.Write(buf[n:])
   445  	got := make([]byte, 32)
   446  	h.Read(got)
   447  	if !bytes.Equal(got, want) {
   448  		t.Errorf("got %x, want %x", got, want)
   449  	}
   450  }
   451  
   452  // benchmarkHash tests the speed to hash num buffers of buflen each.
   453  func benchmarkHash(b *testing.B, h fips140.Hash, size, num int) {
   454  	b.StopTimer()
   455  	h.Reset()
   456  	data := sequentialBytes(size)
   457  	b.SetBytes(int64(size * num))
   458  	b.StartTimer()
   459  
   460  	var state []byte
   461  	for i := 0; i < b.N; i++ {
   462  		for j := 0; j < num; j++ {
   463  			h.Write(data)
   464  		}
   465  		state = h.Sum(state[:0])
   466  	}
   467  	b.StopTimer()
   468  	h.Reset()
   469  }
   470  
   471  // benchmarkShake is specialized to the Shake instances, which don't
   472  // require a copy on reading output.
   473  func benchmarkShake(b *testing.B, h *SHAKE, size, num int) {
   474  	b.StopTimer()
   475  	h.Reset()
   476  	data := sequentialBytes(size)
   477  	d := make([]byte, 32)
   478  
   479  	b.SetBytes(int64(size * num))
   480  	b.StartTimer()
   481  
   482  	for i := 0; i < b.N; i++ {
   483  		h.Reset()
   484  		for j := 0; j < num; j++ {
   485  			h.Write(data)
   486  		}
   487  		h.Read(d)
   488  	}
   489  }
   490  
   491  func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) }
   492  func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) }
   493  func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) }
   494  func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) }
   495  
   496  func BenchmarkShake128_MTU(b *testing.B)  { benchmarkShake(b, NewSHAKE128(), 1350, 1) }
   497  func BenchmarkShake256_MTU(b *testing.B)  { benchmarkShake(b, NewSHAKE256(), 1350, 1) }
   498  func BenchmarkShake256_16x(b *testing.B)  { benchmarkShake(b, NewSHAKE256(), 16, 1024) }
   499  func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewSHAKE256(), 1024, 1024) }
   500  
   501  func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) }
   502  

View as plain text