# An explanation of Whirlpool C implementation - or the general algorithm

Note: Question originally asked on StackOverflow - was directed here

Anyone got a tutorial on the designers concept implementation of Whirlpool in C, or the Whirlpool algorithm in general? I find the source code hard to understand, mostly because I do not know anything about the algorithm. I am not a cryptographer, nor am I a good mathematician, so their documentation kind of went over my head...

The implementation and other documentation can be found here.

Initially I was just searching for a free (public domain) implementation to use in a project, but I figured I better know something about it as well... Currently I do not even know how to use it.

-
Also, it would be good if somebody with enough rep could retag the question to something more sane.. Any attempt from me to tag it caused an error because the tags did not exist – Oystein Nov 5 '10 at 22:25
Next time please include the link to your specific question instead of the link to the top page of stackoverflow.com. – Tsuyoshi Ito Nov 5 '10 at 22:36
Good point. Fixed. EDIT: You fixed it. – Oystein Nov 5 '10 at 22:37
I don't know of any tutorial. You might find useful information at en.wikipedia.org/wiki/One-way_compression_function, and specifically in the "Construction from block ciphers" and "Miyaguchi–Preneel" sections. Whirlpool is a special case of the Miyagushi-Preneel method for building hash functions from block ciphers. If those don't help, can you be more specific in what you hope to learn? – David Cash Nov 5 '10 at 23:26
I don't think you need to know anything about the internal workings of the algorithm in this case, as long as you use the provided API. On the other hand, you can certainly misuse hash functions in a generic way. – Yuval Filmus Nov 5 '10 at 23:32

In the following, I try to minimize the background knowledge needed to understand the answer. If you need more details, I suggest taking a look at section 12.2 of Cryptography and Network Security Principles and Practices, Fourth Edition. (Though it requires a fair knowledge of crypto.)

First take a look at Merkle–Damgård construction. Virtually all hash functions follow such construction. Informally, it applies a compression function iteratively to reduce the input size to get some fixed-length output. For instance, you can hash a whole DVD (~ 4.3 GB) and get a 128-bit code.

Let $M$ be the input to an MD-based hash function. The MD construction appends a pad and the length of $M$ to it, so as to prevent several attacks.

Whirlpool uses a compression function named $W$. $W$ is similar to a block cipher named Rijndael, which is now standardized under the name AES (Advanced Encryption Standard). Rijndael has 3 variants: 128-bit, 192-bit, and 256-bit. The inventors of Whirlpool decided that no Rijndael variant is secure enough to be used as the compression function for a hash. Thus, $W$ is designed so as it accepts 512-bit inputs, and produces 512-bit outputs. The key size of $W$ is 512 bits as well.

Whirlpool works as follows. Let $M$ be the input. $M$ is divided into 512-bit segments: $M=(M_0,M_1,\ldots,M_t)$. Let $h_0$ be some initial value (fixed by Whirlpool standard).

For $i=1,2,\ldots,t$, apply $W$ iteratively as follows:

$h_i = W(h_{i-1},m_i) \oplus h_{i-1} \oplus m_i$

where the first input to $W$ is a block-to-be-encrypted, and its second input is the encryption key. $\oplus$ denotes the XOR operation.

$h_t$ is considered as the output of the Whirlpool hash function.

The whole complexity lies in designing $W$. As pointed out before, it is similar to Rijndael, so you can understand it if you get familiar with Rijndael, on which I have designed a set of slides. The slides are self-contained and do not assume any math background beyond high school.

-
That certainly made things a lot clearer, thank you for your efforts. Yuval also gets a +1 for helping to understand how to use the code – Oystein Nov 6 '10 at 14:47

The API for using Whirlpool consists of the functions NESSIEInit, NESSIEadd and NESSIEFinalize.

The first function initializes an instance of NESSIEstruct (in the C version). You the enter the data using NESSIEadd. You get the hash using NESSIEfinalize.

The only non-trivial part is how to add the data. The function that adds data gets a vector of bytes and its length in bits; the last byte can be partial, but I guess you have no need for that.

You can add data as many times as you want; adding $d_1$ then $d_2$ then $d_3$ is the same as adding $d_1d_2d_3$, i.e. it doesn't matter how you break the input. It's like an input stream then gets a vector of bits and takes care of everything else.

Internally, it's a pretty standard hash function built around an AES variant. The hash function works on chunks of 512 bits - you have to be careful about padding if your input isn't an integer multiple of this (if you do this improperly, there are some simple attacks).

The internal state S is also 512 bits, and is init with zero. To process a block B, first encrypt S using some constant round keys; the intermediate results are used as round keys for an encryption of B. Xor the result (B encrypted with round keys generated from S) to S and to B to get the new S. The hash itself is the value of S after the last input block.

All encryptions are with respect to the AES-like cipher, which is pretty similar to AES but uses for example a different S-box and a different MDS (there are other small differences).

-
Aha. But why three functions instead of one? Also I notice some of the code has an #ifdef OBSOLETE wrapping - what is that for? Can I remove it to reduce file size? – Oystein Nov 5 '10 at 23:50
Also when you say that you have to be careful with the padding, do you mean that the user (me) of the API should pad the input data, or were you talking about the inner workings of the API? The way I understand it, padding is a part of the hash function? – Oystein Nov 6 '10 at 0:01
@oystein, while this site is a great place to ask about an algorithm - it's less good of a place to ask about specific code. That said (without looking at the code), #ifdef is normally used for conditional compilation. If you're working with "good" code, it's probably there to save space/time rather than the opposite (and could even have an effect of the correctness of the code). – Daniel Apon Nov 6 '10 at 1:21
Padding is taken care of by the API. If you use the API, you don't have to pad - it probably won't hurt, but there's no reason for you to. – Yuval Filmus Nov 6 '10 at 23:06
Re the OBSOLETE parts, I don't know why it's there, but this code doesn't get compiled, so all that happens when you remove it is that the source code gets smaller; the compiled code remains the same. – Yuval Filmus Nov 6 '10 at 23:06