rclone/vendor/github.com/jzelinskie/whirlpool/whirlpool.go
2019-06-30 18:35:01 +01:00

241 lines
6.3 KiB
Go

// Copyright 2012 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package whirlpool implements the ISO/IEC 10118-3:2004 whirlpool
// cryptographic hash. Whirlpool is defined in
// http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html
package whirlpool
import (
"encoding/binary"
"hash"
)
// whirlpool represents the partial evaluation of a checksum.
type whirlpool struct {
bitLength [lengthBytes]byte // Number of hashed bits.
buffer [wblockBytes]byte // Buffer of data to be hashed.
bufferBits int // Current number of bits on the buffer.
bufferPos int // Current byte location on buffer.
hash [digestBytes / 8]uint64 // Hash state.
}
// New returns a new hash.Hash computing the whirlpool checksum.
func New() hash.Hash {
return new(whirlpool)
}
func (w *whirlpool) Reset() {
// Cleanup the buffer.
w.buffer = [wblockBytes]byte{}
w.bufferBits = 0
w.bufferPos = 0
// Cleanup the digest.
w.hash = [digestBytes / 8]uint64{}
// Clean up the number of hashed bits.
w.bitLength = [lengthBytes]byte{}
}
func (w *whirlpool) Size() int {
return digestBytes
}
func (w *whirlpool) BlockSize() int {
return wblockBytes
}
func (w *whirlpool) transform() {
var (
K [8]uint64 // Round key.
block [8]uint64 // μ(buffer).
state [8]uint64 // Cipher state.
L [8]uint64
)
// Map the buffer to a block.
for i := 0; i < 8; i++ {
b := 8 * i
block[i] = binary.BigEndian.Uint64(w.buffer[b:])
}
// Compute & apply K^0 to the cipher state.
for i := 0; i < 8; i++ {
K[i] = w.hash[i]
state[i] = block[i] ^ K[i]
}
// Iterate over all the rounds.
for r := 1; r <= rounds; r++ {
// Compute K^rounds from K^(rounds-1).
for i := 0; i < 8; i++ {
L[i] = _C0[byte(K[i%8]>>56)] ^
_C1[byte(K[(i+7)%8]>>48)] ^
_C2[byte(K[(i+6)%8]>>40)] ^
_C3[byte(K[(i+5)%8]>>32)] ^
_C4[byte(K[(i+4)%8]>>24)] ^
_C5[byte(K[(i+3)%8]>>16)] ^
_C6[byte(K[(i+2)%8]>>8)] ^
_C7[byte(K[(i+1)%8])]
}
L[0] ^= rc[r]
for i := 0; i < 8; i++ {
K[i] = L[i]
}
// Apply r-th round transformation.
for i := 0; i < 8; i++ {
L[i] = _C0[byte(state[i%8]>>56)] ^
_C1[byte(state[(i+7)%8]>>48)] ^
_C2[byte(state[(i+6)%8]>>40)] ^
_C3[byte(state[(i+5)%8]>>32)] ^
_C4[byte(state[(i+4)%8]>>24)] ^
_C5[byte(state[(i+3)%8]>>16)] ^
_C6[byte(state[(i+2)%8]>>8)] ^
_C7[byte(state[(i+1)%8])] ^
K[i%8]
}
for i := 0; i < 8; i++ {
state[i] = L[i]
}
}
// Apply the Miyaguchi-Preneel compression function.
for i := 0; i < 8; i++ {
w.hash[i] ^= state[i] ^ block[i]
}
}
func (w *whirlpool) Write(source []byte) (int, error) {
var (
sourcePos int // Index of the leftmost source.
nn int = len(source) // Num of bytes to process.
sourceBits uint64 = uint64(nn * 8) // Num of bits to process.
sourceGap uint = uint((8 - (int(sourceBits & 7))) & 7) // Space on source[sourcePos].
bufferRem uint = uint(w.bufferBits & 7) // Occupied bits on buffer[bufferPos].
b uint32 // Current byte.
)
// Tally the length of the data added.
for i, carry, value := 31, uint32(0), uint64(sourceBits); i >= 0 && (carry != 0 || value != 0); i-- {
carry += uint32(w.bitLength[i]) + (uint32(value & 0xff))
w.bitLength[i] = byte(carry)
carry >>= 8
value >>= 8
}
// Process data in chunks of 8 bits.
for sourceBits > 8 {
// Take a byte form the source.
b = uint32(((source[sourcePos] << sourceGap) & 0xff) |
((source[sourcePos+1] & 0xff) >> (8 - sourceGap)))
// Process this byte.
w.buffer[w.bufferPos] |= uint8(b >> bufferRem)
w.bufferPos++
w.bufferBits += int(8 - bufferRem)
if w.bufferBits == digestBits {
// Process this block.
w.transform()
// Reset the buffer.
w.bufferBits = 0
w.bufferPos = 0
}
w.buffer[w.bufferPos] = byte(b << (8 - bufferRem))
w.bufferBits += int(bufferRem)
// Proceed to remaining data.
sourceBits -= 8
sourcePos++
}
// 0 <= sourceBits <= 8; All data leftover is in source[sourcePos].
if sourceBits > 0 {
b = uint32((source[sourcePos] << sourceGap) & 0xff) // The bits are left-justified.
// Process the remaining bits.
w.buffer[w.bufferPos] |= byte(b) >> bufferRem
} else {
b = 0
}
if uint64(bufferRem)+sourceBits < 8 {
// The remaining data fits on the buffer[bufferPos].
w.bufferBits += int(sourceBits)
} else {
// The buffer[bufferPos] is full.
w.bufferPos++
w.bufferBits += 8 - int(bufferRem) // bufferBits = 8*bufferPos
sourceBits -= uint64(8 - bufferRem)
// Now, 0 <= sourceBits <= 8; all data leftover is in source[sourcePos].
if w.bufferBits == digestBits {
// Process this data block.
w.transform()
// Reset buffer.
w.bufferBits = 0
w.bufferPos = 0
}
w.buffer[w.bufferPos] = byte(b << (8 - bufferRem))
w.bufferBits += int(sourceBits)
}
return nn, nil
}
func (w *whirlpool) Sum(in []byte) []byte {
// Copy the whirlpool so that the caller can keep summing.
n := *w
// Append a 1-bit.
n.buffer[n.bufferPos] |= 0x80 >> (uint(n.bufferBits) & 7)
n.bufferPos++
// The remaining bits should be 0. Pad with 0s to be complete.
if n.bufferPos > wblockBytes-lengthBytes {
if n.bufferPos < wblockBytes {
for i := 0; i < wblockBytes-n.bufferPos; i++ {
n.buffer[n.bufferPos+i] = 0
}
}
// Process this data block.
n.transform()
// Reset the buffer.
n.bufferPos = 0
}
if n.bufferPos < wblockBytes-lengthBytes {
for i := 0; i < (wblockBytes-lengthBytes)-n.bufferPos; i++ {
n.buffer[n.bufferPos+i] = 0
}
}
n.bufferPos = wblockBytes - lengthBytes
// Append the bit length of the hashed data.
for i := 0; i < lengthBytes; i++ {
n.buffer[n.bufferPos+i] = n.bitLength[i]
}
// Process this data block.
n.transform()
// Return the final digest as []byte.
var digest [digestBytes]byte
for i := 0; i < digestBytes/8; i++ {
digest[i*8] = byte(n.hash[i] >> 56)
digest[i*8+1] = byte(n.hash[i] >> 48)
digest[i*8+2] = byte(n.hash[i] >> 40)
digest[i*8+3] = byte(n.hash[i] >> 32)
digest[i*8+4] = byte(n.hash[i] >> 24)
digest[i*8+5] = byte(n.hash[i] >> 16)
digest[i*8+6] = byte(n.hash[i] >> 8)
digest[i*8+7] = byte(n.hash[i])
}
return append(in, digest[:digestBytes]...)
}