ci_block_io 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ci_block_io.rb +48 -51
- data/lib/ci_block_io/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93926bfd4df14d9634d0ea789c86fdd7737e485f129a7b966b52ea110ab96d46
|
4
|
+
data.tar.gz: b605c6dc58f4f2eb583691767f1ee2a7c97b8a920675787e4fcc6b46785d5480
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a492c73929e34c787b4cb7d4773f3024a0ffbbf0984a49bbccbf3e9c6739697ff70ea1cd04fe6cc39e419c45e2cc56d02c650dc076898cda9ed8187192caa03b
|
7
|
+
data.tar.gz: 26e60257ab22e318649b2f741807b591e73e41c4ce377ab6802f83510777f4a92e41bd5af9354e7f61c295b052f188b3279bba309b18685294ea8c9e545dfddd
|
data/lib/ci_block_io.rb
CHANGED
@@ -30,18 +30,18 @@ module CiBlockIo
|
|
30
30
|
# initialize BlockIo
|
31
31
|
@api_key = args[:api_key]
|
32
32
|
@pin = args[:pin]
|
33
|
-
|
33
|
+
|
34
34
|
@encryptionKey = Helper.pinToAesKey(@pin) if !@pin.nil?
|
35
35
|
|
36
36
|
hostname = args[:hostname] || "block.io"
|
37
37
|
@base_url = "https://" << hostname << "/api/VERSION/API_CALL/?api_key="
|
38
|
-
|
38
|
+
|
39
39
|
@client = HTTPClient.new
|
40
40
|
@client.tcp_keepalive = true
|
41
41
|
@client.ssl_config.ssl_version = :auto
|
42
|
-
|
42
|
+
|
43
43
|
@version = args[:version] || 2 # default version is 2
|
44
|
-
|
44
|
+
|
45
45
|
self.api_call(['get_balance',""])
|
46
46
|
end
|
47
47
|
|
@@ -59,7 +59,7 @@ module CiBlockIo
|
|
59
59
|
params = get_params(args.first)
|
60
60
|
self.api_call([method_name, params])
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
end
|
64
64
|
|
65
65
|
def self.withdraw(args = {}, method_name = 'withdraw')
|
@@ -72,7 +72,7 @@ module CiBlockIo
|
|
72
72
|
params << "&pin=" << @pin if @version == 1 # Block.io handles the Secret PIN in the legacy API (v1)
|
73
73
|
|
74
74
|
response = self.api_call([method_name, params])
|
75
|
-
|
75
|
+
|
76
76
|
if response['data'].has_key?('reference_id') then
|
77
77
|
# Block.io's asking us to provide some client-side signatures, let's get to it
|
78
78
|
|
@@ -114,7 +114,7 @@ module CiBlockIo
|
|
114
114
|
params = get_params(args)
|
115
115
|
|
116
116
|
response = self.api_call([method_name, params])
|
117
|
-
|
117
|
+
|
118
118
|
if response['data'].has_key?('reference_id') then
|
119
119
|
# Block.io's asking us to provide some client-side signatures, let's get to it
|
120
120
|
|
@@ -136,7 +136,7 @@ module CiBlockIo
|
|
136
136
|
|
137
137
|
|
138
138
|
private
|
139
|
-
|
139
|
+
|
140
140
|
def self.api_call(endpoint)
|
141
141
|
return @mock_response[endpoint[0]] if @mock_response.keys.include?(endpoint[0])
|
142
142
|
|
@@ -145,21 +145,19 @@ module CiBlockIo
|
|
145
145
|
return nil if base_url.nil? || @api_key.nil?
|
146
146
|
|
147
147
|
response = @client.post(base_url + @api_key, endpoint[1])
|
148
|
-
|
148
|
+
|
149
149
|
begin
|
150
150
|
body = Oj.load(response.body)
|
151
151
|
return unless body['status'].eql?('success')
|
152
152
|
case endpoint[0]
|
153
|
-
when 'withdraw'
|
153
|
+
when 'withdraw'
|
154
154
|
raise CiBlockIoWithdrawException.new(body['data']['error_message'])
|
155
155
|
else
|
156
156
|
raise Exception.new(body['data']['error_message'])
|
157
157
|
end
|
158
|
-
end
|
159
158
|
rescue
|
160
159
|
raise Exception.new('Unknown error occurred. Please report this.')
|
161
160
|
end
|
162
|
-
|
163
161
|
body
|
164
162
|
end
|
165
163
|
|
@@ -169,7 +167,7 @@ module CiBlockIo
|
|
169
167
|
# construct the parameter string
|
170
168
|
params = ""
|
171
169
|
args = {} if args.nil?
|
172
|
-
|
170
|
+
|
173
171
|
args.each do |k,v|
|
174
172
|
params += '&' if params.length > 0
|
175
173
|
params += "#{k.to_s}=#{v.to_s}"
|
@@ -191,23 +189,23 @@ module CiBlockIo
|
|
191
189
|
@compressed = compressed
|
192
190
|
|
193
191
|
end
|
194
|
-
|
192
|
+
|
195
193
|
def private_key
|
196
194
|
# returns private key in hex form
|
197
195
|
return @private_key.to_s(16)
|
198
196
|
end
|
199
|
-
|
197
|
+
|
200
198
|
def public_key
|
201
199
|
# returns the compressed form of the public key to save network fees (shorter scripts)
|
202
200
|
|
203
201
|
return ECDSA::Format::PointOctetString.encode(@public_key, compression: @compressed).unpack("H*")[0]
|
204
202
|
end
|
205
|
-
|
203
|
+
|
206
204
|
def sign(data)
|
207
205
|
# signed the given hexadecimal string
|
208
206
|
|
209
207
|
nonce = deterministicGenerateK([data].pack("H*"), @private_key) # RFC6979
|
210
|
-
|
208
|
+
|
211
209
|
signature = ECDSA.sign(@group, @private_key, data.to_i(16), nonce)
|
212
210
|
|
213
211
|
# BIP0062 -- use lower S values only
|
@@ -221,14 +219,14 @@ module CiBlockIo
|
|
221
219
|
# DER encode this, and return it in hex form
|
222
220
|
return ECDSA::Format::SignatureDerString.encode(signature).unpack("H*")[0]
|
223
221
|
end
|
224
|
-
|
222
|
+
|
225
223
|
def self.from_passphrase(passphrase)
|
226
224
|
# create a private+public key pair from a given passphrase
|
227
225
|
# think of this as your brain wallet. be very sure to use a sufficiently long passphrase
|
228
226
|
# if you don't want a passphrase, just use Key.new and it will generate a random key for you
|
229
|
-
|
227
|
+
|
230
228
|
raise Exception.new('Must provide passphrase at least 8 characters long.') if passphrase.nil? or passphrase.length < 8
|
231
|
-
|
229
|
+
|
232
230
|
hashed_key = Helper.sha256([passphrase].pack("H*")) # must pass bytes to sha256
|
233
231
|
|
234
232
|
return Key.new(hashed_key)
|
@@ -246,65 +244,65 @@ module CiBlockIo
|
|
246
244
|
return Key.new(actual_key, compressed)
|
247
245
|
|
248
246
|
end
|
249
|
-
|
247
|
+
|
250
248
|
def isPositive(i)
|
251
249
|
sig = "!+-"[i <=> 0]
|
252
|
-
|
250
|
+
|
253
251
|
return sig.eql?("+")
|
254
252
|
end
|
255
|
-
|
253
|
+
|
256
254
|
def deterministicGenerateK(data, privkey, group = ECDSA::Group::Secp256k1)
|
257
255
|
# returns a deterministic K -- RFC6979
|
258
256
|
|
259
257
|
hash = data.bytes.to_a
|
260
258
|
|
261
259
|
x = [privkey.to_s(16)].pack("H*").bytes.to_a
|
262
|
-
|
260
|
+
|
263
261
|
k = []
|
264
262
|
32.times { k.insert(0, 0) }
|
265
|
-
|
263
|
+
|
266
264
|
v = []
|
267
265
|
32.times { v.insert(0, 1) }
|
268
|
-
|
266
|
+
|
269
267
|
# step D
|
270
268
|
k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).concat(x).concat(hash).pack("C*")).bytes.to_a
|
271
|
-
|
269
|
+
|
272
270
|
# step E
|
273
271
|
v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
|
274
|
-
|
272
|
+
|
275
273
|
# puts "E: " + v.pack("C*").unpack("H*")[0]
|
276
|
-
|
274
|
+
|
277
275
|
# step F
|
278
276
|
k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([1]).concat(x).concat(hash).pack("C*")).bytes.to_a
|
279
|
-
|
277
|
+
|
280
278
|
# step G
|
281
279
|
v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
|
282
|
-
|
280
|
+
|
283
281
|
# step H2b (Step H1/H2a ignored)
|
284
282
|
v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
|
285
|
-
|
283
|
+
|
286
284
|
h2b = v.pack("C*").unpack("H*")[0]
|
287
285
|
tNum = h2b.to_i(16)
|
288
|
-
|
286
|
+
|
289
287
|
# step H3
|
290
288
|
while (!isPositive(tNum) or tNum >= group.order) do
|
291
289
|
# k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k)
|
292
290
|
k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).pack("C*")).bytes.to_a
|
293
|
-
|
291
|
+
|
294
292
|
# v = crypto.HmacSHA256(v, k)
|
295
293
|
v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
|
296
|
-
|
294
|
+
|
297
295
|
# T = BigInteger.fromBuffer(v)
|
298
296
|
tNum = v.pack("C*").unpack("H*")[0].to_i(16)
|
299
297
|
end
|
300
|
-
|
298
|
+
|
301
299
|
return tNum
|
302
300
|
end
|
303
301
|
|
304
302
|
end
|
305
|
-
|
303
|
+
|
306
304
|
module Helper
|
307
|
-
|
305
|
+
|
308
306
|
def self.signData(inputs, keys)
|
309
307
|
# sign the given data with the given keys
|
310
308
|
# TODO loop is O(n^3), make it better
|
@@ -320,7 +318,7 @@ module CiBlockIo
|
|
320
318
|
while j < input['signers'].size do
|
321
319
|
# if our public key matches this signer's public key, sign the data
|
322
320
|
signer = inputs[i]['signers'][j]
|
323
|
-
|
321
|
+
|
324
322
|
k = 0
|
325
323
|
while k < keys.size do
|
326
324
|
# sign for each key provided, if we can
|
@@ -342,19 +340,19 @@ module CiBlockIo
|
|
342
340
|
# passphrase is in plain text
|
343
341
|
# encrypted_data is in base64, as it was stored on Block.io
|
344
342
|
# returns the private key extracted from the given encrypted data
|
345
|
-
|
343
|
+
|
346
344
|
decrypted = self.decrypt(encrypted_data, b64_enc_key)
|
347
|
-
|
345
|
+
|
348
346
|
return Key.from_passphrase(decrypted)
|
349
347
|
end
|
350
|
-
|
348
|
+
|
351
349
|
def self.sha256(value)
|
352
350
|
# returns the hex of the hash of the given value
|
353
351
|
hash = Digest::SHA2.new(256)
|
354
352
|
hash << value
|
355
353
|
hash.hexdigest # return hex
|
356
354
|
end
|
357
|
-
|
355
|
+
|
358
356
|
def self.pinToAesKey(secret_pin, iterations = 2048)
|
359
357
|
# converts the pincode string to PBKDF2
|
360
358
|
# returns a base64 version of PBKDF2 pincode
|
@@ -366,10 +364,10 @@ module CiBlockIo
|
|
366
364
|
|
367
365
|
return Base64.strict_encode64(aes_key_bin) # the base64 encryption key
|
368
366
|
end
|
369
|
-
|
367
|
+
|
370
368
|
# Decrypts a block of data (encrypted_data) given an encryption key
|
371
369
|
def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
|
372
|
-
|
370
|
+
|
373
371
|
response = nil
|
374
372
|
|
375
373
|
begin
|
@@ -385,7 +383,7 @@ module CiBlockIo
|
|
385
383
|
|
386
384
|
return response
|
387
385
|
end
|
388
|
-
|
386
|
+
|
389
387
|
# Encrypts a block of data given an encryption key
|
390
388
|
def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
|
391
389
|
aes = OpenSSL::Cipher.new(cipher_type)
|
@@ -396,7 +394,7 @@ module CiBlockIo
|
|
396
394
|
end
|
397
395
|
|
398
396
|
# courtesy bitcoin-ruby
|
399
|
-
|
397
|
+
|
400
398
|
def self.int_to_base58(int_val, leading_zero_bytes=0)
|
401
399
|
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
402
400
|
base58_val, base = '', alpha.size
|
@@ -406,7 +404,7 @@ module CiBlockIo
|
|
406
404
|
end
|
407
405
|
base58_val
|
408
406
|
end
|
409
|
-
|
407
|
+
|
410
408
|
def self.base58_to_int(base58_val)
|
411
409
|
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
412
410
|
int_val, base = 0, alpha.size
|
@@ -416,12 +414,12 @@ module CiBlockIo
|
|
416
414
|
end
|
417
415
|
int_val
|
418
416
|
end
|
419
|
-
|
417
|
+
|
420
418
|
def self.encode_base58(hex)
|
421
419
|
leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
|
422
420
|
("1"*leading_zero_bytes) + Helper.int_to_base58( hex.to_i(16) )
|
423
421
|
end
|
424
|
-
|
422
|
+
|
425
423
|
def self.decode_base58(base58_val)
|
426
424
|
s = Helper.base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
|
427
425
|
s = '' if s == '00'
|
@@ -430,5 +428,4 @@ module CiBlockIo
|
|
430
428
|
s
|
431
429
|
end
|
432
430
|
end
|
433
|
-
|
434
431
|
end
|
data/lib/ci_block_io/version.rb
CHANGED