Whirlpool (hash function)

Uses your system voice; voice and rate in settings.

Snapshot

≈ 15 min read

Names in other languages

No names in other languages are held for this entry yet.

Article bodies are English and are not machine-translated. A name in another language is not a translation of the article.

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 . The hash has been recommended by the NESSIE project. It has also been adopted by the International Organization for Standardization (ISO) and the International Electrotechnical Commission (IEC) as part of the joint ISO/IEC 10118-3 international standard.

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 . The hash has been recommended by the NESSIE project. It has also been adopted by the International Organization for Standardization (ISO) and the International Electrotechnical Commission (IEC) as part of the joint ISO/IEC 10118-3 international standard. The encryption process consists of updating the state with four round functions over 10 rounds. The four round functions are SubBytes (SB or \gamma ), ShiftColumns (SC or \pi ), MixRows (MR or \theta ) and AddRoundKey (AK or \sigma[k] ). During each round the new state is computed as S := \left( AK \circ MR \circ SC \circ SB \right)(S) . a \oplus b is the bitwise XOR of a and b . If a and b are matrices, the XOR is applied element-wise ( a \oplus b = c \ \Leftrightarrow \ c_{i,j} = a_{i,j} \oplus b_{i,j} ). f \circ g is the composition function of f and g such that \left(f \circ g\right)\left(x\right) = g\left(f\left(x\right)\right) ; {\underset{k=m}\overset{n}{\bigcirc}} \alpha_k, \ \forall \left(m; n; k\right) \in \mathbb{Z}^3, \ m \leq k \leq n is the ascending repetition of \alpha_m \circ \alpha_{m + 1} \circ \ldots \circ \alpha_{n - 1} \circ \alpha_n ; {\underset{m}\overset{k=n}{\bigcirc}} \alpha_k, \ \forall \left(m; n; k\right) \in \mathbb{Z}^3, \ m \leq k \leq n is the descending repetition of \alpha_n \circ \alpha_{n - 1} \circ \ldots \circ \alpha_{m + 1} \circ \alpha_m . \eta_i & = \mu\left(m_i\right), \\ H_0 & = \mu\left(IV\right), \\ H_i & = W\left[H_{i-1}\right]\left(\eta_i\right) \oplus H_{i-1} \oplus \eta_i, \ 1 \leq i \leq t \\ \end{align} \\ & \text{Whirlpool}\left(M\right) \equiv \mu^{-1}\left(H_t\right) \end{align} Where: \mu: \{0, 1\}^{512} \to \mathcal{M}_{8 \times 8} is the string to matrix conversion function; \mu^{-1}: \mathcal{M}_{8 \times 8} \to \{0, 1\}^{512} is the matrix to string conversion function; IV is the initialization vector, a string of 512 0-bits; W is the Whirlpool cipher function. W\left[K\right] = \left( {\underset{1}\overset{r=R}{\bigcirc}} \rho\left[K_r\right] \right) \circ \sigma\left[K_0\right] \end{align} Where: R is the number of rounds (the standard Whirlpool uses R = 10 ); K_n is the nth key of the K key schedule; \rho is the round function. The key schedule expands the key K \in \mathcal{M}_{8 \times 8} into a sequence of keys K_r \in \mathcal{M}_{8 \times 8}, \ 0 \leq r \leq R [section 3.8] : \begin{align} K_0 & = K, \\ K_r & = \rho\left[c^r\right]\left(K_{r-1}\right), \ 1 \leq r \leq R \\ \end{align} Where: c^r \in \mathcal{M}_{8 \times 8} is the rth round constant matrix. The round constant for the rth round, r > 0 , is a matrix c^r \in \mathcal{M}_{8 \times 8} , defined as: \begin{align} c^r_{0, j} & = S\left[8\left(r - 1\right) + j\right], & 0 \leq j Where: S \in \mathcal{M}_{16 \times 16} is the S-box. \rho\left[k\right] & = \sigma\left[k\right] \circ \theta \circ \pi \circ \gamma \\ & _{\left( = \ AK \ \circ \ MR \ \circ \ SC \ \circ \ SB \right)} \\ \end{align} Where: \gamma is the non-linear layer; \pi is the cyclical permutation; \theta is the diffusion layer; \sigma is the key addition. \gamma\left(a\right) = b \ \Leftrightarrow \ b_{i, j} = S\left[a_{i, j}\right], \ 0 \leq i \pi\left(a\right) = b \ \Leftrightarrow \ b_{i, j} = a_{\left( \left(i - j\right) \ \text{mod} \ 8 \right), \ j}, \ 0 \leq i The linear diffusion layer \theta: \mathcal{M}_{8 \times 8} \to \mathcal{M}_{8 \times 8} is a linear mapping based on the circular matrix C = \text{cir}\left(01_{(16)}, 01_{(16)}, 04_{(16)}, 01_{(16)}, 08_{(16)}, 05_{(16)}, 02_{(16)}, 09_{(16)}\right) [section 3.4] : \begin{align} \theta\left(a\right) = a \cdot C, \ 0 \leq i A circulant matrix C\in\mathcal{M}_{n\times n} is defined as a matrix where each row is a cyclic shift of the previous row [section 2.2] . Formally, a circulant matrix can be represented as: \begin{align} & \text{cir}(a_0, a_1, \ldots, a_{n-1}) = \begin{pmatrix} a_0 & a_1 & a_2 & \cdots & a_{n-1} \\ a_{n-1} & a_0 & a_1 & \cdots & a_{n-2} \\ a_{n-2} & a_{n-1} & a_0 & \cdots & a_{n-3} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ a_1 & a_2 & a_3 & \cdots & a_0 \\ \end{pmatrix}, \ \forall a_n \in \mathbb{F}_{2^8}, \ n \in \mathbb{N} \\ & \text{or simply} \\ & \text{cir}(a_0, a_1, \ldots, a_{n-1}) = c \ \Leftrightarrow \ c_{i, j} = a_{\left(i - j\right) \ \text{mod} \ 8}, \ 0 \leq i For example, circulant matrix C = \text{cir}\left(01_{(16)}, 01_{(16)}, 04_{(16)}, 01_{(16)}, 08_{(16)}, 05_{(16)}, 02_{(16)}, 09_{(16)}\right) is the matrix: \begin{align} C = \begin{pmatrix} 01_{(16)} & 01_{(16)} & 04_{(16)} & 01_{(16)} & 08_{(16)} & 05_{(16)} & 02_{(16)} & 09_{(16)} \\ 09_{(16)} & 01_{(16)} & 01_{(16)} & 04_{(16)} & 01_{(16)} & 08_{(16)} & 05_{(16)} & 02_{(16)} \\ 02_{(16)} & 09_{(16)} & 01_{(16)} & 01_{(16)} & 04_{(16)} & 01_{(16)} & 08_{(16)} & 05_{(16)} \\ 05_{(16)} & 02_{(16)} & 09_{(16)} & 01_{(16)} & 01_{(16)} & 04_{(16)} & 01_{(16)} & 08_{(16)} \\ 08_{(16)} & 05_{(16)} & 02_{(16)} & 09_{(16)} & 01_{(16)} & 01_{(16)} & 04_{(16)} & 01_{(16)} \\ 01_{(16)} & 08_{(16)} & 05_{(16)} & 02_{(16)} & 09_{(16)} & 01_{(16)} & 01_{(16)} & 04_{(16)} \\ 04_{(16)} & 01_{(16)} & 08_{(16)} & 05_{(16)} & 02_{(16)} & 09_{(16)} & 01_{(16)} & 01_{(16)} \\ 01_{(16)} & 04_{(16)} & 01_{(16)} & 08_{(16)} & 05_{(16)} & 02_{(16)} & 09_{(16)} & 01_{(16)} \\ \end{pmatrix} \\ \end{align} \sigma\left[k\right]\left(a\right) = a \oplus k \\ \end{align} & S = \begin{pmatrix} 18_{(16)} & 23_{(16)} & c6_{(16)} & e8_{(16)} & 87_{(16)} & b8_{(16)} & 01_{(16)} & 4f_{(16)} & 36_{(16)} & a6_{(16)} & d2_{(16)} & f5_{(16)} & 79_{(16)} & 6f_{(16)} & 91_{(16)} & 52_{(16)} \\ 60_{(16)} & bc_{(16)} & 9b_{(16)} & 8e_{(16)} & a3_{(16)} & 0c_{(16)} & 7b_{(16)} & 35_{(16)} & 1d_{(16)} & e0_{(16)} & d7_{(16)} & c2_{(16)} & 2e_{(16)} & 4b_{(16)} & fe_{(16)} & 57_{(16)} \\ 15_{(16)} & 77_{(16)} & 37_{(16)} & e5_{(16)} & 9f_{(16)} & f0_{(16)} & 4a_{(16)} & da_{(16)} & 58_{(16)} & c9_{(16)} & 29_{(16)} & 0a_{(16)} & b1_{(16)} & a0_{(16)} & 6b_{(16)} & 85_{(16)} \\ bd_{(16)} & 5d_{(16)} & 10_{(16)} & f4_{(16)} & cb_{(16)} & 3e_{(16)} & 05_{(16)} & 67_{(16)} & e4_{(16)} & 27_{(16)} & 41_{(16)} & 8b_{(16)} & a7_{(16)} & 7d_{(16)} & 95_{(16)} & d8_{(16)} \\ fb_{(16)} & ee_{(16)} & 7c_{(16)} & 66_{(16)} & dd_{(16)} & 17_{(16)} & 47_{(16)} & 9e_{(16)} & ca_{(16)} & 2d_{(16)} & bf_{(16)} & 07_{(16)} & ad_{(16)} & 5a_{(16)} & 83_{(16)} & 33_{(16)} \\ 63_{(16)} & 02_{(16)} & aa_{(16)} & 71_{(16)} & c8_{(16)} & 19_{(16)} & 49_{(16)} & d9_{(16)} & f2_{(16)} & e3_{(16)} & 5b_{(16)} & 88_{(16)} & 9a_{(16)} & 26_{(16)} & 32_{(16)} & b0_{(16)} \\ e9_{(16)} & 0f_{(16)} & d5_{(16)} & 80_{(16)} & be_{(16)} & cd_{(16)} & 34_{(16)} & 48_{(16)} & ff_{(16)} & 7a_{(16)} & 90_{(16)} & 5f_{(16)} & 20_{(16)} & 68_{(16)} & 1a_{(16)} & ae_{(16)} \\ b4_{(16)} & 54_{(16)} & 93_{(16)} & 22_{(16)} & 64_{(16)} & f1_{(16)} & 73_{(16)} & 12_{(16)} & 40_{(16)} & 08_{(16)} & c3_{(16)} & ec_{(16)} & db_{(16)} & a1_{(16)} & 8d_{(16)} & 3d_{(16)} \\ 97_{(16)} & 00_{(16)} & cf_{(16)} & 2b_{(16)} & 76_{(16)} & 82_{(16)} & d6_{(16)} & 1b_{(16)} & b5_{(16)} & af_{(16)} & 6a_{(16)} & 50_{(16)} & 45_{(16)} & f3_{(16)} & 30_{(16)} & ef_{(16)} \\ 3f_{(16)} & 55_{(16)} & a2_{(16)} & ea_{(16)} & 65_{(16)} & ba_{(16)} & 2f_{(16)} & c0_{(16)} & de_{(16)} & 1c_{(16)} & fd_{(16)} & 4d_{(16)} & 92_{(16)} & 75_{(16)} & 06_{(16)} & 8a_{(16)} \\ b2_{(16)} & e6_{(16)} & 0e_{(16)} & 1f_{(16)} & 62_{(16)} & d4_{(16)} & a8_{(16)} & 96_{(16)} & f9_{(16)} & c5_{(16)} & 25_{(16)} & 59_{(16)} & 84_{(16)} & 72_{(16)} & 39_{(16)} & 4c_{(16)} \\ 5e_{(16)} & 78_{(16)} & 38_{(16)} & 8c_{(16)} & d1_{(16)} & a5_{(16)} & e2_{(16)} & 61_{(16)} & b3_{(16)} & 21_{(16)} & 9c_{(16)} & 1e_{(16)} & 43_{(16)} & c7_{(16)} & fc_{(16)} & 04_{(16)} \\ 51_{(16)} & 99_{(16)} & 6d_{(16)} & 0d_{(16)} & fa_{(16)} & df_{(16)} & 7e_{(16)} & 24_{(16)} & 3b_{(16)} & ab_{(16)} & ce_{(16)} & 11_{(16)} & 8f_{(16)} & 4e_{(16)} & b7_{(16)} & eb_{(16)} \\ 3c_{(16)} & 81_{(16)} & 94_{(16)} & f7_{(16)} & b9_{(16)} & 13_{(16)} & 2c_{(16)} & d3_{(16)} & e7_{(16)} & 6e_{(16)} & c4_{(16)} & 03_{(16)} & 56_{(16)} & 44_{(16)} & 7f_{(16)} & a9_{(16)} \\ 2a_{(16)} & bb_{(16)} & c1_{(16)} & 53_{(16)} & dc_{(16)} & 0b_{(16)} & 9d_{(16)} & 6c_{(16)} & 31_{(16)} & 74_{(16)} & f6_{(16)} & 46_{(16)} & ac_{(16)} & 89_{(16)} & 14_{(16)} & e1_{(16)} \\ 16_{(16)} & 3a_{(16)} & 69_{(16)} & 09_{(16)} & 70_{(16)} & b6_{(16)} & d0_{(16)} & ed_{(16)} & cc_{(16)} & 42_{(16)} & 98_{(16)} & a4_{(16)} & 28_{(16)} & 5c_{(16)} & f8_{(16)} & 86_{(16)} \\ \end{pmatrix} \\ \end{align} 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 For the linear-diffusion \theta , a matrix multiplication is required. Galois Field arithmetic can be used to write this multiplication algorithm: 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 endwhile tmp[i * 8 + j] := tmp[i * 8 + j] xor product endfor endfor endfor return tmp endfunc Here is an implementation of the 512-bit (64-bits size, big-endian) padding: 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 And here is an example of a matrix-to-string conversion: 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

Text derived from https://en.wikipedia.org/w/index.php?oldid=1336636417 under CC BY-SA 4.0; modifications have been made.

Sources cited in this revision

Extracted from the cited revision's reference blocks; absent citations are not invented.