digix-eth 0.5.2

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,9 @@
1
+ module Eth
2
+ class Gas
3
+
4
+ GTXCOST = 21000 # TX BASE GAS COST
5
+ GTXDATANONZERO = 68 # TX DATA NON ZERO BYTE GAS COST
6
+ GTXDATAZERO = 4 # TX DATA ZERO BYTE GAS COST
7
+
8
+ end
9
+ end
@@ -0,0 +1,119 @@
1
+ module Eth
2
+ class Key
3
+ autoload :Decrypter, 'eth/key/decrypter'
4
+ autoload :Encrypter, 'eth/key/encrypter'
5
+
6
+ attr_accessor :private_key, :public_key
7
+
8
+ def self.encrypt(key, password)
9
+ key = new(priv: key) unless key.is_a?(Key)
10
+
11
+ Encrypter.perform key.private_hex, password
12
+ end
13
+
14
+ def self.decrypt(data, password)
15
+ priv = Decrypter.perform data, password
16
+ default priv: priv
17
+ end
18
+
19
+ class << self
20
+ def from_private_key_hex(private_key_hex)
21
+ private_key = MoneyTree::PrivateKey.new(key: private_key_hex)
22
+ private_key.remove_instance_variable(:@raw_key)
23
+ private_key.options[:key] = nil
24
+ key = self.new
25
+ key.private_key = private_key
26
+ key.public_key = MoneyTree::PublicKey.new private_key, compressed: false
27
+ return key
28
+ end
29
+
30
+ def from_node(node)
31
+ key = self.new
32
+ private_key = MoneyTree::PrivateKey.new(key: node.private_key.to_hex)
33
+ public_key = MoneyTree::PublicKey.new(node.private_key, compressed: false)
34
+ key.private_key = private_key
35
+ key.public_key = public_key
36
+ return key
37
+ end
38
+
39
+ def default(priv: nil)
40
+ key = self.new
41
+ private_key = MoneyTree::PrivateKey.new key: priv
42
+ public_key = MoneyTree::PublicKey.new(private_key, compressed: false)
43
+ key.private_key = private_key
44
+ key.public_key = public_key
45
+ return key
46
+ end
47
+
48
+ end
49
+
50
+ #def initialize(priv: nil)
51
+ #@private_key = MoneyTree::PrivateKey.new key: priv
52
+ #@public_key = MoneyTree::PublicKey.new private_key, compressed: false
53
+ #end
54
+
55
+ def private_hex
56
+ private_key.to_hex
57
+ end
58
+
59
+ def public_bytes
60
+ public_key.to_bytes
61
+ end
62
+
63
+ def public_hex
64
+ public_key.to_hex
65
+ end
66
+
67
+ def address
68
+ Utils.public_key_to_address public_hex
69
+ end
70
+ alias_method :to_address, :address
71
+
72
+ def sign(message)
73
+ sign_hash message_hash(message)
74
+ end
75
+
76
+ def sign_hash(hash)
77
+ loop do
78
+ signature = OpenSsl.sign_compact hash, private_hex, public_hex
79
+ return signature if valid_s? signature
80
+ end
81
+ end
82
+
83
+ def verify_signature(message, signature)
84
+ hash = message_hash(message)
85
+ public_hex == OpenSsl.recover_compact(hash, signature)
86
+ end
87
+
88
+ def get_signer_key(message, signature)
89
+ hash = message_hash(message)
90
+ signer_public_hex = OpenSsl.recover_compact(hash, signature)
91
+ signer_public_key = MoneyTree::PublicKey.new(signer_public_hex, compressed: false)
92
+ signer_key = Eth::Key.new
93
+ signer_key.public_key = signer_public_key
94
+ return signer_key
95
+ end
96
+
97
+ def verify_rpc_signature(message, signature, signer_address)
98
+ signer = get_signer_key(message, signature)
99
+ signer.address == signer_address
100
+ end
101
+
102
+ def verify_rpc_signature_no_prefix(message, signature, signer_address)
103
+ prefixed_message = Eth::Utils.prefix_message(message)
104
+ verify_rpc_signature(prefixed_message, signature, signer_address)
105
+ end
106
+
107
+ private
108
+
109
+ def message_hash(message)
110
+ Utils.keccak256 message
111
+ end
112
+
113
+ def valid_s?(signature)
114
+ s_value = Utils.v_r_s_for(signature).last
115
+ s_value <= Secp256k1::N/2 && s_value != 0
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,87 @@
1
+ require 'json'
2
+
3
+ class Eth::Key::Decrypter
4
+ include Eth::Utils
5
+
6
+ def self.perform(data, password)
7
+ new(data, password).perform
8
+ end
9
+
10
+ def initialize(data, password)
11
+ @data = JSON.parse(data)
12
+ @password = password
13
+ end
14
+
15
+ def perform
16
+ derive_key password
17
+ check_macs
18
+ bin_to_hex decrypted_data
19
+ end
20
+
21
+
22
+ private
23
+
24
+ attr_reader :data, :key, :password
25
+
26
+ def derive_key(password)
27
+ @key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
28
+ end
29
+
30
+ def check_macs
31
+ mac1 = keccak256(key[(key_length/2), key_length] + ciphertext)
32
+ mac2 = hex_to_bin crypto_data['mac']
33
+
34
+ if mac1 != mac2
35
+ raise "Message Authentications Codes do not match!"
36
+ end
37
+ end
38
+
39
+ def decrypted_data
40
+ @decrypted_data ||= cipher.update(ciphertext) + cipher.final
41
+ end
42
+
43
+ def crypto_data
44
+ @crypto_data ||= data['crypto'] || data['Crypto']
45
+ end
46
+
47
+ def ciphertext
48
+ hex_to_bin crypto_data['ciphertext']
49
+ end
50
+
51
+ def cipher_name
52
+ "aes-128-ctr"
53
+ end
54
+
55
+ def cipher
56
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
57
+ cipher.decrypt
58
+ cipher.key = key[0, (key_length/2)]
59
+ cipher.iv = iv
60
+ end
61
+ end
62
+
63
+ def iv
64
+ hex_to_bin crypto_data['cipherparams']['iv']
65
+ end
66
+
67
+ def salt
68
+ hex_to_bin crypto_data['kdfparams']['salt']
69
+ end
70
+
71
+ def iterations
72
+ crypto_data['kdfparams']['c'].to_i
73
+ end
74
+
75
+ def key_length
76
+ 32
77
+ end
78
+
79
+ def digest
80
+ OpenSSL::Digest.new digest_name
81
+ end
82
+
83
+ def digest_name
84
+ "sha256"
85
+ end
86
+
87
+ end
@@ -0,0 +1,127 @@
1
+ require 'json'
2
+
3
+ class Eth::Key::Encrypter
4
+ include Eth::Utils
5
+
6
+ def self.perform(key, password, options = {})
7
+ new(key, options).perform(password)
8
+ end
9
+
10
+ def initialize(key, options = {})
11
+ @key = key
12
+ @options = options
13
+ end
14
+
15
+ def perform(password)
16
+ derive_key password
17
+ encrypt
18
+
19
+ data.to_json
20
+ end
21
+
22
+ def data
23
+ {
24
+ crypto: {
25
+ cipher: cipher_name,
26
+ cipherparams: {
27
+ iv: bin_to_hex(iv),
28
+ },
29
+ ciphertext: bin_to_hex(encrypted_key),
30
+ kdf: "pbkdf2",
31
+ kdfparams: {
32
+ c: iterations,
33
+ dklen: 32,
34
+ prf: prf,
35
+ salt: bin_to_hex(salt),
36
+ },
37
+ mac: bin_to_hex(mac),
38
+ },
39
+ id: id,
40
+ version: 3,
41
+ }.tap do |data|
42
+ data[:address] = address unless options[:skip_address]
43
+ end
44
+ end
45
+
46
+ def id
47
+ @id ||= options[:id] || SecureRandom.uuid
48
+ end
49
+
50
+
51
+ private
52
+
53
+ attr_reader :derived_key, :encrypted_key, :key, :options
54
+
55
+ def cipher
56
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
57
+ cipher.encrypt
58
+ cipher.iv = iv
59
+ cipher.key = derived_key[0, (key_length/2)]
60
+ end
61
+ end
62
+
63
+ def digest
64
+ @digest ||= OpenSSL::Digest.new digest_name
65
+ end
66
+
67
+ def derive_key(password)
68
+ @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
69
+ end
70
+
71
+ def encrypt
72
+ @encrypted_key = cipher.update(hex_to_bin key) + cipher.final
73
+ end
74
+
75
+ def mac
76
+ keccak256(derived_key[(key_length/2), key_length] + encrypted_key)
77
+ end
78
+
79
+ def cipher_name
80
+ "aes-128-ctr"
81
+ end
82
+
83
+ def digest_name
84
+ "sha256"
85
+ end
86
+
87
+ def prf
88
+ "hmac-#{digest_name}"
89
+ end
90
+
91
+ def key_length
92
+ 32
93
+ end
94
+
95
+ def salt_length
96
+ 32
97
+ end
98
+
99
+ def iv_length
100
+ 16
101
+ end
102
+
103
+ def iterations
104
+ options[:iterations] || 262_144
105
+ end
106
+
107
+ def salt
108
+ @salt ||= if options[:salt]
109
+ hex_to_bin options[:salt]
110
+ else
111
+ SecureRandom.random_bytes(salt_length)
112
+ end
113
+ end
114
+
115
+ def iv
116
+ @iv ||= if options[:iv]
117
+ hex_to_bin options[:iv]
118
+ else
119
+ SecureRandom.random_bytes(iv_length)
120
+ end
121
+ end
122
+
123
+ def address
124
+ Eth::Key.default(priv: key).address
125
+ end
126
+
127
+ end
@@ -0,0 +1,197 @@
1
+ # originally lifted from https://github.com/lian/bitcoin-ruby
2
+ # thanks to everyone there for figuring this out
3
+
4
+ module Eth
5
+ class OpenSsl
6
+ extend FFI::Library
7
+
8
+ if FFI::Platform.windows?
9
+ ffi_lib 'libeay32', 'ssleay32'
10
+ else
11
+ ffi_lib ['libssl.so.1.0.0', 'ssl']
12
+ end
13
+
14
+ NID_secp256k1 = 714
15
+ POINT_CONVERSION_COMPRESSED = 2
16
+ POINT_CONVERSION_UNCOMPRESSED = 4
17
+
18
+ attach_function :SSL_library_init, [], :int
19
+ attach_function :ERR_load_crypto_strings, [], :void
20
+ attach_function :SSL_load_error_strings, [], :void
21
+ attach_function :RAND_poll, [], :int
22
+
23
+ attach_function :BN_CTX_free, [:pointer], :int
24
+ attach_function :BN_CTX_new, [], :pointer
25
+ attach_function :BN_add, [:pointer, :pointer, :pointer], :int
26
+ attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
27
+ attach_function :BN_bn2bin, [:pointer, :pointer], :int
28
+ attach_function :BN_cmp, [:pointer, :pointer], :int
29
+ attach_function :BN_dup, [:pointer], :pointer
30
+ attach_function :BN_free, [:pointer], :int
31
+ attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
32
+ attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
33
+ attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
34
+ attach_function :BN_mul_word, [:pointer, :int], :int
35
+ attach_function :BN_new, [], :pointer
36
+ attach_function :BN_num_bits, [:pointer], :int
37
+ attach_function :BN_rshift, [:pointer, :pointer, :int], :int
38
+ attach_function :BN_set_word, [:pointer, :int], :int
39
+ attach_function :ECDSA_SIG_free, [:pointer], :void
40
+ attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
41
+ attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
42
+ attach_function :EC_GROUP_get_degree, [:pointer], :int
43
+ attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
44
+ attach_function :EC_KEY_free, [:pointer], :int
45
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
46
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
47
+ attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
48
+ attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
49
+ attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
50
+ attach_function :EC_POINT_free, [:pointer], :int
51
+ attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
52
+ attach_function :EC_POINT_new, [:pointer], :pointer
53
+ attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
54
+ attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
55
+
56
+ class << self
57
+ def BN_num_bytes(ptr)
58
+ (BN_num_bits(ptr) + 7) / 8
59
+ end
60
+
61
+ def sign_compact(hash, private_key, public_key_hex)
62
+ private_key = [private_key].pack("H*") if private_key.bytesize >= 64
63
+ pubkey_compressed = false
64
+
65
+ init_ffi_ssl
66
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
67
+ priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
68
+
69
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
70
+ EC_GROUP_get_order(group, order, ctx)
71
+
72
+ pub_key = EC_POINT_new(group)
73
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
74
+ EC_KEY_set_private_key(eckey, priv_key)
75
+ EC_KEY_set_public_key(eckey, pub_key)
76
+
77
+ signature = ECDSA_do_sign(hash, hash.bytesize, eckey)
78
+
79
+ BN_free(order)
80
+ BN_CTX_free(ctx)
81
+ EC_POINT_free(pub_key)
82
+ BN_free(priv_key)
83
+ EC_KEY_free(eckey)
84
+
85
+ buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
86
+ 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") }
87
+
88
+ if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
89
+ 4.times{|i|
90
+ head = [ Eth.v_base + i ].pack("C")
91
+ if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
92
+ rec_id = i; break
93
+ end
94
+ }
95
+ end
96
+
97
+ ECDSA_SIG_free(signature)
98
+
99
+ [ head, [r,s] ].join if rec_id
100
+ end
101
+
102
+ def recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
103
+ return nil if rec_id < 0 or signature.bytesize != 65
104
+ init_ffi_ssl
105
+
106
+ signature = FFI::MemoryPointer.from_string(signature)
107
+ r = BN_bin2bn(signature[1], 32, BN_new())
108
+ s = BN_bin2bn(signature[33], 32, BN_new())
109
+
110
+ _n, i = 0, rec_id / 2
111
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
112
+
113
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
114
+
115
+ group = EC_KEY_get0_group(eckey)
116
+ order = BN_new()
117
+ EC_GROUP_get_order(group, order, nil)
118
+ x = BN_dup(order)
119
+ BN_mul_word(x, i)
120
+ BN_add(x, x, r)
121
+
122
+ field = BN_new()
123
+ EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
124
+
125
+ if BN_cmp(x, field) >= 0
126
+ bn_free_each r, s, order, x, field
127
+ EC_KEY_free(eckey)
128
+ return nil
129
+ end
130
+
131
+ big_r = EC_POINT_new(group)
132
+ EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
133
+
134
+ big_q = EC_POINT_new(group)
135
+ n = EC_GROUP_get_degree(group)
136
+ e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
137
+ BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
138
+
139
+ ctx = BN_CTX_new()
140
+ zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
141
+ BN_set_word(zero, 0)
142
+ BN_mod_sub(e, zero, e, order, ctx)
143
+ BN_mod_inverse(rr, r, order, ctx)
144
+ BN_mod_mul(sor, s, rr, order, ctx)
145
+ BN_mod_mul(eor, e, rr, order, ctx)
146
+ EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
147
+ EC_KEY_set_public_key(eckey, big_q)
148
+ BN_CTX_free(ctx)
149
+
150
+ bn_free_each r, s, order, x, field, e, zero, rr, sor, eor
151
+ [big_r, big_q].each{|j| EC_POINT_free(j) }
152
+
153
+ recover_public_hex eckey
154
+ end
155
+
156
+ def recover_compact(hash, signature)
157
+ return false if signature.bytesize != 65
158
+
159
+ version = signature.unpack('C')[0]
160
+ v_base = Eth.replayable_v?(version) ? Eth.replayable_chain_id : Eth.v_base
161
+ return false if version < v_base
162
+
163
+ recover_public_key_from_signature(hash, signature, (version - v_base), false)
164
+ end
165
+
166
+ def init_ffi_ssl
167
+ return if @ssl_loaded
168
+ SSL_library_init()
169
+ ERR_load_crypto_strings()
170
+ SSL_load_error_strings()
171
+ RAND_poll()
172
+ @ssl_loaded = true
173
+ end
174
+
175
+
176
+ private
177
+
178
+ def bn_free_each(*list)
179
+ list.each{|j| BN_free(j) }
180
+ end
181
+
182
+ def recover_public_hex(eckey)
183
+ length = i2o_ECPublicKey(eckey, nil)
184
+ buf = FFI::MemoryPointer.new(:uint8, length)
185
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
186
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
187
+ buf.read_string(length).unpack("H*")[0]
188
+ end
189
+
190
+ EC_KEY_free(eckey)
191
+
192
+ pub_hex
193
+ end
194
+ end
195
+
196
+ end
197
+ end