Qt Cryptographic Hash Classes

Here are five simple classes wrapping the NIST competition finalists for the new SHA-3 (Secure Hash Algorithm), also known as the SHA-3-Zoo: Blake, Grøstl, JH, Keccak and Skein.

Most hash functions in use today are arguably MD5 and an older SHA, i.e. SHA-1, SHA-256, etc. (MD4, MD5 and SHA-1 are available in the Qt library via QCryptographicHash.)
However, these algorithms have quite a history and thus some security issues, especially MD5 can be considered broken – It’s frightening to see how many applications still use it. Developing new applications, It’s best to use one of the modern versions. They all have been tested for years by cryptography experts, and one of the five algorithms will become the new SHA-3.

The Hash Classes for Qt

My wrapper classes QBlakeHash, QGrostlHash, QJHHash, QKeccakHash and QSkeinHash all share an identical interface, so when you build your application, say with QSkeinHash, and you later want to switch to Grøstl, you just replace “QSkeinHash” with “QGrostlHash” in your code and that’s it. All algorithms provide 256 bit and 512 bit hashes, some even more (see below).

The interface is extremely easy, yet versatile.

First, you construct the object, then you pass the data that shall be hashed, then you call the “to(…)” functions to get the hash. As an example, we’ll use QGrostlHash for explanation of the API. (Note: In this section we don’t tell the class what hash length to calculate, so it defaults to 256 bit):

Hashing a string

QString hash = QGrostlHash("hello").toHexString();

This will calculate the grøstl-256 of “hello” and put the resulting hash in hex-representation into the string hash.
If you wanted the hex-representation as QByteArray, you could also have called toHex(). If you wanted the raw 32-byte (=256/8) QByteArray of the hash, call toRaw().

Hashing a file

QGrostlHash gh;
gh.file("/home/dermanu/sources.tar.gz");
QString hash = gh.toHexString();

This will successively load 8-kilobyte chunks from the file and progressively calculate the grostl-256 hash of the entire file. The result will be the same as loading the whole file to memory and calculating the hash at once, but will obviously consume far less memory.
The file() function returns a bool value which indicates whether the hashing was successful or not. (If not, the file probably doesn’t exist or we don’t have read permissions.)

Hashing arbitrary data

QGrostlHash gh;
gh.startBatch();

now you call as often as needed

gh.putBatch(data);

and finally, you call

gh.stopBatch();
QString hash = gh.toHexString();

where data is a QByteArray or a char*. In the latter case, you must pass the number of bytes to read from the char-array as second parameter.
This way, you can split up the possibly large chunk of data into smaller parts, allowing progressive hash calculation, for example when the data is being generated on-the-fly or being downloaded. Or of course, when you just want to keep memory consumption low, and maybe display a progress bar – It wouldn’t be smart to hash 16 gigabytes of data in one run.

That’s the gist of it.

Slightly more advanced usage

As mentioned in the introduction, the algorithms don’t only provide 256 bit hashes but at least also a 512 bit version. All three methods of generating a hash (On a string via constructor, on a file via file(), on arbitrary data via startBatch()) take an additional parameter hashBits which is an enum of type QGrostlHash::HashBits (in the Grøstl case). This enum contains the specific lengths the algorithm supports. These are:

Blake: hb224, hb256, hb384, hb512
Grøstl: hb256, hb512
JH: hb224, hb256, hb384, hb512
Keccak: hb224, hb256, hb384, hb512
Skein: hb256, hb512, hb1024

So let’s calculate the skein-1024 of a file and get it as raw byte array:

QSkeinHash sh;
sh.file("/home/dermanu/a_file", QSkeinHash::hb1024);
QByteArray rawHash = sh.toRaw();

Since we’ve requested a hash of length 1024 bits, the byte array rawHash will have a size of 128 bytes now.

Let’s calculate the Keccak-224 of the string “workslikeclockwork”, as a hex-string:

QString hash = QKeccakHash("workslikeclockwork", QKeccakHash::hb224).toHexString();

The string hash now has a length of 56. Why? Well we’ve calculated a hash of length 224 bits, those are 28 bytes. Since we’ve requested those 28 bytes to be transformed into a human-readable hexadecimal representation, and one byte (which can have 256 states) translates to two hexadecimal digits (each can have 16 states, so 16*16 is 256), we receive 28*2 = 56 hexadecimal digits, i.e. characters in the resulting string.

Setup

  • Get the latest version of the hash classes you want from the download section at the bottom of this page.
  • Use the q(…)hash.h and .cpp file like any other ordinary class file
  • Make sure the (…)impl.cpp file is in the same directory as the .h/.cpp pair

For QtCreator users

  • Right click on your project folder in the left sidebar, click the “Add existing file” (or similar) menu item. In the appearing file dialog, select the q(…)hash.h and .cpp file, to add them to your project

The file named (…)impl.cpp (e.g. grostlimpl.cpp) carries the actual implementation by the inventors of the respective algorithm, slightly modified by me to compile with C++ and the API. You don’t need to do anything with it, except make sure it’s in the same directory as the actual class files q(…)hash.h and q(…)hash.cpp (i.e. don’t build an object from it, q(…)hash.cpp includes it directly).

Download

The packages also contain a small console application which demonstrates the usage of the class, in the example directory.

Blake hash class: qblakehash.tar.gz
Grøstl hash class: qgrostlhash.tar.gz
JH hash class qjhhash.tar.gz
Keccak hash class: qkeccakhash.tar.gz
Skein hash class: qskeinhash.tar.gz
Release date: 12.01.2012

2 comments on “Qt Cryptographic Hash Classes

  1. Hi, very impressive, neat and useful work you have done. Have you consider (if not already done) contributing this code to Qt Project (http://qt-project.org/contribute). I guess it would be great to have these function built-in any future Qt release (i.e. 5.2) so the developers using the framework easily can abandon the bad habit of still using MD5…

    Thanks.
    Pablo