sha256

Pure Haskell SHA-256, HMAC-SHA256 (docs.ppad.tech/sha256).
git clone git://git.ppad.tech/sha256.git
Log | Files | Refs | README | LICENSE

commit 282fa90825bbc04c324c58186da473cb380d0fc2
parent ab0957e305dff0243dcab11e381470585849fd20
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 22 Jun 2025 13:12:10 +0400

lib: vertical integration

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

diff --git a/README.md b/README.md @@ -58,42 +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-sha256/SHA256 (32B input)/hash - time 1.387 μs (1.365 μs .. 1.409 μs) - 0.999 R² (0.998 R² .. 1.000 R²) - mean 1.386 μs (1.378 μs .. 1.399 μs) - std dev 34.07 ns (24.55 ns .. 52.14 ns) - variance introduced by outliers: 31% (moderately inflated) + time 879.7 ns (879.5 ns .. 879.9 ns) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 880.1 ns (879.5 ns .. 882.1 ns) + std dev 3.504 ns (994.6 ps .. 7.537 ns) benchmarking ppad-sha256/HMAC-SHA256 (32B input)/hmac - time 5.618 μs (5.564 μs .. 5.681 μs) - 0.999 R² (0.999 R² .. 1.000 R²) - mean 5.648 μs (5.603 μs .. 5.697 μs) - std dev 159.1 ns (130.7 ns .. 194.1 ns) - variance introduced by outliers: 34% (moderately inflated) + time 3.322 μs (3.322 μs .. 3.322 μs) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 3.321 μs (3.317 μs .. 3.323 μs) + std dev 10.53 ns (4.987 ns .. 19.12 ns) ``` -Compare this to Hackage's famous SHA package: +Compare this to Hackage's venerable SHA package: ``` benchmarking ppad-sha256/SHA256 (32B input)/SHA.sha256 - time 2.585 μs (2.565 μs .. 2.613 μs) - 0.999 R² (0.999 R² .. 1.000 R²) - mean 2.635 μs (2.616 μs .. 2.654 μs) - std dev 68.00 ns (58.45 ns .. 80.94 ns) - variance introduced by outliers: 32% (moderately inflated) + time 1.415 μs (1.414 μs .. 1.415 μs) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 1.415 μs (1.415 μs .. 1.415 μs) + std dev 1.334 ns (1.158 ns .. 1.576 ns) benchmarking ppad-sha256/HMAC-SHA256 (32B input)/SHA.hmacSha256 - time 9.672 μs (9.533 μs .. 9.810 μs) - 0.998 R² (0.998 R² .. 0.999 R²) - mean 9.715 μs (9.608 μs .. 9.858 μs) - std dev 394.7 ns (315.3 ns .. 576.2 ns) - variance introduced by outliers: 50% (moderately inflated) - + time 5.157 μs (5.156 μs .. 5.158 μs) + 1.000 R² (1.000 R² .. 1.000 R²) + mean 5.158 μs (5.157 μs .. 5.159 μs) + std dev 2.947 ns (2.413 ns .. 3.606 ns) ``` Or the relevant SHA-256-based functions from a library with similar 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-sha256"; @@ -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-sha256.cabal b/ppad-sha256.cabal @@ -43,8 +43,8 @@ test-suite sha256-tests build-depends: aeson , base - , base16-bytestring , bytestring + , ppad-base16 , ppad-sha256 , 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 (SHA256.hmac key msg) if mt_result == "invalid" then assertBool "invalid" (pec /= out) @@ -149,7 +154,7 @@ hv5_pec = "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e" -- 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 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" 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 = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe" 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 = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b" 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 = "a3b6167473100ee06e0c796c2955552b" 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"