# New HMAC-DRBG and SHA-2 Libraries

07 Oct 2024Just FYI, I’ve dropped a few simple libraries supporting SHA-{256,512}, HMAC-SHA{256, 512}, and HMAC-DRBG. You can find the repos here:

Each is packaged there as a Nix flake, and each is also available on Hackage.

This is the first battery of a series of libraries I’m writing that were primarily inspired by noble-cryptography after the death (or at least deprecation) of cryptonite. The libraries are pure, readable, concise GHC Haskell, having minimal dependencies, and aim for clarity, security, performance, and user-friendliness.

I finally got around to going through most of the famous cryptopals challenges last year and have since felt like writing some “foundational” cryptography (and cryptography-adjacent) libraries that I myself would want to use. I’d like to understand them well, test and benchmark them myself, eke out performance and UX wins where I can get them, etc. etc.

An example is found in the case of *ppad-hmac-drbg* – I want to use
this DRBG in the manner I’m accustomed to using generators from e.g.
mwc-random, in which I can utilise any PrimMonad to handle the
generator state:

```
ghci> :set -XOverloadedStrings
ghci> import qualified Crypto.DRBG.HMAC as DRBG
ghci> import qualified Crypto.Hash.SHA256 as SHA256
ghci>
ghci> let entropy = "very random"
ghci> let nonce = "very unused"
ghci> let personalization_string = "very personal"
ghci>
ghci> drbg <- DRBG.new SHA256.hmac entropy nonce personalization_string
ghci> bytes <- DRBG.gen mempty 32 drbg
ghci> more_bytes <- DRBG.gen mempty 16 drbg
```

I haven’t actually tried the other DRBG library I found on
Hackage, but it has different UX, a lot of dependencies, and has since
apparently been deprecated. The generator in *ppad-hmac-drbg* matches my
preferred UX, passes the official DRBGVS vectors, depends only
on ‘base’, ‘bytestring’, and ‘primitive’, and can be used with arbitrary
appropriate HMAC functions (and is maintained!).

The *ppad-sha256* and *ppad-sha512* libraries depend only on ‘base’ and
‘bytestring’ and are faster than any other pure Haskell SHA-2
implementations I’m aware of, even if the performance differences are
moral, rather than practical, victories:

*ppad-sha256*’s SHA-256, vs SHA’s, on a 32B input:

```
benchmarking ppad-sha256/SHA256 (32B input)/hash
time 1.898 μs (1.858 μs .. 1.941 μs)
0.997 R² (0.996 R² .. 0.999 R²)
mean 1.874 μs (1.856 μs .. 1.902 μs)
std dev 75.90 ns (60.30 ns .. 101.8 ns)
variance introduced by outliers: 55% (severely inflated)
benchmarking SHA/SHA256 (32B input)/sha256
time 2.929 μs (2.871 μs .. 2.995 μs)
0.997 R² (0.995 R² .. 0.998 R²)
mean 2.879 μs (2.833 μs .. 2.938 μs)
std dev 170.4 ns (130.4 ns .. 258.9 ns)
variance introduced by outliers: 71% (severely inflated)
```

And the same, but now a HMAC-SHA256 battle:

```
benchmarking ppad-sha256/HMAC-SHA256 (32B input)/hmac
time 7.287 μs (7.128 μs .. 7.424 μs)
0.996 R² (0.995 R² .. 0.998 R²)
mean 7.272 μs (7.115 μs .. 7.455 μs)
std dev 565.2 ns (490.9 ns .. 689.7 ns)
variance introduced by outliers: 80% (severely inflated)
benchmarking SHA/HMAC-SHA256 (32B input)/hmacSha256
time 11.42 μs (11.09 μs .. 11.80 μs)
0.994 R² (0.992 R² .. 0.997 R²)
mean 11.36 μs (11.09 μs .. 11.61 μs)
std dev 903.5 ns (766.5 ns .. 1.057 μs)
variance introduced by outliers: 79% (severely inflated)
```

The performance differential is larger on larger inputs; I think the difference between the two on a contrived 1GB input was 22 vs 32s, all on my mid-2020 MacBook Air. I haven’t bothered to implement e.g. SHA-224 and SHA-384, which are trivial adjustments of SHA-256 and SHA-512, but if anyone could use them for some reason, please just let me know.

Anyway: enjoy, and let me know if you get any use out of these. Expect more releases in this spirit!