In computer science and cryptography, Whirlpool (sometimes styled WHIRLPOOL) is a cryptographic hash function. It was designed by Vincent Rijmen (co-creator of the Advanced Encryption Standard) and Paulo S. L. M. Barreto, who first described it in 2000. It is named after the Whirlpool galaxy in Canes Venatici (M51, or NGC 5194), the first one recognized to have a spiral structure by William Parsons, third Earl of Rosse, in April 1845]]
Whirlpool is a hash designed after the Square block cipher, and is considered to be in that family of block cipher functions.
Whirlpool is a Miyaguchi–Preneel construction based on a substantially modified Advanced Encryption Standard (AES).
Whirlpool takes a message of any length less than 2<sup>256</sup> bits and returns a 512-bit message digest.
The authors have declared that
:"WHIRLPOOL is not (and will never be) patented. It may be used free of charge for any purpose." Changing the 8x8 rotating matrix constants from (1, 1, 3, 1, 5, 8, 9, 5) to (1, 1, 4, 1, 8, 5, 2, 9) solved this issue.
Internal structure
The Whirlpool hash function is a Merkle–Damgård construction based on an AES-like block cipher W in Miyaguchi–Preneel mode.
Pseudo-code
Here is an implementation example of the standard Whirlpool algorithm:
<small>S := 0x18, 0x23, 0xc6, 0xe8, 0x87, 0xb8, 0x01, 0x4f, 0x36, 0xa6, 0xd2, 0xf5, 0x79, 0x6f, 0x91, 0x52, \
0x60, 0xbc, 0x9b, 0x8e, 0xa3, 0x0c, 0x7b, 0x35, 0x1d, 0xe0, 0xd7, 0xc2, 0x2e, 0x4b, 0xfe, 0x57, \
0x15, 0x77, 0x37, 0xe5, 0x9f, 0xf0, 0x4a, 0xda, 0x58, 0xc9, 0x29, 0x0a, 0xb1, 0xa0, 0x6b, 0x85, \
0xbd, 0x5d, 0x10, 0xf4, 0xcb, 0x3e, 0x05, 0x67, 0xe4, 0x27, 0x41, 0x8b, 0xa7, 0x7d, 0x95, 0xd8, \
0xfb, 0xee, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9e, 0xca, 0x2d, 0xbf, 0x07, 0xad, 0x5a, 0x83, 0x33, \
0x63, 0x02, 0xaa, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xf2, 0xe3, 0x5b, 0x88, 0x9a, 0x26, 0x32, 0xb0, \
0xe9, 0x0f, 0xd5, 0x80, 0xbe, 0xcd, 0x34, 0x48, 0xff, 0x7a, 0x90, 0x5f, 0x20, 0x68, 0x1a, 0xae, \
0xb4, 0x54, 0x93, 0x22, 0x64, 0xf1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xec, 0xdb, 0xa1, 0x8d, 0x3d, \
0x97, 0x00, 0xcf, 0x2b, 0x76, 0x82, 0xd6, 0x1b, 0xb5, 0xaf, 0x6a, 0x50, 0x45, 0xf3, 0x30, 0xef, \
0x3f, 0x55, 0xa2, 0xea, 0x65, 0xba, 0x2f, 0xc0, 0xde, 0x1c, 0xfd, 0x4d, 0x92, 0x75, 0x06, 0x8a, \
0xb2, 0xe6, 0x0e, 0x1f, 0x62, 0xd4, 0xa8, 0x96, 0xf9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c, \
0x5e, 0x78, 0x38, 0x8c, 0xd1, 0xa5, 0xe2, 0x61, 0xb3, 0x21, 0x9c, 0x1e, 0x43, 0xc7, 0xfc, 0x04, \
0x51, 0x99, 0x6d, 0x0d, 0xfa, 0xdf, 0x7e, 0x24, 0x3b, 0xab, 0xce, 0x11, 0x8f, 0x4e, 0xb7, 0xeb, \
0x3c, 0x81, 0x94, 0xf7, 0xb9, 0x13, 0x2c, 0xd3, 0xe7, 0x6e, 0xc4, 0x03, 0x56, 0x44, 0x7f, 0xa9, \
0x2a, 0xbb, 0xc1, 0x53, 0xdc, 0x0b, 0x9d, 0x6c, 0x31, 0x74, 0xf6, 0x46, 0xac, 0x89, 0x14, 0xe1, \
0x16, 0x3a, 0x69, 0x09, 0x70, 0xb6, 0xd0, 0xed, 0xcc, 0x42, 0x98, 0xa4, 0x28, 0x5c, 0xf8, 0x86
C := 0x01, 0x01, 0x04, 0x01, 0x08, 0x05, 0x02, 0x09, \
0x09, 0x01, 0x01, 0x04, 0x01, 0x08, 0x05, 0x02, \
0x02, 0x09, 0x01, 0x01, 0x04, 0x01, 0x08, 0x05, \
0x05, 0x02, 0x09, 0x01, 0x01, 0x04, 0x01, 0x08, \
0x08, 0x05, 0x02, 0x09, 0x01, 0x01, 0x04, 0x01, \
0x01, 0x08, 0x05, 0x02, 0x09, 0x01, 0x01, 0x04, \
0x04, 0x01, 0x08, 0x05, 0x02, 0x09, 0x01, 0x01, \
0x01, 0x04, 0x01, 0x08, 0x05, 0x02, 0x09, 0x01
- Matrix built from initialization vector
IM := 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, 0, 0, 0
R := 10
func getConstantRoundMatrix(r)
cr := IM
for j from 0 to 7
cr[j] := S[8 * (r - 1) + j]
endfor
return cr
endfunc
func whirlpoolRound(matrix, key)
- Apply the non-linear transformation γ
for i from 0 to 7
for j from 0 to 7
matrix[i * 8 + j] = S[matrix[i * 8 + j]]
endfor
endfor
- Apply cyclical permutation π
tmp := matrix
for i from 0 to 7
for j from 0 to 7
- '+ 8' to prevent negative indices
matrix[i * 8 + j] = tmp[((i - j + 8) % 8) * 8 + j]
endfor
endfor
matrix := tmp
- Apply linear diffusion θ
matrix := dotProduct(matrix, C)
- Apply key addition σ[key]
matrix := matrix xor key
return matrix
endfunc
func whirlpool(M)
m, t := pad(M) # Returns (paddedMessageDividedInChunks, amountOfChunks)
H := IM
for i from 0 to t
W := m[t]
Kr := H
W := W xor H
for r from 1 to R
cr := getConstantRoundMatrix(r)
Kr := whirlpoolRound(Kr, cr)
W := whirlpoolRound(W, Kr)
endfor
H := H xor W
H := H xor m[t]
endfor
return matrixToHexString(H)
endfunc</small>
For the linear-diffusion <math>\theta</math>, a matrix multiplication is required. Galois Field arithmetic can be used to write this multiplication algorithm:
<small>func dotProduct(A, B)
tmp: Matrix
for i from 0 to 7
for j from 0 to 7
tmp[i * 8 + j] := 0
for k from 0 to 7
- Galois Field (2^8) multiplication
a := A[i * 8 + k];
b := B[k * 8 + j];
product := 0;
while b > 0
if b & 1 == 1
product := product xor a
endif
if a & 0x80 != 0
a := (a << 1) xor 0x11d # x^8 + x^4 + x^3 + x^2 + 1
else
a := a << 1
endif
b := b >> 1
endwhile
tmp[i * 8 + j] := tmp[i * 8 + j] xor product
endfor
endfor
endfor
return tmp
endfunc</small>
Here is an implementation of the 512-bit (64-bits size, big-endian) padding:
<small>func pad(M)
original_length := len(M) # In bytes
- 512 bits (total length) - 256 bits (size length) - 1 bit (padding bit)
- 64 bytes - 32 bytes - 1 byte = 31 bytes
padding := (31 - original_length) % 64
padding := (padding + 64) % 64 # Avoid negative padding
total_length := original_length + 1 + padding + 32 # In bytes
padded: Byte[total_length]
- Copy original message
for i from 0 to original_length - 1
padded[i] := M[i]
endfor
padded[original_length] := 0x80 # Append the '1' bit, then 7 '0' bits
for i from original_length + 1 to original_length + padding
padded[i] := 0x00 # Append 8 '0' bits
endfor
for i from 0 to 31
padded[total_length - 32 + i] := (original_length * 8) >> (8 * (31 - i)) & 0xff
endfor
chunk_amount := total_length / 64
divided := Byte[chunk_amount][64]
for i from 0 to chunk_amount - 1
for j from 0 to 63
divided[i][j] := padded[i * 64 + j]
endfor
endfor
return divided, chunk_amount
endfunc</small>
And here is an example of a matrix-to-string conversion:
<small>func matrixToHexString(matrix)
HEX := "0123456789abcdef"
result: Byte[128]
for i from 0 to 63
byte := matrix[i]
result[i * 2] := HEX[byte >> 4]
result[i * 2 + 1] := HEX[byte & 0xf]
endfor
return result
endfunc</small>
Adoption
Two of the first widely used mainstream cryptographic programs that started using Whirlpool were FreeOTFE, followed by TrueCrypt in 2005.
VeraCrypt (a fork of TrueCrypt) included Whirlpool (the final version) as one of its supported hash algorithms.
See also
- Digital timestamping
References
External links
- , a Java implementation of all three revisions of Whirlpool
- – An open source Go implementation of the latest revision of Whirlpool
- A Matlab Implementation of the Whirlpool Hashing Function
- RHash, an open source command-line tool, which can calculate and verify Whirlpool hash.
- Perl Whirlpool module at CPAN
- Digest module implementing the Whirlpool hashing algorithm in Ruby
- Ironclad a Common Lisp cryptography package containing a Whirlpool implementation
- The ISO/IEC 10118-3:2004 standard
- Test vectors for the Whirlpool hash from the NESSIE project
- Managed C# implementation
- Python Whirlpool module
