sha512

Pure Haskell SHA-512, HMAC-SHA512 (docs.ppad.tech/sha512).
git clone git://git.ppad.tech/sha512.git
Log | Files | Refs | README | LICENSE

commit 77931566c41dd82b17b8e69970d098f6827c9301
parent ff165b29fb21b99749460ae7e3fdca42a85c822b
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 22 Jun 2025 13:16:47 +0400

lib: vertical integration

Diffstat:
MREADME.md | 48++++++++++++++++++------------------------------
Mflake.lock | 32++++++++++++++++++++++++++++++++
Mflake.nix | 16++++++++++++++--
Mppad-sha512.cabal | 2+-
Mtest/Main.hs | 25+++++++++++++++----------
5 files changed, 80 insertions(+), 43 deletions(-)

diff --git a/README.md b/README.md @@ -58,49 +58,37 @@ Haddocks (API documentation, etc.) are hosted at The aim is best-in-class performance for pure, highly-auditable Haskell code. -Current benchmark figures on my mid-2020 MacBook Air look like (use +Current benchmark figures on an M4 Silicon MacBook Air look like (use `cabal bench` to run the benchmark suite): ``` benchmarking ppad-sha512/SHA512 (32B input)/hash - time 1.116 μs (1.103 μs .. 1.130 μs) - 0.999 R² (0.999 R² .. 0.999 R²) - mean 1.142 μs (1.132 μs .. 1.154 μs) - std dev 37.35 ns (30.15 ns .. 49.36 ns) - variance introduced by outliers: 45% (moderately inflated) + time 957.1 ns (956.3 ns .. 957.7 ns) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 956.1 ns (955.6 ns .. 956.6 ns) + std dev 1.714 ns (1.436 ns .. 2.174 ns) benchmarking ppad-sha512/HMAC-SHA512 (32B input)/hmac - time 4.943 μs (4.823 μs .. 5.086 μs) - 0.997 R² (0.994 R² .. 1.000 R²) - mean 4.878 μs (4.838 μs .. 4.946 μs) - std dev 180.9 ns (105.1 ns .. 337.4 ns) - variance introduced by outliers: 48% (moderately inflated) + time 3.460 μs (3.448 μs .. 3.475 μs) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 3.474 μs (3.468 μs .. 3.478 μs) + std dev 16.60 ns (11.71 ns .. 24.66 ns) ``` -Compare this to Hackage's famous SHA package: +Compare this to Hackage's venerable SHA package: ``` benchmarking ppad-sha512/SHA512 (32B input)/SHA.sha512 - time 2.371 μs (2.350 μs .. 2.401 μs) - 0.999 R² (0.999 R² .. 1.000 R²) - mean 2.422 μs (2.403 μs .. 2.443 μs) - std dev 69.84 ns (51.04 ns .. 114.0 ns) - variance introduced by outliers: 37% (moderately inflated) + time 1.437 μs (1.436 μs .. 1.437 μs) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 1.440 μs (1.435 μs .. 1.452 μs) + std dev 23.57 ns (11.13 ns .. 43.08 ns) benchmarking ppad-sha512/HMAC-SHA512 (32B input)/SHA.hmacSha512 - time 8.832 μs (8.714 μs .. 8.976 μs) - 0.999 R² (0.998 R² .. 1.000 R²) - mean 8.911 μs (8.834 μs .. 9.006 μs) - std dev 278.9 ns (215.8 ns .. 365.1 ns) - variance introduced by outliers: 37% (moderately inflated) -``` - -Or the relevant SHA-512-based functions from a library with similar -aims, [noble-hashes][noble] (though with no HMAC-SHA512 benchmark -available): - -``` -SHA512 32B x 217,296 ops/sec @ 4μs/op ± 2.00% (min: 3μs, max: 20ms) + time 5.164 μs (5.162 μs .. 5.166 μs) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 5.159 μs (5.157 μs .. 5.161 μs) + std dev 6.522 ns (5.534 ns .. 7.662 ns) ``` ## Security diff --git a/flake.lock b/flake.lock @@ -34,6 +34,37 @@ "type": "github" } }, + "ppad-base16": { + "inputs": { + "flake-utils": [ + "ppad-base16", + "ppad-nixpkgs", + "flake-utils" + ], + "nixpkgs": [ + "ppad-base16", + "ppad-nixpkgs", + "nixpkgs" + ], + "ppad-nixpkgs": [ + "ppad-nixpkgs" + ] + }, + "locked": { + "lastModified": 1741625558, + "narHash": "sha256-ZBDXRD5fsVqA5bGrAlcnhiu67Eo50q0M9614nR3NBwY=", + "ref": "master", + "rev": "fb63457f2e894eda28250dfe65d0fcd1d195ac2f", + "revCount": 24, + "type": "git", + "url": "git://git.ppad.tech/base16.git" + }, + "original": { + "ref": "master", + "type": "git", + "url": "git://git.ppad.tech/base16.git" + } + }, "ppad-nixpkgs": { "inputs": { "flake-utils": "flake-utils", @@ -64,6 +95,7 @@ "ppad-nixpkgs", "nixpkgs" ], + "ppad-base16": "ppad-base16", "ppad-nixpkgs": "ppad-nixpkgs" } }, diff --git a/flake.nix b/flake.nix @@ -7,11 +7,18 @@ url = "git://git.ppad.tech/nixpkgs.git"; ref = "master"; }; + ppad-base16 = { + type = "git"; + url = "git://git.ppad.tech/base16.git"; + ref = "master"; + inputs.ppad-nixpkgs.follows = "ppad-nixpkgs"; + }; flake-utils.follows = "ppad-nixpkgs/flake-utils"; nixpkgs.follows = "ppad-nixpkgs/nixpkgs"; }; - outputs = { self, nixpkgs, flake-utils, ppad-nixpkgs }: + outputs = { self, nixpkgs, flake-utils, ppad-nixpkgs + , ppad-base16 }: flake-utils.lib.eachDefaultSystem (system: let lib = "ppad-sha512"; @@ -19,8 +26,13 @@ pkgs = import nixpkgs { inherit system; }; hlib = pkgs.haskell.lib; + base16 = ppad-base16.packages.${system}.default; + hpkgs = pkgs.haskell.packages.ghc981.extend (new: old: { - ${lib} = old.callCabal2nixWithOptions lib ./. "--enable-profiling" {}; + ppad-base16 = base16; + ${lib} = old.callCabal2nixWithOptions lib ./. "--enable-profiling" { + ppad-base16 = new.ppad-base16; + }; }); cc = pkgs.stdenv.cc; diff --git a/ppad-sha512.cabal b/ppad-sha512.cabal @@ -43,8 +43,8 @@ test-suite sha512-tests build-depends: aeson , base - , base16-bytestring , bytestring + , ppad-base16 , ppad-sha512 , tasty , tasty-hunit diff --git a/test/Main.hs b/test/Main.hs @@ -36,11 +36,16 @@ execute_group W.MacTestGroup {..} = where msg = "keysize " <> show mtg_keySize <> ", tagsize " <> show mtg_tagSize +decodeLenient :: BS.ByteString -> BS.ByteString +decodeLenient bs = case B16.decode bs of + Nothing -> error "bang" + Just b -> b + execute :: Int -> W.MacTest -> TestTree execute tag_size W.MacTest {..} = testCase t_msg $ do - let key = B16.decodeLenient (TE.encodeUtf8 mt_key) - msg = B16.decodeLenient (TE.encodeUtf8 mt_msg) - pec = B16.decodeLenient (TE.encodeUtf8 mt_tag) + let key = decodeLenient (TE.encodeUtf8 mt_key) + msg = decodeLenient (TE.encodeUtf8 mt_msg) + pec = decodeLenient (TE.encodeUtf8 mt_tag) out = BS.take bytes (SHA512.hmac key msg) if mt_result == "invalid" then assertBool "invalid" (pec /= out) @@ -149,7 +154,7 @@ hv5_pec = "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d77be6 -- https://datatracker.ietf.org/doc/html/rfc4231#section-4.1 hmv1_key :: BS.ByteString -hmv1_key = B16.decodeLenient "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" +hmv1_key = decodeLenient "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" hmv1_put :: BS.ByteString hmv1_put = "Hi There" @@ -167,25 +172,25 @@ hmv2_pec :: BS.ByteString hmv2_pec = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737" hmv3_key :: BS.ByteString -hmv3_key = B16.decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +hmv3_key = decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" hmv3_put :: BS.ByteString -hmv3_put = B16.decodeLenient "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" +hmv3_put = decodeLenient "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" hmv3_pec :: BS.ByteString hmv3_pec = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb" hmv4_key :: BS.ByteString -hmv4_key = B16.decodeLenient "0102030405060708090a0b0c0d0e0f10111213141516171819" +hmv4_key = decodeLenient "0102030405060708090a0b0c0d0e0f10111213141516171819" hmv4_put :: BS.ByteString -hmv4_put = B16.decodeLenient "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" +hmv4_put = decodeLenient "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" hmv4_pec :: BS.ByteString hmv4_pec = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd" hmv5_key :: BS.ByteString -hmv5_key = B16.decodeLenient "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c" +hmv5_key = decodeLenient "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c" hmv5_put :: BS.ByteString hmv5_put = "Test With Truncation" @@ -194,7 +199,7 @@ hmv5_pec :: BS.ByteString hmv5_pec = "415fad6271580a531d4179bc891d87a6" hmv6_key :: BS.ByteString -hmv6_key = B16.decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +hmv6_key = decodeLenient "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" hmv6_put :: BS.ByteString hmv6_put = "Test Using Larger Than Block-Size Key - Hash Key First"