coin-op 0.4.3 → 0.4.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 482ce5f6a886789d878fe1deeb82331bb5f57bf0
4
- data.tar.gz: 4236d35aa587afde392f84d27e49c82df2397ad4
3
+ metadata.gz: 763ee324d095d2518ed75c63c176c03bc3c2cf8e
4
+ data.tar.gz: b6eec07e082b37b2cadb269bd741713aad1be530
5
5
  SHA512:
6
- metadata.gz: 5766d93ae76df0503e23403200d8ef76e266b9ba25de4e4ce8c94011634922457b3de9acff2776b4dc05048de7ee35e9723edd35244ff006056fe6791ce466e9
7
- data.tar.gz: 2cc169b1b66b3b8823e89697da361aedaf389d1bb94977d9b9afcd91153d11463e32d3d0c3b2d848b1caed72774b51184d7b909641528f4c500149018e9525f0
6
+ metadata.gz: 241a753e6a272af37c651c303bf96aa239b725b232a68381e9221e98577d05a21901f8a8ac4f1e01a02b556a2117c19ba907a7e0b480f3ac1f69b2b16b873cb5
7
+ data.tar.gz: 0c823fc19951647daa65312e0f5af73395594bb52461c5f09dc432a716b9789de484cc5168fb8159308e1d0bc104109506edb1d347b413753cb2d6760da56bc2
Binary file
data.tar.gz.sig CHANGED
@@ -1 +1 @@
1
- ��Wg����;��{��������M�y���.5R�]K����C8_�!���{�K(c_W��0e^w�o�|��H�~| Ɔ����-1������.�ar'���䢚��m^p�;*���
1
+ ,��JH�?:~=\tU] ��R0�\ $ӡ^����P����s��L�bg �\��ٖ����Si��{��!j��w?-Oe"QTsǍdtMT��U3L1�`�H����/bl�C�Ӣ�����5U�%ym��z|~��68i��w��C2A���[��t�Z�������M�ؾ�%���dr�m �ۥ�ĿH�
@@ -4,8 +4,7 @@ module CoinOp
4
4
 
5
5
  end
6
6
 
7
- require_relative "coin-op/encodings"
8
- require_relative "coin-op/crypto"
9
- require_relative "coin-op/bit"
10
- require_relative "coin-op/blockchain/blockr"
7
+ require_relative 'coin-op/encodings'
8
+ require_relative 'coin-op/crypto'
9
+ require_relative 'coin-op/bit'
11
10
 
@@ -1,6 +1,7 @@
1
1
  # Ruby bindings for libsodium, a port of DJB's NaCl crypto library
2
- require "rbnacl"
3
- require "openssl"
2
+ require 'rbnacl'
3
+ require 'openssl'
4
+
4
5
 
5
6
  module CoinOp
6
7
  module Crypto
@@ -29,7 +30,12 @@ module CoinOp
29
30
  include CoinOp::Encodings
30
31
 
31
32
  # PBKDF2 work factor
32
- ITERATIONS = 100_000
33
+ ITERATIONS = 90_000
34
+ ITERATIONS_WINDOW = 20_000
35
+
36
+ SALT_RANDOM_BYTES = 16
37
+ KEY_SIZE = 32
38
+ AES_CIPHER = 'AES-256-CBC'
33
39
 
34
40
  # Given passphrase and plaintext as strings, returns a Hash
35
41
  # containing the ciphertext and other values needed for later
@@ -43,10 +49,18 @@ module CoinOp
43
49
  # :salt => salt, :nonce => nonce, :ciphertext => ciphertext
44
50
  #
45
51
  def self.decrypt(passphrase, hash)
46
- salt, nonce, ciphertext =
47
- hash.values_at(:salt, :nonce, :ciphertext).map {|s| decode_hex(s) }
48
- box = self.new(passphrase, salt, hash[:iterations])
49
- box.decrypt(nonce, ciphertext)
52
+ salt, iv, nonce, ciphertext =
53
+ hash.values_at(:salt, :iv, :nonce, :ciphertext).map {|s| decode_hex(s) }
54
+
55
+ mode = nil
56
+ if iv.empty?
57
+ mode = :nacl
58
+ elsif nonce.empty?
59
+ mode = :aes
60
+ end
61
+
62
+ box = self.new(passphrase, mode, salt, hash[:iterations] || ITERATIONS)
63
+ box.decrypt(iv, nonce, ciphertext)
50
64
  end
51
65
 
52
66
  attr_reader :salt
@@ -54,33 +68,82 @@ module CoinOp
54
68
  # Initialize with an existing salt and iterations to allow
55
69
  # decryption. Otherwise, creates new values for these, meaning
56
70
  # it creates an entirely new secret-box.
57
- def initialize(passphrase, salt=nil, iterations=nil)
58
- @salt = salt || RbNaCl::Random.random_bytes(16)
59
- @iterations = iterations || ITERATIONS
60
-
61
- key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(
62
- passphrase, @salt,
63
- # TODO: decide on a very safe work factor
64
- # https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
65
- #
66
- @iterations, # number of iterations
67
- 32 # key length in bytes
68
- )
69
- @box = RbNaCl::SecretBox.new(key)
71
+ def initialize(passphrase, mode=:aes, salt=SecureRandom.random_bytes(SALT_RANDOM_BYTES), iterations=nil)
72
+ @salt = salt
73
+ @iterations = iterations || ITERATIONS + SecureRandom.random_number(ITERATIONS_WINDOW)
74
+ @mode = mode
75
+
76
+ if @mode == :aes
77
+ @key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(
78
+ passphrase,
79
+ @salt,
80
+ # TODO: decide on a very safe work factor
81
+ # https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
82
+ #
83
+ @iterations, # number of iterations
84
+ KEY_SIZE * 2 # key length in bytes
85
+ )
86
+
87
+ @aes_key = @key[0, KEY_SIZE]
88
+ @hmac_key = @key[KEY_SIZE, KEY_SIZE]
89
+ @cipher = OpenSSL::Cipher.new(AES_CIPHER)
90
+ @cipher.padding = 0
91
+ elsif @mode == :nacl
92
+ @key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(
93
+ passphrase,
94
+ @salt,
95
+ # TODO: decide on a very safe work factor
96
+ # https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
97
+ #
98
+ @iterations, # number of iterations
99
+ KEY_SIZE # key length in bytes
100
+ )
101
+ @box = RbNaCl::SecretBox.new(@key)
102
+ end
103
+
70
104
  end
71
105
 
72
- def encrypt(plaintext)
73
- nonce = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.nonce_bytes)
74
- ciphertext = @box.encrypt(nonce, plaintext)
106
+ def encrypt(plaintext, iv=@cipher.random_iv)
107
+ @cipher.encrypt
108
+ @cipher.iv = iv
109
+ @cipher.key = @aes_key
110
+ encrypted = @cipher.update(plaintext)
111
+ encrypted << @cipher.final
112
+ digest = OpenSSL::Digest::SHA256.new
113
+ hmac_digest = OpenSSL::HMAC.digest(digest, @hmac_key, iv + encrypted)
114
+ ciphertext = encrypted + hmac_digest
75
115
  {
76
- :iterations => @iterations,
77
- :salt => hex(@salt),
78
- :nonce => hex(nonce),
79
- :ciphertext => hex(ciphertext)
116
+ iterations: @iterations,
117
+ salt: hex(@salt),
118
+ iv: hex(iv),
119
+ ciphertext: hex(ciphertext)
80
120
  }
81
121
  end
82
122
 
83
- def decrypt(nonce, ciphertext)
123
+ def decrypt(iv, nonce, ciphertext)
124
+ if @mode == :aes
125
+ return decrypt_aes(iv, ciphertext)
126
+ elsif @mode == :nacl
127
+ return decrypt_nacl(nonce, ciphertext)
128
+ end
129
+ raise('Incompatible ciphertext')
130
+ end
131
+
132
+ def decrypt_aes(iv, ciphertext)
133
+ mac, ctext = ciphertext[-KEY_SIZE, KEY_SIZE], ciphertext[0...-KEY_SIZE]
134
+ digest = OpenSSL::Digest::SHA256.new
135
+ hmac_digest = OpenSSL::HMAC.digest(digest, @hmac_key, iv + ctext)
136
+ if hmac_digest != mac
137
+ raise('Invalid authentication code - this ciphertext may have been tampered with.')
138
+ end
139
+ @cipher.decrypt
140
+ @cipher.iv = iv
141
+ @cipher.key = @aes_key
142
+ decrypted = @cipher.update(ctext)
143
+ decrypted << @cipher.final
144
+ end
145
+
146
+ def decrypt_nacl(nonce, ciphertext)
84
147
  @box.decrypt(nonce, ciphertext)
85
148
  end
86
149
 
@@ -1,3 +1,3 @@
1
1
  module CoinOp
2
- VERSION = "0.4.3"
2
+ VERSION = "0.4.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coin-op
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew King
@@ -32,7 +32,7 @@ cert_chain:
32
32
  tdc4VS7IlSRxlZ3dBOgiigy9GXpJ+7F831AqjxL39EPwdr7RguTNz+pi//RKaT/U
33
33
  IlpVB+Xfk0vQdP7iYfjGxDzUf0FACMjsR95waJmadKW1Iy6STw2hwPhYIQz1Hu1A
34
34
  -----END CERTIFICATE-----
35
- date: 2015-05-27 00:00:00.000000000 Z
35
+ date: 2015-07-22 00:00:00.000000000 Z
36
36
  dependencies:
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bitcoin-ruby
@@ -62,20 +62,6 @@ dependencies:
62
62
  - - "~>"
63
63
  - !ruby/object:Gem::Version
64
64
  version: '0.9'
65
- - !ruby/object:Gem::Dependency
66
- name: rbnacl-libsodium
67
- requirement: !ruby/object:Gem::Requirement
68
- requirements:
69
- - - "~>"
70
- - !ruby/object:Gem::Version
71
- version: '1.0'
72
- type: :runtime
73
- prerelease: false
74
- version_requirements: !ruby/object:Gem::Requirement
75
- requirements:
76
- - - "~>"
77
- - !ruby/object:Gem::Version
78
- version: '1.0'
79
65
  - !ruby/object:Gem::Dependency
80
66
  name: hashie
81
67
  requirement: !ruby/object:Gem::Requirement
@@ -91,19 +77,19 @@ dependencies:
91
77
  - !ruby/object:Gem::Version
92
78
  version: '2.0'
93
79
  - !ruby/object:Gem::Dependency
94
- name: starter
80
+ name: rbnacl-libsodium
95
81
  requirement: !ruby/object:Gem::Requirement
96
82
  requirements:
97
83
  - - '='
98
84
  - !ruby/object:Gem::Version
99
- version: 0.1.12
100
- type: :development
85
+ version: 1.0.3
86
+ type: :runtime
101
87
  prerelease: false
102
88
  version_requirements: !ruby/object:Gem::Requirement
103
89
  requirements:
104
90
  - - '='
105
91
  - !ruby/object:Gem::Version
106
- version: 0.1.12
92
+ version: 1.0.3
107
93
  - !ruby/object:Gem::Dependency
108
94
  name: sequel
109
95
  requirement: !ruby/object:Gem::Requirement
@@ -133,19 +119,33 @@ dependencies:
133
119
  - !ruby/object:Gem::Version
134
120
  version: '1.3'
135
121
  - !ruby/object:Gem::Dependency
136
- name: minitest-reporters
122
+ name: rspec
137
123
  requirement: !ruby/object:Gem::Requirement
138
124
  requirements:
139
- - - "~>"
125
+ - - ">="
140
126
  - !ruby/object:Gem::Version
141
- version: '1.0'
127
+ version: '0'
142
128
  type: :development
143
129
  prerelease: false
144
130
  version_requirements: !ruby/object:Gem::Requirement
145
131
  requirements:
146
- - - "~>"
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ - !ruby/object:Gem::Dependency
136
+ name: pry
137
+ requirement: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ type: :development
143
+ prerelease: false
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
147
  - !ruby/object:Gem::Version
148
- version: '1.0'
148
+ version: '0'
149
149
  description: A pretty, simple to use interface for all of the cryptocurrency libraries
150
150
  you love to use.
151
151
  email:
@@ -168,7 +168,6 @@ files:
168
168
  - lib/coin-op/bit/script.rb
169
169
  - lib/coin-op/bit/spendable.rb
170
170
  - lib/coin-op/bit/transaction.rb
171
- - lib/coin-op/blockchain/blockr.rb
172
171
  - lib/coin-op/blockchain/mockchain.rb
173
172
  - lib/coin-op/crypto.rb
174
173
  - lib/coin-op/encodings.rb
@@ -193,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
192
  version: '0'
194
193
  requirements: []
195
194
  rubyforge_project:
196
- rubygems_version: 2.2.2
195
+ rubygems_version: 2.4.5
197
196
  signing_key:
198
197
  specification_version: 4
199
198
  summary: Crypto currency classes in Ruby
metadata.gz.sig CHANGED
Binary file
@@ -1,162 +0,0 @@
1
- require "http"
2
- require "json"
3
- require 'enumerator'
4
-
5
- require_relative "../bit"
6
-
7
- module CoinOp
8
- module Blockchain
9
-
10
- # Blockr.io API documentation: http://blockr.io/documentation/api
11
- class Blockr
12
- include CoinOp::Encodings
13
-
14
- def initialize(env=:test)
15
- subdomain = (env.to_sym == :test) ? "tbtc" : "btc"
16
- @base_url = "http://#{subdomain}.blockr.io/api/v1"
17
-
18
- # Testing says 20 is the absolute max
19
- @max_per_request = 20
20
-
21
- @http = HTTP.with_headers(
22
- "User-Agent" => "bv-blockchain-worker v0.1.0",
23
- "Accept" => "application/json"
24
- )
25
- end
26
-
27
-
28
- attr_accessor :max_per_request
29
-
30
-
31
- def unspent(addresses, confirmations=6)
32
-
33
- result = request(
34
- :address, :unspent, addresses,
35
- :confirmations => confirmations
36
- )
37
-
38
- outputs = []
39
- result.each do |record|
40
- record[:unspent].each do |output|
41
- address = record[:address]
42
-
43
- transaction_hex, index, value, script_hex =
44
- output.values_at :tx, :n, :amount, :script
45
-
46
- outputs << CoinOp::Bit::Output.new(
47
- :transaction_hex => transaction_hex,
48
- :index => index,
49
- :value => bitcoins_to_satoshis(value),
50
- :script => {:hex => script_hex},
51
- :address => address
52
- )
53
- end
54
- end
55
-
56
- outputs.sort_by {|output| -output.value }
57
- end
58
-
59
-
60
- def balance(addresses)
61
- result = request(:address, :balance, addresses)
62
- balances = {}
63
- result.each do |record|
64
- balances[record[:address]] = float_to_satoshis(record[:balance])
65
- end
66
-
67
- balances
68
- end
69
-
70
-
71
- def transactions(tx_ids)
72
- results = request(:tx, :raw, tx_ids)
73
- results.map do |record|
74
- hex = record[:tx][:hex]
75
-
76
- transaction = CoinOp::Bit::Transaction.hex(hex)
77
- end
78
- end
79
-
80
-
81
- def address_info(addresses, confirmations=6)
82
- # Useful for testing transactions()
83
- request(
84
- :address, :info, addresses,
85
- :confirmations => confirmations
86
- )
87
- end
88
-
89
-
90
- def block_info(block_list)
91
- request(:block, :info, block_list)
92
- end
93
-
94
-
95
- def block_txs(block_list)
96
- request(:block, :txs, block_list)
97
- end
98
-
99
-
100
- # Helper methods
101
-
102
- def bitcoins_to_satoshis(string)
103
- string.gsub(".", "").to_i
104
- end
105
-
106
- def float_to_satoshis(float)
107
- (float * 100_000_000).to_i
108
- end
109
-
110
-
111
- # Queries the Blockr Bitcoin from_type => to_type API with
112
- # list, returning the results or throwing an exception on
113
- # failure.
114
- def request(from_type, to_type, args, query=nil)
115
-
116
- unless args.is_a? Array
117
- args = [args]
118
- end
119
-
120
- data = []
121
- args.each_slice(@max_per_request) do |arg_slice|
122
- # Permit calling with either an array or a scalar
123
- slice_string = arg_slice.join(",")
124
- url = "#{@base_url}/#{from_type}/#{to_type}/#{slice_string}"
125
-
126
- # Construct query string if any params were passed.
127
- if query
128
- # TODO: validation. The value of the "confirmations" parameter
129
- # must be an integer.
130
- params = query.map { |name, value| "#{name}=#{value}" }.join("&")
131
- url = "#{url}?#{params}"
132
- end
133
-
134
- response = @http.request "GET", url, :response => :object
135
- # FIXME: rescue any JSON parsing exception and raise an
136
- # exception explaining that it's blockr's fault.
137
- begin
138
- content = JSON.parse(response.body, :symbolize_names => true)
139
- rescue JSON::ParserError => e
140
- raise "Blockr returned invalid JSON: #{e}"
141
- end
142
-
143
- if content[:status] != "success"
144
- raise "Blockr.io failure: #{content.to_json}"
145
- end
146
-
147
- slice_data = content[:data]
148
- if content[:data].is_a? Array
149
- data.concat slice_data
150
- else
151
- data << slice_data
152
- end
153
- end
154
-
155
- data
156
- end
157
-
158
- end
159
-
160
- end
161
- end
162
-