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.
- checksums.yaml +5 -5
- data/.github/workflows/build.yml +36 -0
- data/.github/workflows/codeql.yml +44 -0
- data/.github/workflows/docs.yml +26 -0
- data/.gitignore +40 -7
- data/CHANGELOG.md +22 -12
- data/Gemfile +7 -2
- data/LICENSE.txt +2 -1
- data/README.md +10 -4
- data/eth.gemspec +40 -24
- data/lib/eth/address.rb +0 -3
- data/lib/eth/gas.rb +0 -2
- data/lib/eth/key/decrypter.rb +17 -20
- data/lib/eth/key/encrypter.rb +12 -14
- data/lib/eth/key.rb +6 -7
- data/lib/eth/open_ssl.rb +305 -174
- data/lib/eth/secp256k1.rb +0 -2
- data/lib/eth/sedes.rb +0 -1
- data/lib/eth/tx.rb +4 -5
- data/lib/eth/utils.rb +10 -14
- data/lib/eth/version.rb +1 -1
- data/lib/eth.rb +13 -13
- metadata +65 -27
- data/.travis.yml +0 -10
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
|
-
|
9
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
-
|
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, [
|
77
|
-
attach_function :BN_bin2bn, [
|
78
|
-
attach_function :BN_bn2bin, [
|
79
|
-
attach_function :BN_cmp, [
|
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, [
|
83
|
-
attach_function :BN_mod_mul, [
|
84
|
-
attach_function :BN_mod_sub, [
|
85
|
-
attach_function :BN_mul_word, [
|
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 :
|
88
|
-
attach_function :
|
89
|
-
attach_function :BN_set_word, [
|
90
|
-
attach_function :
|
91
|
-
attach_function :
|
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, [
|
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, [
|
99
|
-
attach_function :EC_KEY_set_private_key, [
|
100
|
-
attach_function :EC_KEY_set_public_key,
|
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, [
|
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,
|
105
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
113
|
-
|
114
|
-
pubkey_compressed = false
|
120
|
+
[priv_hex, key.public_key.to_bn.to_s(16).downcase]
|
121
|
+
end
|
115
122
|
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
121
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
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
|
-
|
131
|
-
|
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
|
-
|
137
|
-
|
260
|
+
public_key_hex ||= regenerate_key(private_key_hex).last
|
261
|
+
pubkey_compressed ||= public_key_hex[0..1] != "04"
|
138
262
|
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
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
|
-
|
151
|
-
|
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
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
162
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
174
|
-
|
308
|
+
[head, [r, s]].join if rec_id
|
309
|
+
end
|
175
310
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
208
|
-
return false if signature.bytesize != 65
|
323
|
+
return false if version < 27 || version > 34
|
209
324
|
|
210
|
-
|
325
|
+
compressed = version >= 31
|
326
|
+
version -= 4 if compressed
|
211
327
|
|
212
|
-
|
213
|
-
|
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
|
-
|
331
|
+
# lifted from https://github.com/GemHQ/money-tree
|
332
|
+
def self.ec_add(point0, point1)
|
333
|
+
init_ffi_ssl
|
219
334
|
|
220
|
-
|
335
|
+
eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
|
336
|
+
group = EC_KEY_get0_group(eckey)
|
221
337
|
|
222
|
-
|
223
|
-
|
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
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
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
|
-
|
239
|
-
|
240
|
-
|
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
|
-
|
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
|
-
|
246
|
-
list.each{|j| BN_free(j) }
|
247
|
-
end
|
364
|
+
norm_sig = d2i_ECDSA_SIG(nil, sig_ptr, signature.bytesize)
|
248
365
|
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
370
|
+
ret = norm_der.read_pointer.read_string(derlen)
|
371
|
+
OPENSSL_free(norm_der.read_pointer)
|
258
372
|
|
259
|
-
|
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
data/lib/eth/sedes.rb
CHANGED
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])
|