digix-eth 0.5.2

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