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