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


      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 
      6 A pure Haskell implementation of [BIP0340][bp340] Schnorr signatures
      7 and deterministic [RFC6979][r6979] ECDSA (with [BIP0146][bp146]-style
      8 "low-S" signatures) on the elliptic curve secp256k1.
      9 
     10 (See also [ppad-csecp256k1][csecp] for FFI bindings to
     11 bitcoin-core/secp256k1.)
     12 
     13 ## Usage
     14 
     15 A sample GHCi session:
     16 
     17 ```
     18   > -- pragmas and b16 import for illustration only; not required
     19   > :set -XOverloadedStrings
     20   > :set -XBangPatterns
     21   > import qualified Data.ByteString.Base16 as B16
     22   >
     23   > -- import qualified
     24   > import qualified Crypto.Curve.Secp256k1 as Secp256k1
     25   >
     26   > -- secret, public keys
     27   > let sec = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
     28   :{
     29   ghci| let Just pub = Secp256k1.parse_point . B16.decodeLenient $
     30   ghci|       "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"
     31   ghci| :}
     32   >
     33   > let msg = "i approve of this message"
     34   >
     35   > -- create and verify a schnorr signature for the message
     36   > let sig0 = Secp256k1.sign_schnorr sec msg mempty
     37   > Secp256k1.verify_schnorr msg pub sig0
     38   True
     39   >
     40   > -- create and verify a low-S ECDSA signature for the message
     41   > let sig1 = Secp256k1.sign_ecdsa sec msg
     42   > Secp256k1.verify_ecdsa msg pub sig1
     43   True
     44   >
     45   > -- for faster signs (especially w/ECDSA) and verifies, use a context
     46   > let !tex = Secp256k1.precompute
     47   > Secp256k1.verify_schnorr' tex msg pub sig0
     48   True
     49 ```
     50 
     51 ## Documentation
     52 
     53 Haddocks (API documentation, etc.) are hosted at
     54 [docs.ppad.tech/secp256k1][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 schnorr/sign_schnorr'
     66   time                 2.557 ms   (2.534 ms .. 2.596 ms)
     67                        0.998 R²   (0.997 R² .. 0.999 R²)
     68   mean                 2.579 ms   (2.556 ms .. 2.605 ms)
     69   std dev              83.75 μs   (69.50 μs .. 100.5 μs)
     70 
     71   benchmarking schnorr/verify_schnorr'
     72   time                 1.429 ms   (1.381 ms .. 1.482 ms)
     73                        0.993 R²   (0.987 R² .. 0.998 R²)
     74   mean                 1.372 ms   (1.355 ms .. 1.396 ms)
     75   std dev              67.72 μs   (50.44 μs .. 110.5 μs)
     76 
     77   benchmarking ecdsa/sign_ecdsa'
     78   time                 233.7 μs   (230.3 μs .. 237.2 μs)
     79                        0.997 R²   (0.996 R² .. 0.998 R²)
     80   mean                 238.6 μs   (234.8 μs .. 243.3 μs)
     81   std dev              14.85 μs   (12.27 μs .. 19.17 μs)
     82 
     83   benchmarking ecdsa/verify_ecdsa'
     84   time                 1.460 ms   (1.418 ms .. 1.497 ms)
     85                        0.994 R²   (0.989 R² .. 0.997 R²)
     86   mean                 1.419 ms   (1.398 ms .. 1.446 ms)
     87   std dev              80.76 μs   (66.73 μs .. 104.9 μs)
     88 ```
     89 
     90 In terms of allocations, we get:
     91 
     92 ```
     93 schnorr
     94 
     95   Case                   Allocated  GCs
     96   sign_schnorr'          3,273,824    0
     97   verify_schnorr'        1,667,360    0
     98 
     99 ecdsa
    100 
    101   Case                 Allocated  GCs
    102   sign_ecdsa'            324,672    0
    103   verify_ecdsa'        3,796,328    0
    104 ```
    105 
    106 ## Security
    107 
    108 This library aims at the maximum security achievable in a
    109 garbage-collected language under an optimizing compiler such as GHC, in
    110 which strict constant-timeness can be [challenging to achieve][const].
    111 
    112 The Schnorr implementation within has been tested against the [official
    113 BIP0340 vectors][ut340], and ECDSA has been tested against the relevant
    114 [Wycheproof vectors][wyche], so their implementations are likely to be
    115 accurate and safe from attacks targeting e.g. faulty nonce generation or
    116 malicious inputs for signature parameters. Timing-sensitive operations,
    117 e.g. elliptic curve scalar multiplication, have been explicitly written
    118 so as to execute *algorithmically* in time constant with respect to
    119 secret data, and evidence from benchmarks supports this:
    120 
    121 ```
    122   benchmarking derive_public/sk = 2
    123   time                 1.703 ms   (1.680 ms .. 1.728 ms)
    124                        0.997 R²   (0.995 R² .. 0.999 R²)
    125   mean                 1.704 ms   (1.684 ms .. 1.730 ms)
    126   std dev              81.99 μs   (67.86 μs .. 98.34 μs)
    127 
    128   benchmarking derive_public/sk = 2 ^ 255 - 19
    129   time                 1.686 ms   (1.654 ms .. 1.730 ms)
    130                        0.998 R²   (0.997 R² .. 0.999 R²)
    131   mean                 1.658 ms   (1.645 ms .. 1.673 ms)
    132   std dev              44.75 μs   (34.84 μs .. 59.81 μs)
    133 
    134   benchmarking schnorr/sign_schnorr (small secret)
    135   time                 5.388 ms   (5.345 ms .. 5.438 ms)
    136                        1.000 R²   (0.999 R² .. 1.000 R²)
    137   mean                 5.429 ms   (5.410 ms .. 5.449 ms)
    138   std dev              58.14 μs   (48.93 μs .. 68.69 μs)
    139 
    140   benchmarking schnorr/sign_schnorr (large secret)
    141   time                 5.364 ms   (5.324 ms .. 5.410 ms)
    142                        0.999 R²   (0.999 R² .. 1.000 R²)
    143   mean                 5.443 ms   (5.414 ms .. 5.486 ms)
    144   std dev              102.1 μs   (74.16 μs .. 146.4 μs)
    145 
    146   benchmarking ecdsa/sign_ecdsa (small secret)
    147   time                 1.740 ms   (1.726 ms .. 1.753 ms)
    148                        1.000 R²   (0.999 R² .. 1.000 R²)
    149   mean                 1.752 ms   (1.744 ms .. 1.761 ms)
    150   std dev              32.33 μs   (26.12 μs .. 43.34 μs)
    151 
    152   benchmarking ecdsa/sign_ecdsa (large secret)
    153   time                 1.748 ms   (1.731 ms .. 1.766 ms)
    154                        0.999 R²   (0.998 R² .. 0.999 R²)
    155   mean                 1.756 ms   (1.743 ms .. 1.771 ms)
    156   std dev              48.99 μs   (40.83 μs .. 62.77 μs)
    157 ```
    158 
    159 Due to the use of arbitrary-precision integers, integer division modulo
    160 the elliptic curve group order does display persistent substantial
    161 timing differences on the order of 2 nanoseconds when the inputs differ
    162 dramatically in size (here 2 bits vs 255 bits):
    163 
    164 ```
    165   benchmarking remQ (remainder modulo _CURVE_Q)/remQ 2
    166   time                 27.44 ns   (27.19 ns .. 27.72 ns)
    167                        1.000 R²   (0.999 R² .. 1.000 R²)
    168   mean                 27.23 ns   (27.03 ns .. 27.43 ns)
    169   std dev              669.1 ps   (539.9 ps .. 860.2 ps)
    170 
    171   benchmarking remQ (remainder modulo _CURVE_Q)/remQ (2 ^ 255 - 19)
    172   time                 29.11 ns   (28.87 ns .. 29.33 ns)
    173                        0.999 R²   (0.999 R² .. 1.000 R²)
    174   mean                 29.04 ns   (28.82 ns .. 29.40 ns)
    175   std dev              882.9 ps   (647.8 ps .. 1.317 ns)
    176 ```
    177 
    178 This represents the worst-case scenario (real-world private keys will
    179 never differ so extraordinarily) and is likely to be well within
    180 acceptable limits for all but the most extreme security requirements.
    181 But because we don't make "hard" guarantees of constant-time execution,
    182 take reasonable security precautions as appropriate. You shouldn't
    183 deploy the implementations within in any situation where they could
    184 easily be used as an oracle to construct a [timing attack][timea],
    185 and you shouldn't give sophisticated malicious actors [access to your
    186 computer][flurl].
    187 
    188 If you discover any vulnerabilities, please disclose them via
    189 security@ppad.tech.
    190 
    191 ## Development
    192 
    193 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
    194 development shell with:
    195 
    196 ```
    197 $ nix develop
    198 ```
    199 
    200 Then do e.g.:
    201 
    202 ```
    203 $ cabal repl ppad-secp256k1
    204 ```
    205 
    206 to get a REPL for the main library.
    207 
    208 [bp340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
    209 [ut340]: https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv
    210 [bp146]: https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki
    211 [r6979]: https://www.rfc-editor.org/rfc/rfc6979
    212 [nixos]: https://nixos.org/
    213 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html
    214 [hadoc]: https://docs.ppad.tech/secp256k1
    215 [wyche]: https://github.com/C2SP/wycheproof
    216 [timea]: https://en.wikipedia.org/wiki/Timing_attack
    217 [flurl]: https://eprint.iacr.org/2014/140.pdf
    218 [const]: https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html
    219 [csecp]: https://git.ppad.tech/csecp256k1