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


      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, highly-auditable Haskell
     61 code.
     62 
     63 Current benchmark figures on an M4 MacBook Air look like (use `cabal
     64 bench` to run the benchmark suite):
     65 
     66 ```
     67   benchmarking schnorr/sign_schnorr' (large)
     68   time                 1.400 ms   (1.399 ms .. 1.402 ms)
     69                        1.000 R²   (1.000 R² .. 1.000 R²)
     70   mean                 1.406 ms   (1.404 ms .. 1.408 ms)
     71   std dev              5.989 μs   (5.225 μs .. 7.317 μs)
     72 
     73   benchmarking schnorr/verify_schnorr'
     74   time                 720.2 μs   (716.7 μs .. 724.8 μs)
     75                        1.000 R²   (1.000 R² .. 1.000 R²)
     76   mean                 724.6 μs   (722.0 μs .. 730.4 μs)
     77   std dev              12.68 μs   (6.334 μs .. 26.31 μs)
     78 
     79   benchmarking ecdsa/sign_ecdsa' (large)
     80   time                 115.3 μs   (115.1 μs .. 115.7 μs)
     81                        1.000 R²   (1.000 R² .. 1.000 R²)
     82   mean                 116.0 μs   (115.6 μs .. 116.4 μs)
     83   std dev              1.367 μs   (1.039 μs .. 1.839 μs)
     84 
     85   benchmarking ecdsa/verify_ecdsa'
     86   time                 702.3 μs   (699.9 μs .. 704.9 μs)
     87                        1.000 R²   (1.000 R² .. 1.000 R²)
     88   mean                 704.9 μs   (702.7 μs .. 708.4 μs)
     89   std dev              9.641 μs   (6.638 μs .. 14.04 μs)
     90 ```
     91 
     92 In terms of allocations, we get:
     93 
     94 ```
     95 schnorr
     96 
     97   Case                   Allocated  GCs
     98   sign_schnorr'          3,273,824    0
     99   verify_schnorr'        1,667,360    0
    100 
    101 ecdsa
    102 
    103   Case                 Allocated  GCs
    104   sign_ecdsa'            324,672    0
    105   verify_ecdsa'        3,796,328    0
    106 
    107 ecdh
    108 
    109   Case          Allocated  GCs
    110   ecdh (small)  2,141,736    0
    111   ecdh (large)  2,145,464    0
    112 ```
    113 
    114 ## Security
    115 
    116 This library aims at the maximum security achievable in a
    117 garbage-collected language under an optimizing compiler such as GHC, in
    118 which strict constant-timeness can be [challenging to achieve][const].
    119 
    120 The Schnorr implementation within has been tested against the [official
    121 BIP0340 vectors][ut340], and ECDSA and ECDH have been tested against the
    122 relevant [Wycheproof vectors][wyche] (with the former also being tested
    123 against [noble-secp256k1's][noble] vectors), so their implementations
    124 are likely to be accurate and safe from attacks targeting e.g.
    125 faulty nonce generation or malicious inputs for signature or public
    126 key parameters. Timing-sensitive operations, e.g. elliptic curve
    127 scalar multiplication, have been explicitly written so as to execute
    128 *algorithmically* in time constant with respect to secret data, and
    129 evidence from benchmarks supports this:
    130 
    131 ```
    132   benchmarking derive_pub/wnaf, sk = 2
    133   time                 76.20 μs   (75.62 μs .. 77.33 μs)
    134                        0.999 R²   (0.998 R² .. 1.000 R²)
    135   mean                 75.87 μs   (75.61 μs .. 76.48 μs)
    136   std dev              1.218 μs   (614.3 ns .. 2.291 μs)
    137   variance introduced by outliers: 11% (moderately inflated)
    138 
    139   benchmarking derive_pub/wnaf, sk = 2 ^ 255 - 19
    140   time                 76.50 μs   (75.88 μs .. 77.37 μs)
    141                        0.999 R²   (0.998 R² .. 1.000 R²)
    142   mean                 76.26 μs   (75.99 μs .. 76.93 μs)
    143   std dev              1.317 μs   (570.7 ns .. 2.583 μs)
    144   variance introduced by outliers: 12% (moderately inflated)
    145 
    146   benchmarking schnorr/sign_schnorr' (small)
    147   time                 1.430 ms   (1.424 ms .. 1.438 ms)
    148                        1.000 R²   (1.000 R² .. 1.000 R²)
    149   mean                 1.429 ms   (1.425 ms .. 1.433 ms)
    150   std dev              13.71 μs   (10.48 μs .. 18.85 μs)
    151 
    152   benchmarking schnorr/sign_schnorr' (large)
    153   time                 1.400 ms   (1.399 ms .. 1.402 ms)
    154                        1.000 R²   (1.000 R² .. 1.000 R²)
    155   mean                 1.406 ms   (1.404 ms .. 1.408 ms)
    156   std dev              5.989 μs   (5.225 μs .. 7.317 μs)
    157 
    158   benchmarking ecdsa/sign_ecdsa' (small)
    159   time                 114.5 μs   (114.0 μs .. 115.3 μs)
    160                        1.000 R²   (0.999 R² .. 1.000 R²)
    161   mean                 115.2 μs   (114.8 μs .. 115.8 μs)
    162   std dev              1.650 μs   (1.338 μs .. 2.062 μs)
    163 
    164   benchmarking ecdsa/sign_ecdsa' (large)
    165   time                 115.3 μs   (115.1 μs .. 115.7 μs)
    166                        1.000 R²   (1.000 R² .. 1.000 R²)
    167   mean                 116.0 μs   (115.6 μs .. 116.4 μs)
    168   std dev              1.367 μs   (1.039 μs .. 1.839 μs)
    169 
    170   benchmarking ecdh/ecdh (small)
    171   time                 907.0 μs   (902.8 μs .. 912.0 μs)
    172                        1.000 R²   (0.999 R² .. 1.000 R²)
    173   mean                 909.5 μs   (907.0 μs .. 913.0 μs)
    174   std dev              10.05 μs   (6.943 μs .. 17.11 μs)
    175 
    176   benchmarking ecdh/ecdh (large)
    177   time                 922.9 μs   (911.0 μs .. 937.4 μs)
    178                        0.999 R²   (0.998 R² .. 1.000 R²)
    179   mean                 915.8 μs   (911.9 μs .. 922.5 μs)
    180   std dev              16.84 μs   (9.830 μs .. 26.48 μs)
    181 ```
    182 
    183 Due to the use of arbitrary-precision integers, integer division modulo
    184 the elliptic curve group order does display persistent substantial
    185 timing differences on the order of 1-2 nanoseconds when the inputs
    186 differ dramatically in size (here 2 bits vs 255 bits):
    187 
    188 ```
    189   benchmarking remQ (remainder modulo _CURVE_Q)/remQ 2
    190   time                 11.13 ns   (11.12 ns .. 11.14 ns)
    191                        1.000 R²   (1.000 R² .. 1.000 R²)
    192   mean                 11.10 ns   (11.09 ns .. 11.11 ns)
    193   std dev              33.75 ps   (30.27 ps .. 38.31 ps)
    194 
    195   benchmarking remQ (remainder modulo _CURVE_Q)/remQ (2 ^ 255 - 19)
    196   time                 12.50 ns   (12.49 ns .. 12.51 ns)
    197                        1.000 R²   (1.000 R² .. 1.000 R²)
    198   mean                 12.51 ns   (12.51 ns .. 12.52 ns)
    199   std dev              26.72 ps   (14.45 ps .. 45.87 ps)
    200 ```
    201 
    202 This represents the worst-case scenario (real-world private keys will
    203 never differ so extraordinarily) and is likely to be well within
    204 acceptable limits for all but the most extreme security requirements.
    205 But because we don't make "hard" guarantees of constant-time execution,
    206 take reasonable security precautions as appropriate. You shouldn't
    207 deploy the implementations within in any situation where they could
    208 easily be used as an oracle to construct a [timing attack][timea],
    209 and you shouldn't give sophisticated malicious actors [access to your
    210 computer][flurl].
    211 
    212 If you discover any vulnerabilities, please disclose them via
    213 security@ppad.tech.
    214 
    215 ## Development
    216 
    217 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
    218 development shell with:
    219 
    220 ```
    221 $ nix develop
    222 ```
    223 
    224 Then do e.g.:
    225 
    226 ```
    227 $ cabal repl ppad-secp256k1
    228 ```
    229 
    230 to get a REPL for the main library.
    231 
    232 [bp340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
    233 [ut340]: https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv
    234 [bp146]: https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki
    235 [r6979]: https://www.rfc-editor.org/rfc/rfc6979
    236 [nixos]: https://nixos.org/
    237 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html
    238 [hadoc]: https://docs.ppad.tech/secp256k1
    239 [wyche]: https://github.com/C2SP/wycheproof
    240 [timea]: https://en.wikipedia.org/wiki/Timing_attack
    241 [flurl]: https://eprint.iacr.org/2014/140.pdf
    242 [const]: https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html
    243 [csecp]: https://git.ppad.tech/csecp256k1
    244 [noble]: https://github.com/paulmillr/noble-secp256k1