Navigation

Native hash functions for Java

This is a library of popular cryptographic hash functions implemented in pure Java, along with speed-optimized versions in C, x86 assembly, and x86-64 assembly. The Java Native Interface (JNI) is used to achieve this functionality.

Benchmark

Hash function

Java speed

C speed (-O0)

C speed (-O1)

x86-64 speed

Native speedup

MD2

9.42 MiB/s

3.55 MiB/s

4.74 MiB/s

0.50×

MD4

249 MiB/s

145 MiB/s

452 MiB/s

576 MiB/s

2.31×

MD5

187 MiB/s

108 MiB/s

326 MiB/s

418 MiB/s

2.24×

RIPEMD-128

148 MiB/s

63 MiB/s

197 MiB/s

190 MiB/s

1.33×

RIPEMD-160

59 MiB/s

49 MiB/s

139 MiB/s

134 MiB/s

2.36×

RIPEMD-256

139 MiB/s

111 MiB/s

265 MiB/s

1.91×

RIPEMD-320

65 MiB/s

83 MiB/s

196 MiB/s

3.02×

SHA-1

123 MiB/s

110 MiB/s

305 MiB/s

322 MiB/s

2.62×

SHA-224

78 MiB/s

71 MiB/s

118 MiB/s

131 MiB/s

1.68×

SHA-256

78 MiB/s

71 MiB/s

118 MiB/s

131 MiB/s

1.68×

SHA-384

106 MiB/s

106 MiB/s

176 MiB/s

200 MiB/s

1.89×

SHA-512

106 MiB/s

106 MiB/s

176 MiB/s

200 MiB/s

1.89×

Tiger

145 MiB/s

136 MiB/s

284 MiB/s

1.96×

Tiger2

145 MiB/s

136 MiB/s

284 MiB/s

1.96×

Whirlpool

23.7 MiB/s

11.3 MiB/s

44.2 MiB/s

61.5 MiB/s

2.59×

Higher GCC optimization levels than -O1 produced slower C code in most cases. For -O2 and -O3, most algorithms were a few percent slower, SHA-1 was 25% slower, RIPEMD-320 achieved 199 MiB/s at -O2, and Whirlpool achieved 46.7 MiB/s at -O3. As for the x86-64 code, testing was done at -O1, which was about a percent faster than -O0 (no meaningful difference).

These implement the non-speed-critical parts of the hash function such as initialization, block accumulation, final padding, and hash value serialization. The compression function can be either pure Java or native code.

These implement the speed-critical compression function of each hash function in pure Java. These classes provide full hashing capability on any platform (even if the C or assembly code is ignored), a reference implementation to check that the native implementation produces the same values, and a comparison point for speed benchmarking (to see how much gain the native code yields).

These test suites include test vectors (known input-output values) for each hash function, as well as generic tests for block splitting equivalence and Java vs. native implementation value checking. For production usage with native code only, the entire java/test directory can be disregarded.

These implement the interfacing and data conversion between Java data types and native data types. The Java code calls into these native functions, which do some processing before calling the native compression functions.

These implement the hash function’s main compression function in C code (good for any platform), or assembly code for the x86 or x86-64 CPU instruction sets. For the x86/x86-64 code, SSE2 is often required.

Instructions

To build and run the code, follow these steps (Linux only, not supported on Windows):

Download the full ZIP archive from the repository and unpack it.

Compile the Java classes as per the normal procedure, using javac, your favorite Java build system, or by setting up a project in an IDE. Compiling the runnable main test classes (all the *Test.java files) is optional.

For the native code, choose whether you want to use the hash compression functions from C, x86, or x86-64.

If using the C compression functions, then invoke the C compiler with a command like this (split into multiple lines for clarity):

Notes

This was my first project involving the use of JNI, and it turned out quite well. After the initial hurdle of configuring paths and stuff properly and understanding the usage model, everything was a matter of straightforward effort (not much guessing, cleverness, or debugging).

In the interest of simplicity, this project loses a number of nice features implemented in the Project79068 Cryptography Library, such as: Separate objects for hash functions vs. hashers, convenience function for hashing a byte array without explicitly creating a hasher, smart hash value objects that can be compared to each other and serialized as bytes and serialized as hexadecimal strings, unification of certain hashers that have very similar implementation details.