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 (6677B)


      1 # sha256
      2 
      3 [![](https://img.shields.io/hackage/v/ppad-sha256?color=blue)](https://hackage.haskell.org/package/ppad-sha256)
      4 ![](https://img.shields.io/badge/license-MIT-brightgreen)
      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