rbsecp256k1 5.1.1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f0c0d659103f52aa0a9762d464bfacd9a656d90dfd38815b7b954c863aa5bb2
4
- data.tar.gz: 7e74a91710993a87a4a931be6091e16af32960e91ebe67a731ffb0ee498593ba
3
+ metadata.gz: fbd0a847d061c266d044174528f14b1351484f58a703e7ec76719122689ae6d4
4
+ data.tar.gz: c7bbdaf0dc9bdaf24dd5de7c40d95c16f08b72d466216af6789e523ee14bba8b
5
5
  SHA512:
6
- metadata.gz: 69cdae2b446230a9908aa0b799b4c36d09bf5486c118468a43be4ffa128f304e7cf608bab222bebb8acdbb1bf7848619dd2a55e583ce07b14a851d3657cc0d6b
7
- data.tar.gz: 52b48f20f66ac1635358e924fcc9a1c47bf53962610ecbaca099611d69c42d994f4c8c886a172ed1e3402c241be86e6977edd9b674960b1f5acedc30c5d8326a
6
+ metadata.gz: cf695d750f38e4d27de61caa0e1ec8d9160925d7c10cc556691c7335478137f71f7f49901d50ade3c9e2c917779b38a3d4ee49ca931a8691cc99e7a751fd8243
7
+ data.tar.gz: 7b5a75562a90c3e845a0b42477873562991d19ca610b89aef95581e0f7dc010f123e8ed4055225978cb2ad4b6667e184d18915114f69cfdaabb993bfcf4b06a7
data/README.md CHANGED
@@ -2,12 +2,16 @@
2
2
 
