README.md (7220B)
1 # ppad-sha256 2 3 A pure Haskell implementation of SHA-256 and HMAC-SHA256 on strict and 4 lazy ByteStrings, as specified by RFC's [6234][r6234] and [2104][r2104]. 5 6 ## Usage 7 8 A sample GHCi session: 9 10 ``` 11 > :set -XOverloadedStrings 12 > 13 > -- import qualified 14 > import qualified Crypto.Hash.SHA256 as SHA256 15 > 16 > -- 'hash' and 'hmac' operate on strict bytestrings 17 > 18 > let hash_s = SHA256.hash "strict bytestring input" 19 > let hmac_s = SHA256.hmac "strict secret" "strict bytestring input" 20 > 21 > -- 'hash_lazy' and 'hmac_lazy' operate on lazy bytestrings 22 > -- but note that the key for HMAC is always strict 23 > 24 > let hash_l = SHA256.hash_lazy "lazy bytestring input" 25 > let hmac_l = SHA256.hmac_lazy "strict secret" "lazy bytestring input" 26 > 27 > -- results are always unformatted 256-bit (32-byte) strict bytestrings 28 > 29 > import qualified Data.ByteString as BS 30 > 31 > BS.take 10 hash_s 32 "1\223\152Ha\USB\171V\a" 33 > BS.take 10 hmac_l 34 "\DELSOk\180\242\182'v\187" 35 > 36 > -- you can use third-party libraries for rendering if needed 37 > -- e.g., using base16-bytestring: 38 > 39 > import qualified Data.ByteString.Base16 as B16 40 > 41 > B16.encode hash_s 42 "31df9848611f42ab5607ea9e6de84b05d5259085abb30a7917d85efcda42b0e3" 43 > B16.encode hmac_l 44 "7f534f6bb4f2b62776bba3d6466e384505f2ff89c91f39800d7a0d4623a4711e" 45 ``` 46 47 ## Documentation 48 49 Haddocks (API documentation, etc.) are hosted at 50 [docs.ppad.tech/sha256][hadoc]. 51 52 ## Performance 53 54 The aim is best-in-class performance for pure, highly-auditable Haskell 55 code. 56 57 Current benchmark figures on my mid-2020 MacBook Air look like (use 58 `cabal bench` to run the benchmark suite): 59 60 ``` 61 benchmarking ppad-sha256/SHA256 (32B input)/hash 62 time 1.898 μs (1.858 μs .. 1.941 μs) 63 0.997 R² (0.996 R² .. 0.999 R²) 64 mean 1.874 μs (1.856 μs .. 1.902 μs) 65 std dev 75.90 ns (60.30 ns .. 101.8 ns) 66 variance introduced by outliers: 55% (severely inflated) 67 68 benchmarking ppad-sha256/SHA256 (32B input)/hash_lazy 69 time 1.930 μs (1.891 μs .. 1.967 μs) 70 0.998 R² (0.997 R² .. 0.999 R²) 71 mean 1.912 μs (1.885 μs .. 1.947 μs) 72 std dev 104.6 ns (77.34 ns .. 162.6 ns) 73 variance introduced by outliers: 69% (severely inflated) 74 75 benchmarking ppad-sha256/HMAC-SHA256 (32B input)/hmac 76 time 7.287 μs (7.128 μs .. 7.424 μs) 77 0.996 R² (0.995 R² .. 0.998 R²) 78 mean 7.272 μs (7.115 μs .. 7.455 μs) 79 std dev 565.2 ns (490.9 ns .. 689.7 ns) 80 variance introduced by outliers: 80% (severely inflated) 81 82 benchmarking ppad-sha256/HMAC-SHA256 (32B input)/hmac_lazy 83 time 7.231 μs (7.102 μs .. 7.393 μs) 84 0.996 R² (0.994 R² .. 0.998 R²) 85 mean 7.436 μs (7.290 μs .. 7.621 μs) 86 std dev 559.2 ns (446.3 ns .. 732.7 ns) 87 variance introduced by outliers: 78% (severely inflated) 88 ``` 89 90 Compare this to Hackage's famous SHA package: 91 92 ``` 93 benchmarking ppad-sha256/SHA256 (32B input)/SHA.sha256 94 time 2.929 μs (2.871 μs .. 2.995 μs) 95 0.997 R² (0.995 R² .. 0.998 R²) 96 mean 2.879 μs (2.833 μs .. 2.938 μs) 97 std dev 170.4 ns (130.4 ns .. 258.9 ns) 98 variance introduced by outliers: 71% (severely inflated) 99 100 benchmarking ppad-sha256/HMAC-SHA256 (32B input)/SHA.hmacSha256 101 time 11.42 μs (11.09 μs .. 11.80 μs) 102 0.994 R² (0.992 R² .. 0.997 R²) 103 mean 11.36 μs (11.09 μs .. 11.61 μs) 104 std dev 903.5 ns (766.5 ns .. 1.057 μs) 105 variance introduced by outliers: 79% (severely inflated) 106 ``` 107 108 Or the relevant SHA-256-based functions from a library with similar 109 aims, [noble-hashes][noble]: 110 111 ``` 112 SHA256 32B x 420,875 ops/sec @ 2μs/op ± 1.33% (min: 1μs, max: 3ms) 113 HMAC-SHA256 32B x 97,304 ops/sec @ 10μs/op 114 ``` 115 116 When reading a 1GB input from disk and testing it with `hash_lazy`, we 117 get statistics like the following: 118 119 ``` 120 2,310,899,616 bytes allocated in the heap 121 93,800 bytes copied during GC 122 78,912 bytes maximum residency (2 sample(s)) 123 35,776 bytes maximum slop 124 10 MiB total memory in use (0 MiB lost due to fragmentation) 125 126 Tot time (elapsed) Avg pause Max pause 127 Gen 0 295 colls, 0 par 0.007s 0.008s 0.0000s 0.0001s 128 Gen 1 2 colls, 0 par 0.000s 0.001s 0.0004s 0.0004s 129 130 INIT time 0.003s ( 0.003s elapsed) 131 MUT time 22.205s ( 22.260s elapsed) 132 GC time 0.007s ( 0.009s elapsed) 133 EXIT time 0.000s ( 0.001s elapsed) 134 Total time 22.216s ( 22.273s elapsed) 135 136 %GC time 0.0% (0.0% elapsed) 137 138 Alloc rate 104,073,382 bytes per MUT second 139 140 Productivity 100.0% of total user, 99.9% of total elapsed 141 ``` 142 143 SHA.sha256 gets more like: 144 145 ``` 146 74,403,596,936 bytes allocated in the heap 147 12,971,992 bytes copied during GC 148 79,176 bytes maximum residency (2 sample(s)) 149 35,512 bytes maximum slop 150 6 MiB total memory in use (0 MiB lost due to fragmentation) 151 152 Tot time (elapsed) Avg pause Max pause 153 Gen 0 17883 colls, 0 par 0.103s 0.148s 0.0000s 0.0001s 154 Gen 1 2 colls, 0 par 0.000s 0.000s 0.0002s 0.0003s 155 156 INIT time 0.006s ( 0.006s elapsed) 157 MUT time 32.367s ( 32.408s elapsed) 158 GC time 0.104s ( 0.149s elapsed) 159 EXIT time 0.000s ( 0.001s elapsed) 160 Total time 32.477s ( 32.563s elapsed) 161 162 %GC time 0.0% (0.0% elapsed) 163 164 Alloc rate 2,298,740,250 bytes per MUT second 165 166 Productivity 99.7% of total user, 99.5% of total elapsed 167 ``` 168 169 ## Security 170 171 This library aims at the maximum security achievable in a 172 garbage-collected language under an optimizing compiler such as GHC, in 173 which strict constant-timeness can be challenging to achieve. 174 175 The HMAC-SHA256 functions within pass all [Wycheproof vectors][wyche], 176 as well as various other useful unit test vectors found around the 177 internet. 178 179 If you discover any vulnerabilities, please disclose them via 180 security@ppad.tech. 181 182 ## Development 183 184 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a 185 development shell with: 186 187 ``` 188 $ nix develop 189 ``` 190 191 Then do e.g.: 192 193 ``` 194 $ cabal repl ppad-sha256 195 ``` 196 197 to get a REPL for the main library. 198 199 ## Attribution 200 201 This implementation has benefitted immensely from the [SHA][hacka] 202 package available on Hackage, which was used as a reference during 203 development. Many parts wound up being direct translations. 204 205 [nixos]: https://nixos.org/ 206 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html 207 [hadoc]: https://docs.ppad.tech/sha256 208 [hacka]: https://hackage.haskell.org/package/SHA 209 [r6234]: https://datatracker.ietf.org/doc/html/rfc6234 210 [r2104]: https://datatracker.ietf.org/doc/html/rfc2104 211 [noble]: https://github.com/paulmillr/noble-hashes 212 [wyche]: https://github.com/C2SP/wycheproof