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


      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 [![](https://img.shields.io/badge/haddock-sha256-lightblue)](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