eth 0.4.18 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/codeql.yml +6 -2
- data/.github/workflows/docs.yml +1 -1
- data/.github/workflows/spec.yml +52 -0
- data/.gitignore +24 -24
- data/.gitmodules +3 -3
- data/.yardopts +1 -0
- data/AUTHORS.txt +27 -0
- data/CHANGELOG.md +63 -13
- data/Gemfile +12 -4
- data/LICENSE.txt +202 -22
- data/README.md +231 -76
- data/bin/console +4 -4
- data/bin/setup +5 -4
- data/codecov.yml +6 -0
- data/eth.gemspec +23 -19
- data/lib/eth/abi/type.rb +178 -0
- data/lib/eth/abi.rb +396 -0
- data/lib/eth/address.rb +57 -10
- data/lib/eth/api.rb +223 -0
- data/lib/eth/chain.rb +151 -0
- data/lib/eth/client/http.rb +63 -0
- data/lib/eth/client/ipc.rb +50 -0
- data/lib/eth/client.rb +232 -0
- data/lib/eth/constant.rb +71 -0
- data/lib/eth/eip712.rb +184 -0
- data/lib/eth/key/decrypter.rb +121 -85
- data/lib/eth/key/encrypter.rb +180 -99
- data/lib/eth/key.rb +134 -45
- data/lib/eth/rlp/decoder.rb +114 -0
- data/lib/eth/rlp/encoder.rb +78 -0
- data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
- data/lib/eth/rlp/sedes/binary.rb +97 -0
- data/lib/eth/rlp/sedes/list.rb +84 -0
- data/lib/eth/rlp/sedes.rb +74 -0
- data/lib/eth/rlp.rb +63 -0
- data/lib/eth/signature.rb +163 -0
- data/lib/eth/solidity.rb +75 -0
- data/lib/eth/tx/eip1559.rb +337 -0
- data/lib/eth/tx/eip2930.rb +329 -0
- data/lib/eth/tx/legacy.rb +297 -0
- data/lib/eth/tx.rb +269 -146
- data/lib/eth/unit.rb +49 -0
- data/lib/eth/util.rb +235 -0
- data/lib/eth/version.rb +18 -1
- data/lib/eth.rb +34 -67
- metadata +47 -95
- data/.github/workflows/build.yml +0 -36
- data/lib/eth/gas.rb +0 -7
- data/lib/eth/open_ssl.rb +0 -395
- data/lib/eth/secp256k1.rb +0 -5
- data/lib/eth/sedes.rb +0 -39
- data/lib/eth/utils.rb +0 -126
data/lib/eth/key/decrypter.rb
CHANGED
@@ -1,110 +1,146 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
# Copyright (c) 2016-2022 The Ruby-Eth Contributors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# Provides the {Eth} module.
|
16
|
+
module Eth
|
17
|
+
|
18
|
+
# The {Eth::Key::Decrypter} class to handle PBKDF2-SHA-256 decryption.
|
19
|
+
class Key::Decrypter
|
20
|
+
|
21
|
+
# Provides a specific decrypter error if decryption fails.
|
22
|
+
class DecrypterError < StandardError; end
|
23
|
+
|
24
|
+
# Class method {Eth::Key::Decrypter.perform} to perform an keystore
|
25
|
+
# decryption.
|
26
|
+
#
|
27
|
+
# @param data [JSON] encryption data including cypherkey.
|
28
|
+
# @param password [String] password to decrypt the key.
|
29
|
+
# @return [Eth::Key] decrypted key-pair.
|
30
|
+
def self.perform(data, password)
|
31
|
+
new(data, password).perform
|
32
|
+
end
|
21
33
|
|
22
|
-
|
34
|
+
# Constructor of the {Eth::Key::Decrypter} class for secret key
|
35
|
+
# decryption. Should not be used; use {Eth::Key::Decrypter.perform}
|
36
|
+
# instead.
|
37
|
+
#
|
38
|
+
# @param data [JSON] encryption data including cypherkey.
|
39
|
+
# @param password [String] password to decrypt the key.
|
40
|
+
def initialize(data, password)
|
41
|
+
data = JSON.parse(data) if data.is_a? String
|
42
|
+
@data = data
|
43
|
+
@password = password
|
44
|
+
end
|
23
45
|
|
24
|
-
|
46
|
+
# Method to decrypt key using password.
|
47
|
+
#
|
48
|
+
# @return [Eth::Key] decrypted key.
|
49
|
+
def perform
|
50
|
+
derive_key password
|
51
|
+
check_macs
|
52
|
+
private_key = Util.bin_to_hex decrypted_data
|
53
|
+
Eth::Key.new priv: private_key
|
54
|
+
end
|
25
55
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :data
|
59
|
+
attr_reader :key
|
60
|
+
attr_reader :password
|
61
|
+
|
62
|
+
def derive_key(password)
|
63
|
+
case kdf
|
64
|
+
when "pbkdf2"
|
65
|
+
@key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
|
66
|
+
when "scrypt"
|
67
|
+
@key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
|
68
|
+
else
|
69
|
+
raise DecrypterError, "Unsupported key derivation function: #{kdf}!"
|
70
|
+
end
|
35
71
|
end
|
36
|
-
end
|
37
72
|
|
38
|
-
|
39
|
-
|
40
|
-
|
73
|
+
def check_macs
|
74
|
+
mac1 = Util.keccak256(key[(key_length / 2), key_length] + ciphertext)
|
75
|
+
mac2 = Util.hex_to_bin crypto_data["mac"]
|
41
76
|
|
42
|
-
|
43
|
-
|
77
|
+
if mac1 != mac2
|
78
|
+
raise DecrypterError, "Message Authentications Codes do not match!"
|
79
|
+
end
|
44
80
|
end
|
45
|
-
end
|
46
81
|
|
47
|
-
|
48
|
-
|
49
|
-
|
82
|
+
def decrypted_data
|
83
|
+
@decrypted_data ||= cipher.update(ciphertext) + cipher.final
|
84
|
+
end
|
50
85
|
|
51
|
-
|
52
|
-
|
53
|
-
|
86
|
+
def crypto_data
|
87
|
+
@crypto_data ||= data["crypto"] || data["Crypto"]
|
88
|
+
end
|
54
89
|
|
55
|
-
|
56
|
-
|
57
|
-
|
90
|
+
def ciphertext
|
91
|
+
Util.hex_to_bin crypto_data["ciphertext"]
|
92
|
+
end
|
58
93
|
|
59
|
-
|
60
|
-
|
61
|
-
|
94
|
+
def cipher_name
|
95
|
+
"aes-128-ctr"
|
96
|
+
end
|
62
97
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
98
|
+
def cipher
|
99
|
+
@cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
|
100
|
+
cipher.decrypt
|
101
|
+
cipher.key = key[0, (key_length / 2)]
|
102
|
+
cipher.iv = iv
|
103
|
+
end
|
68
104
|
end
|
69
|
-
end
|
70
105
|
|
71
|
-
|
72
|
-
|
73
|
-
|
106
|
+
def iv
|
107
|
+
Util.hex_to_bin crypto_data["cipherparams"]["iv"]
|
108
|
+
end
|
74
109
|
|
75
|
-
|
76
|
-
|
77
|
-
|
110
|
+
def salt
|
111
|
+
Util.hex_to_bin crypto_data["kdfparams"]["salt"]
|
112
|
+
end
|
78
113
|
|
79
|
-
|
80
|
-
|
81
|
-
|
114
|
+
def iterations
|
115
|
+
crypto_data["kdfparams"]["c"].to_i
|
116
|
+
end
|
82
117
|
|
83
|
-
|
84
|
-
|
85
|
-
|
118
|
+
def kdf
|
119
|
+
crypto_data["kdf"]
|
120
|
+
end
|
86
121
|
|
87
|
-
|
88
|
-
|
89
|
-
|
122
|
+
def key_length
|
123
|
+
crypto_data["kdfparams"]["dklen"].to_i
|
124
|
+
end
|
90
125
|
|
91
|
-
|
92
|
-
|
93
|
-
|
126
|
+
def n
|
127
|
+
crypto_data["kdfparams"]["n"].to_i
|
128
|
+
end
|
94
129
|
|
95
|
-
|
96
|
-
|
97
|
-
|
130
|
+
def r
|
131
|
+
crypto_data["kdfparams"]["r"].to_i
|
132
|
+
end
|
98
133
|
|
99
|
-
|
100
|
-
|
101
|
-
|
134
|
+
def p
|
135
|
+
crypto_data["kdfparams"]["p"].to_i
|
136
|
+
end
|
102
137
|
|
103
|
-
|
104
|
-
|
105
|
-
|
138
|
+
def digest
|
139
|
+
OpenSSL::Digest.new digest_name
|
140
|
+
end
|
106
141
|
|
107
|
-
|
108
|
-
|
142
|
+
def digest_name
|
143
|
+
"sha256"
|
144
|
+
end
|
109
145
|
end
|
110
146
|
end
|
data/lib/eth/key/encrypter.rb
CHANGED
@@ -1,126 +1,207 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Copyright (c) 2016-2022 The Ruby-Eth Contributors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# Provides the {Eth} module.
|
16
|
+
module Eth
|
17
|
+
|
18
|
+
# The {Eth::Key::Encrypter} class to handle PBKDF2-SHA-256 encryption.
|
19
|
+
class Key::Encrypter
|
20
|
+
|
21
|
+
# Provides a specific encrypter error if decryption fails.
|
22
|
+
class EncrypterError < StandardError; end
|
23
|
+
|
24
|
+
# Class method {Eth::Key::Encrypter.perform} to performa an key-store
|
25
|
+
# encryption.
|
26
|
+
#
|
27
|
+
# @param key [Eth::Key] representing a secret key-pair used for encryption.
|
28
|
+
# @param options [Hash] the options to encrypt with.
|
29
|
+
# @option options [String] :kdf key derivation function defaults to pbkdf2.
|
30
|
+
# @option options [String] :id uuid given to the secret key.
|
31
|
+
# @option options [String] :iterations number of iterations for the hash function.
|
32
|
+
# @option options [String] :salt passed to PBKDF.
|
33
|
+
# @option options [String] :iv 128-bit initialisation vector for the cipher.
|
34
|
+
# @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8.
|
35
|
+
# @option options [Integer] :block_size for scrypt, defaults to 1.
|
36
|
+
# @return [JSON] formatted with encrypted key (cyphertext) and [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256).
|
37
|
+
def self.perform(key, password, options = {})
|
38
|
+
new(key, options).perform(password)
|
39
|
+
end
|
3
40
|
|
4
|
-
|
5
|
-
|
41
|
+
# Constructor of the {Eth::Key::Encrypter} class for secret key
|
42
|
+
# encryption. Should not be used; use {Eth::Key::Encrypter.perform}
|
43
|
+
# instead.
|
44
|
+
#
|
45
|
+
# @param key [Eth::Key] representing a secret key-pair used for encryption.
|
46
|
+
# @param options [Hash] the options to encrypt with.
|
47
|
+
# @option options [String] :kdf key derivation function defaults to pbkdf2.
|
48
|
+
# @option options [String] :id uuid given to the secret key.
|
49
|
+
# @option options [String] :iterations number of iterations for the hash function.
|
50
|
+
# @option options [String] :salt passed to PBKDF.
|
51
|
+
# @option options [String] :iv 128-bit initialisation vector for the cipher.
|
52
|
+
# @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8.
|
53
|
+
# @option options [Integer] :block_size for scrypt, defaults to 1.
|
54
|
+
def initialize(key, options = {})
|
55
|
+
key = Key.new(priv: key) if key.is_a? String
|
56
|
+
@key = key
|
57
|
+
@options = options
|
58
|
+
|
59
|
+
# the key derivation functions default to pbkdf2 if no option is specified
|
60
|
+
# however, if an option is given then it must be either pbkdf2 or scrypt
|
61
|
+
if kdf != "scrypt" && kdf != "pbkdf2"
|
62
|
+
raise EncrypterError, "Unsupported key derivation function: #{kdf}!"
|
63
|
+
end
|
64
|
+
end
|
6
65
|
|
7
|
-
|
8
|
-
|
9
|
-
|
66
|
+
# Encrypt the key with a given password.
|
67
|
+
#
|
68
|
+
# @param password [String] a secret key used for encryption
|
69
|
+
# @return [String] a JSON-formatted keystore string.
|
70
|
+
def perform(password)
|
71
|
+
derive_key password
|
72
|
+
encrypt
|
73
|
+
data.to_json
|
74
|
+
end
|
10
75
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
76
|
+
# Output containing the encrypted key and
|
77
|
+
# [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256)
|
78
|
+
#
|
79
|
+
# @return [Hash] the encrypted keystore data.
|
80
|
+
def data
|
81
|
+
# default to pbkdf2
|
82
|
+
kdfparams = if kdf == "scrypt"
|
83
|
+
{
|
84
|
+
dklen: 32,
|
85
|
+
n: iterations,
|
86
|
+
p: parallelization,
|
87
|
+
r: block_size,
|
88
|
+
salt: Util.bin_to_hex(salt),
|
89
|
+
}
|
90
|
+
else
|
91
|
+
{
|
92
|
+
c: iterations,
|
93
|
+
dklen: 32,
|
94
|
+
prf: prf,
|
95
|
+
salt: Util.bin_to_hex(salt),
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
{
|
100
|
+
crypto: {
|
101
|
+
cipher: cipher_name,
|
102
|
+
cipherparams: {
|
103
|
+
iv: Util.bin_to_hex(iv),
|
104
|
+
},
|
105
|
+
ciphertext: Util.bin_to_hex(encrypted_key),
|
106
|
+
kdf: kdf,
|
107
|
+
kdfparams: kdfparams,
|
108
|
+
mac: Util.bin_to_hex(mac),
|
109
|
+
},
|
110
|
+
id: id,
|
111
|
+
version: 3,
|
112
|
+
}
|
113
|
+
end
|
15
114
|
|
16
|
-
|
17
|
-
derive_key password
|
18
|
-
encrypt
|
115
|
+
private
|
19
116
|
|
20
|
-
|
21
|
-
end
|
117
|
+
attr_reader :derived_key, :encrypted_key, :key, :options
|
22
118
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
cipher
|
27
|
-
|
28
|
-
|
29
|
-
},
|
30
|
-
ciphertext: bin_to_hex(encrypted_key),
|
31
|
-
kdf: "pbkdf2",
|
32
|
-
kdfparams: {
|
33
|
-
c: iterations,
|
34
|
-
dklen: 32,
|
35
|
-
prf: prf,
|
36
|
-
salt: bin_to_hex(salt),
|
37
|
-
},
|
38
|
-
mac: bin_to_hex(mac),
|
39
|
-
},
|
40
|
-
id: id,
|
41
|
-
version: 3,
|
42
|
-
}.tap do |data|
|
43
|
-
data[:address] = address unless options[:skip_address]
|
119
|
+
def cipher
|
120
|
+
@cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
|
121
|
+
cipher.encrypt
|
122
|
+
cipher.iv = iv
|
123
|
+
cipher.key = derived_key[0, (key_length / 2)]
|
124
|
+
end
|
44
125
|
end
|
45
|
-
end
|
46
126
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
private
|
127
|
+
def digest
|
128
|
+
@digest ||= OpenSSL::Digest.new digest_name
|
129
|
+
end
|
52
130
|
|
53
|
-
|
131
|
+
def derive_key(password)
|
132
|
+
if kdf == "scrypt"
|
133
|
+
@derived_key = SCrypt::Engine.scrypt(password, salt, iterations, block_size, parallelization, key_length)
|
134
|
+
else
|
135
|
+
@derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
|
136
|
+
end
|
137
|
+
end
|
54
138
|
|
55
|
-
|
56
|
-
|
57
|
-
cipher.encrypt
|
58
|
-
cipher.iv = iv
|
59
|
-
cipher.key = derived_key[0, (key_length / 2)]
|
139
|
+
def encrypt
|
140
|
+
@encrypted_key = cipher.update(Util.hex_to_bin key.private_hex) + cipher.final
|
60
141
|
end
|
61
|
-
end
|
62
142
|
|
63
|
-
|
64
|
-
|
65
|
-
|
143
|
+
def mac
|
144
|
+
Util.keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
|
145
|
+
end
|
66
146
|
|
67
|
-
|
68
|
-
|
69
|
-
|
147
|
+
def kdf
|
148
|
+
options[:kdf] || "pbkdf2"
|
149
|
+
end
|
70
150
|
|
71
|
-
|
72
|
-
|
73
|
-
|
151
|
+
def cipher_name
|
152
|
+
"aes-128-ctr"
|
153
|
+
end
|
74
154
|
|
75
|
-
|
76
|
-
|
77
|
-
|
155
|
+
def digest_name
|
156
|
+
"sha256"
|
157
|
+
end
|
78
158
|
|
79
|
-
|
80
|
-
|
81
|
-
|
159
|
+
def prf
|
160
|
+
"hmac-#{digest_name}"
|
161
|
+
end
|
82
162
|
|
83
|
-
|
84
|
-
|
85
|
-
|
163
|
+
def key_length
|
164
|
+
32
|
165
|
+
end
|
86
166
|
|
87
|
-
|
88
|
-
|
89
|
-
|
167
|
+
def salt_length
|
168
|
+
32
|
169
|
+
end
|
90
170
|
|
91
|
-
|
92
|
-
|
93
|
-
|
171
|
+
def iv_length
|
172
|
+
16
|
173
|
+
end
|
94
174
|
|
95
|
-
|
96
|
-
|
97
|
-
|
175
|
+
def id
|
176
|
+
@id ||= options[:id] || SecureRandom.uuid
|
177
|
+
end
|
98
178
|
|
99
|
-
|
100
|
-
|
101
|
-
|
179
|
+
def iterations
|
180
|
+
options[:iterations] || 262_144
|
181
|
+
end
|
102
182
|
|
103
|
-
|
104
|
-
|
105
|
-
|
183
|
+
def salt
|
184
|
+
@salt ||= if options[:salt]
|
185
|
+
Util.hex_to_bin options[:salt]
|
186
|
+
else
|
187
|
+
SecureRandom.random_bytes(salt_length)
|
188
|
+
end
|
189
|
+
end
|
106
190
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
191
|
+
def iv
|
192
|
+
@iv ||= if options[:iv]
|
193
|
+
Util.hex_to_bin options[:iv]
|
194
|
+
else
|
195
|
+
SecureRandom.random_bytes(iv_length)
|
196
|
+
end
|
197
|
+
end
|
114
198
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
else
|
119
|
-
SecureRandom.random_bytes(iv_length)
|
120
|
-
end
|
121
|
-
end
|
199
|
+
def parallelization
|
200
|
+
options[:parallelization] || 8
|
201
|
+
end
|
122
202
|
|
123
|
-
|
124
|
-
|
203
|
+
def block_size
|
204
|
+
options[:block_size] || 1
|
205
|
+
end
|
125
206
|
end
|
126
207
|
end
|