eth-custom 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +18 -0
  3. data/.github/workflows/codeql.yml +48 -0
  4. data/.github/workflows/docs.yml +26 -0
  5. data/.github/workflows/spec.yml +52 -0
  6. data/.gitignore +43 -0
  7. data/.gitmodules +3 -0
  8. data/.rspec +4 -0
  9. data/.yardopts +1 -0
  10. data/AUTHORS.txt +29 -0
  11. data/CHANGELOG.md +218 -0
  12. data/Gemfile +17 -0
  13. data/LICENSE.txt +202 -0
  14. data/README.md +347 -0
  15. data/Rakefile +6 -0
  16. data/bin/console +10 -0
  17. data/bin/setup +9 -0
  18. data/codecov.yml +6 -0
  19. data/eth.gemspec +51 -0
  20. data/lib/eth/abi/event.rb +137 -0
  21. data/lib/eth/abi/type.rb +178 -0
  22. data/lib/eth/abi.rb +446 -0
  23. data/lib/eth/address.rb +106 -0
  24. data/lib/eth/api.rb +223 -0
  25. data/lib/eth/chain.rb +157 -0
  26. data/lib/eth/client/http.rb +63 -0
  27. data/lib/eth/client/ipc.rb +50 -0
  28. data/lib/eth/client.rb +499 -0
  29. data/lib/eth/constant.rb +71 -0
  30. data/lib/eth/contract/event.rb +42 -0
  31. data/lib/eth/contract/function.rb +57 -0
  32. data/lib/eth/contract/function_input.rb +38 -0
  33. data/lib/eth/contract/function_output.rb +37 -0
  34. data/lib/eth/contract/initializer.rb +47 -0
  35. data/lib/eth/contract.rb +143 -0
  36. data/lib/eth/eip712.rb +184 -0
  37. data/lib/eth/key/decrypter.rb +146 -0
  38. data/lib/eth/key/encrypter.rb +207 -0
  39. data/lib/eth/key.rb +167 -0
  40. data/lib/eth/rlp/decoder.rb +114 -0
  41. data/lib/eth/rlp/encoder.rb +78 -0
  42. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  43. data/lib/eth/rlp/sedes/binary.rb +97 -0
  44. data/lib/eth/rlp/sedes/list.rb +84 -0
  45. data/lib/eth/rlp/sedes.rb +74 -0
  46. data/lib/eth/rlp.rb +63 -0
  47. data/lib/eth/signature.rb +163 -0
  48. data/lib/eth/solidity.rb +75 -0
  49. data/lib/eth/tx/eip1559.rb +337 -0
  50. data/lib/eth/tx/eip2930.rb +329 -0
  51. data/lib/eth/tx/legacy.rb +297 -0
  52. data/lib/eth/tx.rb +322 -0
  53. data/lib/eth/unit.rb +49 -0
  54. data/lib/eth/util.rb +235 -0
  55. data/lib/eth/version.rb +20 -0
  56. data/lib/eth.rb +35 -0
  57. metadata +184 -0
