rbsecp256k1 5.1.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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')