eth 0.4.11 → 0.4.18

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.
data/lib/eth/open_ssl.rb CHANGED
@@ -1,28 +1,22 @@
1
1
  # originally lifted from https://github.com/lian/bitcoin-ruby
2
2
  # thanks to everyone there for figuring this out
3
3
 
4
+ # encoding: ascii-8bit
5
+
6
+ require "openssl"
7
+ require "ffi"
8
+
4
9
  module Eth
5
10
  class OpenSsl
6
11
  extend FFI::Library
7
12
 
8
- if FFI::Platform.windows?
9
- ffi_lib 'libeay32', 'ssleay32'
10
- else
11
- ffi_lib [
12
- 'libssl.so.1.1.0', 'libssl.so.1.1',
13
- 'libssl.so.1.0.0', 'libssl.so.10',
14
- 'ssl'
15
- ]
16
- end
13
+ # Use the library loaded by the extension require above.
14
+ ffi_lib FFI::CURRENT_PROCESS
17
15
 
18
16
  NID_secp256k1 = 714
19
17
  POINT_CONVERSION_COMPRESSED = 2
20
18
  POINT_CONVERSION_UNCOMPRESSED = 4
21
19
 
22
- # OpenSSL 1.1.0 version as a numerical version value as defined in:
23
- # https://www.openssl.org/docs/man1.1.0/man3/OpenSSL_version.html
24
- VERSION_1_1_0_NUM = 0x10100000
25
-
26
20
  # OpenSSL 1.1.0 engine constants, taken from:
27
21
  # https://github.com/openssl/openssl/blob/2be8c56a39b0ec2ec5af6ceaf729df154d784a43/include/openssl/crypto.h
28
22
  OPENSSL_INIT_ENGINE_RDRAND = 0x00000200
@@ -30,13 +24,11 @@ module Eth
30
24
  OPENSSL_INIT_ENGINE_CRYPTODEV = 0x00001000
31
25
  OPENSSL_INIT_ENGINE_CAPI = 0x00002000
32
26
  OPENSSL_INIT_ENGINE_PADLOCK = 0x00004000
33
- OPENSSL_INIT_ENGINE_ALL_BUILTIN = (
34
- OPENSSL_INIT_ENGINE_RDRAND |
35
- OPENSSL_INIT_ENGINE_DYNAMIC |
36
- OPENSSL_INIT_ENGINE_CRYPTODEV |
37
- OPENSSL_INIT_ENGINE_CAPI |
38
- OPENSSL_INIT_ENGINE_PADLOCK
39
- )
27
+ OPENSSL_INIT_ENGINE_ALL_BUILTIN = (OPENSSL_INIT_ENGINE_RDRAND |
28
+ OPENSSL_INIT_ENGINE_DYNAMIC |
29
+ OPENSSL_INIT_ENGINE_CRYPTODEV |
30
+ OPENSSL_INIT_ENGINE_CAPI |
31
+ OPENSSL_INIT_ENGINE_PADLOCK)
40
32
 
41
33
  # OpenSSL 1.1.0 load strings constant, taken from:
42
34
  # https://github.com/openssl/openssl/blob/c162c126be342b8cd97996346598ecf7db56130f/include/openssl/ssl.h
@@ -50,215 +42,354 @@ module Eth
50
42
  attach_function :SSLeay, [], :long
51
43
  end
52
44
 
53
- # Returns the version of SSL present.
54
- #
55
- # @return [Integer] version number as an integer.
56
- def self.version
57
- if self.respond_to?(:OpenSSL_version_num)
58
- OpenSSL_version_num()
59
- else
60
- SSLeay()
61
- end
62
- end
63
-
64
- if version >= VERSION_1_1_0_NUM
45
+ begin
65
46
  # Initialization procedure for the library was changed in OpenSSL 1.1.0
66
47
  attach_function :OPENSSL_init_ssl, [:uint64, :pointer], :int
