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

  1. 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)

  1. 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

  1. Apply cyclical permutation π

tmp := matrix

for i from 0 to 7

for j from 0 to 7

  1. '+ 8' to prevent negative indices

matrix[i * 8 + j] = tmp[((i - j + 8) % 8) * 8 + j]

endfor

endfor

matrix := tmp

  1. Apply linear diffusion θ

matrix := dotProduct(matrix, C)

  1. 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

  1. 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

  1. 512 bits (total length) - 256 bits (size length) - 1 bit (padding bit)
  2. 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]

  1. 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

  • , 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