bitcoin-secp256k1 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07e0b24c6a5eff2bebfa1287f30299e3e20516a9
4
+ data.tar.gz: 64b92f4a1274272b62a2b4f8447f7f97b3726e05
5
+ SHA512:
6
+ metadata.gz: f4123386a3b2add1c25e711eae40013a418fad7d614f0283caa0f5f8e961889de623be103b023035d441d42d4823b964ea129218d857ad66c722560b29f910f1
7
+ data.tar.gz: 45abd20b35e91cbf4ac5b33fa3b472a32383f140426edce77eabab58180f39ea9afc4e63b85f4cfc6a59f257e6e1dfe6ed8bca1ac3199e8239dedae860ada7bb
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ tmp
3
+ lib/ethash/ethash.so
4
+ tags
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "secp256k1"]
2
+ path = secp256k1
3
+ url = git@github.com:bitcoin/secp256k1.git
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,25 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bitcoin-secp256k1 (0.1.1)
5
+ ffi (>= 1.9.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ffi (1.9.10)
11
+ minitest (5.8.3)
12
+ rake (10.5.0)
13
+ yard (0.8.7.6)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ bitcoin-secp256k1!
20
+ minitest (= 5.8.3)
21
+ rake (~> 10.5)
22
+ yard (= 0.8.7.6)
23
+
24
+ BUNDLED WITH
25
+ 1.11.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jan Xie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # ruby-bitcoin-secp256k1
2
+
3
+ ## Prerequiste
4
+
5
+ In order to use this gem, [libsecp256k1](https://github.com/bitcoin/secp256k1) with recovery module enabled must be in place.
6
+
7
+ If you have cloned the project and in project root:
8
+
9
+ ```
10
+ git submodule update --init --recursive
11
+ ./install_lib.sh
12
+ ```
13
+
14
+ ## Install
15
+
16
+ ```
17
+ gem i bitcoin-secp256k1
18
+ ```
19
+
20
+ Then require 'secp256k1' (without `bitcoin-` prefix) in your source code.
21
+
22
+ ## Usage
23
+
24
+ Check [test](test) for examples.
25
+
26
+ ## LICENSE
27
+
28
+ [MIT License](LICENSE)
29
+
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs += %w(lib test)
5
+ t.test_files = FileList['test/**/*_test.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task default: [:test]
@@ -0,0 +1,23 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require 'secp256k1/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "bitcoin-secp256k1"
7
+ s.version = Secp256k1::VERSION
8
+ s.authors = ["Jan Xie"]
9
+ s.email = ["jan.h.xie@gmail.com"]
10
+ s.homepage = "https://github.com/janx/ruby-bitcoin-secp256k1"
11
+ s.summary = "Ruby binding to bitcoin's secp256k1 implementation."
12
+ s.description = "Ruby binding to bitcoin's secp256k1 implementation."
13
+ s.license = 'MIT'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+
18
+ s.add_dependency('ffi', '>= 1.9.10')
19
+
20
+ s.add_development_dependency('rake', '~> 10.5')
21
+ s.add_development_dependency('minitest', '5.8.3')
22
+ s.add_development_dependency('yard', '0.8.7.6')
23
+ end
data/install_lib.sh ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ pushd secp256k1
4
+ ./autogen.sh
5
+ ./configure --enable-module-recovery --enable-experimental --enable-module-ecdh --enable-module-schnorr
6
+ make && sudo make install
7
+ popd
data/lib/secp256k1.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'secp256k1/version'
2
+ require 'secp256k1/c'
3
+ require 'secp256k1/utils'
4
+ require 'secp256k1/ecdsa'
5
+ require 'secp256k1/key'
6
+
7
+ module Secp256k1
8
+
9
+ EC_COMPRESSED = C::Constants['SECP256K1_EC_COMPRESSED'].to_i
10
+ EC_UNCOMPRESSED = C::Constants['SECP256K1_EC_UNCOMPRESSED'].to_i
11
+
12
+ FLAG_SIGN = C::Constants['SECP256K1_CONTEXT_SIGN'].to_i
13
+ FLAG_VERIFY = C::Constants['SECP256K1_CONTEXT_VERIFY'].to_i
14
+ NO_FLAGS = C::Constants['SECP256K1_CONTEXT_NONE'].to_i
15
+ ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY
16
+
17
+ class AssertError < StandardError; end
18
+
19
+ end
@@ -0,0 +1,61 @@
1
+ require 'ffi'
2
+ require 'ffi/tools/const_generator'
3
+
4
+ module Secp256k1
5
+ module C
6
+ extend FFI::Library
7
+
8
+ ffi_lib (ENV['LIBSECP256K1'] || 'libsecp256k1')
9
+
10
+ Constants = FFI::ConstGenerator.new('Secp256k1', required: true) do |gen|
11
+ gen.include 'secp256k1.h'
12
+
13
+ gen.const(:SECP256K1_EC_COMPRESSED)
14
+ gen.const(:SECP256K1_EC_UNCOMPRESSED)
15
+
16
+ gen.const(:SECP256K1_CONTEXT_SIGN)
17
+ gen.const(:SECP256K1_CONTEXT_VERIFY)
18
+ gen.const(:SECP256K1_CONTEXT_NONE)
19
+ end
20
+
21
+ class Pubkey < FFI::Struct
22
+ layout :data, [:uchar, 64]
23
+ end
24
+
25
+ class ECDSASignature < FFI::Struct
26
+ layout :data, [:uchar, 64]
27
+ end
28
+
29
+ class ECDSARecoverableSignature < FFI::Struct
30
+ layout :data, [:uchar, 65]
31
+ end
32
+
33
+ # secp256k1_context* secp256k1_context_create(unsigned int flags)
34
+ attach_function :secp256k1_context_create, [:uint], :pointer
35
+
36
+ # int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey)
37
+ attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int
38
+
39
+ # int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey)
40
+ attach_function :secp256k1_ec_seckey_verify, [:pointer, :pointer], :int
41
+
42
+ # int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig)
43
+ attach_function :secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int
44
+
45
+ # int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig)
46
+ attach_function :secp256k1_ecdsa_signature_serialize_compact, [:pointer, :pointer, :pointer], :int
47
+
48
+ # int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen)
49
+ attach_function :secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int
50
+
51
+ # int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64)
52
+ attach_function :secp256k1_ecdsa_signature_parse_compact, [:pointer, :pointer, :pointer], :int
53
+
54
+ # int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void *ndata)
55
+ attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
56
+
57
+ # int secp256k1_ecdsa_verify(const secp256k1_context *ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey)
58
+ attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int
59
+
60
+ end
61
+ end
@@ -0,0 +1,47 @@
1
+ module Secp256k1
2
+ module ECDSA
3
+
4
+ SIZE_SERIALIZED = 74
5
+ SIZE_COMPACT = 64
6
+
7
+ def ecdsa_serialize(raw_sig)
8
+ output = FFI::MemoryPointer.new(:uchar, SIZE_SERIALIZED)
9
+ outputlen = FFI::MemoryPointer.new(:size_t).put_uint(0, SIZE_SERIALIZED)
10
+
11
+ res = C.secp256k1_ecdsa_signature_serialize_der(@ctx, output, outputlen, raw_sig)
12
+ raise AssertError, "failed to seriazlie signature" unless res == 1
13
+
14
+ output.read_bytes(outputlen.read_uint)
15
+ end
16
+
17
+ def ecdsa_deserialize(ser_sig)
18
+ raw_sig = C::ECDSASignature.new.pointer
19
+
20
+ res = C.secp256k1_ecdsa_signature_parse_der(@ctx, raw_sig, ser_sig, ser_sig.size)
21
+ raise AssertError, "raw signature parse failed" unless res == 1
22
+
23
+ raw_sig
24
+ end
25
+
26
+ def ecdsa_serialize_compact(raw_sig)
27
+ output = FFI::MemoryPointer.new(:uchar, SIZE_COMPACT)
28
+
29
+ res = C.secp256k1_ecdsa_signature_serialize_compact(@ctx, output, raw_sig)
30
+ raise AssertError, "failed to seriazlie compact signature" unless res == 1
31
+
32
+ output.read_bytes(SIZE_COMPACT)
33
+ end
34
+
35
+ def ecdsa_deserialize_compact(ser_sig)
36
+ raise ArgumentError, 'invalid signature length' unless ser_sig.size == 64
37
+
38
+ raw_sig = C::ECDSASignature.new.pointer
39
+
40
+ res = C.secp256k1_ecdsa_signature_parse_compact(@ctx, raw_sig, ser_sig)
41
+ raise AssertError, "failed to deserialize compact signature" unless res == 1
42
+
43
+ raw_sig
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,253 @@
1
+ require 'digest'
2
+ require 'securerandom'
3
+
4
+ module Secp256k1
5
+
6
+ class BaseKey
7
+ def initialize(ctx, flags)
8
+ @destroy = false
9
+
10
+ unless ctx
11
+ raise ArgumentError, "invalid flags" unless [NO_FLAGS, FLAG_SIGN, FLAG_VERIFY, ALL_FLAGS].include?(flags)
12
+ ctx = C.secp256k1_context_create flags
13
+ @destroy = true
14
+ end
15
+
16
+ @flags = flags
17
+ @ctx = ctx
18
+
19
+ ObjectSpace.define_finalizer(self) do |id|
20
+ C.secp256k1_context_destroy @ctx if @destroy
21
+ end
22
+ end
23
+ end
24
+
25
+ class PublicKey < BaseKey
26
+ include ECDSA, Utils
27
+
28
+ attr :public_key
29
+
30
+ def initialize(pubkey: nil, raw: false, flags: FLAG_VERIFY, ctx: nil)
31
+ super(ctx, flags)
32
+
33
+ if pubkey
34
+ if raw
35
+ raise ArgumentError, 'raw pubkey must be bytes' unless pubkey.instance_of?(String)
36
+ @public_key = deserialize pubkey
37
+ else
38
+ #raise ArgumentError, 'pubkey must be an internal object' unless pubkey.instance_of?(String)
39
+ @public_key = pubkey
40
+ end
41
+ else
42
+ @public_key = nil
43
+ end
44
+ end
45
+
46
+ def serialize(compressed: true)
47
+ raise AssertError, 'No public key defined' unless @public_key
48
+
49
+ len_compressed = compressed ? 33 : 65
50
+ res_compressed = FFI::MemoryPointer.new :char, len_compressed
51
+ outlen = FFI::MemoryPointer.new(:size_t).write_uint(len_compressed)
52
+ compflag = compressed ? EC_COMPRESSED : EC_UNCOMPRESSED
53
+
54
+ res = C.secp256k1_ec_pubkey_serialize(@ctx, res_compressed, outlen, @public_key, compflag)
55
+ raise AssertError, 'pubkey serialization failed' unless res == 1
56
+
57
+ res_compressed.read_bytes(len_compressed)
58
+ end
59
+
60
+ def deserialize(pubkey_ser)
61
+ raise ArgumentError, 'unknown public key size (expected 33 or 65)' unless [33,65].include?(pubkey_ser.size)
62
+
63
+ pubkey = C::Pubkey.new.pointer
64
+
65
+ res = C.secp256k1_ec_pubkey_parse(@ctx, pubkey, pubkey_ser, pubkey_ser.size)
66
+ raise AssertError, 'invalid public key' unless res == 1
67
+
68
+ @public_key = pubkey
69
+ pubkey
70
+ end
71
+
72
+ ##
73
+ # Add a number of public keys together.
74
+ #
75
+ def combine(pubkeys)
76
+ raise ArgumentError, 'must give at least 1 pubkey' if pubkeys.empty?
77
+
78
+ outpub = FFI::Pubkey.new.pointer
79
+ #pubkeys.each {|item| }
80
+
81
+ res = C.secp256k1_ec_pubkey_combine(@ctx, outpub, pubkeys, pubkeys.size)
82
+ raise AssertError, 'failed to combine public keys' unless res == 1
83
+
84
+ @public_key = outpub
85
+ outpub
86
+ end
87
+
88
+ ##
89
+ # Tweak the current public key by adding a 32 byte scalar times the
90
+ # generator to it and return a new PublicKey instance.
91
+ #
92
+ def tweak_add(scalar)
93
+ tweak_public :secp256k1_ec_pubkey_tweak_add, scalar
94
+ end
95
+
96
+ ##
97
+ # Tweak the current public key by multiplying it by a 32 byte scalar and
98
+ # return a new PublicKey instance.
99
+ #
100
+ def tweak_mul(scalar)
101
+ tweak_public :secp256k1_ec_pubkey_tweak_mul, scalar
102
+ end
103
+
104
+ def ecdsa_verify(msg, raw_sig, raw: false, digest: Digest::SHA256)
105
+ raise AssertError, 'No public key defined' unless @public_key
106
+ raise AssertError, 'instance not configured for sig verification' if (@flags & FLAG_VERIFY) != FLAG_VERIFY
107
+
108
+ msg32 = hash32 msg, raw, digest
109
+
110
+ !!C.secp256k1_ecdsa_verify(@ctx, raw_sig, msg32, @public_key)
111
+ end
112
+
113
+ def ecdh(scalar)
114
+ raise AssertError, 'No public key defined' unless @public_key
115
+ raise ArgumentError, 'scalar must be composed of 32 bytes' unless scalar.instance_of?(String) && scalar.size == 32
116
+
117
+ result = FFI::MemoryPointer.new :char, 32
118
+
119
+ res = C.secp256k1_ecdh @ctx, result, @public_key, scalar
120
+ raise AssertError, "invalid scalar (#{scalar})" unless res == 1
121
+
122
+ result.read_bytes(32)
123
+ end
124
+
125
+ private
126
+
127
+ def tweak_public(meth, scalar)
128
+ raise ArgumentError, 'scalar must be composed of 32 bytes' unless scalar.instance_of?(String) && scalar.size == 32
129
+ raise AssertError, 'No public key defined.' unless @public_key
130
+
131
+ newpub = self.class.new serialize, raw: true
132
+
133
+ res = C.send meth, newpub.public_key, scalar
134
+ raise AssertError, 'Tweak is out of range' unless res == 1
135
+
136
+ newpub
137
+ end
138
+
139
+ end
140
+
141
+ class PrivateKey < BaseKey
142
+ include ECDSA, Utils
143
+
144
+ attr :pubkey
145
+
146
+ def initialize(privkey: nil, raw: true, flags: ALL_FLAGS, ctx: nil)
147
+ raise AssertError, "invalid flags" unless [ALL_FLAGS, FLAG_SIGN].include?(flags)
148
+
149
+ super(ctx, flags)
150
+
151
+ @pubkey = nil
152
+ @private_key = nil
153
+
154
+ if privkey
155
+ if raw
156
+ raise ArgumentError, "privkey must be composed of 32 bytes" unless privkey.instance_of?(String) && privkey.size == 32
157
+ set_raw_privkey privkey
158
+ else
159
+ deserialize privkey
160
+ end
161
+ else
162
+ set_raw_privkey generate_private_key
163
+ end
164
+ end
165
+
166
+ def ecdsa_sign(msg, raw: false, digest: Digest::SHA256)
167
+ msg32 = hash32 msg, raw, digest
168
+ raw_sig = C::ECDSASignature.new.pointer
169
+
170
+ res = C.secp256k1_ecdsa_sign @ctx, raw_sig, msg32, @private_key, nil, nil
171
+ raise AssertError, "failed to sign" unless res == 1
172
+
173
+ raw_sig
174
+ end
175
+
176
+ def ecdsa_sign_recoverable(msg, raw: false, digest: Digest::SHA256)
177
+ msg32 = hash32 msg, raw, digest
178
+ raw_sig = FFI::MemoryPointer.new :byte, C::ECDSARecoverableSignature, false
179
+
180
+ res = C.secp256k1_ecdsa_sign_recoverable @ctx, raw_sig, msg32, @private_key, nil, nil
181
+ raise AssertError, "failed to sign" unless res == 1
182
+
183
+ raw_sig
184
+ end
185
+
186
+ def set_raw_privkey(privkey)
187
+ raise ArgumentError, "invalid private key" unless C.secp256k1_ec_seckey_verify(@ctx, privkey)
188
+ @private_key = privkey
189
+ update_public_key
190
+ end
191
+
192
+ ##
193
+ # Tweak the current private key by adding a 32 bytes scalar to it and
194
+ # return a new raw private key composed of 32 bytes.
195
+ #
196
+ def tweak_add(scalar)
197
+ tweak_private :secp256k1_ec_privkey_tweak_add, scalar
198
+ end
199
+
200
+ ##
201
+ # Tweak the current private key by multiplying it by a 32 byte scalar and
202
+ # return a new raw private key composed of 32 bytes.
203
+ #
204
+ def tweak_mul(scalar)
205
+ tweak_private :secp256k1_ec_pubkey_tweak_mul, scalar
206
+ end
207
+
208
+ private
209
+
210
+ def tweak_private(meth, scalar)
211
+ raise ArgumentError, "scalar must be composed of 32 bytes" unless scalar.instance_of?(String) && scalar.size == 32
212
+
213
+ key = FFI::MemoryPointer.new(:uchar, 32).put_string(@private_key)
214
+
215
+ C.send meth, @ctx, key, scalar
216
+ raise AssertError, "Tweak is out of range" unless res == 1
217
+
218
+ key.read_string(32)
219
+ end
220
+
221
+ def update_public_key
222
+ public_key = generate_public_key @private_key
223
+ @pubkey = PublicKey.new pubkey: public_key, raw: false, ctx: @ctx, flags: @flags
224
+ end
225
+
226
+ def generate_public_key(privkey)
227
+ pubkey_ptr = C::Pubkey.new.pointer
228
+
229
+ res = C.secp256k1_ec_pubkey_create @ctx, pubkey_ptr, privkey
230
+ raise AssertError, "failed to generate public key" unless res == 1
231
+
232
+ pubkey_ptr
233
+ end
234
+
235
+ def generate_private_key
236
+ SecureRandom.random_bytes(32)
237
+ end
238
+
239
+ def serialize
240
+ encode_hex @private_key
241
+ end
242
+
243
+ def deserialize(privkey_serialized)
244
+ raise ArgumentError, "invalid private key" unless privkey_serialized.size == 64
245
+
246
+ rawkey = decode_hex privkey_serialized
247
+ set_raw_privkey rawkey
248
+
249
+ @private_key
250
+ end
251
+
252
+ end
253
+ end