67
- else
48
+ rescue FFI::NotFoundError
68
49
  attach_function :SSL_library_init, [], :int
69
50
  attach_function :ERR_load_crypto_strings, [], :void
70
51
  attach_function :SSL_load_error_strings, [], :void
71
52
  end
72
53
 
73
54
  attach_function :RAND_poll, [], :int
55
+
74
56
  attach_function :BN_CTX_free, [:pointer], :int
75
57
  attach_function :BN_CTX_new, [], :pointer
76
- attach_function :BN_add, [:pointer, :pointer, :pointer], :int
77
- attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
78
- attach_function :BN_bn2bin, [:pointer, :pointer], :int
79
- attach_function :BN_cmp, [:pointer, :pointer], :int
58
+ attach_function :BN_add, %i[pointer pointer pointer], :int
59
+ attach_function :BN_bin2bn, %i[pointer int pointer], :pointer
60
+ attach_function :BN_bn2bin, %i[pointer pointer], :int
61
+ attach_function :BN_cmp, %i[pointer pointer], :int
80
62
  attach_function :BN_dup, [:pointer], :pointer
81
63
  attach_function :BN_free, [:pointer], :int
82
- attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
83
- attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
84
- attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
85
- attach_function :BN_mul_word, [:pointer, :int], :int
64
+ attach_function :BN_mod_inverse, %i[pointer pointer pointer pointer], :pointer
65
+ attach_function :BN_mod_mul, %i[pointer pointer pointer pointer pointer], :int
66
+ attach_function :BN_mod_sub, %i[pointer pointer pointer pointer pointer], :int
67
+ attach_function :BN_mul_word, %i[pointer int], :int
86
68
  attach_function :BN_new, [], :pointer
87
- attach_function :BN_num_bits, [:pointer], :int
88
- attach_function :BN_rshift, [:pointer, :pointer, :int], :int
89
- attach_function :BN_set_word, [:pointer, :int], :int
90
- attach_function :ECDSA_SIG_free, [:pointer], :void
91
- attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
92
- attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
69
+ attach_function :BN_rshift, %i[pointer pointer int], :int
70
+ attach_function :BN_rshift1, %i[pointer pointer], :int
71
+ attach_function :BN_set_word, %i[pointer int], :int
72
+ attach_function :BN_sub, %i[pointer pointer pointer], :int
73
+ attach_function :EC_GROUP_get_curve_GFp, %i[pointer pointer pointer pointer pointer], :int
93
74
  attach_function :EC_GROUP_get_degree, [:pointer], :int
94
- attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
75
+ attach_function :EC_GROUP_get_order, %i[pointer pointer pointer], :int
95
76
  attach_function :EC_KEY_free, [:pointer], :int
96
77
  attach_function :EC_KEY_get0_group, [:pointer], :pointer
78
+ attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
97
79
  attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
98
- attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
99
- attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
100
- attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
80
+ attach_function :EC_KEY_set_conv_form, %i[pointer int], :void
81
+ attach_function :EC_KEY_set_private_key, %i[pointer pointer], :int
82
+ attach_function :EC_KEY_set_public_key, %i[pointer pointer], :int
101
83
  attach_function :EC_POINT_free, [:pointer], :int
102
- attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
84
+ attach_function :EC_POINT_mul, %i[pointer pointer pointer pointer pointer pointer], :int
103
85
  attach_function :EC_POINT_new, [:pointer], :pointer
