monacoin-ruby 0.1.2 → 0.1.3

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.
@@ -0,0 +1,191 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+
5
+ def self.hmac_sha512(key, data)
6
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA512'), key, data)
7
+ end
8
+
9
+ # Integers modulo the order of the curve(secp256k1)
10
+ CURVE_ORDER = 115792089237316195423570985008687907852837564279074904382605163141518161494337
11
+
12
+ # BIP32 Extended private key
13
+ class ExtKey
14
+
15
+ attr_accessor :depth
16
+ attr_accessor :number
17
+ attr_accessor :chain_code
18
+ attr_accessor :priv_key
19
+ attr_accessor :parent_fingerprint
20
+
21
+ # generate master key from seed.
22
+ def self.generate_master(seed)
23
+ key = ExtKey.new
24
+ key.depth = key.number = 0
25
+ key.parent_fingerprint = '00000000'
26
+ l = Bitcoin.hmac_sha512('Bitcoin seed', seed)
27
+ left = OpenSSL::BN.from_hex(l[0..31].bth).to_i
28
+ raise 'invalid key' if left >= CURVE_ORDER || left == 0
29
+ key.priv_key = Bitcoin::Key.new(l[0..31].bth)
30
+ key.chain_code = l[32..-1]
31
+ key
32
+ end
33
+
34
+ # get ExtPubkey from priv_key
35
+ def ext_pubkey
36
+ k = ExtPubkey.new
37
+ k.depth = depth
38
+ k.number = number
39
+ k.parent_fingerprint = parent_fingerprint
40
+ k.chain_code = chain_code
41
+ key = Bitcoin::Key.new(nil, priv_key.pub, compressed: true)
42
+ k.pub_key = key.key.public_key
43
+ k
44
+ end
45
+
46
+ # serialize extended private key
47
+ def to_payload
48
+ Bitcoin.network[:extended_privkey_version].htb << [depth].pack('C') << parent_fingerprint.htb << [number].pack('N') << chain_code << [0x00].pack('C') << priv_key.priv.htb
49
+ end
50
+
51
+ # Base58 encoded extended private key
52
+ def to_base58
53
+ h = to_payload.bth
54
+ hex = h + Bitcoin.checksum(h)
55
+ Bitcoin.encode_base58(hex)
56
+ end
57
+
58
+ # get private key(hex)
59
+ def priv
60
+ priv_key.priv
61
+ end
62
+
63
+ # get public key(hex)
64
+ def pub
65
+ priv_key.pub
66
+ end
67
+
68
+ # get address
69
+ def addr
70
+ priv_key.addr
71
+ end
72
+
73
+ # get key identifier
74
+ def identifier
75
+ Bitcoin.hash160(priv_key.pub)
76
+ end
77
+
78
+ # get fingerprint
79
+ def fingerprint
80
+ identifier.slice(0..7)
81
+ end
82
+
83
+ # derive new key
84
+ def derive(number)
85
+ new_key = ExtKey.new
86
+ new_key.depth = depth + 1
87
+ new_key.number = number
88
+ new_key.parent_fingerprint = fingerprint
89
+ if number > (2**31 -1)
90
+ data = [0x00].pack('C') << priv_key.priv.htb << [number].pack('N')
91
+ else
92
+ data = priv_key.pub.htb << [number].pack('N')
93
+ end
94
+ l = Bitcoin.hmac_sha512(chain_code, data)
95
+ left = OpenSSL::BN.from_hex(l[0..31].bth).to_i
96
+ raise 'invalid key' if left >= CURVE_ORDER
97
+ child_priv = OpenSSL::BN.new((left + OpenSSL::BN.from_hex(priv_key.priv).to_i) % CURVE_ORDER)
98
+ raise 'invalid key ' if child_priv.to_i >= CURVE_ORDER
99
+ new_key.priv_key = Bitcoin::Key.new(child_priv.to_hex.rjust(64, '0'))
100
+ new_key.chain_code = l[32..-1]
101
+ new_key
102
+ end
103
+
104
+ # import private key from Base58 private key address
105
+ def self.from_base58(address)
106
+ data = StringIO.new(Bitcoin.decode_base58(address).htb)
107
+ key = ExtKey.new
108
+ data.read(4).bth # version
109
+ key.depth = data.read(1).unpack('C').first
110
+ key.parent_fingerprint = data.read(4).bth
111
+ key.number = data.read(4).unpack('N').first
112
+ key.chain_code = data.read(32)
113
+ data.read(1) # 0x00
114
+ key.priv_key = Bitcoin::Key.new(data.read(32).bth)
115
+ key
116
+ end
117
+
118
+ end
119
+
120
+ # BIP-32 Extended public key
121
+ class ExtPubkey
122
+ attr_accessor :depth
123
+ attr_accessor :number
124
+ attr_accessor :chain_code
125
+ attr_accessor :pub_key
126
+ attr_accessor :parent_fingerprint
127
+
128
+ # serialize extended pubkey
129
+ def to_payload
130
+ Bitcoin.network[:extended_pubkey_version].htb << [depth].pack('C') << parent_fingerprint.htb << [number].pack('N') << chain_code << pub.htb
131
+ end
132
+
133
+ # get public key(hex)
134
+ def pub
135
+ pub_key.group.point_conversion_form = :compressed
136
+ pub_key.to_hex.rjust(66, '0')
137
+ end
138
+
139
+ # get address
140
+ def addr
141
+ Bitcoin.hash160_to_address(Bitcoin.hash160(pub))
142
+ end
143
+
144
+ # get key identifier
145
+ def identifier
146
+ Bitcoin.hash160(pub)
147
+ end
148
+
149
+ # get fingerprint
150
+ def fingerprint
151
+ identifier.slice(0..7)
152
+ end
153
+
154
+ # Base58 encoded extended pubkey
155
+ def to_base58
156
+ h = to_payload.bth
157
+ hex = h + Bitcoin.checksum(h)
158
+ Bitcoin.encode_base58(hex)
159
+ end
160
+
161
+ # derive child key
162
+ def derive(number)
163
+ new_key = ExtPubkey.new
164
+ new_key.depth = depth + 1
165
+ new_key.number = number
166
+ new_key.parent_fingerprint = fingerprint
167
+ raise 'hardened key is not support' if number > (2**31 -1)
168
+ data = pub.htb << [number].pack('N')
169
+ l = Bitcoin.hmac_sha512(chain_code, data)
170
+ left = OpenSSL::BN.from_hex(l[0..31].bth)
171
+ raise 'invalid key' if left.to_i >= CURVE_ORDER
172
+ new_key.pub_key = Bitcoin.bitcoin_elliptic_curve.group.generator.mul(left).ec_add(pub_key)
173
+ new_key.chain_code = l[32..-1]
174
+ new_key
175
+ end
176
+
177
+ # import private key from Base58 private key address
178
+ def self.from_base58(address)
179
+ data = StringIO.new(Bitcoin.decode_base58(address).htb)
180
+ key = ExtPubkey.new
181
+ data.read(4).bth # version
182
+ key.depth = data.read(1).unpack('C').first
183
+ key.parent_fingerprint = data.read(4).bth
184
+ key.number = data.read(4).unpack('N').first
185
+ key.chain_code = data.read(32)
186
+ key.pub_key = OpenSSL::PKey::EC::Point.from_hex(Bitcoin.bitcoin_elliptic_curve.group, data.read(33).bth)
187
+ key
188
+ end
189
+ end
190
+
191
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'ffi'
4
+
5
+ # binding for src/.libs/bitcoinconsensus.so (https://github.com/bitcoin/bitcoin)
6
+ # tag: v0.11.0
7
+ # commit: d26f951802c762de04fb68e1a112d611929920ba
8
+
9
+ module Bitcoin
10
+ module BitcoinConsensus
11
+ extend FFI::Library
12
+
13
+ SCRIPT_VERIFY_NONE = 0
14
+ SCRIPT_VERIFY_P2SH = (1 << 0)
15
+ SCRIPT_VERIFY_STRICTENC = (1 << 1)
16
+ SCRIPT_VERIFY_DERSIG = (1 << 2)
17
+ SCRIPT_VERIFY_LOW_S = (1 << 3)
18
+ SCRIPT_VERIFY_NULLDUMMY = (1 << 4)
19
+ SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5)
20
+ SCRIPT_VERIFY_MINIMALDATA = (1 << 6)
21
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7)
22
+ SCRIPT_VERIFY_CLEANSTACK = (1 << 8)
23
+
24
+ ERR_CODES = { 0 => :ok, 1 => :tx_index, 2 => :tx_size_mismatch, 3 => :tx_deserialize }
25
+
26
+ def self.ffi_load_functions(file)
27
+ class_eval <<-RUBY
28
+ ffi_lib [ %[#{file}] ]
29
+ attach_function :bitcoinconsensus_version, [], :uint
30
+
31
+ # int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
32
+ # const unsigned char *txTo , unsigned int txToLen,
33
+ # unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
34
+ attach_function :bitcoinconsensus_verify_script, [:pointer, :uint, :pointer, :uint, :uint, :uint, :pointer], :int
35
+ RUBY
36
+ end
37
+
38
+ def self.lib_available?
39
+ @__lib_path ||= [ ENV['BITCOINCONSENSUS_LIB_PATH'], 'vendor/bitcoin/src/.libs/libbitcoinconsensus.so' ].find{|f| File.exists?(f.to_s) }
40
+ end
41
+
42
+ def self.init
43
+ return if @bitcoin_consensus
44
+ lib_path = lib_available?
45
+ ffi_load_functions(lib_path)
46
+ @bitcoin_consensus = true
47
+ end
48
+
49
+ # api version
50
+ def self.version
51
+ init
52
+ bitcoinconsensus_version
53
+ end
54
+
55
+ def self.verify_script(input_index, script_pubkey, tx_payload, script_flags)
56
+ init
57
+
58
+ scriptPubKey = FFI::MemoryPointer.new(:uchar, script_pubkey.bytesize).put_bytes(0, script_pubkey)
59
+ txTo = FFI::MemoryPointer.new(:uchar, tx_payload.bytesize).put_bytes(0, tx_payload)
60
+ error_ret = FFI::MemoryPointer.new(:uint)
61
+
62
+ ret = bitcoinconsensus_verify_script(scriptPubKey, scriptPubKey.size, txTo, txTo.size, input_index, script_flags, error_ret)
63
+
64
+ case ret
65
+ when 0
66
+ false
67
+ when 1
68
+ (ERR_CODES[error_ret.read_int] == :ok) ? true : false
69
+ else
70
+ raise "error invalid result"
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,388 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # autoload when you need to re-generate a public_key from only its private_key.
4
+ # ported from: https://github.com/sipa/bitcoin/blob/2d40fe4da9ea82af4b652b691a4185431d6e47a8/key.h
5
+
6
+ require 'ffi'
7
+
8
+ module Bitcoin
9
+ module OpenSSL_EC
10
+ extend FFI::Library
11
+ if FFI::Platform.windows?
12
+ ffi_lib 'libeay32', 'ssleay32'
13
+ else
14
+ ffi_lib [ 'libssl.so.1.0.0', 'ssl' ]
15
+ end
16
+
17
+ NID_secp256k1 = 714
18
+ POINT_CONVERSION_COMPRESSED = 2
19
+ POINT_CONVERSION_UNCOMPRESSED = 4
20
+
21
+ attach_function :SSL_library_init, [], :int
22
+ attach_function :ERR_load_crypto_strings, [], :void
23
+ attach_function :SSL_load_error_strings, [], :void
24
+ attach_function :RAND_poll, [], :int
25
+
26
+ attach_function :BN_CTX_free, [:pointer], :int
27
+ attach_function :BN_CTX_new, [], :pointer
28
+ attach_function :BN_add, [:pointer, :pointer, :pointer], :int
29
+ attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
30
+ attach_function :BN_bn2bin, [:pointer, :pointer], :int
31
+ attach_function :BN_cmp, [:pointer, :pointer], :int
32
+ attach_function :BN_copy, [:pointer, :pointer], :pointer
33
+ attach_function :BN_dup, [:pointer], :pointer
34
+ attach_function :BN_free, [:pointer], :int
35
+ attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
36
+ attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
37
+ attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
38
+ attach_function :BN_mul_word, [:pointer, :int], :int
39
+ attach_function :BN_new, [], :pointer
40
+ attach_function :BN_rshift, [:pointer, :pointer, :int], :int
41
+ attach_function :BN_rshift1, [:pointer, :pointer], :int
42
+ attach_function :BN_set_word, [:pointer, :int], :int
43
+ attach_function :BN_sub, [:pointer, :pointer, :pointer], :int
44
+ attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
45
+ attach_function :EC_GROUP_get_degree, [:pointer], :int
46
+ attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
47
+ attach_function :EC_KEY_free, [:pointer], :int
48
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
49
+ attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
50
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
51
+ attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
52
+ attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
53
+ attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
54
+ attach_function :EC_POINT_free, [:pointer], :int
55
+ attach_function :EC_POINT_is_at_infinity, [:pointer, :pointer], :int
56
+ attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
57
+ attach_function :EC_POINT_new, [:pointer], :pointer
58
+ attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
59
+ attach_function :d2i_ECPrivateKey, [:pointer, :pointer, :long], :pointer
60
+ attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
61
+ attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
62
+ attach_function :EC_KEY_check_key, [:pointer], :uint
63
+ attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
64
+ attach_function :BN_num_bits, [:pointer], :int
65
+ attach_function :ECDSA_SIG_free, [:pointer], :void
66
+ attach_function :EC_POINT_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
67
+ attach_function :EC_POINT_point2hex, [:pointer, :pointer, :int, :pointer], :string
68
+ attach_function :EC_POINT_hex2point, [:pointer, :string, :pointer, :pointer], :pointer
69
+ attach_function :ECDSA_SIG_new, [], :pointer
70
+ attach_function :d2i_ECDSA_SIG, [:pointer, :pointer, :long], :pointer
71
+ attach_function :i2d_ECDSA_SIG, [:pointer, :pointer], :int
72
+ attach_function :OPENSSL_free, :CRYPTO_free, [:pointer], :void
73
+
74
+ def self.BN_num_bytes(ptr); (BN_num_bits(ptr) + 7) / 8; end
75
+
76
+
77
+ # resolve public from private key, using ffi and libssl.so
78
+ # example:
79
+ # keypair = Bitcoin.generate_key; Bitcoin::OpenSSL_EC.regenerate_key(keypair.first) == keypair
80
+ def self.regenerate_key(private_key)
81
+ private_key = [private_key].pack("H*") if private_key.bytesize >= (32*2)
82
+ private_key_hex = private_key.unpack("H*")[0]
83
+
84
+ #private_key = FFI::MemoryPointer.new(:uint8, private_key.bytesize)
85
+ # .put_bytes(0, private_key, 0, private_key.bytesize)
86
+ private_key = FFI::MemoryPointer.from_string(private_key)
87
+
88
+ init_ffi_ssl
89
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
90
+ #priv_key = BN_bin2bn(private_key, private_key.size, BN_new())
91
+ priv_key = BN_bin2bn(private_key, private_key.size-1, BN_new())
92
+
93
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
94
+ EC_GROUP_get_order(group, order, ctx)
95
+
96
+ pub_key = EC_POINT_new(group)
97
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
98
+ EC_KEY_set_private_key(eckey, priv_key)
99
+ EC_KEY_set_public_key(eckey, pub_key)
100
+
101
+ BN_free(order)
102
+ BN_CTX_free(ctx)
103
+ EC_POINT_free(pub_key)
104
+ BN_free(priv_key)
105
+
106
+
107
+ length = i2d_ECPrivateKey(eckey, nil)
108
+ buf = FFI::MemoryPointer.new(:uint8, length)
109
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
110
+ priv_hex = if i2d_ECPrivateKey(eckey, ptr) == length
111
+ size = buf.get_array_of_uint8(8, 1)[0]
112
+ buf.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00").unpack("H*")[0]
113
+ #der_to_private_key( ptr.read_pointer.read_string(length).unpack("H*")[0] )
114
+ end
115
+
116
+ if priv_hex != private_key_hex
117
+ raise "regenerated wrong private_key, raise here before generating a faulty public_key too!"
118
+ end
119
+
120
+
121
+ length = i2o_ECPublicKey(eckey, nil)
122
+ buf = FFI::MemoryPointer.new(:uint8, length)
123
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
124
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
125
+ buf.read_string(length).unpack("H*")[0]
126
+ end
127
+
128
+ EC_KEY_free(eckey)
129
+
130
+ [ priv_hex, pub_hex ]
131
+ end
132
+
133
+ # extract private key from uncompressed DER format
134
+ def self.der_to_private_key(der_hex)
135
+ init_ffi_ssl
136
+ #k = EC_KEY_new_by_curve_name(NID_secp256k1)
137
+ #kp = FFI::MemoryPointer.new(:pointer).put_pointer(0, eckey)
138
+
139
+ buf = FFI::MemoryPointer.from_string([der_hex].pack("H*"))
140
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
141
+
142
+ #ec_key = d2i_ECPrivateKey(kp, ptr, buf.size-1)
143
+ ec_key = d2i_ECPrivateKey(nil, ptr, buf.size-1)
144
+ return nil if ec_key.null?
145
+ bn = EC_KEY_get0_private_key(ec_key)
146
+ BN_bn2bin(bn, buf)
147
+ buf.read_string(32).unpack("H*")[0]
148
+ end
149
+
150
+ # Given the components of a signature and a selector value, recover and
151
+ # return the public key that generated the signature according to the
152
+ # algorithm in SEC1v2 section 4.1.6.
153
+ #
154
+ # rec_id is an index from 0 to 3 that indicates which of the 4 possible
155
+ # keys is the correct one. Because the key recovery operation yields
156
+ # multiple potential keys, the correct key must either be stored alongside
157
+ # the signature, or you must be willing to try each rec_id in turn until
158
+ # you find one that outputs the key you are expecting.
159
+ #
160
+ # If this method returns nil, it means recovery was not possible and rec_id
161
+ # should be iterated.
162
+ #
163
+ # Given the above two points, a correct usage of this method is inside a
164
+ # for loop from 0 to 3, and if the output is nil OR a key that is not the
165
+ # one you expect, you try again with the next rec_id.
166
+ #
167
+ # message_hash = hash of the signed message.
168
+ # signature = the R and S components of the signature, wrapped.
169
+ # rec_id = which possible key to recover.
170
+ # is_compressed = whether or not the original pubkey was compressed.
171
+ def self.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
172
+ return nil if rec_id < 0 or signature.bytesize != 65
173
+ init_ffi_ssl
174
+
175
+ signature = FFI::MemoryPointer.from_string(signature)
176
+ #signature_bn = BN_bin2bn(signature, 65, BN_new())
177
+ r = BN_bin2bn(signature[1], 32, BN_new())
178
+ s = BN_bin2bn(signature[33], 32, BN_new())
179
+
180
+ n, i = 0, rec_id / 2
181
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
182
+
183
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
184
+
185
+ group = EC_KEY_get0_group(eckey)
186
+ order = BN_new()
187
+ EC_GROUP_get_order(group, order, nil)
188
+ x = BN_dup(order)
189
+ BN_mul_word(x, i)
190
+ BN_add(x, x, r)
191
+
192
+ field = BN_new()
193
+ EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
194
+
195
+ if BN_cmp(x, field) >= 0
196
+ [r, s, order, x, field].each{|i| BN_free(i) }
197
+ EC_KEY_free(eckey)
198
+ return nil
199
+ end
200
+
201
+ big_r = EC_POINT_new(group)
202
+ EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
203
+
204
+ big_q = EC_POINT_new(group)
205
+ n = EC_GROUP_get_degree(group)
206
+ e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
207
+ BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
208
+
209
+ ctx = BN_CTX_new()
210
+ zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
211
+ BN_set_word(zero, 0)
212
+ BN_mod_sub(e, zero, e, order, ctx)
213
+ BN_mod_inverse(rr, r, order, ctx)
214
+ BN_mod_mul(sor, s, rr, order, ctx)
215
+ BN_mod_mul(eor, e, rr, order, ctx)
216
+ EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
217
+ EC_KEY_set_public_key(eckey, big_q)
218
+ BN_CTX_free(ctx)
219
+
220
+ [r, s, order, x, field, e, zero, rr, sor, eor].each{|i| BN_free(i) }
221
+ [big_r, big_q].each{|i| EC_POINT_free(i) }
222
+
223
+ length = i2o_ECPublicKey(eckey, nil)
224
+ buf = FFI::MemoryPointer.new(:uint8, length)
225
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
226
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
227
+ buf.read_string(length).unpack("H*")[0]
228
+ end
229
+
230
+ EC_KEY_free(eckey)
231
+
232
+ pub_hex
233
+ end
234
+
235
+ # Regenerate a DER-encoded signature such that the S-value complies with the BIP62
236
+ # specification.
237
+ #
238
+ def self.signature_to_low_s(signature)
239
+ init_ffi_ssl
240
+
241
+ buf = FFI::MemoryPointer.new(:uint8, 34)
242
+ temp = signature.unpack("C*")
243
+ length_r = temp[3]
244
+ length_s = temp[5+length_r]
245
+ sig = FFI::MemoryPointer.from_string(signature)
246
+
247
+ # Calculate the lower s value
248
+ s = BN_bin2bn(sig[6 + length_r], length_s, BN_new())
249
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
250
+ group, order, halforder, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_new(), BN_CTX_new()
251
+
252
+ EC_GROUP_get_order(group, order, ctx)
253
+ BN_rshift1(halforder, order)
254
+ if BN_cmp(s, halforder) > 0
255
+ BN_sub(s, order, s)
256
+ end
257
+
258
+ BN_free(halforder)
259
+ BN_free(order)
260
+ BN_CTX_free(ctx)
261
+
262
+ length_s = BN_bn2bin(s, buf)
263
+ # p buf.read_string(length_s).unpack("H*")
264
+
265
+ # Re-encode the signature in DER format
266
+ sig = [0x30, 0, 0x02, length_r]
267
+ sig.concat(temp.slice(4, length_r))
268
+ sig << 0x02
269
+ sig << length_s
270
+ sig.concat(buf.read_string(length_s).unpack("C*"))
271
+ sig[1] = sig.size - 2
272
+
273
+ BN_free(s)
274
+ EC_KEY_free(eckey)
275
+
276
+ sig.pack("C*")
277
+ end
278
+
279
+ def self.sign_compact(hash, private_key, public_key_hex = nil, pubkey_compressed = nil)
280
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, hash)
281
+
282
+ private_key = [private_key].pack("H*") if private_key.bytesize >= 64
283
+ private_key_hex = private_key.unpack("H*")[0]
284
+
285
+ public_key_hex = regenerate_key(private_key_hex).last unless public_key_hex
286
+ pubkey_compressed = (public_key_hex[0..1] == "04" ? false : true) unless pubkey_compressed
287
+
288
+ init_ffi_ssl
289
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
290
+ priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
291
+
292
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
293
+ EC_GROUP_get_order(group, order, ctx)
294
+
295
+ pub_key = EC_POINT_new(group)
296
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
297
+ EC_KEY_set_private_key(eckey, priv_key)
298
+ EC_KEY_set_public_key(eckey, pub_key)
299
+
300
+ signature = ECDSA_do_sign(msg32, msg32.size, eckey)
301
+
302
+ BN_free(order)
303
+ BN_CTX_free(ctx)
304
+ EC_POINT_free(pub_key)
305
+ BN_free(priv_key)
306
+ EC_KEY_free(eckey)
307
+
308
+ buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
309
+ r, s = signature.get_array_of_pointer(0, 2).map{|i| BN_bn2bin(i, buf); buf.read_string(BN_num_bytes(i)).rjust(32, "\x00") }
310
+
311
+ if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
312
+ 4.times{|i|
313
+ head = [ 27 + i + (pubkey_compressed ? 4 : 0) ].pack("C")
314
+ if public_key_hex == recover_public_key_from_signature(msg32.read_string(32), [head, r, s].join, i, pubkey_compressed)
315
+ rec_id = i; break
316
+ end
317
+ }
318
+ end
319
+
320
+ ECDSA_SIG_free(signature)
321
+
322
+ [ head, [r,s] ].join if rec_id
323
+ end
324
+
325
+ def self.recover_compact(hash, signature)
326
+ return false if signature.bytesize != 65
327
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, hash)
328
+
329
+ version = signature.unpack('C')[0]
330
+ return false if version < 27 or version > 34
331
+
332
+ compressed = (version >= 31) ? (version -= 4; true) : false
333
+ pubkey = recover_public_key_from_signature(msg32.read_string(32), signature, version-27, compressed)
334
+ end
335
+
336
+ # lifted from https://github.com/GemHQ/money-tree
337
+ def self.ec_add(point_0, point_1)
338
+ init_ffi_ssl
339
+
340
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
341
+ group = EC_KEY_get0_group(eckey)
342
+
343
+ point_0_hex = point_0.to_bn.to_s(16)
344
+ point_0_pt = EC_POINT_hex2point(group, point_0_hex, nil, nil)
345
+ point_1_hex = point_1.to_bn.to_s(16)
346
+ point_1_pt = EC_POINT_hex2point(group, point_1_hex, nil, nil)
347
+
348
+ sum_point = EC_POINT_new(group)
349
+ success = EC_POINT_add(group, sum_point, point_0_pt, point_1_pt, nil)
350
+ hex = EC_POINT_point2hex(group, sum_point, POINT_CONVERSION_UNCOMPRESSED, nil)
351
+ EC_KEY_free(eckey)
352
+ EC_POINT_free(sum_point)
353
+ hex
354
+ end
355
+
356
+ # repack signature for OpenSSL 1.0.1k handling of DER signatures
357
+ # https://github.com/bitcoin/bitcoin/pull/5634/files
358
+ def self.repack_der_signature(signature)
359
+ init_ffi_ssl
360
+
361
+ return false if signature.empty?
362
+
363
+ # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
364
+ norm_der = FFI::MemoryPointer.new(:pointer)
365
+ sig_ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, FFI::MemoryPointer.from_string(signature))
366
+
367
+ norm_sig = d2i_ECDSA_SIG(nil, sig_ptr, signature.bytesize)
368
+
369
+ derlen = i2d_ECDSA_SIG(norm_sig, norm_der)
370
+ ECDSA_SIG_free(norm_sig)
371
+ return false if derlen <= 0
372
+
373
+ ret = norm_der.read_pointer.read_string(derlen)
374
+ OPENSSL_free(norm_der.read_pointer)
375
+
376
+ ret
377
+ end
378
+
379
+ def self.init_ffi_ssl
380
+ return if @ssl_loaded
381
+ SSL_library_init()
382
+ ERR_load_crypto_strings()
383
+ SSL_load_error_strings()
384
+ RAND_poll()
385
+ @ssl_loaded = true
386
+ end
387
+ end
388
+ end