eth 0.4.18 → 0.5.0

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.
@@ -1,126 +1,205 @@
1
- require "json"
2
- require "securerandom"
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
- class Eth::Key::Encrypter
5
- include Eth::Utils
41
+ # Constructor of the `Eth::Key::Encrypter` class for secret key
42
+ # encryption.
43
+ #
44
+ # @param key [Eth::Key] representing a secret key-pair used for encryption
45
+ # @param options [Hash] the options to encrypt with
46
+ # @option options [String] :kdf key derivation function defaults to pbkdf2
47
+ # @option options [String] :id uuid given to the secret key
48
+ # @option options [String] :iterations number of iterations for the hash function
49
+ # @option options [String] :salt passed to PBKDF
50
+ # @option options [String] :iv 128-bit initialisation vector for the cipher
51
+ # @option options [Integer] :parallelization parallelization factor for scrypt, defaults to 8
52
+ # @option options [Integer] :block_size for scrypt, defaults to 1
53
+ def initialize(key, options = {})
54
+ @key = key
55
+ @options = options
56
+
57
+ # the key derivation functions default to pbkdf2 if no option is specified
58
+ # however, if an option is given then it must be either pbkdf2 or scrypt
59
+ if kdf != "scrypt" && kdf != "pbkdf2"
60
+ raise EncrypterError, "Unsupported key derivation function: #{kdf}!"
61
+ end
62
+ end
6
63
 
7
- def self.perform(key, password, options = {})
8
- new(key, options).perform(password)
9
- end
64
+ # Encrypt the key with a given password.
65
+ #
66
+ # @param password [String] a secret key used for encryption
67
+ # @return [String] a json-formatted keystore string.
68
+ def perform(password)
69
+ derive_key password
70
+ encrypt
71
+ data.to_json
72
+ end
10
73
 
11
- def initialize(key, options = {})
12
- @key = key
13
- @options = options
14
- end
74
+ # Output containing the encrypted key and
75
+ # [other identifying data](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#pbkdf2-sha-256)
76
+ #
77
+ # @return [Hash] the encrypted keystore data.
78
+ def data
79
+ # default to pbkdf2
80
+ kdfparams = if kdf == "scrypt"
81
+ {
82
+ dklen: 32,
83
+ n: iterations,
84
+ p: parallelization,
85
+ r: block_size,
86
+ salt: Util.bin_to_hex(salt),
87
+ }
88
+ else
89
+ {
90
+ c: iterations,
91
+ dklen: 32,
92
+ prf: prf,
93
+ salt: Util.bin_to_hex(salt),
94
+ }
95
+ end
96
+
97
+ {
98
+ crypto: {
99
+ cipher: cipher_name,
100
+ cipherparams: {
101
+ iv: Util.bin_to_hex(iv),
102
+ },
103
+ ciphertext: Util.bin_to_hex(encrypted_key),
104
+ kdf: kdf,
105
+ kdfparams: kdfparams,
106
+ mac: Util.bin_to_hex(mac),
107
+ },
108
+ id: id,
109
+ version: 3,
110
+ }
111
+ end
15
112
 
16
- def perform(password)
17
- derive_key password
18
- encrypt
113
+ private
19
114
 
20
- data.to_json
21
- end
115
+ attr_reader :derived_key, :encrypted_key, :key, :options
22
116
 
23
- def data
24
- {
25
- crypto: {
26
- cipher: cipher_name,
27
- cipherparams: {
28
- iv: bin_to_hex(iv),
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]
117
+ def cipher
118
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
119
+ cipher.encrypt
120
+ cipher.iv = iv
121
+ cipher.key = derived_key[0, (key_length / 2)]
122
+ end
44
123
  end
45
- end
46
124
 
47
- def id
48
- @id ||= options[:id] || SecureRandom.uuid
49
- end
50
-
51
- private
125
+ def digest
126
+ @digest ||= OpenSSL::Digest.new digest_name
127
+ end
52
128
 
53
- attr_reader :derived_key, :encrypted_key, :key, :options
129
+ def derive_key(password)
130
+ if kdf == "scrypt"
131
+ @derived_key = SCrypt::Engine.scrypt(password, salt, iterations, block_size, parallelization, key_length)
132
+ else
133
+ @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
134
+ end
135
+ end
54
136
 
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)]
137
+ def encrypt
138
+ @encrypted_key = cipher.update(Util.hex_to_bin key.private_hex) + cipher.final
60
139
  end