104
- attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
105
- attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
86
+ attach_function :EC_POINT_set_compressed_coordinates_GFp,
87
+ %i[pointer pointer pointer int pointer], :int
88
+ attach_function :i2o_ECPublicKey, %i[pointer pointer], :uint
89
+ attach_function :ECDSA_do_sign, %i[pointer uint pointer], :pointer
90
+ attach_function :BN_num_bits, [:pointer], :int
91
+ attach_function :ECDSA_SIG_free, [:pointer], :void
92
+ attach_function :EC_POINT_add, %i[pointer pointer pointer pointer pointer], :int
93
+ attach_function :EC_POINT_point2hex, %i[pointer pointer int pointer], :string
94
+ attach_function :EC_POINT_hex2point, %i[pointer string pointer pointer], :pointer
95
+ attach_function :d2i_ECDSA_SIG, %i[pointer pointer long], :pointer
96
+ attach_function :i2d_ECDSA_SIG, %i[pointer pointer], :int
97
+ attach_function :OPENSSL_free, :CRYPTO_free, [:pointer], :void
98
+
99
+ def self.BN_num_bytes(ptr) # rubocop:disable Naming/MethodName
100
+ (BN_num_bits(ptr) + 7) / 8
101
+ end
106
102
 
107
- class << self
108
- def BN_num_bytes(ptr)
109
- (BN_num_bits(ptr) + 7) / 8
103
+ # resolve public from private key, using ffi and libssl.so
104
+ # example:
105
+ # keypair = Bitcoin.generate_key; Bitcoin::OpenSSL_EC.regenerate_key(keypair.first) == keypair
106
+ def self.regenerate_key(private_key)
107
+ private_key = [private_key].pack("H*") if private_key.bytesize >= (32 * 2)
108
+ private_key_hex = private_key.unpack("H*")[0]
109
+
110
+ group = OpenSSL::PKey::EC::Group.new("secp256k1")
111
+ key = OpenSSL::PKey::EC.new(group)
112
+ key.private_key = OpenSSL::BN.new(private_key_hex, 16)
113
+ key.public_key = group.generator.mul(key.private_key)
114
+
115
+ priv_hex = key.private_key.to_bn.to_s(16).downcase.rjust(64, "0")
116
+ if priv_hex != private_key_hex
117
+ raise "regenerated wrong private_key, raise here before generating a faulty public_key too!"
110
118
  end
111
119
 
112
- def sign_compact(hash, private_key, public_key_hex)
113
- private_key = [private_key].pack("H*") if private_key.bytesize >= 64
114
- pubkey_compressed = false
120
+ [priv_hex, key.public_key.to_bn.to_s(16).downcase]
121
+ end
115
122
 
116
- init_ffi_ssl
117
- eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
118
- priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
123
+ # Given the components of a signature and a selector value, recover and
124
+ # return the public key that generated the signature according to the
125
+ # algorithm in SEC1v2 section 4.1.6.
126
+ #
127
+ # rec_id is an index from 0 to 3 that indicates which of the 4 possible
128
+ # keys is the correct one. Because the key recovery operation yields
129
+ # multiple potential keys, the correct key must either be stored alongside
130
+ # the signature, or you must be willing to try each rec_id in turn until
131
+ # you find one that outputs the key you are expecting.
132
+ #
133
+ # If this method returns nil, it means recovery was not possible and rec_id
134
+ # should be iterated.
135
+ #
136
+ # Given the above two points, a correct usage of this method is inside a
137
+ # for loop from 0 to 3, and if the output is nil OR a key that is not the
138
+ # one you expect, you try again with the next rec_id.
139
+ #
140
+ # message_hash = hash of the signed message.
141
+ # signature = the R and S components of the signature, wrapped.
142
+ # rec_id = which possible key to recover.
143
+ # is_compressed = whether or not the original pubkey was compressed.
144
+ def self.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
145
+ return nil if rec_id < 0 || signature.bytesize != 65
146
+ init_ffi_ssl
147
+
148
+ signature = FFI::MemoryPointer.from_string(signature)
149
+ # signature_bn = BN_bin2bn(signature, 65, BN_new())
150
+ r = BN_bin2bn(signature[1], 32, BN_new())
151
+ s = BN_bin2bn(signature[33], 32, BN_new())
152
+
153
+ i = rec_id / 2
154
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
155
+
156
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
157
+
158
+ group = EC_KEY_get0_group(eckey)
159
+ order = BN_new()
160
+ EC_GROUP_get_order(group, order, nil)
161
+ x = BN_dup(order)
162
+ BN_mul_word(x, i)
163
+ BN_add(x, x, r)
164
+
165
+ field = BN_new()
166
+ EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
167
+
168
+ if BN_cmp(x, field) >= 0
169
+ [r, s, order, x, field].each { |item| BN_free(item) }
170
+ EC_KEY_free(eckey)
171
+ return nil
172
+ end
119
173
 
