README.md (6769B)
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 my mid-2020 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 1.387 μs (1.365 μs .. 1.409 μs) 67 0.999 R² (0.998 R² .. 1.000 R²) 68 mean 1.386 μs (1.378 μs .. 1.399 μs) 69 std dev 34.07 ns (24.55 ns .. 52.14 ns) 70 variance introduced by outliers: 31% (moderately inflated) 71 72 benchmarking ppad-sha256/HMAC-SHA256 (32B input)/hmac 73 time 5.618 μs (5.564 μs .. 5.681 μs) 74 0.999 R² (0.999 R² .. 1.000 R²) 75 mean 5.648 μs (5.603 μs .. 5.697 μs) 76 std dev 159.1 ns (130.7 ns .. 194.1 ns) 77 variance introduced by outliers: 34% (moderately inflated) 78 ``` 79 80 Compare this to Hackage's famous SHA package: 81 82 ``` 83 benchmarking ppad-sha256/SHA256 (32B input)/SHA.sha256 84 time 2.585 μs (2.565 μs .. 2.613 μs) 85 0.999 R² (0.999 R² .. 1.000 R²) 86 mean 2.635 μs (2.616 μs .. 2.654 μs) 87 std dev 68.00 ns (58.45 ns .. 80.94 ns) 88 variance introduced by outliers: 32% (moderately inflated) 89 90 benchmarking ppad-sha256/HMAC-SHA256 (32B input)/SHA.hmacSha256 91 time 9.672 μs (9.533 μs .. 9.810 μs) 92 0.998 R² (0.998 R² .. 0.999 R²) 93 mean 9.715 μs (9.608 μs .. 9.858 μs) 94 std dev 394.7 ns (315.3 ns .. 576.2 ns) 95 variance introduced by outliers: 50% (moderately inflated) 96 97 ``` 98 99 Or the relevant SHA-256-based functions from a library with similar 100 aims, [noble-hashes][noble]: 101 102 ``` 103 SHA256 32B x 420,875 ops/sec @ 2μs/op ± 1.33% (min: 1μs, max: 3ms) 104 HMAC-SHA256 32B x 97,304 ops/sec @ 10μs/op 105 ``` 106 107 When reading a 1GB input from disk and testing it with `hash_lazy`, we 108 get statistics like the following: 109 110 ``` 111 2,310,899,616 bytes allocated in the heap 112 93,800 bytes copied during GC 113 78,912 bytes maximum residency (2 sample(s)) 114 35,776 bytes maximum slop 115 10 MiB total memory in use (0 MiB lost due to fragmentation) 116 117 Tot time (elapsed) Avg pause Max pause 118 Gen 0 295 colls, 0 par 0.007s 0.008s 0.0000s 0.0001s 119 Gen 1 2 colls, 0 par 0.000s 0.001s 0.0004s 0.0004s 120 121 INIT time 0.003s ( 0.003s elapsed) 122 MUT time 22.205s ( 22.260s elapsed) 123 GC time 0.007s ( 0.009s elapsed) 124 EXIT time 0.000s ( 0.001s elapsed) 125 Total time 22.216s ( 22.273s elapsed) 126 127 %GC time 0.0% (0.0% elapsed) 128 129 Alloc rate 104,073,382 bytes per MUT second 130 131 Productivity 100.0% of total user, 99.9% of total elapsed 132 ``` 133 134 SHA.sha256 gets more like: 135 136 ``` 137 74,403,596,936 bytes allocated in the heap 138 12,971,992 bytes copied during GC 139 79,176 bytes maximum residency (2 sample(s)) 140 35,512 bytes maximum slop 141 6 MiB total memory in use (0 MiB lost due to fragmentation) 142 143 Tot time (elapsed) Avg pause Max pause 144 Gen 0 17883 colls, 0 par 0.103s 0.148s 0.0000s 0.0001s 145 Gen 1 2 colls, 0 par 0.000s 0.000s 0.0002s 0.0003s 146 147 INIT time 0.006s ( 0.006s elapsed) 148 MUT time 32.367s ( 32.408s elapsed) 149 GC time 0.104s ( 0.149s elapsed) 150 EXIT time 0.000s ( 0.001s elapsed) 151 Total time 32.477s ( 32.563s elapsed) 152 153 %GC time 0.0% (0.0% elapsed) 154 155 Alloc rate 2,298,740,250 bytes per MUT second 156 157 Productivity 99.7% of total user, 99.5% of total elapsed 158 ``` 159 160 ## Security 161 162 This library aims at the maximum security achievable in a 163 garbage-collected language under an optimizing compiler such as GHC, in 164 which strict constant-timeness can be challenging to achieve. 165 166 The HMAC-SHA256 functions within pass all [Wycheproof vectors][wyche], 167 as well as various other useful unit test vectors found around the 168 internet. 169 170 If you discover any vulnerabilities, please disclose them via 171 security@ppad.tech. 172 173 ## Development 174 175 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a 176 development shell with: 177 178 ``` 179 $ nix develop 180 ``` 181 182 Then do e.g.: 183 184 ``` 185 $ cabal repl ppad-sha256 186 ``` 187 188 to get a REPL for the main library. 189 190 ## Attribution 191 192 This implementation has benefitted immensely from the [SHA][hacka] 193 package available on Hackage, which was used as a reference during 194 development. Many parts wound up being direct translations. 195 196 [nixos]: https://nixos.org/ 197 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html 198 [hadoc]: https://docs.ppad.tech/sha256 199 [hacka]: https://hackage.haskell.org/package/SHA 200 [r6234]: https://datatracker.ietf.org/doc/html/rfc6234 201 [r2104]: https://datatracker.ietf.org/doc/html/rfc2104 202 [noble]: https://github.com/paulmillr/noble-hashes 203 [wyche]: https://github.com/C2SP/wycheproof