61
- end
62
140
 
63
- def digest
64
- @digest ||= OpenSSL::Digest.new digest_name
65
- end
141
+ def mac
142
+ Util.keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
143
+ end
66
144
 
67
- def derive_key(password)
68
- @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
69
- end
145
+ def kdf
146
+ options[:kdf] || "pbkdf2"
147
+ end
70
148
 
71
- def encrypt
72
- @encrypted_key = cipher.update(hex_to_bin key) + cipher.final
73
- end
149
+ def cipher_name
150
+ "aes-128-ctr"
151
+ end
74
152
 
75
- def mac
76
- keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
77
- end
153
+ def digest_name
154
+ "sha256"
155
+ end
78
156
 
79
- def cipher_name
80
- "aes-128-ctr"
81
- end
157
+ def prf
158
+ "hmac-#{digest_name}"
159
+ end
82
160
 
83
- def digest_name
84
- "sha256"
85
- end
161
+ def key_length
162
+ 32
163
+ end
86
164
 
87
- def prf
88
- "hmac-#{digest_name}"
89
- end
165
+ def salt_length
166
+ 32
167
+ end
90
168
 
91
- def key_length
92
- 32
93
- end
169
+ def iv_length
170
+ 16
171
+ end
94
172
 
95
- def salt_length
96
- 32
97
- end
173
+ def id
174
+ @id ||= options[:id] || SecureRandom.uuid
175
+ end
98
176
 
99
- def iv_length
100
- 16
101
- end
177
+ def iterations
178
+ options[:iterations] || 262_144
179
+ end
102
180
 
103
- def iterations
104
- options[:iterations] || 262_144
105
- end
181
+ def salt
182
+ @salt ||= if options[:salt]
183
+ Util.hex_to_bin options[:salt]
184
+ else
185
+ SecureRandom.random_bytes(salt_length)
186
+ end
187
+ end
106
188
 
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
189
+ def iv
190
+ @iv ||= if options[:iv]
191
+ Util.hex_to_bin options[:iv]
192
+ else
193
+ SecureRandom.random_bytes(iv_length)
194
+ end
195
+ end
114
196
 
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
197
+ def parallelization
198
+ options[:parallelization] || 8
199
+ end
122
200
 
123
- def address
124
- Eth::Key.new(priv: key).address
201
+ def block_size
202
+ options[:block_size] || 1
203
+ end
125
204
  end
126
205
  end
data/lib/eth/key.rb CHANGED
@@ -1,78 +1,162 @@
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
+ require "json"
16
+ require "openssl"
17
+ require "rbsecp256k1"
18
+ require "scrypt"
19
+ require "securerandom"
20
+
21
+ # Provides the `Eth` module.
1
22
  module Eth
23
+
24
+ # The `Eth::Key` class to handle Secp256k1 private/public key-pairs.
2
25
  class Key
26
+
27
+ # The Eth::Key::Decrypter class to handle PBKDF2-SHA-256 decryption.
3
28
  autoload :Decrypter, "eth/key/decrypter"
29
+
30
+ # The Eth::Key::Encrypter class to handle PBKDF2-SHA-256 encryption.
4
31
  autoload :Encrypter, "eth/key/encrypter"
5
32
 
6
- attr_reader :private_key, :public_key
33
+ # The `Secp256k1::PrivateKey` of the `Eth::Key` pair.
34
+ attr_reader :private_key
7
35
 
8
- def self.encrypt(key, password)
9
- key = new(priv: key) unless key.is_a?(Key)
36
+ # The `Secp256k1::PublicKey` of the `Eth::Key` pair.
37
+ attr_reader :public_key
10
38
 
11
- Encrypter.perform key.private_hex, password
12
- end
39
+ # Constructor of the `Eth::Key` class. Creates a new random key-pair
40
+ # if no `priv` key is provided.
41
+ #
42
+ # @param priv [String] binary string of private key data (optional).
43
+ def initialize(priv: nil)
13
44
 
14
- def self.decrypt(data, password)
15
- priv = Decrypter.perform data, password
16
- new priv: priv
17
- end
45
+ # Creates a new, randomized libsecp256k1 context.
46
+ ctx = Secp256k1::Context.new context_randomization_bytes: SecureRandom.random_bytes(32)
18
47
 
