eth 0.4.11 → 0.4.18

Sign up to get free protection for your applications and to get access to all the features.
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])