120
- group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
121
- EC_GROUP_get_order(group, order, ctx)
174
+ big_r = EC_POINT_new(group)
175
+ EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
176
+
177
+ big_q = EC_POINT_new(group)
178
+ n = EC_GROUP_get_degree(group)
179
+ e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
180
+ BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
181
+
182
+ ctx = BN_CTX_new()
183
+ zero = BN_new()
184
+ rr = BN_new()
185
+ sor = BN_new()
186
+ eor = BN_new()
187
+ BN_set_word(zero, 0)
188
+ BN_mod_sub(e, zero, e, order, ctx)
189
+ BN_mod_inverse(rr, r, order, ctx)
190
+ BN_mod_mul(sor, s, rr, order, ctx)
191
+ BN_mod_mul(eor, e, rr, order, ctx)
192
+ EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
193
+ EC_KEY_set_public_key(eckey, big_q)
194
+ BN_CTX_free(ctx)
195
+
196
+ [r, s, order, x, field, e, zero, rr, sor, eor].each { |item| BN_free(item) }
197
+ [big_r, big_q].each { |item| EC_POINT_free(item) }
198
+
199
+ length = i2o_ECPublicKey(eckey, nil)
200
+ buf = FFI::MemoryPointer.new(:uint8, length)
201
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
202
+ pub_hex = buf.read_string(length).unpack("H*")[0] if i2o_ECPublicKey(eckey, ptr) == length
203
+
204
+ EC_KEY_free(eckey)
205
+
206
+ pub_hex
207
+ end
122
208
 
123
- pub_key = EC_POINT_new(group)
124
- EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
125
- EC_KEY_set_private_key(eckey, priv_key)
126
- EC_KEY_set_public_key(eckey, pub_key)
209
+ # Regenerate a DER-encoded signature such that the S-value complies with the BIP62
210
+ # specification.
211
+ #
212
+ def self.signature_to_low_s(signature)
213
+ init_ffi_ssl
214
+
215
+ buf = FFI::MemoryPointer.new(:uint8, 34)
216
+ temp = signature.unpack("C*")
217
+ length_r = temp[3]
218
+ length_s = temp[5 + length_r]
219
+ sig = FFI::MemoryPointer.from_string(signature)
220
+
221
+ # Calculate the lower s value
222
+ s = BN_bin2bn(sig[6 + length_r], length_s, BN_new())
223
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
224
+ group = EC_KEY_get0_group(eckey)
225
+ order = BN_new()
226
+ halforder = BN_new()
227
+ ctx = BN_CTX_new()
228
+
229
+ EC_GROUP_get_order(group, order, ctx)
230
+ BN_rshift1(halforder, order)
231
+ BN_sub(s, order, s) if BN_cmp(s, halforder) > 0
232
+
233
+ BN_free(halforder)
234
+ BN_free(order)
235
+ BN_CTX_free(ctx)
236
+
237
+ length_s = BN_bn2bin(s, buf)
238
+ # p buf.read_string(length_s).unpack("H*")
239
+
240
+ # Re-encode the signature in DER format
241
+ sig = [0x30, 0, 0x02, length_r]
242
+ sig.concat(temp.slice(4, length_r))
243
+ sig << 0x02
244
+ sig << length_s
245
+ sig.concat(buf.read_string(length_s).unpack("C*"))
246
+ sig[1] = sig.size - 2
247
+
248
+ BN_free(s)
249
+ EC_KEY_free(eckey)
250
+
251
+ sig.pack("C*")
252
+ end
127
253
 