19
- def self.personal_recover(message, signature)
20
- bin_signature = Utils.hex_to_bin(signature).bytes.rotate(-1).pack("c*")
21
- OpenSsl.recover_compact(Utils.keccak256(Utils.prefix_message(message)), bin_signature)
22
- end
48
+ # Creates a new random key pair (public, private).
49
+ key = ctx.generate_key_pair
23
50
 
24
- def initialize(priv: nil)
25
- @private_key = MoneyTree::PrivateKey.new key: priv
26
- @public_key = MoneyTree::PublicKey.new private_key, compressed: false
27
- end
51
+ unless priv.nil?
28
52
 
29
- def private_hex
30
- private_key.to_hex
31
- end
53
+ # Converts hex private keys to binary strings.
54
+ priv = Util.hex_to_bin priv if Util.is_hex? priv
32
55
 
33
- def public_bytes
34
- public_key.to_bytes
56
+ # Creates a keypair from existing private key data.
57
+ key = ctx.key_pair_from_private_key priv
58
+ end
59
+
60
+ # Sets the attributes.
61
+ @private_key = key.private_key
62
+ @public_key = key.public_key
35
63
  end
36
64
 
37
- def public_hex
38
- public_key.to_hex
65
+ # Signs arbitrary data without validation. Should not be used unless really
66
+ # desired. See also: personal_sign, sign_typed_data.
67
+ #
68
+ # @param blob [String] that arbitrary data to be signed.
69
+ # @param chain_id [Integer] the chain id the signature should be generated on.
70
+ # @return [String] a hexa-decimal signature.
71
+ def sign(blob, chain_id = nil)
72
+ context = Secp256k1::Context.new
73
+ compact, recovery_id = context.sign_recoverable(@private_key, blob).compact
74
+ signature = compact.bytes
75
+ v = Chain.to_v recovery_id, chain_id
76
+ is_leading_zero = true
77
+ [v].pack("N").unpack("C*").each do |byte|
78
+ is_leading_zero = false if byte > 0 and is_leading_zero
79
+ signature.append byte unless is_leading_zero and byte === 0
80
+ end
81
+ Util.bin_to_hex signature.pack "c*"
39
82
  end
40
83
 
41
- def address
42
- Utils.public_key_to_address public_hex
84
+ # Prefixes a message with "\x19Ethereum Signed Message:" and signs
85
+ # it in the common way used by many web3 wallets. Complies with
86
+ # EIP-191 prefix 0x19 and version byte 0x45 (E).
87
+ #
88
+ # @param message [String] the message string to be prefixed and signed.
89
+ # @param chain_id [Integer] the chain id the signature should be generated on.
90
+ # @return [String] an EIP-191 conform, hexa-decimal signature.
91
+ def personal_sign(message, chain_id = nil)
92
+ prefixed_message = Signature.prefix_message message
93
+ hashed_message = Util.keccak256 prefixed_message
94
+ sign hashed_message, chain_id
43
95
  end
44
96
 
45
- alias_method :to_address, :address
97
+ # Prefixes, hashes, and signes a typed data structure in the common
98
+ # way used by many web3 wallets. Complies with EIP-191 prefix 0x19
99
+ # and EIP-712 version byte 0x01. Supports `V3`, `V4`.
100
+ #
101
+ # @param typed_data [Array] all the data in the typed data structure to be signed.
102
+ # @param chain_id [Integer] the chain id the signature should be generated on.
103
+ # @return [String] an EIP-712 conform, hexa-decimal signature.
104
+ def sign_typed_data(typed_data, chain_id = nil)
105
+ hash_to_sign = Eip712.hash typed_data
106
+ sign hash_to_sign, chain_id
107
+ end
46
108
 
47
- def sign(message)
48
- sign_hash message_hash(message)
109
+ # Converts the private key data into a hexa-decimal string.
110
+ #
111
+ # @return [String] private key as hexa-decimal string.
112
+ def private_hex
113
+ Util.bin_to_hex @private_key.data
49
114
  end
50
115
 
51
- def sign_hash(hash)
52
- loop do
53
- signature = OpenSsl.sign_compact hash, private_hex, public_hex
54
- return signature if valid_s? signature
55
- end
116
+ # Exports the private key bytes in a wrapper function to maintain
117
+ # backward-compatibility with older versions of `Eth::Key`.
118
+ #
119
+ # @return [String] private key as packed byte-string.
120
+ def private_bytes
121
+ @private_key.data
56
122
  end
57
123
 
