Class: BlockIo::Helper

Inherits:
Object
  • Object
show all
Defined in:
lib/block_io/helper.rb

Class Method Summary collapse

Class Method Details

.base58_to_int(base58_val) ⇒ Object



139
140
141
142
143
144
145
146
147
# File 'lib/block_io/helper.rb', line 139

def self.base58_to_int(base58_val)
  alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
  int_val, base = 0, alpha.size
  base58_val.reverse.each_char.with_index do |char,index|
    raise ArgumentError, "Value not a valid Base58 String." unless char_index = alpha.index(char)
    int_val += char_index*(base**index)
  end
  int_val
end

.decode_base58(base58_val) ⇒ Object



154
155
156
157
158
159
160
161
# File 'lib/block_io/helper.rb', line 154

def self.decode_base58(base58_val)
  s = Helper.base58_to_int(base58_val).to_s(16)
  s = (s.bytesize.odd? ? ("0" << s) : s)
  s = "" if s == "00"
  leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : "").size
  s = ("00"*leading_zero_bytes) << s  if leading_zero_bytes > 0
  s
end

.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = "AES-256-ECB") ⇒ Object

Decrypts a block of data (encrypted_data) given an encryption key



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/block_io/helper.rb', line 100

def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = "AES-256-ECB")
  
  response = nil

  begin
    aes = OpenSSL::Cipher.new(cipher_type)
    aes.decrypt
    aes.key = b64_enc_key.unpack("m0")[0]
    aes.iv = iv unless iv.nil?
    response = aes.update(encrypted_data.unpack("m0")[0]) << aes.final
  rescue Exception => e
    # decryption failed, must be an invalid Secret PIN
    raise Exception.new("Invalid Secret PIN provided.")
  end

  response
end

.encode_base58(hex) ⇒ Object



149
150
151
152
# File 'lib/block_io/helper.rb', line 149

def self.encode_base58(hex)
  leading_zero_bytes  = (hex.match(/^([0]+)/) ? $1 : "").size / 2
  ("1"*leading_zero_bytes) << Helper.int_to_base58( hex.to_i(16) )
end

.encrypt(data, b64_enc_key, iv = nil, cipher_type = "AES-256-ECB") ⇒ Object

Encrypts a block of data given an encryption key



119
120
121
122
123
124
125
# File 'lib/block_io/helper.rb', line 119

def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = "AES-256-ECB")
  aes = OpenSSL::Cipher.new(cipher_type)
  aes.encrypt
  aes.key = b64_enc_key.unpack("m0")[0]
  aes.iv = iv unless iv.nil?
  [aes.update(data) << aes.final].pack("m0")
end

.extractKey(encrypted_data, b64_enc_key, use_low_r = true) ⇒ Object



52
53
54
55
56
57
58
59
60
61
# File 'lib/block_io/helper.rb', line 52

def self.extractKey(encrypted_data, b64_enc_key, use_low_r = true)
  # passphrase is in plain text
  # encrypted_data is in base64, as it was stored on Block.io
  # returns the private key extracted from the given encrypted data
  
  decrypted = self.decrypt(encrypted_data, b64_enc_key)
  
  Key.from_passphrase(decrypted, use_low_r)

end

.int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object

courtesy bitcoin-ruby



129
130
131
132
133
134
135
136
137
# File 'lib/block_io/helper.rb', line 129

def self.int_to_base58(int_val, leading_zero_bytes=0)
  alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
  base58_val, base = "", alpha.size
  while int_val > 0
    int_val, remainder = int_val.divmod(base)
    base58_val = alpha[remainder] << base58_val
  end
  base58_val
end

.low_r?(r) ⇒ Boolean

Returns:

  • (Boolean)


93
94
95
96
97
# File 'lib/block_io/helper.rb', line 93

def self.low_r?(r)
  # https://github.com/bitcoin/bitcoin/blob/v0.20.0/src/key.cpp#L207
  h = r.scan(/../)
  h[3].to_i(16) == 32 and h[4].to_i(16) < 0x80
end

.pinToAesKey(secret_pin, iterations = 2048) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/block_io/helper.rb', line 68

def self.pinToAesKey(secret_pin, iterations = 2048)
  # converts the pincode string to PBKDF2
  # returns a base64 version of PBKDF2 pincode
  salt = ""

  part1 = OpenSSL::PKCS5.pbkdf2_hmac(
    secret_pin,
    "",
    1024,
    128/8,
    OpenSSL::Digest::SHA256.new
  ).unpack("H*")[0]
  
  part2 = OpenSSL::PKCS5.pbkdf2_hmac(
    part1,
    "",
    1024,
    256/8,
    OpenSSL::Digest::SHA256.new
  ) # binary

  [part2].pack("m0") # the base64 encryption key

end

.sha256(value) ⇒ Object



63
64
65
66
# File 'lib/block_io/helper.rb', line 63

def self.sha256(value)
  # returns the hex of the hash of the given value
  OpenSSL::Digest::SHA256.digest(value).unpack("H*")[0]
end

.signData(inputs, keys) ⇒ Object

Raises:

  • (Exception)


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/block_io/helper.rb', line 5

def self.signData(inputs, keys)
  # sign the given data with the given keys

  raise Exception.new("Keys object must be a hash or array containing the appropriate keys.") unless keys.size >= 1

  signatures_added = false
  
  # create a dictionary of keys we have
  # saves the next loop from being O(n^3)
  hkeys = (keys.is_a?(Hash) ? keys : keys.inject({}){|h,v| h[v.public_key] = v; h})
  odata = []

  # saves the next loop from being O(n^2)
  inputs.each{|input| odata << input["data_to_sign"]; odata << input["signatures_needed"]; odata.push(*input["signers"])}

  data_to_sign = nil
  signatures_needed = nil
  
  while !(cdata = odata.shift).nil? do
    # O(n)
    
    if cdata.is_a?(String) then
      # this is data to sign

      # make a copy of this
      data_to_sign = '' << cdata

      # number of signatures needed
      signatures_needed = 0 + odata.shift

    else
      # add signatures if necessary
      # dTrust required signatures may be lower than number of keys provided
      
      if hkeys.key?(cdata["signer_public_key"]) and signatures_needed > 0 and cdata["signed_data"].nil? then
        cdata["signed_data"] = hkeys[cdata["signer_public_key"]].sign(data_to_sign) 
        signatures_needed -= 1
        signatures_added ||= true
      end
      
    end

  end

  signatures_added
end