README.md (8188B)
1 # secp256k1 2 3 [](https://hackage.haskell.org/package/ppad-secp256k1) 4  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