mirror of
https://github.com/rclone/rclone.git
synced 2024-12-26 00:49:22 +01:00
241 lines
6.3 KiB
Go
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]...)
|
|
}
|