secp256k1

Pure Haskell Schnorr, ECDSA on the elliptic curve secp256k1 (docs.ppad.tech/secp256k1).
git clone git://git.ppad.tech/secp256k1.git
Log | Files | Refs | README | LICENSE

README.md (9321B)


      1 # secp256k1
      2 
      3 [![](https://img.shields.io/hackage/v/ppad-secp256k1?color=blue)](https://hackage.haskell.org/package/ppad-secp256k1)
      4 ![](https://img.shields.io/badge/license-MIT-brightgreen)
      5 [![](https://img.shields.io/badge/haddock-secp256k1-lightblue)](https://docs.ppad.tech/secp256k1)
      6 
      7 A pure Haskell implementation of [BIP0340][bp340] Schnorr signatures,
      8 deterministic [RFC6979][r6979] ECDSA (with [BIP0146][bp146]-style
      9 "low-S" signatures), ECDH, and various primitives on the elliptic curve
     10 secp256k1.
     11 
     12 (See also [ppad-csecp256k1][csecp] for FFI bindings to
     13 bitcoin-core/secp256k1.)
     14 
     15 ## Usage
     16 
     17 A sample GHCi session:
     18 
     19 ```
     20   > -- pragmas and b16 import for illustration only; not required
     21   > :set -XOverloadedStrings
     22   > :set -XBangPatterns
     23   > import qualified Data.ByteString.Base16 as B16
     24   >
     25   > -- import qualified
     26   > import qualified Crypto.Curve.Secp256k1 as Secp256k1
     27   >
     28   > -- secret, public keys
     29   > let sec = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
     30   :{
     31   ghci| let Just pub = Secp256k1.parse_point . B16.decodeLenient $
     32   ghci|       "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
     33   ghci| :}
     34   >
     35   > let msg = "i approve of this message"
     36   >
     37   > -- create and verify a schnorr signature for the message
     38   > let Just sig0 = Secp256k1.sign_schnorr sec msg mempty
     39   > Secp256k1.verify_schnorr msg pub sig0
     40   True
     41   >
     42   > -- create and verify a low-S ECDSA signature for the message
     43   > let Just sig1 = Secp256k1.sign_ecdsa sec msg
     44   > Secp256k1.verify_ecdsa msg pub sig1
     45   True
     46   >
     47   > -- for faster signs (especially w/ECDSA) and verifies, use a context
     48   > let !tex = Secp256k1.precompute
     49   > Secp256k1.verify_schnorr' tex msg pub sig0
     50   True
     51 ```
     52 
     53 ## Documentation
     54 
     55 Haddocks (API documentation, etc.) are hosted at
     56 [docs.ppad.tech/secp256k1][hadoc].
     57 
     58 ## Performance
     59 
     60 The aim is best-in-class performance for pure Haskell code.
     61 
     62 Current benchmark figures on an M4 MacBook Air look like (use `cabal
     63 bench` to run the benchmark suite):
     64 
     65 ```
     66   benchmarking schnorr/sign_schnorr' (large)
     67   time                 76.57 μs   (76.46 μs .. 76.73 μs)
     68                        1.000 R²   (0.999 R² .. 1.000 R²)
     69   mean                 77.81 μs   (77.23 μs .. 79.13 μs)
     70   std dev              2.732 μs   (1.296 μs .. 5.251 μs)
     71 
     72   benchmarking schnorr/verify_schnorr'
     73   time                 112.8 μs   (112.4 μs .. 113.1 μs)
     74                        1.000 R²   (1.000 R² .. 1.000 R²)
     75   mean                 112.4 μs   (112.0 μs .. 112.8 μs)
     76   std dev              1.246 μs   (1.023 μs .. 1.554 μs)
     77 
     78   benchmarking ecdsa/sign_ecdsa' (large)
     79   time                 44.02 μs   (44.00 μs .. 44.03 μs)
     80                        1.000 R²   (1.000 R² .. 1.000 R²)
     81   mean                 43.99 μs   (43.97 μs .. 44.01 μs)
     82   std dev              58.86 ns   (46.66 ns .. 72.16 ns)
     83 
     84   benchmarking ecdsa/verify_ecdsa'
     85   time                 105.1 μs   (104.8 μs .. 105.4 μs)
     86                        1.000 R²   (1.000 R² .. 1.000 R²)
     87   mean                 104.8 μs   (104.4 μs .. 105.8 μs)
     88   std dev              2.037 μs   (1.170 μs .. 3.692 μs)
     89 
     90   benchmarking ecdh/ecdh (large)
     91   time                 138.5 μs   (137.8 μs .. 139.4 μs)
     92                        1.000 R²   (0.999 R² .. 1.000 R²)
     93   mean                 137.8 μs   (137.4 μs .. 138.4 μs)
     94   std dev              1.584 μs   (1.119 μs .. 2.541 μs)
     95 ```
     96 
     97 Ensure you compile with the 'llvm' flag (and that [ppad-fixed][fixed]
     98 and [ppad-sha256][sha256] have been compiled with the 'llvm' flag) for
     99 maximum performance.
    100 
    101 ## Security
    102 
    103 (See both a [security analysis](https://ppad.tech/security-analysis-secp256k1)
    104 and follow-up [assembly analysis](https://ppad.tech/static-assembly-analysis)
    105 of the library at [ppad.tech](https://ppad.tech).)
    106 
    107 This library aims at the maximum security achievable in a
    108 garbage-collected language under an optimizing compiler such as GHC, in
    109 which strict constant-timeness can be [challenging to achieve][const].
    110 
    111 The Schnorr implementation within has been tested against the [official
    112 BIP0340 vectors][ut340], and ECDSA and ECDH have been tested against the
    113 relevant [Wycheproof vectors][wyche] (with the former also being tested
    114 against [noble-secp256k1's][noble] vectors), so their implementations
    115 are likely to be accurate and safe from attacks targeting e.g.
    116 faulty nonce generation or malicious inputs for signature or public
    117 key parameters.
    118 
    119 Timing-sensitive operations, e.g. elliptic curve scalar multiplication,
    120 have been explicitly written so as to execute in time constant with
    121 respect to secret data. Moreover, fixed-size (256-bit) wide words with
    122 constant-time operations provided by [ppad-fixed][fixed] are used
    123 exclusively internally, avoiding timing variations incurred by use of
    124 GHC's variable-size Integer type.
    125 
    126 Criterion benchmarks attest that any timing variation between cases with
    127 differing inputs is attributable to noise:
    128 
    129 ```
    130   benchmarking derive_pub/wnaf, sk = 2
    131   time                 29.67 μs   (29.64 μs .. 29.71 μs)
    132                        1.000 R²   (1.000 R² .. 1.000 R²)
    133   mean                 29.68 μs   (29.64 μs .. 29.71 μs)
    134   std dev              121.1 ns   (83.80 ns .. 177.2 ns)
    135 
    136   benchmarking derive_pub/wnaf, sk = 2 ^ 255 - 19
    137   time                 29.68 μs   (29.65 μs .. 29.72 μs)
    138                        1.000 R²   (1.000 R² .. 1.000 R²)
    139   mean                 29.71 μs   (29.68 μs .. 29.75 μs)
    140   std dev              106.7 ns   (71.74 ns .. 174.7 ns)
    141 
    142   benchmarking schnorr/sign_schnorr' (small)
    143   time                 76.27 μs   (76.21 μs .. 76.32 μs)
    144                        1.000 R²   (1.000 R² .. 1.000 R²)
    145   mean                 76.44 μs   (76.40 μs .. 76.50 μs)
    146   std dev              162.3 ns   (123.1 ns .. 246.7 ns)
    147 
    148   benchmarking schnorr/sign_schnorr' (large)
    149   time                 76.35 μs   (76.31 μs .. 76.38 μs)
    150                        1.000 R²   (1.000 R² .. 1.000 R²)
    151   mean                 76.37 μs   (76.35 μs .. 76.40 μs)
    152   std dev              84.10 ns   (67.03 ns .. 112.7 ns)
    153 
    154   benchmarking ecdsa/sign_ecdsa' (small)
    155   time                 43.95 μs   (43.89 μs .. 44.02 μs)
    156                        1.000 R²   (1.000 R² .. 1.000 R²)
    157   mean                 44.04 μs   (43.96 μs .. 44.15 μs)
    158   std dev              310.2 ns   (259.9 ns .. 384.6 ns)
    159 
    160   benchmarking ecdsa/sign_ecdsa' (large)
    161   time                 44.02 μs   (44.00 μs .. 44.03 μs)
    162                        1.000 R²   (1.000 R² .. 1.000 R²)
    163   mean                 43.99 μs   (43.97 μs .. 44.01 μs)
    164   std dev              58.86 ns   (46.66 ns .. 72.16 ns)
    165 
    166   benchmarking ecdh/ecdh (small)
    167   time                 143.6 μs   (143.4 μs .. 143.7 μs)
    168                        1.000 R²   (1.000 R² .. 1.000 R²)
    169   mean                 143.7 μs   (143.3 μs .. 144.6 μs)
    170   std dev              2.022 μs   (846.9 ns .. 3.402 μs)
    171 
    172   benchmarking ecdh/ecdh (large)
    173   time                 143.8 μs   (143.7 μs .. 143.9 μs)
    174                        1.000 R²   (1.000 R² .. 1.000 R²)
    175   mean                 143.8 μs   (143.7 μs .. 143.9 μs)
    176   std dev              385.2 ns   (265.9 ns .. 544.5 ns)
    177 ```
    178 
    179 Note also that care has been taken to ensure that allocation is held
    180 constant across input sizes for all sensitive operations:
    181 
    182 ```
    183   derive_pub
    184 
    185     Case                     Allocated  GCs
    186     wnaf, sk = 2                   312    0
    187     wnaf, sk = 2 ^ 255 - 19        312    0
    188 
    189   schnorr
    190 
    191     Case                   Allocated  GCs
    192     sign_schnorr' (small)     14,416    0
    193     sign_schnorr' (large)     14,416    0
    194 
    195   ecdsa
    196 
    197     Case                   Allocated  GCs
    198     sign_ecdsa' (small)      1,560    0
    199     sign_ecdsa' (large)      1,560    0
    200 
    201   ecdh
    202 
    203     Case          Allocated  GCs
    204     ecdh (small)        616    0
    205     ecdh (large)        616    0
    206 
    207 ```
    208 
    209 Though constant-resource execution is enforced rigorously, take
    210 reasonable security precautions as appropriate. You shouldn't deploy the
    211 implementations within in any situation where they could easily be used
    212 as an oracle to construct a [timing attack][timea], and you shouldn't
    213 give sophisticated malicious actors [access to your computer][flurl].
    214 
    215 If you discover any vulnerabilities, please disclose them via
    216 security@ppad.tech.
    217 
    218 ## Development
    219 
    220 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
    221 development shell with:
    222 
    223 ```
    224 $ nix develop
    225 ```
    226 
    227 Then do e.g.:
    228 
    229 ```
    230 $ cabal repl ppad-secp256k1
    231 ```
    232 
    233 to get a REPL for the main library.
    234 
    235 [bp340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
    236 [ut340]: https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv
    237 [bp146]: https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki
    238 [r6979]: https://www.rfc-editor.org/rfc/rfc6979
    239 [nixos]: https://nixos.org/
    240 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html
    241 [hadoc]: https://docs.ppad.tech/secp256k1
    242 [wyche]: https://github.com/C2SP/wycheproof
    243 [timea]: https://en.wikipedia.org/wiki/Timing_attack
    244 [flurl]: https://eprint.iacr.org/2014/140.pdf
    245 [const]: https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html
    246 [csecp]: https://git.ppad.tech/csecp256k1
    247 [noble]: https://github.com/paulmillr/noble-secp256k1
    248 [fixed]: https://git.ppad.tech/fixed
    249 [sha256]: https://git.ppad.tech/sha256