Class: Rmega::Nodes::Node

Inherits:
Object
  • Object
show all
Includes:
Crypto, Loggable, Traversable, Rmega::NotInspectable
Defined in:
lib/rmega/nodes/node.rb

Direct Known Subclasses

File, Folder, Inbox, Root, Trash

Constant Summary collapse

TYPES =
{0 => :file, 1 => :folder, 2 => :root, 3 => :inbox, 4 => :trash}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Crypto::Rsa

#powm, #rsa_decrypt

Methods included from Crypto::AesCtr

#aes_ctr_cipher, #aes_ctr_decrypt, #aes_ctr_encrypt

Methods included from Crypto::AesEcb

#aes_ecb_cipher, #aes_ecb_decrypt, #aes_ecb_encrypt

Methods included from Crypto::AesCbc

#aes_cbc_cipher, #aes_cbc_decrypt, #aes_cbc_encrypt, #aes_cbc_mac

Methods included from Traversable

#children, #empty?, #files, #folders, #parent

Methods included from Loggable

included, #logger

Methods included from Rmega::NotInspectable

#inspect

Constructor Details

#initialize(session, data) ⇒ Node

Returns a new instance of Node.



18
19
20
21
# File 'lib/rmega/nodes/node.rb', line 18

def initialize(session, data)
  @session = session
  @data = data
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



9
10
11
# File 'lib/rmega/nodes/node.rb', line 9

def data
  @data
end

#sessionObject (readonly)

Returns the value of attribute session.



9
10
11
# File 'lib/rmega/nodes/node.rb', line 9

def session
  @session
end

Class Method Details

.each_chunk(size, &block) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rmega/nodes/node.rb', line 110

def self.each_chunk(size, &block)
  start, p = 0, 0

  return if size <= 0

  loop do
    offset = p < 8 ? (131072 * (p += 1)) : 1048576
    next_start = offset + start

    if next_start >= size
      yield(start, size - start)
      break
    else
      yield(start, offset)
      start = next_start
    end
  end
end

Instance Method Details

#attributesObject



145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/rmega/nodes/node.rb', line 145

def attributes
  encrypted = data['a'] || data['at']
  return if !encrypted or encrypted.empty?
  node_key = NodeKey.load(decrypted_file_key)
  encrypted = Utils.base64urldecode(encrypted)
  encrypted.strip! if encrypted.size % 16 != 0 # Fix possible errors
  json = aes_cbc_decrypt(node_key.aes_key, encrypted)
  # Remove invalid bytes at the end of the string
  json.strip!
  json.gsub!(/^MEGA\{(.+)\}.*/, '{\1}')
  return JSON.parse(json)
end

#decrypted_file_keyObject



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rmega/nodes/node.rb', line 133

def decrypted_file_key
  h, shared_key = *process_shared_key

  if shared_key
    aes_ecb_decrypt(shared_key, file_keys[h])
  elsif file_key
    aes_ecb_decrypt(master_key, file_key)
  else
    Utils.base64urldecode(public_url.split('!').last)
  end
end

#each_chunk(&block) ⇒ Object



129
130
131
# File 'lib/rmega/nodes/node.rb', line 129

def each_chunk(&block)
  self.class.each_chunk(filesize, &block)
end

#file_keyObject



78
79
80
81
# File 'lib/rmega/nodes/node.rb', line 78

def file_key
  k = file_keys.values.first
  return k ? k : nil
end

#file_keysObject



67
68
69
70
71
72
73
74
75
76
# File 'lib/rmega/nodes/node.rb', line 67

def file_keys
  return {} unless data['k']

  pairs = data['k'].split('/')
  pairs.inject({}) do |hash, pair|
    h, k = pair.split(':')
    hash[h] = Utils.base64urldecode(k)
    hash
  end
end

#handleObject



55
56
57
# File 'lib/rmega/nodes/node.rb', line 55

def handle
  data['h']
end

#nameObject



63
64
65
# File 'lib/rmega/nodes/node.rb', line 63

def name
  attributes['n'] if attributes
end

#parent_handleObject



59
60
61
# File 'lib/rmega/nodes/node.rb', line 59

def parent_handle
  data['p']
end

#process_shared_keyObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/rmega/nodes/node.rb', line 87

def process_shared_key
  h = (shared_keys.keys & file_keys.keys).first
  return [h, shared_keys[h]] if h

  sk = data['sk']

  return unless sk

  shared_key = if sk.size > 22
    # Decrypt sk
    sk = Utils.base64_mpi_to_bn(sk)
    sk = rsa_decrypt(sk, rsa_privk)
    sk = sk.to_s(16)
    sk = '0' + sk if sk.size % 2 > 0
    Utils.hexstr_to_bstr(sk)[0..15]
  else
    aes_ecb_decrypt(master_key, Utils.base64urldecode(sk))
  end

  shared_keys[handle] = shared_key
  [handle, shared_key]
end

#public_handleObject



27
28
29
# File 'lib/rmega/nodes/node.rb', line 27

def public_handle
  @public_handle ||= request(a: 'l', n: handle)
end

#public_urlObject



23
24
25
# File 'lib/rmega/nodes/node.rb', line 23

def public_url
  @public_url ||= "https://mega.co.nz/#!#{public_handle}!#{Utils.base64urlencode(decrypted_file_key)}"
end

#rename(new_name) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rmega/nodes/node.rb', line 38

def rename(new_name)
  node_key = NodeKey.load(decrypted_file_key)

  _attr = serialize_attributes(attributes.merge("n" => new_name))
  _attr = aes_cbc_encrypt(node_key.aes_key, _attr)
  _attr = Utils.base64urlencode(_attr)

  resp = request(:a => "a", :attr => _attr, :key => Utils.base64urlencode(node_key.aes_key), :n => handle)

  if resp != 0
    raise("Rename failed")
  else
    @data['a'] = _attr
    return self
  end
end

#serialize_attributes(hash) ⇒ Object



31
32
33
34
35
36
# File 'lib/rmega/nodes/node.rb', line 31

def serialize_attributes(hash)
  str = "MEGA"
  str << hash.to_json
  str << ("\x00" * (16 - (str.size % 16)))
  return str
end

#shared_root?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/rmega/nodes/node.rb', line 83

def shared_root?
  data['su'] && data['sk'] && data['k']
end

#typeObject



158
159
160
# File 'lib/rmega/nodes/node.rb', line 158

def type
  TYPES[data['t']]
end