@@ -0,0 +1,38 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provide classes for contract function input.
21
+ class Contract::FunctionInput
22
+ attr_accessor :type, :raw_type, :name
23
+
24
+ # Constructor of the {Eth::Contract::FunctionInput} class.
25
+ #
26
+ # @param data [Hash] contract abi data.
27
+ def initialize(data)
28
+ @raw_type = data["type"]
29
+ @type = Eth::Abi::Type.parse(data["type"])
30
+ @name = data["name"]
31
+ end
32
+
33
+ # Returns complete types with subtypes, e.g., `uint256`.
34
+ def type
35
+ @type.base_type + @type.sub_type + @type.dimensions.map { |dimension| "[#{dimension > 0 ? dimension : ""}]" }.join("")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provide classes for contract function output.
21
+ class Contract::FunctionOutput
22
+ attr_accessor :type, :name
23
+
24
+ # Constructor of the {Eth::Contract::FunctionOutput} class.
25
+ #
26
+ # @param data [Hash] contract abi data.
27
+ def initialize(data)
28
+ @type = Eth::Abi::Type.parse(data["type"])
29
+ @name = data["name"]
30
+ end
31
+
32
+ # Returns complete types with subtypes, e.g., `uint256`.
33
+ def type
34
+ @type.base_type + @type.sub_type + @type.dimensions.map { |dimension| "[#{dimension > 0 ? dimension : ""}]" }.join("")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provide classes for contract initializer.
21
+ class Contract::Initializer
22
+ attr_accessor :contracts, :file
23
+
24
+ # Constructor of the {Eth::Contract::Initializer} class.
25
+ #
26
+ # @param file [String] file path to solidity code.
27
+ def initialize(file)
28
+ sol_output = Eth::Solidity.new.compile(file)
29
+ contracts = sol_output.keys
30
+
31
+ @contracts = []
32
+ contracts.each do |contract|
33
+ abi = sol_output[contract]["abi"]
34
+ name = contract
35
+ code = sol_output[contract]["bin"]
36
+ @contracts << Contract.new(name, code, abi)
37
+ end
38
+ end
39
+
40
+ # Builds and returns all contracts.
41
+ def build_all
42
+ @contracts.each do |contract|
43
+ contract.build
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,143 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides classes to access smart contracts
21
+ class Contract
22
+ attr_reader :address
23
+ attr_accessor :key
24
+ attr_accessor :gas_limit, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :nonce
25
+ attr_accessor :bin, :name, :abi, :class_object
26
+ attr_accessor :events, :functions, :constructor_inputs
27
+
28
+ # Constructor of the {Eth::Contract} class.
29
+ #
30
+ # @param name [String] contract name.
31
+ # @param bin [String] contract bin string.
32
+ # @param abi [String] contract abi string.
33
+ def initialize(name, bin, abi)
34
+ @name = name
35
+ @bin = bin
36
+ @abi = abi
37
+ @constructor_inputs, @functions, @events = parse_abi(abi)
38
+ end
39
+
40
+ # Creates a contract wrapper from a Solidity file.
41
+ #
42
+ # @param file [String] solidity file path.
43
+ # @param contract_index [Number] specify contract.
44
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
45
+ # @raise [ArgumentError] if the file path is empty or no contracts were compiled.
46
+ def self.from_file(file:, contract_index: 0)
47
+ raise ArgumentError, "Cannot find the contract at #{file.to_s}!" if !File.exist?(file.to_s)
48
+ contracts = Eth::Contract::Initializer.new(file).build_all
49
+ raise ArgumentError, "No contracts compiled." if contracts.empty?
50
+ contracts[contract_index].class_object.new
51
+ end
52
+
53
+ # Creates a contract wrapper from ABI and address.
54
+ #
55
+ # @param abi [String] contract abi string.
56
+ # @param address [String] contract address.
57
+ # @param name [String] name of contract.
58
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
59
+ # @raise [JSON::ParserError] if the json format is wrong.
60
+ # @raise [ArgumentError] if ABI, address, or name is missing.
61
+ def self.from_abi(abi:, address:, name:)
62
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
63
+ contract = Eth::Contract.new(name, nil, abi)
64
+ contract.build
65
+ contract = contract.class_object.new
66
+ contract.address = address
67
+ contract
68
+ end
69
+
70
+ # Creates a contract wrapper from binary and ABI.
71
+ #
72
+ # @param bin [String] contract bin string.
73
+ # @param abi [String] contract abi string.
74
+ # @param name [String] name of contract.
75
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
76
+ # @raise [JSON::ParserError] if the json format is wrong.
77
+ # @raise [ArgumentError] if ABI, binary, or name is missing.
78
+ def self.from_bin(bin:, abi:, name:)
79
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
80
+ contract = Eth::Contract.new(name, bin, abi)
81
+ contract.build
82
+ contract.class_object.new
83
+ end
84
+
85
+ # Sets the address of the smart contract.
86
+ #
87
+ # @param addr [String|Eth::Address] contract address string.
88
+ def address=(addr)
89
+ if addr.is_a? Eth::Address
90
+ @address = addr.to_s
91
+ else
92
+ @address = Eth::Address.new(addr).to_s
93
+ end
94
+ @events.each do |event|
95
+ event.set_address(@address)
96
+ end
97
+ end
98
+
99
+ # Create meta classes for smart contracts.
100
+ def build
101
+ class_name = @name
102
+ parent = self
103
+ class_methods = Class.new do
104
+ extend Forwardable
105
+ def_delegators :parent, :key, :key=
106
+ def_delegators :parent, :name, :abi, :bin
107
+ def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
108
+ def_delegators :parent, :max_fee_per_gas, :max_fee_per_gas=, :max_priority_fee_per_gas, :max_priority_fee_per_gas=
109
+ def_delegators :parent, :events
110
+ def_delegators :parent, :address, :address=
111
+ def_delegator :parent, :functions
112
+ def_delegator :parent, :constructor_inputs
113
+ define_method :parent do
114
+ parent
115
+ end
116
+ end
117
+ Eth::Contract.send(:remove_const, class_name) if Eth::Contract.const_defined?(class_name, false)
118
+ Eth::Contract.const_set(class_name, class_methods)
119
+ @class_object = class_methods
120
+ end
121
+
122
+ private
123
+
124
+ def parse_abi(abi)
125
+ constructor = abi.detect { |x| x["type"] == "constructor" }
126
+ if !constructor.nil?
127
+ constructor_inputs = constructor["inputs"].map { |input| Eth::Contract::FunctionInput.new(input) }
128
+ else
129
+ constructor_inputs = []
130
+ end
131
+ functions = abi.select { |x| x["type"] == "function" }.map { |fun| Eth::Contract::Function.new(fun) }
132
+ events = abi.select { |x| x["type"] == "event" }.map { |evt| Eth::Contract::Event.new(evt) }
133
+ [constructor_inputs, functions, events]
134
+ end
135
+ end
136
+ end
137
+
138
+ # Load the contract/* libraries
139
+ require "eth/contract/event"
140
+ require "eth/contract/function"
141
+ require "eth/contract/function_input"
142
+ require "eth/contract/function_output"
143
+ require "eth/contract/initializer"
data/lib/eth/eip712.rb ADDED
@@ -0,0 +1,184 @@
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
+ # Defines handy tools for encoding typed structured data as per EIP-712.
19
+ # Ref: https://eips.ethereum.org/EIPS/eip-712
20
+ module Eip712
21
+ extend self
22
+
23
+ # Provides a special typed-data error if data structure fails basic
24
+ # verification.
25
+ class TypedDataError < StandardError; end
26
+
27
+ # Scans all dependencies of a given type recursively and returns
28
+ # either all dependencies or none if not found.
29
+ #
30
+ # @param primary_type [String] the primary type which we want to scan.
31
+ # @param types [Array] all existing types in the data structure.
32
+ # @param result [Array] found results from previous recursions.
33
+ # @return [Array] all dependent types for the given primary type.
34
+ def type_dependencies(primary_type, types, result = [])
35
+ if result.include? primary_type
36
+
37
+ # ignore if we already have the give type in results
38
+ return result
39
+ elsif types[primary_type.to_sym].nil?
40
+
41
+ # ignore if the type is not used, e.g., a string or address.
42
+ return result
43
+ else
44
+
45
+ # we found something
46
+ result.push primary_type
47
+
48
+ # recursively look for further nested dependencies
49
+ types[primary_type.to_sym].each do |t|
50
+ dependency = type_dependencies t[:type], types, result
51
+ end
52
+ return result
53
+ end
54
+ end
55
+
56
+ # Encode types as an EIP-712 confrom string, e.g.,
57
+ # `MyType(string attribute)`.
58
+ #
59
+ # @param primary_type [String] the type which we want to encode.
60
+ # @param types [Array] all existing types in the data structure.
61
+ # @return [String] an EIP-712 encoded type-string.
62
+ # @raise [TypedDataError] if non-primary type found.
63
+ def encode_type(primary_type, types)
64
+
65
+ # get all used types
66
+ all_dependencies = type_dependencies primary_type, types
67
+
68
+ # remove primary types and sort the rest alphabetically
69
+ filtered_dependencies = all_dependencies.delete_if { |type| type.to_s == primary_type }
70
+ sorted_dependencies = filtered_dependencies.sort
71
+ dependencies = [primary_type]
72
+ sorted_dependencies.each do |sorted|
73
+ dependencies.push sorted
74
+ end
75
+
76
+ # join them all in a string with types and field names
77
+ result = ""
78
+ dependencies.each do |type|
79
+
80
+ # dependencies should not have non-primary types (such as string, address)
81
+ raise TypedDataError, "Non-primary type found: #{type}!" if types[type.to_sym].nil?
82
+
83
+ result += "#{type}("
84
+ result += types[type.to_sym].map { |t| "#{t[:type]} #{t[:name]}" }.join(",")
85
+ result += ")"
86
+ end
87
+ return result
88
+ end
89
+
90
+ # Hashes an EIP-712 confrom type-string.
91
+ #
92
+ # @param primary_type [String] the type which we want to hash.
93
+ # @param types [Array] all existing types in the data structure.
94
+ # @return [String] a Keccak-256 hash of an EIP-712 encoded type-string.
95
+ def hash_type(primary_type, types)
96
+ encoded_type = encode_type primary_type, types
97
+ return Util.keccak256 encoded_type
98
+ end
99
+
100
+ # Recursively ABI-encodes all data and types according to EIP-712.
101
+ #
102
+ # @param primary_type [String] the primary type which we want to encode.
103
+ # @param data [Array] the data in the data structure we want to encode.
104
+ # @param types [Array] all existing types in the data structure.
105
+ # @return [String] an ABI-encoded representation of the data and the types.
106
+ def encode_data(primary_type, data, types)
107
+
108
+ # first data field is the type hash
109
+ encoded_types = ["bytes32"]
110
+ encoded_values = [hash_type(primary_type, types)]
111
+
112
+ # adds field contents
113
+ types[primary_type.to_sym].each do |field|
114
+ value = data[field[:name].to_sym]
115
+ type = field[:type]
116
+ raise NotImplementedError, "Arrays currently unimplemented for EIP-712." if type.end_with? "]"
117
+ if type == "string" or type == "bytes"
118
+ encoded_types.push "bytes32"
119
+ encoded_values.push Util.keccak256 value
120
+ elsif !types[type.to_sym].nil?
121
+ encoded_types.push "bytes32"
122
+ value = encode_data type, value, types
123
+ encoded_values.push Util.keccak256 value
124
+ else
125
+ encoded_types.push type
126
+ encoded_values.push value
127
+ end
128
+ end
129
+
130
+ # all data is abi-encoded
131
+ return Abi.encode encoded_types, encoded_values
132
+ end
133
+
134
+ # Recursively ABI-encodes and hashes all data and types.
135
+ #
136
+ # @param primary_type [String] the primary type which we want to hash.
137
+ # @param data [Array] the data in the data structure we want to hash.
138
+ # @param types [Array] all existing types in the data structure.
139
+ # @return [String] a Keccak-256 hash of the ABI-encoded data and types.
140
+ def hash_data(primary_type, data, types)
141
+ encoded_data = encode_data primary_type, data, types
142
+ return Util.keccak256 encoded_data
143
+ end
144
+
145
+ # Enforces basic properties to be represented in the EIP-712 typed
146
+ # data structure: types, domain, message, etc.
147
+ #
148
+ # @param data [Array] the data in the data structure we want to hash.
149
+ # @return [Array] the data in the data structure we want to hash.
150
+ # @raise [TypedDataError] if the data fails validation.
151
+ def enforce_typed_data(data)
152
+ data = JSON.parse data if Util.is_hex? data
153
+ raise TypedDataError, "Data is missing, try again with data." if data.nil? or data.empty?
154
+ raise TypedDataError, "Data types are missing." if data[:types].nil? or data[:types].empty?
155
+ raise TypedDataError, "Data primaryType is missing." if data[:primaryType].nil? or data[:primaryType].empty?
156
+ raise TypedDataError, "Data domain is missing." if data[:domain].nil?
157
+ raise TypedDataError, "Data message is missing." if data[:message].nil? or data[:message].empty?
158
+ raise TypedDataError, "Data EIP712Domain is missing." if data[:types][:EIP712Domain].nil?
159
+ return data
160
+ end
161
+
162
+ # Hashes a typed data structure with Keccak-256 to prepare a signed
163
+ # typed data operation respecting EIP-712.
164
+ #
165
+ # @param data [Array] all the data in the typed data structure.
166
+ # @return [String] a Keccak-256 hash of the EIP-712-encoded typed data.
167
+ def hash(data)
168
+ data = enforce_typed_data data
169
+
170
+ # EIP-191 prefix byte
171
+ buffer = Signature::EIP191_PREFIX_BYTE
172
+
173
+ # EIP-712 version byte
174
+ buffer += Signature::EIP712_VERSION_BYTE
175
+
176
+ # hashed domain data
177
+ buffer += hash_data "EIP712Domain", data[:domain], data[:types]
178
+
179
+ # hashed message data
180
+ buffer += hash_data data[:primaryType], data[:message], data[:types]
181
+ return Util.keccak256 buffer
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,146 @@
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::Decrypter} class to handle PBKDF2-SHA-256 decryption.
19
+ class Key::Decrypter
20
+
21
+ # Provides a specific decrypter error if decryption fails.
22
+ class DecrypterError < StandardError; end
23
+
24
+ # Class method {Eth::Key::Decrypter.perform} to perform an keystore
25
+ # decryption.
26
+ #
27
+ # @param data [JSON] encryption data including cypherkey.
28
+ # @param password [String] password to decrypt the key.
29
+ # @return [Eth::Key] decrypted key-pair.
30
+ def self.perform(data, password)
31
+ new(data, password).perform
32
+ end
33
+
34
+ # Constructor of the {Eth::Key::Decrypter} class for secret key
35
+ # decryption. Should not be used; use {Eth::Key::Decrypter.perform}
36
+ # instead.
37
+ #
38
+ # @param data [JSON] encryption data including cypherkey.
39
+ # @param password [String] password to decrypt the key.
40
+ def initialize(data, password)
41
+ data = JSON.parse(data) if data.is_a? String
42
+ @data = data
43
+ @password = password
44
+ end
45
+
46
+ # Method to decrypt key using password.
47
+ #
48
+ # @return [Eth::Key] decrypted key.
49
+ def perform
50
+ derive_key password
51
+ check_macs
52
+ private_key = Util.bin_to_hex decrypted_data
53
+ Eth::Key.new priv: private_key
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :data
59
+ attr_reader :key
60
+ attr_reader :password
61
+
62
+ def derive_key(password)
63
+ case kdf
64
+ when "pbkdf2"
65
+ @key = OpenSSL::PKCS5.pbkdf2_hmac(password, salt, iterations, key_length, digest)
66
+ when "scrypt"
67
+ @key = SCrypt::Engine.scrypt(password, salt, n, r, p, key_length)
68
+ else
69
+ raise DecrypterError, "Unsupported key derivation function: #{kdf}!"
70
+ end
71
+ end
72
+
73
+ def check_macs
74
+ mac1 = Util.keccak256(key[(key_length / 2), key_length] + ciphertext)
75
+ mac2 = Util.hex_to_bin crypto_data["mac"]
76
+
77
+ if mac1 != mac2
78
+ raise DecrypterError, "Message Authentications Codes do not match!"
79
+ end
80
+ end
81
+
82
+ def decrypted_data
83
+ @decrypted_data ||= cipher.update(ciphertext) + cipher.final
84
+ end
85
+
86
+ def crypto_data
87
+ @crypto_data ||= data["crypto"] || data["Crypto"]
88
+ end
89
+
90
+ def ciphertext
91
+ Util.hex_to_bin crypto_data["ciphertext"]
92
+ end
93
+
94
+ def cipher_name
95
+ "aes-128-ctr"
96
+ end
97
+
98
+ def cipher
99
+ @cipher ||= OpenSSL::Cipher.new(cipher_name).tap do |cipher|
100
+ cipher.decrypt
101
+ cipher.key = key[0, (key_length / 2)]
102
+ cipher.iv = iv
103
+ end
104
+ end
105
+
106
+ def iv
107
+ Util.hex_to_bin crypto_data["cipherparams"]["iv"]
108
+ end
109
+
110
+ def salt
111
+ Util.hex_to_bin crypto_data["kdfparams"]["salt"]
112
+ end
113
+
114
+ def iterations
115
+ crypto_data["kdfparams"]["c"].to_i
116
+ end
117
+
118
+ def kdf
119
+ crypto_data["kdf"]
120
+ end
121
+
122
+ def key_length
123
+ crypto_data["kdfparams"]["dklen"].to_i
124
+ end
125
+
126
+ def n
127
+ crypto_data["kdfparams"]["n"].to_i
128
+ end
129
+
130
+ def r
131
+ crypto_data["kdfparams"]["r"].to_i
132
+ end
133
+
134
+ def p
135
+ crypto_data["kdfparams"]["p"].to_i
136
+ end
137
+
138
+ def digest
139
+ OpenSSL::Digest.new digest_name
140
+ end
141
+
142
+ def digest_name
143
+ "sha256"
144
+ end
145
+ end
146
+ end