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


      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                 51.22 μs   (51.02 μs .. 51.36 μs)
     80                        1.000 R²   (1.000 R² .. 1.000 R²)
     81   mean                 51.08 μs   (50.95 μs .. 51.19 μs)
     82   std dev              403.3 ns   (344.5 ns .. 507.5 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 This library aims at the maximum security achievable in a
    104 garbage-collected language under an optimizing compiler such as GHC, in
    105 which strict constant-timeness can be [challenging to achieve][const].
    106 
    107 The Schnorr implementation within has been tested against the [official
    108 BIP0340 vectors][ut340], and ECDSA and ECDH have been tested against the
    109 relevant [Wycheproof vectors][wyche] (with the former also being tested
    110 against [noble-secp256k1's][noble] vectors), so their implementations
    111 are likely to be accurate and safe from attacks targeting e.g.
    112 faulty nonce generation or malicious inputs for signature or public
    113 key parameters.
    114 
    115 Timing-sensitive operations, e.g. elliptic curve scalar multiplication,
    116 have been explicitly written so as to execute in time constant with
    117 respect to secret data. Moreover, fixed-size (256-bit) wide words with
    118 constant-time operations provided by [ppad-fixed][fixed] are used
    119 exclusively internally, avoiding timing variations incurred by use of
    120 GHC's variable-size Integer type.
    121 
    122 Criterion benchmarks attest that any timing variation between cases with
    123 differing inputs is attributable to noise:
    124 
    125 ```
    126   benchmarking derive_pub/wnaf, sk = 2
    127   time                 29.67 μs   (29.64 μs .. 29.71 μs)
    128                        1.000 R²   (1.000 R² .. 1.000 R²)
    129   mean                 29.68 μs   (29.64 μs .. 29.71 μs)
    130   std dev              121.1 ns   (83.80 ns .. 177.2 ns)
    131 
    132   benchmarking derive_pub/wnaf, sk = 2 ^ 255 - 19
    133   time                 29.68 μs   (29.65 μs .. 29.72 μs)
    134                        1.000 R²   (1.000 R² .. 1.000 R²)
    135   mean                 29.71 μs   (29.68 μs .. 29.75 μs)
    136   std dev              106.7 ns   (71.74 ns .. 174.7 ns)
    137 
    138   benchmarking schnorr/sign_schnorr' (small)
    139   time                 76.27 μs   (76.21 μs .. 76.32 μs)
    140                        1.000 R²   (1.000 R² .. 1.000 R²)
    141   mean                 76.44 μs   (76.40 μs .. 76.50 μs)
    142   std dev              162.3 ns   (123.1 ns .. 246.7 ns)
    143 
    144   benchmarking schnorr/sign_schnorr' (large)
    145   time                 76.35 μs   (76.31 μs .. 76.38 μs)
    146                        1.000 R²   (1.000 R² .. 1.000 R²)
    147   mean                 76.37 μs   (76.35 μs .. 76.40 μs)
    148   std dev              84.10 ns   (67.03 ns .. 112.7 ns)
    149 
    150   benchmarking ecdsa/sign_ecdsa' (small)
    151   time                 52.34 μs   (52.22 μs .. 52.49 μs)
    152                        1.000 R²   (1.000 R² .. 1.000 R²)
    153   mean                 52.35 μs   (52.30 μs .. 52.42 μs)
    154   std dev              205.9 ns   (159.2 ns .. 281.1 ns)
    155 
    156   benchmarking ecdsa/sign_ecdsa' (large)
    157   time                 52.40 μs   (52.31 μs .. 52.55 μs)
    158                        1.000 R²   (1.000 R² .. 1.000 R²)
    159   mean                 52.66 μs   (52.47 μs .. 52.99 μs)
    160   std dev              813.7 ns   (427.9 ns .. 1.244 μs)
    161   variance introduced by outliers: 10% (moderately inflated)
    162 
    163   benchmarking ecdh/ecdh (small)
    164   time                 143.6 μs   (143.4 μs .. 143.7 μs)
    165                        1.000 R²   (1.000 R² .. 1.000 R²)
    166   mean                 143.7 μs   (143.3 μs .. 144.6 μs)
    167   std dev              2.022 μs   (846.9 ns .. 3.402 μs)
    168 
    169   benchmarking ecdh/ecdh (large)
    170   time                 143.8 μs   (143.7 μs .. 143.9 μs)
    171                        1.000 R²   (1.000 R² .. 1.000 R²)
    172   mean                 143.8 μs   (143.7 μs .. 143.9 μs)
    173   std dev              385.2 ns   (265.9 ns .. 544.5 ns)
    174 ```
    175 
    176 Note also that care has been taken to ensure that allocation is held
    177 constant across input sizes for all sensitive operations:
    178 
    179 ```
    180   derive_pub
    181 
    182     Case                     Allocated  GCs
    183     wnaf, sk = 2                   304    0
    184     wnaf, sk = 2 ^ 255 - 19        304    0
    185 
    186   schnorr
    187 
    188     Case                   Allocated  GCs
    189     sign_schnorr' (small)     27,104    0
    190     sign_schnorr' (large)     27,104    0
    191 
    192   ecdsa
    193 
    194     Case                   Allocated  GCs
    195     sign_ecdsa' (small)     61,592    0
    196     sign_ecdsa' (large)     61,592    0
    197 
    198   ecdh
    199 
    200     Case          Allocated  GCs
    201     ecdh (small)      1,880    0
    202     ecdh (large)      1,880    0
    203 ```
    204 
    205 Though constant-resource execution is enforced rigorously, take
    206 reasonable security precautions as appropriate. You shouldn't deploy the
    207 implementations within in any situation where they could easily be used
    208 as an oracle to construct a [timing attack][timea], and you shouldn't
    209 give sophisticated malicious actors [access to your computer][flurl].
    210 
    211 If you discover any vulnerabilities, please disclose them via
    212 security@ppad.tech.
    213 
    214 ## Development
    215 
    216 You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
    217 development shell with:
    218 
    219 ```
    220 $ nix develop
    221 ```
    222 
    223 Then do e.g.:
    224 
    225 ```
    226 $ cabal repl ppad-secp256k1
    227 ```
    228 
    229 to get a REPL for the main library.
    230 
    231 [bp340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
    232 [ut340]: https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv
    233 [bp146]: https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki
    234 [r6979]: https://www.rfc-editor.org/rfc/rfc6979
    235 [nixos]: https://nixos.org/
    236 [flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html
    237 [hadoc]: https://docs.ppad.tech/secp256k1
    238 [wyche]: https://github.com/C2SP/wycheproof
    239 [timea]: https://en.wikipedia.org/wiki/Timing_attack
    240 [flurl]: https://eprint.iacr.org/2014/140.pdf
    241 [const]: https://www.chosenplaintext.ca/articles/beginners-guide-constant-time-cryptography.html
    242 [csecp]: https://git.ppad.tech/csecp256k1
    243 [noble]: https://github.com/paulmillr/noble-secp256k1
    244 [fixed]: https://git.ppad.tech/fixed
    245 [sha256]: https://git.ppad.tech/sha256