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