58
- def verify_signature(message, signature)
59
- hash = message_hash(message)
60
- public_hex == OpenSsl.recover_compact(hash, signature)
124
+ # Converts the public key data into an uncompressed
125
+ # hexa-decimal string.
126
+ #
127
+ # @return [String] public key as uncompressed hexa-decimal string.
128
+ def public_hex
129
+ Util.bin_to_hex @public_key.uncompressed
61
130
  end
62
131
 
63
- def personal_sign(message)
64
- Utils.bin_to_hex(sign(Utils.prefix_message(message)).bytes.rotate(1).pack("c*"))
132
+ # Converts the public key data into an compressed
133
+ # hexa-decimal string.
134
+ #
135
+ # @return [String] public key as compressed hexa-decimal string.
136
+ def public_hex_compressed
137
+ Util.bin_to_hex @public_key.compressed
65
138
  end
66
139
 
67
- private
140
+ # Exports the uncompressed public key bytes in a wrapper function to
141
+ # maintain backward-compatibility with older versions of `Eth::Key`.
142
+ #
143
+ # @return [String] uncompressed public key as packed byte-string.
144
+ def public_bytes
145
+ @public_key.uncompressed
146
+ end
68
147
 
69
- def message_hash(message)
70
- Utils.keccak256 message
148
+ # Exports the compressed public key bytes.
149
+ #
150
+ # @return [String] compressed public key as packed byte-string.
151
+ def public_bytes_compressed
152
+ @public_key.compressed
71
153
  end
72
154
 
73
- def valid_s?(signature)
74
- s_value = Utils.v_r_s_for(signature).last
75
- s_value <= Secp256k1::N / 2 && s_value != 0
155
+ # Exports the checksummed public address.
156
+ #
157
+ # @return [Eth::Address] compressed address as packed hex prefixed string.
158
+ def address
159
+ Util.public_key_to_address public_bytes
76
160
  end
77
161
  end
78
162
  end