3
3
  [![Spec](https://github.com/etscrivner/rbsecp256k1/actions/workflows/spec.yml/badge.svg?branch=master)](https://github.com/etscrivner/rbsecp256k1/actions/workflows/spec.yml) [![Gem Version](https://badge.fury.io/rb/rbsecp256k1.svg)](https://badge.fury.io/rb/rbsecp256k1) [![Maintainability](https://api.codeclimate.com/v1/badges/d4b6e27bfa00030ca412/maintainability)](https://codeclimate.com/github/etscrivner/rbsecp256k1/maintainability)
4
4
 
5
- Native extension gem for secp256k1 ECDSA. Wraps [libsecp256k1](https://github.com/bitcoin-core/secp256k1). In
6
- rbsecp256k1 3.0.0 and later libsecp256k1 is bundled with the gem.
5
+ Native extension gem for secp256k1 ECDSA and Schnorr signatures. Wraps [libsecp256k1](https://github.com/bitcoin-core/secp256k1) without the need for FFI.
7
6
 
8
7
  * [Documentation](https://github.com/etscrivner/rbsecp256k1/blob/master/documentation/index.md)
9
8
  * [Examples](https://github.com/etscrivner/rbsecp256k1/blob/master/examples/README.md)
10
9
 
10
+ ### Features
11
+
12
+ * Native extension gem wrapping [libsecp256k1](https://github.com/bitcoin-core/secp256k1), no FFI.
13
+ * Schnorr signature support.
14
+
11
15
  ### Why wrap libsecp256k1?
12
16
 
13
17
  [libsecp256k1](https://github.com/bitcoin-core/secp256k1) is an extremely optimized implementation of public key derivation,
@@ -100,38 +104,6 @@ To test with recovery functionality disabled run:
100
104
  make test WITH_RECOVERY=0
101
105
  ```
102
106
 
103
- To test with ECDH functionality disabled run:
104
-
105
- ```
106
- make test WITH_ECDH=0
107
- ```
108
-
109
- To test with both disabled run:
110
-
111
- ```
112
- make test WITH_RECOVERY=0 WITH_ECDH=0
113
- ```
114
-
115
- Testing for memory leaks with valgrind:
116
-
117
- ```
118
- make memcheck
119
- ```
120
-
121
- ### Building Gem
122
-
123
- ```
124
- make gem
125
- ```
126
-
127
- ### Installing Gem Locally
128
-
129
- To install the gem locally and verify builds you can run:
130
-
131
- ```
132
- make install
133
- ```
134
-
135
107
  ### Uninstall Gem Locally
136
108
 
137
109
  You can similarly uninstall the local gem by running the following:
@@ -35,16 +35,16 @@ Instance Methods
35
35
 
36
36
  #### ecdh(point, scalar)
37
37
 
38
- **Requires:** libsecp256k1 was built with the experimental ECDH module.
39
-
40
38
  Takes a `point` ([PublicKey](public_key.md)) and a `scalar` ([PrivateKey](private_key.md)) and returns a new
41
39
  [SharedSecret](shared_secret.md) containing the 32-byte shared secret. Raises a `Secp256k1::Error` if
42
40
  the `scalar` is invalid (zero or causes an overflow).
43
41
 
44
42
  #### generate_key_pair
45
43
 
46
- Generates and returns a new [KeyPair](key_pair.md) using a cryptographically
47
- secure random number generator (CSRNG) provided by OpenSSL.
44
+ Generates and returns a new [KeyPair](key_pair.md) using Ruby's built-in
45
+ `SecureRandom` cryptographically secure random number generator. This method
46
+ simply invokes `key_pair_from_private_key` with the value from
47
+ `SecureRandom.random_bytes`.
48
48
 
49
49
  #### key_pair_from_private_key(private_key_data)
50
50
 
@@ -73,6 +73,23 @@ Signs the data represented by the SHA-256 hash `hash32` using `private_key` and
73
73
  new [RecoverableSignature](recoverable_signature.md). The `private_key` is expected to be a [PrivateKey](private_key.md) and
74
74
  `data` can be either a binary string or text.
75
75
 
76
+ #### sign_schnorr(keypair, message)
77
+
78
+ Same as `sign_schnorr_custom(keypair, message, auxrand)` but generates
79
+ `auxrand` for you using `SecureRandom`. This should almost always be the method
80
+ used for Schnorr signing.
81
+
82
+ #### sign_schnorr_custom(keypair, message, auxrand)
83
+
84
+ Sign the given 32-byte binary string `message` using the given `keypair`. It is
85
+ recommend that `message` be generated using `tagged_sha256`. `auxrand` should
86
+ be 32-bytes of fresh randomness, but can optionally be `nil` if generating
87
+ randomness is expensive.
88
+
89
+ #### tagged_sha256(tag, message)
90
+
91
+ Computes the tagged hash as defined in [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). Returns the 32-byte binary string tagged hash.
92
+
76
93
  #### verify(signature, public_key, hash32)
77
94
 
78
95
  Verifies the given `signature` ([Signature](signature.md)) was signed by
@@ -11,10 +11,12 @@ Classes and Modules
11
11
  | [Secp256k1](secp256k1.md) | [Context](context.md) | [Util](util.md)
12
12
  | | [KeyPair](key_pair.md) |
13
13
  | | [PublicKey](public_key.md) |
14
+ | | [XOnlyPublicKey](xonly_public_key.md) |
14
15
  | | [PrivateKey](private_key.md) |
15
16
  | | [SharedSecret](shared_secret.md) |
16
17
  | | [Signature](signature.md) |
17
18
  | | [RecoverableSignature](recoverable_signature.md) |
19
+ | | [SchnorrSignature](schnorr_signature.md) |
18
20
 
19
21
  Glossary
20
22
  --------
@@ -28,6 +30,8 @@ efficient.
28
30
  **[PublicKey](public_key.md)** is a Secp256k1 public key. It can come in either
29
31
  compressed or uncompressed format.
30
32
 
33
+ **[XOnlyPublicKey](xonly_public_key.md)** is a Secp256k1 x-only public key.
34
+
31
35
  **[PrivateKey](private_key.md)** is a 64-byte Secp256k1 private key.
32
36
 
33
37
  **[SharedSecret](shared_secret.md)** A 32-byte shared secret computed from a
@@ -39,6 +43,8 @@ of a piece of data.
39
43
  **[RecoverableSignature](recoverable_signature.md)** is a recoverable ECDSA signature of the SHA-256 message
40
44
  hash of a piece of data.
41
45
 
46
+ **[SchnorrSignature](schnorr_signature.md)** is a Schnorr signature of a 32-byte message.
47
+
42
48
  Examples
43
49
  --------
44
50
 
@@ -59,7 +65,7 @@ This example shows how to generate a new public-private key pair:
59
65
  ```ruby
60
66
  context = Secp256k1::Context.create
61
67
  key_pair = context.generate_key_pair
62
- # => #<Secp256k1::KeyPair:0x0000559b0bc876b0 @public_key=#<Secp256k1::PublicKey:0x0000559b0bc876d8>, @private_key=#<Secp256k1::PrivateKey:0x0000559b0bc87700 @data="\r\xA7\xB3<\x92\xCDw\xC1\xDB\xEB[BB;=\x80\xB83\xA8]\x06\xD9\x90\xF8v\xFFi\xF0/\x1E\x96\xF9">>
68
+ # => #<Secp256k1::KeyPair:0x0000559b0bc876b0>
63
69
  ```
64
70
 
65
71
  ### 3. Getting compressed and uncompressed public key representations
@@ -162,11 +168,11 @@ context = Secp256k1::Context.create
162
168
 
163
169
  #1. Load private key alone
164
170
  private_key = Secp256k1::PrivateKey.from_data("I\nX\x85\xAEz}\n\x9B\xA4\\\x81)\xD4\x9Aq\xFDH\t\xBE\x8EP\xC5.\xC6\x1F7-\x86\xA0\xCB\xF9")
165
- # => #<Secp256k1::PrivateKey:0x00005647df1bcd30 @data="I\nX\x85\xAEz}\n\x9B\xA4\\\x81)\xD4\x9Aq\xFDH\t\xBE\x8EP\xC5.\xC6\x1F7-\x86\xA0\xCB\xF9">
171
+ # => #<Secp256k1::PrivateKey:0x00005647df1bcd30>
166
172
 
167
173
  # 2. Load key pair from private key data
168
174
  key_pair = context.key_pair_from_private_key("I\nX\x85\xAEz}\n\x9B\xA4\\\x81)\xD4\x9Aq\xFDH\t\xBE\x8EP\xC5.\xC6\x1F7-\x86\xA0\xCB\xF9")
169
- # => #<Secp256k1::KeyPair:0x0000559b0bbf9a90 @public_key=#<Secp256k1::PublicKey:0x0000559b0bbf9ab8>, @private_key=#<Secp256k1::PrivateKey:0x0000559b0bbf9ae0 @data="I\nX\x85\xAEz}\n\x9B\xA4\\\x81)Ԛq\xFDH\t\xBE\x8EP\xC5.\xC6\u001F7-\x86\xA0\xCB\xF9">>
175
+ # => #<Secp256k1::KeyPair:0x0000559b0bbf9a90>
170
176
  ```
171
177
 
172
178
  ### 7. Loading a public key from binary data
@@ -197,6 +203,59 @@ signature = Secp256k1::Signature.from_compact("<\xC6\x7F/\x921l\x89Z\xFBs\x89p\x
197
203
  # => #<Secp256k1::Signature:0x0000559b0bdcaa68>
198
204
  ```
199
205
 
206
+ Schnorr Signature Examples
207
+ --------------------------
208
+
209
+ ### 1. Checking for the Schnorr module
210
+
211
+ To check if you have compiled the schnorr signature module into your local
212
+ libsecp256k1 run the following:
213
+
214
+ ```ruby
215
+ Secp256k1.have_schnorr?
216
+ # => true
217
+ ```
218
+
219
+ ### 2. Calculate the tagged SHA-256 hash of data
220
+
221
+ You can produce the tagged SHA-256 hash recommended for use with Schnorr
222
+ signatures as follows:
223
+
224
+ ```ruby
225
+ context = Secp256k1::Context.create
226
+
227
+ context.tagged_sha256('my_test_tag', 'data')
228
+ => "0\x85|\xC8h\x9A\x8C^\x0FoD\xDF\xD0,\x1C\x1EV}\xE8\xA8<\xC5\xA5vI\x9E`=\x9B\xC4\xA8\xAE"
229
+ ```
230
+
231
+ ### 3. Sign and verify a message using Schnorr signatures
232
+
233
+ ```ruby
234
+ context = Secp256k1::Context.create
235
+ key_pair = context.generate_key_pair
236
+ message = context.tagged_sha256('my_test_tag', 'data')
237
+
238
+ schnorr_sig = context.sign_schnorr(key_pair, message)
239
+ => #<Secp256k1::SchnorrSignature:0x00007f4de11e5768>
240
+
241
+ schnorr_sig.verify(message, key_pair.xonly_public_key)
242
+ => true
243
+
244
+ schnorr_sig.serialized
245
+ => "\x16\xEF\x04\xB0JP\xE9\x90\xC2\xB1\xE6-l\xDB\x1D\xAE\xAAc\xBF@9s\x02\xC2\xD5[\xFB\x19Q\xFAI^Hj\xD1)\xBE\xEA\xA5!a\xAD\xBEO\xCE7\x9Em\x13;\xE8R\x8A\x81$\nv\xF4\xD3\xB25\xA9\xF9\xC0"
246
+ ```
247
+
248
+ ### 4. Load a Schnorr signature from binary serialized version
249
+
250
+ ```ruby
251
+ context = Secp256k1::Context.create
252
+
253
+ schnorr_sig_raw = "\x16\xEF\x04\xB0JP\xE9\x90\xC2\xB1\xE6-l\xDB\x1D\xAE\xAAc\xBF@9s\x02\xC2\xD5[\xFB\x19Q\xFAI^Hj\xD1)\xBE\xEA\xA5!a\xAD\xBEO\xCE7\x9Em\x13;\xE8R\x8A\x81$\nv\xF4\xD3\xB25\xA9\xF9\xC0"
254
+
255
+ Secp256k1::SchnorrSignature.from_data(schnorr_sig_raw)
256
+ => #<Secp256k1::SchnorrSignature:0x00007f4de1225818>
257
+ ```
258
+
200
259
  Recoverable Signature Examples
201
260
  ------------------------------
202
261
 
@@ -215,7 +274,7 @@ Secp256k1.have_recovery?
215
274
  You can sign data producing a recoverable signature as follows:
216
275
 
217
276
  ```ruby
218
- require 'digest'
277
+ yrequire 'digest'
219
278
 
220
279
  hash = Digest::SHA256.digest('test message')
221
280
  context = Secp256k1::Context.create
@@ -5,13 +5,6 @@ Secp256k1::KeyPair
5
5
 
6
6
  Secp256k1::KeyPair represents a public-private Secp256k1 key pair.
7
7
 
8
- Initializers
9
- ------------
10
-
11
- #### new(public_key, private_key)
12
-
13
- Initializes a new key pair with `public_key` (type: [PublicKey](public_key.md)) and `private_key` (type: [PrivateKey](private_key.md)).
14
-
15
8
  Instance Methods
16
9
  ----------------
17
10
 
@@ -19,6 +12,10 @@ Instance Methods
19
12
 
20
13
  Returns the [PublicKey](public_key.md) part of this key pair.
21
14
 
15
+ #### xonly_public_key
16
+
17
+ Returns the [XOnlyPublicKey](xonly_public_key.md).
18
+
22
19
  #### private_key
23
20
 
24
21
  Returns the [PrivateKey](private_key.md) part of this key pair.
@@ -27,6 +27,10 @@ Returns the binary compressed representation of this public key.
27
27
 
28
28
  Returns the binary uncompressed representation of this public key.
29
29
 
30
+ ### to_xonly
31
+
32
+ Returns the `XOnlyPublicKey` equivalent of this key.
33
+
30
34
  #### ==(other)
31
35
 
32
36
  Return `true` if this public key matches `other`.
@@ -0,0 +1,30 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::SchnorrSignature
4
+ ===========================
5
+
6
+ Secp256k1::SchnorrSignature represents an Schnorr signature signing a 32-byte message.
7
+
8
+ Class Methods
9
+ -------------
10
+
11
+ #### from_data(schnorr_sig_data)
12
+
13
+ Loads a new Schnorr signature from the given 64-byte binary
14
+ `schnorr_sig_data`. Does not perform any validation on the loaded data.
15
+
16
+ Instance Methods
17
+ ----------------
18
+
19
+ #### serialized
20
+
21
+ Returns the 64-byte binary `String` of the serialized Schnorr signature.
22
+
23
+ #### verify(msg, xonly_pubkey)
24
+
25
+ Returns `true` if the schnorr signature is a valid signing of `msg` with the
26
+ private key for `xonly_pubkey`, `false` otherwise.
27
+
28
+ #### ==(other)
29
+
30
+ Returns `true` if this signature matches `other`.
@@ -17,3 +17,8 @@ otherwise.
17
17
 
18
18
  Returns `true` if the EC Diffie-Hellman module was built with libsecp256k1,
19
19
  `false` otherwise.
20
+
21
+ #### have_schnorr?
22
+
23
+ Returns `true` if the Schnorr signature module was built with libsecp256k1,
24
+ `false` otherwise.
@@ -3,8 +3,6 @@
3
3
  Secp256k1::SharedSecret
4
4
  =======================
5
5
 
6
- **Requires:** libsecp256k1 was build with ECDH module.
7
-
8
6
  Secp256k1::SharedSecret represents a 32-byte shared secret computed from a
9
7
  public key (point) and private key (scalar).
10
8
 
@@ -0,0 +1,29 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::XOnlyPublicKey
4
+ =========================
5
+
6
+ Secp256k1::XOnlyPublicKey represents an x-only public key version of a public key.
7
+
8
+ See: [KeyPair](key_pair.md)
9
+
10
+ Class Methods
11
+ -------------
12
+
13
+ #### from_data(xonly_public_key_serialized)
14
+
15
+ Parses the 32-byte serialized binary `xonly_public_key_serialized` and creates
16
+ and returns a new x-only public key from it. Raises a
17
+ `Secp256k1::DeserializationError` if the given x-only public key data is
18
+ invalid.
19
+
20
+ Instance Methods
21
+ ----------------
22
+
23
+ #### serialized
24
+
25
+ Serializes the `XOnlyPublicKey` into a 32-byte binary `String`.
26
+
27
+ #### ==(other)
28
+
29
+ Return `true` if this x-only public key matches `other`.
@@ -4,32 +4,28 @@ require 'mini_portile2'
4
4
  require 'mkmf'
5
5
  require 'zip'
6
6
 
7
+ # Enable the recovery module by default
8
+ WITH_RECOVERY = ENV.fetch('WITH_RECOVERY', '1') == '1'
9
+
7
10
  # Recipe for downloading and building libsecp256k1 as part of installation
8
11
  class Secp256k1Recipe < MiniPortile
9
- # Hard-coded URL for libsecp256k1 zipfile (HEAD of master as of 24-11-2021)
10
- LIBSECP256K1_ZIP_URL = 'https://github.com/bitcoin-core/secp256k1/archive/fecf436d5327717801da84beb3066f5a9b80ea8e.zip'
12
+ # Hard-coded URL for libsecp256k1 zipfile (Official release v0.2.0)
13
+ LIBSECP256K1_ZIP_URL = 'https://github.com/bitcoin-core/secp256k1/archive/refs/tags/v0.2.0.zip'
11
14
 
12
15
  # Expected SHA-256 of the zipfile above (computed using sha256sum)
13
- LIBSECP256K1_SHA256 = '188c6252ab9e829da02c962fe59a329f798c615577ac128ae1f0697126ebb2fc'
14
-
15
- WITH_RECOVERY = ENV.fetch('WITH_RECOVERY', '1') == '1'
16
- WITH_ECDH = ENV.fetch('WITH_ECDH', '1') == '1'
16
+ LIBSECP256K1_SHA256 = '6ece280c0e6ea9d861051077c28a25b7f48800c43a4098a800b7d3b0c124e406'
17
17
 
18
18
  def initialize
19
- super('libsecp256k1', '0.0.0')
19
+ super('libsecp256k1', '0.2.0')
20
20
  @tarball = File.join(Dir.pwd, "/ports/archives/libsecp256k1.zip")
21
21
  @files = ["file://#{@tarball}"]
22
22
  self.configure_options += [
23
- "--disable-benchmark",
24
- "--disable-exhaustive-tests",
25
- "--disable-tests",
26
- "--disable-debug",
27
- "--enable-experimental",
28
23
  "--with-pic=yes"
29
24
  ]
30
25
 
26
+ # ECDH is enabled by default in release v0.2.0, but recovery still needs to
27
+ # be enabled manually.
31
28
  configure_options << "--enable-module-recovery" if WITH_RECOVERY
32
- configure_options << "--enable-module-ecdh" if WITH_ECDH
33
29
  end
34
30
 
35
31
  def configure
@@ -106,11 +102,20 @@ else
106
102
  have_library("gmp")
107
103
  end
108
104
 
105
+ # Sanity check for the basic library
106
+ have_header('secp256k1.h')
107
+
109
108
  # Check if we have the libsecp256k1 recoverable signature header.
110
- have_header('secp256k1_recovery.h')
109
+ have_header('secp256k1_recovery.h') if WITH_RECOVERY
111
110
 
112
111
  # Check if we have EC Diffie-Hellman functionality
113
112
  have_header('secp256k1_ecdh.h')
114
113
 
114
+ # Check if we have Schnorr signatures
115
+ have_header('secp256k1_schnorrsig.h')
116
+
117
+ # Check if we have extra keys module
118
+ have_header('secp256k1_extrakeys.h')
119
+
115
120
  # See: https://guides.rubygems.org/gems-with-extensions/
116
121
  create_makefile('rbsecp256k1/rbsecp256k1')