128
- signature = ECDSA_do_sign(hash, hash.bytesize, eckey)
254
+ def self.sign_compact(hash, private_key, public_key_hex = nil, pubkey_compressed = nil)
255
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, hash)
129
256
 
130
- BN_free(order)
131
- BN_CTX_free(ctx)
132
- EC_POINT_free(pub_key)
133
- BN_free(priv_key)
134
- EC_KEY_free(eckey)
257
+ private_key = [private_key].pack("H*") if private_key.bytesize >= 64
258
+ private_key_hex = private_key.unpack("H*")[0]
135
259
 
136
- buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
137
- 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") }
260
+ public_key_hex ||= regenerate_key(private_key_hex).last
261
+ pubkey_compressed ||= public_key_hex[0..1] != "04"
138
262
 
139
- if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
140
- 4.times{|i|
141
- head = [ Eth.v_base + i ].pack("C")
142
- if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
143
- rec_id = i; break
144
- end
145
- }
146
- end
263
+ init_ffi_ssl
264
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
265
+ priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
147
266
 
148
- ECDSA_SIG_free(signature)
267
+ group = EC_KEY_get0_group(eckey)
268
+ order = BN_new()
269
+ ctx = BN_CTX_new()
270
+ EC_GROUP_get_order(group, order, ctx)
149
271
 
150
- [ head, [r,s] ].join if rec_id
151
- end
272
+ pub_key = EC_POINT_new(group)
273
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
274
+ EC_KEY_set_private_key(eckey, priv_key)
275
+ EC_KEY_set_public_key(eckey, pub_key)
152
276
 
153
- def recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
154
- return nil if rec_id < 0 or signature.bytesize != 65
155
- init_ffi_ssl
277
+ signature = ECDSA_do_sign(msg32, msg32.size, eckey)
156
278
 
157
- signature = FFI::MemoryPointer.from_string(signature)
158
- r = BN_bin2bn(signature[1], 32, BN_new())
159
- s = BN_bin2bn(signature[33], 32, BN_new())
279
+ BN_free(order)
280
+ BN_CTX_free(ctx)
281
+ EC_POINT_free(pub_key)
282
+ BN_free(priv_key)
283
+ EC_KEY_free(eckey)
160
284
 
161
- _n, i = 0, rec_id / 2
162
- eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
285
+ buf = FFI::MemoryPointer.new(:uint8, 32)
286
+ head = nil
287
+ r, s = signature.get_array_of_pointer(0, 2).map do |i|
288
+ BN_bn2bin(i, buf)
289
+ buf.read_string(BN_num_bytes(i)).rjust(32, "\x00")
290
+ end
163
291
 
164
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
292
+ rec_id = nil
293
+ if signature.get_array_of_pointer(0, 2).all? { |i| BN_num_bits(i) <= 256 }
294
+ 4.times do |i|
295
+ head = [27 + i + (pubkey_compressed ? 4 : 0)].pack("C")
296
+ recovered_key = recover_public_key_from_signature(
297
+ msg32.read_string(32), [head, r, s].join, i, pubkey_compressed
298
+ )
299
+ if public_key_hex == recovered_key
300
+ rec_id = i
301
+ break
302
+ end
303
+ end
304
+ end
165
305
 
166
- group = EC_KEY_get0_group(eckey)
167
- order = BN_new()
168
- EC_GROUP_get_order(group, order, nil)
169
- x = BN_dup(order)
170
- BN_mul_word(x, i)
171
- BN_add(x, x, r)
306
+ ECDSA_SIG_free(signature)
172
307
 
173
- field = BN_new()
174
- EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
308
+ [head, [r, s]].join if rec_id
309
+ end
175
310
 
176
- if BN_cmp(x, field) >= 0
177
- bn_free_each r, s, order, x, field
178
- EC_KEY_free(eckey)
179
- return nil
180
- end
311
+ def self.recover_compact(hash, signature)
312
+ return false if signature.bytesize != 65
313
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, hash)
314
+
315
+ version = signature.unpack("C")[0]
181
316
 
