lib/rmega/nodes/uploadable.rb in rmega-0.1.7 vs lib/rmega/nodes/uploadable.rb in rmega-0.2.0
- old
+ new
@@ -1,83 +1,92 @@
-require 'rmega/utils'
-require 'rmega/pool'
-require 'rmega/progress'
-
module Rmega
module Nodes
module Uploadable
+ include Net
+
def upload_chunk(base_url, start, buffer)
size = buffer.length
stop = start + size - 1
url = "#{base_url}/#{start}-#{stop}"
- HTTPClient.new.post(url, buffer).body
+ survive do
+ response = http_post(url, buffer)
+ raise("Upload failed") if response.code.to_i != 200
+ return response.body
+ end
end
def read_chunk(file, start, size)
file.seek(start)
file.read(size)
end
- def encrypt_chunck(rnd_key, file_mac, start, clean_buffer)
- nonce = [rnd_key[4], rnd_key[5], (start/0x1000000000) >> 0, (start/0x10) >> 0]
+ def encrypt_chunck(start, clean_buffer, aes_key, nonce)
+ iv = nonce + [start/0x1000000000, start/0x10].pack('l>*')
+ enc_data = aes_ctr_encrypt(aes_key, clean_buffer, iv)
- encrypted = Crypto::AesCtr.encrypt(rnd_key[0..3], nonce, clean_buffer)
- chunk_mac, data = encrypted[:mac], encrypted[:data]
+ # calculate mac
+ mac_iv = nonce * 2
+ mac = aes_cbc_mac(aes_key, clean_buffer, mac_iv)
- file_mac = [file_mac[0] ^ chunk_mac[0], file_mac[1] ^ chunk_mac[1],
- file_mac[2] ^ chunk_mac[2], file_mac[3] ^ chunk_mac[3]]
-
- file_mac = Crypto::Aes.encrypt(rnd_key[0..3], file_mac)
-
- data
+ return [enc_data, mac]
end
def upload(path)
path = ::File.expand_path(path)
filesize = ::File.size(path)
+
+ raise "Empty file - #{path}" if filesize == 0
+
file = ::File.open(path, 'rb')
- ul_key = Crypto.random_key
- file_mac = [0, 0, 0, 0]
+ rnd_node_key = NodeKey.random
file_handle = nil
base_url = upload_url(filesize)
pool = Pool.new
read_mutex = Mutex.new
- progress = Progress.new(total: filesize, caption: 'Upload')
+ progress = Progress.new(filesize, caption: 'Upload')
- Utils.chunks(filesize).each do |start, size|
- pool.defer do
- encrypted_buffer = nil
+ chunk_macs = {}
+ self.class.each_chunk(filesize) do |start, size|
+ pool.process do
+ clean_buffer = nil
+
read_mutex.synchronize do
clean_buffer = read_chunk(file, start, size)
- encrypted_buffer = encrypt_chunck(ul_key, file_mac, start, clean_buffer)
end
+ encrypted_buffer, chunk_mac = *encrypt_chunck(start, clean_buffer, rnd_node_key.aes_key, rnd_node_key.ctr_nonce)
file_handle = upload_chunk(base_url, start, encrypted_buffer)
+ chunk_macs[start] = chunk_mac
+
progress.increment(size)
end
end
- pool.wait_done
+ pool.shutdown
- attribs = {n: ::File.basename(path)}
- encrypt_attribs = Utils.a32_to_base64(Crypto.encrypt_attributes(ul_key[0..3], attribs))
+ # encrypt attributes
+ attributes_str = "MEGA"
+ attributes_str << {n: ::File.basename(path)}.to_json
+ attributes_str << ("\x00" * (16 - (attributes_str.size % 16)))
+ encrypted_attributes = aes_cbc_encrypt(rnd_node_key.aes_key, attributes_str)
- meta_mac = [file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]]
+ # Calculate meta_mac
+ file_mac = aes_cbc_mac(rnd_node_key.aes_key, chunk_macs.sort.map(&:last).join, "\x0"*16)
+ rnd_node_key.meta_mac = Utils.compact_to_8_bytes(file_mac)
+ encrypted_key = aes_ecb_encrypt(session.master_key, rnd_node_key.generate)
- key = [ul_key[0] ^ ul_key[4], ul_key[1] ^ ul_key[5], ul_key[2] ^ meta_mac[0],
- ul_key[3] ^ meta_mac[1], ul_key[4], ul_key[5], meta_mac[0], meta_mac[1]]
+ resp = request(a: 'p', t: handle, n: [
+ {h: file_handle, t: 0, a: Utils.base64urlencode(encrypted_attributes), k: Utils.base64urlencode(encrypted_key)}
+ ])
- encrypted_key = Utils.a32_to_base64 Crypto.encrypt_key(session.master_key, key)
- request(a: 'p', t: handle, n: [{h: file_handle, t: 0, a: encrypt_attribs, k: encrypted_key}])
-
- attribs[:n]
+ return Nodes::Factory.build(session, resp['f'][0])
ensure
- file.close
+ file.close if file
end
end
end
end