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