eth 0.4.12 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,128 +1,205 @@
1
- require 'json'
2
- require 'securerandom'
3
-
4
- class Eth::Key::Encrypter
5
- include Eth::Utils
6
-
7
- def self.perform(key, password, options = {})
8
- new(key, options).perform(password)
9
- end
10
-
11
- def initialize(key, options = {})
12
- @key = key
13
- @options = options
14
- end
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
15
40
 
16
- def perform(password)
17
- derive_key password
18
- encrypt
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
19
63
 
20
- data.to_json
21
- 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
22
73
 
23
- def data
24
- {
25
- crypto: {
26
- cipher: cipher_name,
27
- cipherparams: {
28
- iv: bin_to_hex(iv),
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),
29
107
  },
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]
108
+ id: id,
109
+ version: 3,
110
+ }
44
111
  end
45
- end
46
112
 
47
- def id
48
- @id ||= options[:id] || SecureRandom.uuid
49
- end
113
+ private
50
114
 
115
+ attr_reader :derived_key, :encrypted_key, :key, :options
51
116
 
52
- private
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
123
+ end
53
124
 
54
- attr_reader :derived_key, :encrypted_key, :key, :options
125
+ def digest
126
+ @digest ||= OpenSSL::Digest.new digest_name
127
+ end
55
128
 
56
- def cipher
57
- @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
58
- cipher.encrypt
59
- cipher.iv = iv
60
- cipher.key = derived_key[0, (key_length/2)]
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
61
135
  end
62
- end
63
136
 
64
- def digest
65
- @digest ||= OpenSSL::Digest.new digest_name
66
- end
137
+ def encrypt
138
+ @encrypted_key = cipher.update(Util.hex_to_bin key.private_hex) + cipher.final
139
+ end
67
140
 
68
- def derive_key(password)
69
- @derived_key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
70
- end
141
+ def mac
142
+ Util.keccak256(derived_key[(key_length / 2), key_length] + encrypted_key)
143
+ end
71
144
 
72
- def encrypt
73
- @encrypted_key = cipher.update(hex_to_bin key) + cipher.final
74
- end
145
+ def kdf
146
+ options[:kdf] || "pbkdf2"
147
+ end
75
148
 
76
- def mac
77
- keccak256(derived_key[(key_length/2), key_length] + encrypted_key)
78
- end
149
+ def cipher_name
150
+ "aes-128-ctr"
151
+ end
79
152
 
80
- def cipher_name
81
- "aes-128-ctr"
82
- end
153
+ def digest_name
154
+ "sha256"
155
+ end
83
156
 
84
- def digest_name
85
- "sha256"
86
- end
157
+ def prf
158
+ "hmac-#{digest_name}"
159
+ end
87
160
 
88
- def prf
89
- "hmac-#{digest_name}"
90
- end
161
+ def key_length
162
+ 32
163
+ end
91
164
 
92
- def key_length
93
- 32
94
- end
165
+ def salt_length
166
+ 32
167
+ end
95
168
 
96
- def salt_length
97
- 32
98
- end
169
+ def iv_length
170
+ 16
171
+ end
99
172
 
100
- def iv_length
101
- 16
102
- end
173
+ def id
174
+ @id ||= options[:id] || SecureRandom.uuid
175
+ end
103
176
 
104
- def iterations
105
- options[:iterations] || 262_144
106
- end
177
+ def iterations
178
+ options[:iterations] || 262_144
179
+ end
107
180
 
108
- def salt
109
- @salt ||= if options[:salt]
110
- hex_to_bin options[:salt]
111
- else
112
- SecureRandom.random_bytes(salt_length)
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
113
187
  end
114
- end
115
188
 
116
- def iv
117
- @iv ||= if options[:iv]
118
- hex_to_bin options[:iv]
119
- else
120
- SecureRandom.random_bytes(iv_length)
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
121
195
  end
122
- end
123
196
 
124
- def address
125
- Eth::Key.new(priv: key).address
126
- end
197
+ def parallelization
198
+ options[:parallelization] || 8
199
+ end
127
200
 
201
+ def block_size
202
+ options[:block_size] || 1
203
+ end
204
+ end
128
205
  end
data/lib/eth/key.rb CHANGED
@@ -1,79 +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
3
- autoload :Decrypter, 'eth/key/decrypter'
4
- autoload :Encrypter, 'eth/key/encrypter'
5
26
 
6
- attr_reader :private_key, :public_key
27
+ # The Eth::Key::Decrypter class to handle PBKDF2-SHA-256 decryption.
28
+ autoload :Decrypter, "eth/key/decrypter"
7
29
 
8
- def self.encrypt(key, password)
9
- key = new(priv: key) unless key.is_a?(Key)
30
+ # The Eth::Key::Encrypter class to handle PBKDF2-SHA-256 encryption.
31
+ autoload :Encrypter, "eth/key/encrypter"
10
32
 
11
- Encrypter.perform key.private_hex, password
12
- end
33
+ # The `Secp256k1::PrivateKey` of the `Eth::Key` pair.
34
+ attr_reader :private_key
13
35
 
14
- def self.decrypt(data, password)
15
- priv = Decrypter.perform data, password
16
- new priv: priv
17
- end
18
-
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
36
+ # The `Secp256k1::PublicKey` of the `Eth::Key` pair.
37
+ attr_reader :public_key
23
38
 
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).
24
43
  def initialize(priv: nil)
25
- @private_key = MoneyTree::PrivateKey.new key: priv
26
- @public_key = MoneyTree::PublicKey.new private_key, compressed: false
27
- end
28
44
 
29
- def private_hex
30
- private_key.to_hex
31
- end
45
+ # Creates a new, randomized libsecp256k1 context.
46
+ ctx = Secp256k1::Context.new context_randomization_bytes: SecureRandom.random_bytes(32)
32
47
 
33
- def public_bytes
34
- public_key.to_bytes
35
- end
48
+ # Creates a new random key pair (public, private).
49
+ key = ctx.generate_key_pair
36
50
 
37
- def public_hex
38
- public_key.to_hex
51
+ unless priv.nil?
52
+
53
+ # Converts hex private keys to binary strings.
54
+ priv = Util.hex_to_bin priv if Util.is_hex? priv
55
+
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
39
63
  end
40
64
 
41
- def address
42
- Utils.public_key_to_address public_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*"
43
82
  end
44
- alias_method :to_address, :address
45
83
 
46
- def sign(message)
47
- sign_hash message_hash(message)
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
48
95
  end
49
96
 
50
- def sign_hash(hash)
51
- loop do
52
- signature = OpenSsl.sign_compact hash, private_hex, public_hex
53
- return signature if valid_s? signature
54
- end
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
55
107
  end
56
108
 
57
- def verify_signature(message, signature)
58
- hash = message_hash(message)
59
- public_hex == OpenSsl.recover_compact(hash, signature)
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
60
114
  end
61
115
 
62
- def personal_sign(message)
63
- Utils.bin_to_hex(sign(Utils.prefix_message(message)).bytes.rotate(1).pack('c*'))
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
64
122
  end
65
123
 
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
130
+ end
66
131
 
67
- private
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
138
+ end
68
139
 
69
- def message_hash(message)
70
- Utils.keccak256 message
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
71
146
  end
72
147
 
73
- def valid_s?(signature)
74
- s_value = Utils.v_r_s_for(signature).last
75
- s_value <= Secp256k1::N/2 && s_value != 0
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
76
153
  end
77
154
 
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
160
+ end
78
161
  end
79
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