coin-op 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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
-