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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/.rspec +4 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +47 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +141 -0
- data/Rakefile +11 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/eth.gemspec +34 -0
- data/lib/eth.rb +77 -0
- data/lib/eth/address.rb +62 -0
- data/lib/eth/gas.rb +9 -0
- data/lib/eth/key.rb +119 -0
- data/lib/eth/key/decrypter.rb +87 -0
- data/lib/eth/key/encrypter.rb +127 -0
- data/lib/eth/open_ssl.rb +197 -0
- data/lib/eth/rpc_signer.rb +26 -0
- data/lib/eth/secp256k1.rb +7 -0
- data/lib/eth/sedes.rb +40 -0
- data/lib/eth/signature.rb +11 -0
- data/lib/eth/tx.rb +154 -0
- data/lib/eth/utils.rb +147 -0
- data/lib/eth/vault.rb +45 -0
- data/lib/eth/version.rb +3 -0
- metadata +229 -0
data/lib/eth/gas.rb
ADDED
data/lib/eth/key.rb
ADDED
@@ -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
|
data/lib/eth/open_ssl.rb
ADDED
@@ -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
|