sha256

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

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