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
5package sha3
6
7// This file defines the ShakeHash interface, and provides
8// functions for creating SHAKE and cSHAKE instances, as well as utility
9// functions for hashing bytes to arbitrary-length output.
10//
11//
12// SHAKE implementation is based on FIPS PUB 202 [1]
13// cSHAKE implementations is based on NIST SP 800-185 [2]
14//
15// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
16// [2] https://doi.org/10.6028/NIST.SP.800-185
17
18import (
19	"encoding/binary"
20	"io"
21)
22
23// ShakeHash defines the interface to hash functions that
24// support arbitrary-length output.
25type ShakeHash interface {
26	// Write absorbs more data into the hash's state. It panics if input is
27	// written to it after output has been read from it.
28	io.Writer
29
30	// Read reads more output from the hash; reading affects the hash's
31	// state. (ShakeHash.Read is thus very different from Hash.Sum)
32	// It never returns an error.
33	io.Reader
34
35	// Clone returns a copy of the ShakeHash in its current state.
36	Clone() ShakeHash
37
38	// Reset resets the ShakeHash to its initial state.
39	Reset()
40}
41
42// cSHAKE specific context
43type cshakeState struct {
44	*state // SHA-3 state context and Read/Write operations
45
46	// initBlock is the cSHAKE specific initialization set of bytes. It is initialized
47	// by newCShake function and stores concatenation of N followed by S, encoded
48	// by the method specified in 3.3 of [1].
49	// It is stored here in order for Reset() to be able to put context into
50	// initial state.
51	initBlock []byte
52}
53
54// Consts for configuring initial SHA-3 state
55const (
56	dsbyteShake  = 0x1f
57	dsbyteCShake = 0x04
58	rate128      = 168
59	rate256      = 136
60)
61
62func bytepad(input []byte, w int) []byte {
63	// leftEncode always returns max 9 bytes
64	buf := make([]byte, 0, 9+len(input)+w)
65	buf = append(buf, leftEncode(uint64(w))...)
66	buf = append(buf, input...)
67	padlen := w - (len(buf) % w)
68	return append(buf, make([]byte, padlen)...)
69}
70
71func leftEncode(value uint64) []byte {
72	var b [9]byte
73	binary.BigEndian.PutUint64(b[1:], value)
74	// Trim all but last leading zero bytes
75	i := byte(1)
76	for i < 8 && b[i] == 0 {
77		i++
78	}
79	// Prepend number of encoded bytes
80	b[i-1] = 9 - i
81	return b[i-1:]
82}
83
84func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash {
85	c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}}
86
87	// leftEncode returns max 9 bytes
88	c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
89	c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...)
90	c.initBlock = append(c.initBlock, N...)
91	c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...)
92	c.initBlock = append(c.initBlock, S...)
93	c.Write(bytepad(c.initBlock, c.rate))
94	return &c
95}
96
97// Reset resets the hash to initial state.
98func (c *cshakeState) Reset() {
99	c.state.Reset()
100	c.Write(bytepad(c.initBlock, c.rate))
101}
102
103// Clone returns copy of a cSHAKE context within its current state.
104func (c *cshakeState) Clone() ShakeHash {
105	b := make([]byte, len(c.initBlock))
106	copy(b, c.initBlock)
107	return &cshakeState{state: c.clone(), initBlock: b}
108}
109
110// Clone returns copy of SHAKE context within its current state.
111func (c *state) Clone() ShakeHash {
112	return c.clone()
113}
114
115// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
116// Its generic security strength is 128 bits against all attacks if at
117// least 32 bytes of its output are used.
118func NewShake128() ShakeHash {
119	if h := newShake128Asm(); h != nil {
120		return h
121	}
122	return &state{rate: rate128, dsbyte: dsbyteShake}
123}
124
125// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
126// Its generic security strength is 256 bits against all attacks if
127// at least 64 bytes of its output are used.
128func NewShake256() ShakeHash {
129	if h := newShake256Asm(); h != nil {
130		return h
131	}
132	return &state{rate: rate256, dsbyte: dsbyteShake}
133}
134
135// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
136// a customizable variant of SHAKE128.
137// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
138// desired. S is a customization byte string used for domain separation - two cSHAKE
139// computations on same input with different S yield unrelated outputs.
140// When N and S are both empty, this is equivalent to NewShake128.
141func NewCShake128(N, S []byte) ShakeHash {
142	if len(N) == 0 && len(S) == 0 {
143		return NewShake128()
144	}
145	return newCShake(N, S, rate128, dsbyteCShake)
146}
147
148// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
149// a customizable variant of SHAKE256.
150// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is
151// desired. S is a customization byte string used for domain separation - two cSHAKE
152// computations on same input with different S yield unrelated outputs.
153// When N and S are both empty, this is equivalent to NewShake256.
154func NewCShake256(N, S []byte) ShakeHash {
155	if len(N) == 0 && len(S) == 0 {
156		return NewShake256()
157	}
158	return newCShake(N, S, rate256, dsbyteCShake)
159}
160
161// ShakeSum128 writes an arbitrary-length digest of data into hash.
162func ShakeSum128(hash, data []byte) {
163	h := NewShake128()
164	h.Write(data)
165	h.Read(hash)
166}
167
168// ShakeSum256 writes an arbitrary-length digest of data into hash.
169func ShakeSum256(hash, data []byte) {
170	h := NewShake256()
171	h.Write(data)
172	h.Read(hash)
173}
174