@@ -0,0 +1,160 @@
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
+ require "rbsecp256k1"
16
+
17
+ # Provides the `Eth` module.
18
+ module Eth
19
+
20
+ # Defines handy tools for verifying and recovering signatures.
21
+ module Signature
22
+ extend self
23
+
24
+ # Provides a special signature error if signature is invalid.
25
+ class SignatureError < StandardError; end
26
+
27
+ # EIP-191 prefix byte 0x19
28
+ EIP191_PREFIX_BYTE = "\x19".freeze
29
+
30
+ # EIP-712 version byte 0x01
31
+ EIP712_VERSION_BYTE = "\x01".freeze
32
+
33
+ # Prefix message as per EIP-191 with 0x19 to ensure the data is not
34
+ # valid RLP and thus not mistaken for a transaction.
35
+ # EIP-191 Version byte: 0x45 (E)
36
+ # ref: https://eips.ethereum.org/EIPS/eip-191
37
+ #
38
+ # @param message [String] the message string to be prefixed.
39
+ # @return [String] an EIP-191 prefixed string
40
+ def prefix_message(message)
41
+ "#{EIP191_PREFIX_BYTE}Ethereum Signed Message:\n#{message.size}#{message}"
42
+ end
43
+
44
+ # Dissects a signature blob of 65 bytes into its r, s, and v values.
45
+ #
46
+ # @param signature [String] a Secp256k1 signature.
47
+ # @return [String, String, String] the r, s, and v values.
48
+ # @raise [SignatureError] if signature is of unknown size.
49
+ def dissect(signature)
50
+ signature = Util.bin_to_hex signature unless Util.is_hex? signature
51
+ signature = Util.remove_hex_prefix signature
52
+ if signature.size < 130
53
+ raise SignatureError, "Unknown signature length #{signature.size}!"
54
+ end
55
+ r = signature[0, 64]
56
+ s = signature[64, 64]
57
+ v = signature[128..]
58
+ return r, s, v
59
+ end
60
+
61
+ # Recovers a signature from arbitrary data without validation on a given chain.
62
+ #
63
+ # @param blob [String] that arbitrary data to be recovered.
64
+ # @param signature [String] the hex string containing the signature.
65
+ # @param chain_id [Integer] the chain ID the signature should be recovered from.
66
+ # @return [String] a hexa-decimal, uncompressed public key.
67
+ # @raise [SignatureError] if signature is of invalid size or invalid v.
68
+ def recover(blob, signature, chain_id = Chain::ETHEREUM)
69
+ context = Secp256k1::Context.new
70
+ r, s, v = dissect signature
71
+ v = v.to_i(16)
72
+ raise SignatureError, "Invalid signature v byte #{v} for chain ID #{chain_id}!" if v < chain_id
73
+ recovery_id = Chain.to_recovery_id v, chain_id
74
+ signature_rs = Util.hex_to_bin "#{r}#{s}"
75
+ recoverable_signature = context.recoverable_signature_from_compact signature_rs, recovery_id
76
+ public_key = recoverable_signature.recover_public_key blob
77
+ Util.bin_to_hex public_key.uncompressed
78
+ end
79
+
80
+ # Recovers a public key from a prefixed, personal message and
81
+ # a signature on a given chain. (EIP-191)
82
+ #
83
+ # @param message [String] the message string.
84
+ # @param signature [String] the hex string containing the signature.
85
+ # @param chain_id [Integer] the chain ID the signature should be recovered from.
86
+ # @return [String] a hexa-decimal, uncompressed public key.
87
+ def personal_recover(message, signature, chain_id = Chain::ETHEREUM)
88
+ prefixed_message = prefix_message message
89
+ hashed_message = Util.keccak256 prefixed_message
90
+ recover hashed_message, signature, chain_id
91
+ end
92
+
93
+ # Recovers a public key from a typed data structure and a signature
94
+ # on a given chain. (EIP-712)
95
+ #
96
+ # @param typed_data [Array] all the data in the typed data structure to be recovered.
97
+ # @param signature [String] the hex string containing the signature.
98
+ # @param chain_id [Integer] the chain ID the signature should be recovered from.
99
+ # @return [String] a hexa-decimal, uncompressed public key.
100
+ def recover_typed_data(typed_data, signature, chain_id = Chain::ETHEREUM)
101
+ hash_to_sign = Eip712.hash typed_data
102
+ recover hash_to_sign, signature, chain_id
103
+ end
104
+
105
+ # Verifies a signature for a given public key or address.
106
+ #
107
+ # @param blob [String] that arbitrary data to be verified.
108
+ # @param signature [String] the hex string containing the signature.
109
+ # @param public_key [String] either a public key or an Ethereum address.
110
+ # @param chain_id [Integer] the chain ID used to sign.
111
+ # @return [Boolean] true if signature matches provided public key.
112
+ # @raise [SignatureError] if it cannot determine the type of data or public key.
113
+ def verify(blob, signature, public_key, chain_id = Chain::ETHEREUM)
114
+ recovered_key = nil
115
+ if blob.instance_of? Array or blob.instance_of? Hash
116
+
117
+ # recover Array from sign_typed_data
118
+ recovered_key = recover_typed_data blob, signature, chain_id
119
+ elsif blob.instance_of? String and blob.encoding != Encoding::ASCII_8BIT
120
+
121
+ # recover message from personal_sign
122
+ recovered_key = personal_recover blob, signature, chain_id
123
+ elsif blob.instance_of? String and (Util.is_hex? blob or blob.encoding == Encoding::ASCII_8BIT)
124
+
125
+ # if nothing else, recover from arbitrary signature
126
+ recovered_key = recover blob, signature, chain_id
127
+ end
128
+
129
+ # raise if we cannot determine the data format
130
+ raise SignatureError, "Unknown data format to verify: #{blob}" if recovered_key.nil?
131
+
132
+ if public_key.instance_of? Address
133
+
134
+ # recovering using an Eth::Address
135
+ address = public_key.to_s
136
+ recovered_address = Util.public_key_to_address(recovered_key).to_s
137
+ return address == recovered_address
138
+ elsif public_key.instance_of? Secp256k1::PublicKey
139
+
140
+ # recovering using an Secp256k1::PublicKey
141
+ public_hex = Util.bin_to_hex public_key.uncompressed
142
+ return public_hex == recovered_key
143
+ elsif public_key.size == 42
144
+
145
+ # recovering using an address String
146
+ address = Address.new(public_key).to_s
147
+ recovered_address = Util.public_key_to_address(recovered_key).to_s
148
+ return address == recovered_address
149
+ elsif public_key.size == 130
150
+
151
+ # recovering using an uncompressed public key String
152
+ return public_key == recovered_key
153
+ else
154
+
155
+ # raise if we cannot determine the public key format used
156
+ raise SignatureError, "Invalid public key or address supplied #{public_key}!"
157
+ end
158
+ end
159
+ end
160
+ end