182
- big_r = EC_POINT_new(group)
183
- EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
184
-
185
- big_q = EC_POINT_new(group)
186
- n = EC_GROUP_get_degree(group)
187
- e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
188
- BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
189
-
190
- ctx = BN_CTX_new()
191
- zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
192
- BN_set_word(zero, 0)
193
- BN_mod_sub(e, zero, e, order, ctx)
194
- BN_mod_inverse(rr, r, order, ctx)
195
- BN_mod_mul(sor, s, rr, order, ctx)
196
- BN_mod_mul(eor, e, rr, order, ctx)
197
- EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
198
- EC_KEY_set_public_key(eckey, big_q)
199
- BN_CTX_free(ctx)
200
-
201
- bn_free_each r, s, order, x, field, e, zero, rr, sor, eor
202
- [big_r, big_q].each{|j| EC_POINT_free(j) }
203
-
204
- recover_public_hex eckey
317
+ # Version of signature should be 27 or 28, but 0 and 1 are also possible versions
318
+ # which can show up in Ledger hardwallet signings
319
+ if version < 27
320
+ version += 27
205
321
  end
206
322
 
207
- def recover_compact(hash, signature)
208
- return false if signature.bytesize != 65
323
+ return false if version < 27 || version > 34
209
324
 
210
- version = signature.unpack('C')[0]
325
+ compressed = version >= 31
326
+ version -= 4 if compressed
211
327
 
212
- # Version of signature should be 27 or 28, but 0 and 1 are also possible versions
213
- # which can show up in Ledger hardwallet signings
214
- if version < 27
215
- version += 27
216
- end
328
+ recover_public_key_from_signature(msg32.read_string(32), signature, version - 27, compressed)
329
+ end
217
330
 
218
- v_base = Eth.replayable_v?(version) ? Eth.replayable_chain_id : Eth.v_base
331
+ # lifted from https://github.com/GemHQ/money-tree
332
+ def self.ec_add(point0, point1)
333
+ init_ffi_ssl
219
334
 
220
- return false if version < v_base
335
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
336
+ group = EC_KEY_get0_group(eckey)
221
337
 
222
- recover_public_key_from_signature(hash, signature, (version - v_base), false)
223
- end
338
+ point_0_hex = point0.to_bn.to_s(16)
339
+ point_0_pt = EC_POINT_hex2point(group, point_0_hex, nil, nil)
340
+ point_1_hex = point1.to_bn.to_s(16)
341
+ point_1_pt = EC_POINT_hex2point(group, point_1_hex, nil, nil)
224
342
 
225
- def init_ffi_ssl
226
- return if @ssl_loaded
227
- if version >= VERSION_1_1_0_NUM
228
- OPENSSL_init_ssl(
229
- OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_ENGINE_ALL_BUILTIN,
230
- nil
231
- )
232
- else
233
- SSL_library_init()
234
- ERR_load_crypto_strings()
235
- SSL_load_error_strings()
236
- end
343
+ sum_point = EC_POINT_new(group)
344
+ EC_POINT_add(group, sum_point, point_0_pt, point_1_pt, nil)
345
+ hex = EC_POINT_point2hex(group, sum_point, POINT_CONVERSION_UNCOMPRESSED, nil)
346
+ EC_KEY_free(eckey)
347
+ EC_POINT_free(sum_point)
348
+ hex
349
+ end
237
350
 
238
- RAND_poll()
239
- @ssl_loaded = true
240
- end
351
+ # repack signature for OpenSSL 1.0.1k handling of DER signatures
352
+ # https://github.com/bitcoin/bitcoin/pull/5634/files
353
+ def self.repack_der_signature(signature)
354
+ init_ffi_ssl
241
355
 
356
+ return false if signature.empty?
242
357
 
243
- private
358
+ # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
359
+ norm_der = FFI::MemoryPointer.new(:pointer)
360
+ sig_ptr = FFI::MemoryPointer.new(:pointer).put_pointer(
361
+ 0, FFI::MemoryPointer.from_string(signature)
362
+ )
244
363
 
