\section{Whirlpool}

Whirlpool is a cryptographic hash function designed by Vincent Rijmen and Paulo
Barreto, first published in 2000 and standardized by ISO/IEC in 2004. It
produces a 512-bit digest from a message of arbitrary length, processing data
in 512-bit blocks. Its internal structure is inspired by the wide-pipe
Miyaguchi-Preneel construction and shares design principles with AES, using a
substitution-permutation network over an $8 \times 8$ matrix of bytes.

\vspace{1em}

Whirlpool maintains a state of eight 64-bit words, forming an $8 \times 8$
matrix of bytes. Each 512-bit block is processed in 10 rounds. Each round
applies four successive transformations to the state matrix: a byte
substitution, a column shift, a row mixing, and a round key addition.

\vspace{1em}

The padding scheme follows the same structure as MD5 and SHA-256: a single
\texttt{1} bit is appended, followed by \texttt{0} bits until the message
length is congruent to 448 bits modulo 512. The original message length in bits
is then appended as a 64-bit big-endian integer.

\vspace{1em}

Each round applies the following four transformations in order:

\textbf{SubBytes} replaces each byte of the state matrix by its image under the
Whirlpool S-box, a fixed 256-entry lookup table defined in the Whirlpool
specification.

\medskip

\textbf{ShiftColumns} cyclically shifts each column $j$ of the state matrix
upward by $j$ positions, producing a transposition that spreads bytes across
rows. Formally, if $a_{i,j}$ denotes the byte at row $i$, column $j$ of the
state matrix, ShiftColumns produces:

\begin{align*}
b_{i,j} = a_{i',\ j} \quad \text{where } i' = (i - j) \bmod 8
\end{align*}

\medskip

\textbf{MixRows} multiplies each row of the state matrix by a fixed MDS matrix
over $\mathrm{GF}(2^8)$ with irreducible polynomial $x^8 + x^4 + x^3 + x^2 +
1$, providing diffusion across the eight bytes of each row. Formally, for each
row $i$, each output byte $b_j$ is computed as:

\begin{align*}
b_j = \bigoplus_{k=0}^{7} \mathrm{MDS}[(j - k) \bmod 8] \cdot a_{i,k}
\end{align*}

\noindent where $\cdot$ denotes multiplication in $\mathrm{GF}(2^8)$ and
$\oplus$ denotes XOR.

\medskip

\textbf{AddRoundKey} XORs the state with the current round key.

\vspace{1em}

The S-box and the MDS matrix coefficients are fixed tables defined in the
Whirlpool specification; their values are too large to reproduce here. The
round constants $\mathrm{RC}[r]$, $r \in \mathbb{N},\ 1 \leq r \leq 10$, are
however directly derived from the S-box. Each $\mathrm{RC}[r]$ is an 8-word
state where only the first word is non-zero:

\begin{align*}
\mathrm{RC}[r][0] &= \sum_{k=0}^{7} S[8(r-1)+k] \cdot 2^{8(7-k)} \\
\mathrm{RC}[r][j] &= 0 \quad \forall j \in \mathbb{N},\ 1 \leq j \leq 7
\end{align*}

\noindent Their role is to break symmetry in the key schedule: without them, a
symmetric input state would produce symmetric round keys, weakening the internal
block transformation.

\vspace{1em}

The round keys $K[r]$, $r \in \mathbb{N},\ 0 \leq r \leq 10$, are derived from
the current hash state. $K[0]$ is set to the state before processing the block.
Each subsequent key is obtained by applying the round function to the previous
key with a precomputed round constant, where $\text{Round}(S, K)$ denotes the
successive application of SubBytes, ShiftColumns, MixRows, and AddRoundKey with
key $K$ to state $S$:

\begin{align*}
K[0] &= H \\
K[r] &= \text{Round}(K[r-1],\ \mathrm{RC}[r]) \quad r \in \mathbb{N},\ 1 \leq r \leq 10
\end{align*}

The block $M$ is then encrypted using these keys under a wide-pipe construction.
The final state update follows the Miyaguchi-Preneel scheme:

\begin{align*}
H \leftarrow E(H,\ M) \oplus M \oplus H
\end{align*}

\noindent where $E(H, M)$ denotes the encryption of $M$ with key schedule
derived from $H$.

\vspace{1em}

The state is initialized to all zeros. After all blocks have been processed,
the eight 64-bit state words are serialized in big-endian order to produce the
512-bit digest.
