1// Copyright 2012 Jimmy Zelinskie. 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 whirlpool implements the ISO/IEC 10118-3:2004 whirlpool
6// cryptographic hash. Whirlpool is defined in
7// http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html
8package whirlpool
9
10import (
11	"encoding/binary"
12	"hash"
13)
14
15// whirlpool represents the partial evaluation of a checksum.
16type whirlpool struct {
17	bitLength  [lengthBytes]byte       // Number of hashed bits.
18	buffer     [wblockBytes]byte       // Buffer of data to be hashed.
19	bufferBits int                     // Current number of bits on the buffer.
20	bufferPos  int                     // Current byte location on buffer.
21	hash       [digestBytes / 8]uint64 // Hash state.
22}
23
24// New returns a new hash.Hash computing the whirlpool checksum.
25func New() hash.Hash {
26	return new(whirlpool)
27}
28
29func (w *whirlpool) Reset() {
30	// Cleanup the buffer.
31	w.buffer = [wblockBytes]byte{}
32	w.bufferBits = 0
33	w.bufferPos = 0
34
35	// Cleanup the digest.
36	w.hash = [digestBytes / 8]uint64{}
37
38	// Clean up the number of hashed bits.
39	w.bitLength = [lengthBytes]byte{}
40}
41
42func (w *whirlpool) Size() int {
43	return digestBytes
44}
45
46func (w *whirlpool) BlockSize() int {
47	return wblockBytes
48}
49
50func (w *whirlpool) transform() {
51	var (
52		K     [8]uint64 // Round key.
53		block [8]uint64 // μ(buffer).
54		state [8]uint64 // Cipher state.
55		L     [8]uint64
56	)
57
58	// Map the buffer to a block.
59	for i := 0; i < 8; i++ {
60		b := 8 * i
61		block[i] = binary.BigEndian.Uint64(w.buffer[b:])
62	}
63
64	// Compute & apply K^0 to the cipher state.
65	for i := 0; i < 8; i++ {
66		K[i] = w.hash[i]
67		state[i] = block[i] ^ K[i]
68	}
69
70	// Iterate over all the rounds.
71	for r := 1; r <= rounds; r++ {
72		// Compute K^rounds from K^(rounds-1).
73		for i := 0; i < 8; i++ {
74			L[i] = _C0[byte(K[i%8]>>56)] ^
75				_C1[byte(K[(i+7)%8]>>48)] ^
76				_C2[byte(K[(i+6)%8]>>40)] ^
77				_C3[byte(K[(i+5)%8]>>32)] ^
78				_C4[byte(K[(i+4)%8]>>24)] ^
79				_C5[byte(K[(i+3)%8]>>16)] ^
80				_C6[byte(K[(i+2)%8]>>8)] ^
81				_C7[byte(K[(i+1)%8])]
82		}
83		L[0] ^= rc[r]
84
85		for i := 0; i < 8; i++ {
86			K[i] = L[i]
87		}
88
89		// Apply r-th round transformation.
90		for i := 0; i < 8; i++ {
91			L[i] = _C0[byte(state[i%8]>>56)] ^
92				_C1[byte(state[(i+7)%8]>>48)] ^
93				_C2[byte(state[(i+6)%8]>>40)] ^
94				_C3[byte(state[(i+5)%8]>>32)] ^
95				_C4[byte(state[(i+4)%8]>>24)] ^
96				_C5[byte(state[(i+3)%8]>>16)] ^
97				_C6[byte(state[(i+2)%8]>>8)] ^
98				_C7[byte(state[(i+1)%8])] ^
99				K[i%8]
100		}
101
102		for i := 0; i < 8; i++ {
103			state[i] = L[i]
104		}
105	}
106
107	// Apply the Miyaguchi-Preneel compression function.
108	for i := 0; i < 8; i++ {
109		w.hash[i] ^= state[i] ^ block[i]
110	}
111}
112
113func (w *whirlpool) Write(source []byte) (int, error) {
114	var (
115		sourcePos  int                                            // Index of the leftmost source.
116		nn         int    = len(source)                           // Num of bytes to process.
117		sourceBits uint64 = uint64(nn * 8)                        // Num of bits to process.
118		sourceGap  uint   = uint((8 - (int(sourceBits & 7))) & 7) // Space on source[sourcePos].
119		bufferRem  uint   = uint(w.bufferBits & 7)                // Occupied bits on buffer[bufferPos].
120		b          uint32                                         // Current byte.
121	)
122
123	// Tally the length of the data added.
124	for i, carry, value := 31, uint32(0), uint64(sourceBits); i >= 0 && (carry != 0 || value != 0); i-- {
125		carry += uint32(w.bitLength[i]) + (uint32(value & 0xff))
126		w.bitLength[i] = byte(carry)
127		carry >>= 8
128		value >>= 8
129	}
130
131	// Process data in chunks of 8 bits.
132	for sourceBits > 8 {
133		// Take a byte form the source.
134		b = uint32(((source[sourcePos] << sourceGap) & 0xff) |
135			((source[sourcePos+1] & 0xff) >> (8 - sourceGap)))
136
137		// Process this byte.
138		w.buffer[w.bufferPos] |= uint8(b >> bufferRem)
139		w.bufferPos++
140		w.bufferBits += int(8 - bufferRem)
141
142		if w.bufferBits == digestBits {
143			// Process this block.
144			w.transform()
145			// Reset the buffer.
146			w.bufferBits = 0
147			w.bufferPos = 0
148		}
149		w.buffer[w.bufferPos] = byte(b << (8 - bufferRem))
150		w.bufferBits += int(bufferRem)
151
152		// Proceed to remaining data.
153		sourceBits -= 8
154		sourcePos++
155	}
156
157	// 0 <= sourceBits <= 8; All data leftover is in source[sourcePos].
158	if sourceBits > 0 {
159		b = uint32((source[sourcePos] << sourceGap) & 0xff) // The bits are left-justified.
160
161		// Process the remaining bits.
162		w.buffer[w.bufferPos] |= byte(b) >> bufferRem
163	} else {
164		b = 0
165	}
166
167	if uint64(bufferRem)+sourceBits < 8 {
168		// The remaining data fits on the buffer[bufferPos].
169		w.bufferBits += int(sourceBits)
170	} else {
171		// The buffer[bufferPos] is full.
172		w.bufferPos++
173		w.bufferBits += 8 - int(bufferRem) // bufferBits = 8*bufferPos
174		sourceBits -= uint64(8 - bufferRem)
175
176		// Now, 0 <= sourceBits <= 8; all data leftover is in source[sourcePos].
177		if w.bufferBits == digestBits {
178			// Process this data block.
179			w.transform()
180			// Reset buffer.
181			w.bufferBits = 0
182			w.bufferPos = 0
183		}
184		w.buffer[w.bufferPos] = byte(b << (8 - bufferRem))
185		w.bufferBits += int(sourceBits)
186	}
187	return nn, nil
188}
189
190func (w *whirlpool) Sum(in []byte) []byte {
191	// Copy the whirlpool so that the caller can keep summing.
192	n := *w
193
194	// Append a 1-bit.
195	n.buffer[n.bufferPos] |= 0x80 >> (uint(n.bufferBits) & 7)
196	n.bufferPos++
197
198	// The remaining bits should be 0. Pad with 0s to be complete.
199	if n.bufferPos > wblockBytes-lengthBytes {
200		if n.bufferPos < wblockBytes {
201			for i := 0; i < wblockBytes-n.bufferPos; i++ {
202				n.buffer[n.bufferPos+i] = 0
203			}
204		}
205		// Process this data block.
206		n.transform()
207		// Reset the buffer.
208		n.bufferPos = 0
209	}
210
211	if n.bufferPos < wblockBytes-lengthBytes {
212		for i := 0; i < (wblockBytes-lengthBytes)-n.bufferPos; i++ {
213			n.buffer[n.bufferPos+i] = 0
214		}
215	}
216	n.bufferPos = wblockBytes - lengthBytes
217
218	// Append the bit length of the hashed data.
219	for i := 0; i < lengthBytes; i++ {
220		n.buffer[n.bufferPos+i] = n.bitLength[i]
221	}
222
223	// Process this data block.
224	n.transform()
225
226	// Return the final digest as []byte.
227	var digest [digestBytes]byte
228	for i := 0; i < digestBytes/8; i++ {
229		digest[i*8] = byte(n.hash[i] >> 56)
230		digest[i*8+1] = byte(n.hash[i] >> 48)
231		digest[i*8+2] = byte(n.hash[i] >> 40)
232		digest[i*8+3] = byte(n.hash[i] >> 32)
233		digest[i*8+4] = byte(n.hash[i] >> 24)
234		digest[i*8+5] = byte(n.hash[i] >> 16)
235		digest[i*8+6] = byte(n.hash[i] >> 8)
236		digest[i*8+7] = byte(n.hash[i])
237	}
238
239	return append(in, digest[:digestBytes]...)
240}
241