245
- def bn_free_each(*list)
246
- list.each{|j| BN_free(j) }
247
- end
364
+ norm_sig = d2i_ECDSA_SIG(nil, sig_ptr, signature.bytesize)
248
365
 
249
- def recover_public_hex(eckey)
250
- length = i2o_ECPublicKey(eckey, nil)
251
- buf = FFI::MemoryPointer.new(:uint8, length)
252
- ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
253
- pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
254
- buf.read_string(length).unpack("H*")[0]
255
- end
366
+ derlen = i2d_ECDSA_SIG(norm_sig, norm_der)
367
+ ECDSA_SIG_free(norm_sig)
368
+ return false if derlen <= 0
256
369
 
257
- EC_KEY_free(eckey)
370
+ ret = norm_der.read_pointer.read_string(derlen)
371
+ OPENSSL_free(norm_der.read_pointer)
258
372
 
259
- pub_hex
260
- end
373
+ ret
261
374
  end
262
375
 
376
+ def self.init_ffi_ssl
377
+ @ssl_loaded ||= false
378
+ return if @ssl_loaded
379
+
380
+ if self.method_defined?(:OPENSSL_init_ssl)
381
+ OPENSSL_init_ssl(
382
+ OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_ENGINE_ALL_BUILTIN,
383
+ nil
384
+ )
385
+ else
386
+ SSL_library_init()
387
+ ERR_load_crypto_strings()
388
+ SSL_load_error_strings()
389
+ end
390
+
391
+ RAND_poll()
392
+ @ssl_loaded = true
393
+ end
263
394
  end
264
395
  end
data/lib/eth/secp256k1.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  module Eth
2
2
  class Secp256k1
3
-
4
3
  N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
5
-
6
4
  end
7
5
  end
data/lib/eth/sedes.rb CHANGED
@@ -35,6 +35,5 @@ module Eth
35
35
  def binary
36
36
  RLP::Sedes.binary
37
37
  end
38
-
39
38
  end
40
39
  end
data/lib/eth/tx.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  module Eth
2
2
  class Tx
3
-
4
3
  include RLP::Sedes::Serializable
5
4
  extend Sedes
6
5
 
@@ -13,7 +12,7 @@ module Eth
13
12
  data_bin: binary,
14
13
  v: big_endian_int,
15
14
  r: big_endian_int,
16
- s: big_endian_int
15
+ s: big_endian_int,
17
16
  })
18
17
 
19
18
  attr_writer :signature
@@ -28,7 +27,7 @@ module Eth
28
27
  end
29
28
 
30
29
  def initialize(params)
31
- fields = {v: 0, r: 0, s: 0}.merge params
30
+ fields = { v: 0, r: 0, s: 0 }.merge params
32
31
  fields[:to] = Utils.normalize_address(fields[:to])
33
32
 
34
33
  self.chain_id = (params[:chain_id]) ? params.delete(:chain_id) : Eth.chain_id
@@ -105,6 +104,7 @@ module Eth
105
104
  def hash
106
105
  "0x#{Utils.bin_to_hex Utils.keccak256_rlp(self)}"
107
106
  end
107
+
108
108
  alias_method :id, :hash
109
109
 
110
110
  def data_hex
@@ -120,7 +120,7 @@ module Eth
120
120
  end
121
121
 
122
122
  def data=(string)
123
- Eth.tx_data_hex? ? self.data_hex=(string) : self.data_bin=(string)
123
+ Eth.tx_data_hex? ? self.data_hex = (string) : self.data_bin = (string)
124
124
  end
125
125
 
126
126
  def chain_id
@@ -190,7 +190,6 @@ module Eth
190
190
  UnsignedTx
191
191
  end
192
192
  end
193
-
194
193
  end
195
194
 
196
195
  UnsignedTx = Tx.exclude([:v, :r, :s])