fixed api resp issue #2, reformatted code pep 8 style
This commit is contained in:
parent
ff91521628
commit
2493a10b88
5 changed files with 66 additions and 32 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Compiled source #
|
||||||
|
###################
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Hidden files #
|
||||||
|
###################
|
||||||
|
.*
|
15
crypto.py
15
crypto.py
|
@ -5,20 +5,25 @@ import struct
|
||||||
import binascii
|
import binascii
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
def aes_cbc_encrypt(data, key):
|
def aes_cbc_encrypt(data, key):
|
||||||
aes_cipher = AES.new(key, AES.MODE_CBC, '\0' * 16)
|
aes_cipher = AES.new(key, AES.MODE_CBC, '\0' * 16)
|
||||||
return aes_cipher.encrypt(data)
|
return aes_cipher.encrypt(data)
|
||||||
|
|
||||||
|
|
||||||
def aes_cbc_decrypt(data, key):
|
def aes_cbc_decrypt(data, key):
|
||||||
aes_cipher = AES.new(key, AES.MODE_CBC, '\0' * 16)
|
aes_cipher = AES.new(key, AES.MODE_CBC, '\0' * 16)
|
||||||
return aes_cipher.decrypt(data)
|
return aes_cipher.decrypt(data)
|
||||||
|
|
||||||
|
|
||||||
def aes_cbc_encrypt_a32(data, key):
|
def aes_cbc_encrypt_a32(data, key):
|
||||||
return str_to_a32(aes_cbc_encrypt(a32_to_str(data), a32_to_str(key)))
|
return str_to_a32(aes_cbc_encrypt(a32_to_str(data), a32_to_str(key)))
|
||||||
|
|
||||||
|
|
||||||
def aes_cbc_decrypt_a32(data, key):
|
def aes_cbc_decrypt_a32(data, key):
|
||||||
return str_to_a32(aes_cbc_decrypt(a32_to_str(data), a32_to_str(key)))
|
return str_to_a32(aes_cbc_decrypt(a32_to_str(data), a32_to_str(key)))
|
||||||
|
|
||||||
|
|
||||||
def stringhash(str, aeskey):
|
def stringhash(str, aeskey):
|
||||||
s32 = str_to_a32(str)
|
s32 = str_to_a32(str)
|
||||||
h32 = [0, 0, 0, 0]
|
h32 = [0, 0, 0, 0]
|
||||||
|
@ -28,6 +33,7 @@ def stringhash(str, aeskey):
|
||||||
h32 = aes_cbc_encrypt_a32(h32, aeskey)
|
h32 = aes_cbc_encrypt_a32(h32, aeskey)
|
||||||
return a32_to_base64((h32[0], h32[2]))
|
return a32_to_base64((h32[0], h32[2]))
|
||||||
|
|
||||||
|
|
||||||
def prepare_key(arr):
|
def prepare_key(arr):
|
||||||
pkey = [0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56]
|
pkey = [0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56]
|
||||||
for r in range(0x10000):
|
for r in range(0x10000):
|
||||||
|
@ -42,13 +48,13 @@ def prepare_key(arr):
|
||||||
|
|
||||||
def encrypt_key(a, key):
|
def encrypt_key(a, key):
|
||||||
return sum(
|
return sum(
|
||||||
(aes_cbc_encrypt_a32(a[i:i+4], key)
|
(aes_cbc_encrypt_a32(a[i:i + 4], key)
|
||||||
for i in range(0, len(a), 4)), ())
|
for i in range(0, len(a), 4)), ())
|
||||||
|
|
||||||
|
|
||||||
def decrypt_key(a, key):
|
def decrypt_key(a, key):
|
||||||
return sum(
|
return sum(
|
||||||
(aes_cbc_decrypt_a32(a[i:i+4], key)
|
(aes_cbc_decrypt_a32(a[i:i + 4], key)
|
||||||
for i in range(0, len(a), 4)), ())
|
for i in range(0, len(a), 4)), ())
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,6 +80,7 @@ def str_to_a32(b):
|
||||||
b += '\0' * (4 - len(b) % 4)
|
b += '\0' * (4 - len(b) % 4)
|
||||||
return struct.unpack('>%dI' % (len(b) / 4), b)
|
return struct.unpack('>%dI' % (len(b) / 4), b)
|
||||||
|
|
||||||
|
|
||||||
def mpi_to_int(s):
|
def mpi_to_int(s):
|
||||||
return int(binascii.hexlify(s[2:]), 16)
|
return int(binascii.hexlify(s[2:]), 16)
|
||||||
|
|
||||||
|
@ -84,18 +91,22 @@ def base64_url_decode(data):
|
||||||
data = data.replace(search, replace)
|
data = data.replace(search, replace)
|
||||||
return base64.b64decode(data)
|
return base64.b64decode(data)
|
||||||
|
|
||||||
|
|
||||||
def base64_to_a32(s):
|
def base64_to_a32(s):
|
||||||
return str_to_a32(base64_url_decode(s))
|
return str_to_a32(base64_url_decode(s))
|
||||||
|
|
||||||
|
|
||||||
def base64_url_encode(data):
|
def base64_url_encode(data):
|
||||||
data = base64.b64encode(data)
|
data = base64.b64encode(data)
|
||||||
for search, replace in (('+', '-'), ('/', '_'), ('=', '')):
|
for search, replace in (('+', '-'), ('/', '_'), ('=', '')):
|
||||||
data = data.replace(search, replace)
|
data = data.replace(search, replace)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def a32_to_base64(a):
|
def a32_to_base64(a):
|
||||||
return base64_url_encode(a32_to_str(a))
|
return base64_url_encode(a32_to_str(a))
|
||||||
|
|
||||||
|
|
||||||
def get_chunks(size):
|
def get_chunks(size):
|
||||||
chunks = {}
|
chunks = {}
|
||||||
p = pp = 0
|
p = pp = 0
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
class ValidationError(Exception):
|
class ValidationError(Exception):
|
||||||
"""
|
"""
|
||||||
Error in validation stage
|
Error in validation stage
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RequestError(Exception):
|
class RequestError(Exception):
|
||||||
"""
|
"""
|
||||||
Error in API request
|
Error in API request
|
||||||
|
|
62
mega.py
62
mega.py
|
@ -10,12 +10,12 @@ import requests
|
||||||
import errors
|
import errors
|
||||||
from crypto import *
|
from crypto import *
|
||||||
|
|
||||||
class Mega(object):
|
|
||||||
|
|
||||||
|
class Mega(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.schema = 'https'
|
self.schema = 'https'
|
||||||
self.domain = 'mega.co.nz'
|
self.domain = 'mega.co.nz'
|
||||||
self.timeout = 160 #max time (secs) to wait for response from api requests
|
self.timeout = 160 #max time (secs) to wait for resp from api requests
|
||||||
self.sid = None
|
self.sid = None
|
||||||
self.sequence_num = random.randint(0, 0xFFFFFFFF)
|
self.sequence_num = random.randint(0, 0xFFFFFFFF)
|
||||||
self.request_id = make_id(10)
|
self.request_id = make_id(10)
|
||||||
|
@ -46,13 +46,14 @@ class Mega(object):
|
||||||
self.sid = resp['tsid']
|
self.sid = resp['tsid']
|
||||||
elif 'csid' in resp:
|
elif 'csid' in resp:
|
||||||
encrypted_rsa_private_key = base64_to_a32(resp['privk'])
|
encrypted_rsa_private_key = base64_to_a32(resp['privk'])
|
||||||
rsa_private_key = decrypt_key(encrypted_rsa_private_key, self.master_key)
|
rsa_private_key = decrypt_key(encrypted_rsa_private_key,
|
||||||
|
self.master_key)
|
||||||
|
|
||||||
private_key = a32_to_str(rsa_private_key)
|
private_key = a32_to_str(rsa_private_key)
|
||||||
self.rsa_private_key = [0, 0, 0, 0]
|
self.rsa_private_key = [0, 0, 0, 0]
|
||||||
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
l = ((ord(private_key[0]) * 256 + ord(private_key[1]) + 7) / 8) + 2
|
l = ((ord(private_key[0])*256+ord(private_key[1]) +7) / 8) + 2
|
||||||
self.rsa_private_key[i] = mpi_to_int(private_key[:l])
|
self.rsa_private_key[i] = mpi_to_int(private_key[:l])
|
||||||
private_key = private_key[l:]
|
private_key = private_key[l:]
|
||||||
|
|
||||||
|
@ -73,8 +74,11 @@ class Mega(object):
|
||||||
if self.sid:
|
if self.sid:
|
||||||
params.update({'sid': self.sid})
|
params.update({'sid': self.sid})
|
||||||
req = requests.post(
|
req = requests.post(
|
||||||
'{0}://g.api.{1}/cs'.format(self.schema,self.domain), params=params, data=json.dumps([data]), timeout=self.timeout)
|
'{0}://g.api.{1}/cs'.format(self.schema, self.domain),
|
||||||
json_resp = req.json()
|
params=params,
|
||||||
|
data=json.dumps([data]),
|
||||||
|
timeout=self.timeout)
|
||||||
|
json_resp = json.loads(req.text)
|
||||||
|
|
||||||
#if numeric error code response
|
#if numeric error code response
|
||||||
if isinstance(json_resp, int):
|
if isinstance(json_resp, int):
|
||||||
|
@ -107,7 +111,8 @@ class Mega(object):
|
||||||
public_handle,
|
public_handle,
|
||||||
decrypted_key)
|
decrypted_key)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Upload() response required as input, use get_link() for regular file input')
|
raise ValueError('''Upload() response required as input,
|
||||||
|
use get_link() for regular file input''')
|
||||||
|
|
||||||
def get_link(self, file):
|
def get_link(self, file):
|
||||||
'''
|
'''
|
||||||
|
@ -144,7 +149,7 @@ class Mega(object):
|
||||||
|
|
||||||
def parse_url(self, url):
|
def parse_url(self, url):
|
||||||
#parse file id and key from url
|
#parse file id and key from url
|
||||||
if('!' in url):
|
if ('!' in url):
|
||||||
match = re.findall(r'/#!(.*)', url)
|
match = re.findall(r'/#!(.*)', url)
|
||||||
path = match[0]
|
path = match[0]
|
||||||
return path
|
return path
|
||||||
|
@ -152,7 +157,7 @@ class Mega(object):
|
||||||
raise errors.RequestError('Url key missing')
|
raise errors.RequestError('Url key missing')
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
user_data = self.api_request({'a': 'ug'})
|
user_data = self.api_request({'a': 'ug'})
|
||||||
return user_data
|
return user_data
|
||||||
|
|
||||||
def delete_url(self, url):
|
def delete_url(self, url):
|
||||||
|
@ -199,7 +204,8 @@ class Mega(object):
|
||||||
if i['h'] is not u'':
|
if i['h'] is not u'':
|
||||||
node_id = i['h']
|
node_id = i['h']
|
||||||
|
|
||||||
return self.api_request({'a': 'm', 'n': node_id, 't': target_node_id, 'i': self.request_id})
|
return self.api_request({'a': 'm', 'n': node_id, 't': target_node_id,
|
||||||
|
'i': self.request_id})
|
||||||
|
|
||||||
def get_node_by_type(self, type):
|
def get_node_by_type(self, type):
|
||||||
'''
|
'''
|
||||||
|
@ -212,7 +218,7 @@ class Mega(object):
|
||||||
'''
|
'''
|
||||||
nodes = self.get_files()
|
nodes = self.get_files()
|
||||||
for node in nodes.items():
|
for node in nodes.items():
|
||||||
if(node[1]['t'] == type):
|
if (node[1]['t'] == type):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +240,9 @@ class Mega(object):
|
||||||
attribs = decrypt_attr(attribs, k)
|
attribs = decrypt_attr(attribs, k)
|
||||||
file_name = attribs['n']
|
file_name = attribs['n']
|
||||||
|
|
||||||
print "downloading {0} (size: {1}), url = {2}".format(attribs['n'], file_size, file_url)
|
print "downloading {0} (size: {1}), url = {2}".format(attribs['n'],
|
||||||
|
file_size,
|
||||||
|
file_url)
|
||||||
|
|
||||||
input_file = requests.get(file_url, stream=True).raw
|
input_file = requests.get(file_url, stream=True).raw
|
||||||
output_file = open(file_name, 'wb')
|
output_file = open(file_name, 'wb')
|
||||||
|
@ -251,7 +259,7 @@ class Mega(object):
|
||||||
|
|
||||||
chunk_mac = [iv[0], iv[1], iv[0], iv[1]]
|
chunk_mac = [iv[0], iv[1], iv[0], iv[1]]
|
||||||
for i in range(0, len(chunk), 16):
|
for i in range(0, len(chunk), 16):
|
||||||
block = chunk[i:i+16]
|
block = chunk[i:i + 16]
|
||||||
if len(block) % 16:
|
if len(block) % 16:
|
||||||
block += '\0' * (16 - (len(block) % 16))
|
block += '\0' * (16 - (len(block) % 16))
|
||||||
block = str_to_a32(block)
|
block = str_to_a32(block)
|
||||||
|
@ -291,7 +299,7 @@ class Mega(object):
|
||||||
|
|
||||||
#generate random aes key (128) for file
|
#generate random aes key (128) for file
|
||||||
ul_key = [random.randint(0, 0xFFFFFFFF) for r in range(6)]
|
ul_key = [random.randint(0, 0xFFFFFFFF) for r in range(6)]
|
||||||
count = Counter.new(128, initial_value=((ul_key[4] << 32) + ul_key[5]) << 64)
|
count = Counter.new(128,initial_value=((ul_key[4]<<32)+ul_key[5])<<64)
|
||||||
aes = AES.new(a32_to_str(ul_key[:4]), AES.MODE_CTR, counter=count)
|
aes = AES.new(a32_to_str(ul_key[:4]), AES.MODE_CTR, counter=count)
|
||||||
|
|
||||||
file_mac = [0, 0, 0, 0]
|
file_mac = [0, 0, 0, 0]
|
||||||
|
@ -301,22 +309,25 @@ class Mega(object):
|
||||||
#determine chunks mac
|
#determine chunks mac
|
||||||
chunk_mac = [ul_key[4], ul_key[5], ul_key[4], ul_key[5]]
|
chunk_mac = [ul_key[4], ul_key[5], ul_key[4], ul_key[5]]
|
||||||
for i in range(0, len(chunk), 16):
|
for i in range(0, len(chunk), 16):
|
||||||
block = chunk[i:i+16]
|
block = chunk[i:i + 16]
|
||||||
if len(block) % 16:
|
if len(block) % 16:
|
||||||
block += '\0' * (16 - len(block) % 16)
|
block += '\0' * (16 - len(block) % 16)
|
||||||
block = str_to_a32(block)
|
block = str_to_a32(block)
|
||||||
chunk_mac = [chunk_mac[0] ^ block[0], chunk_mac[1] ^ block[1], chunk_mac[2] ^ block[2],
|
chunk_mac = [chunk_mac[0] ^ block[0], chunk_mac[1] ^ block[1],
|
||||||
|
chunk_mac[2] ^ block[2],
|
||||||
chunk_mac[3] ^ block[3]]
|
chunk_mac[3] ^ block[3]]
|
||||||
chunk_mac = aes_cbc_encrypt_a32(chunk_mac, ul_key[:4])
|
chunk_mac = aes_cbc_encrypt_a32(chunk_mac, ul_key[:4])
|
||||||
|
|
||||||
#our files mac
|
#our files mac
|
||||||
file_mac = [file_mac[0] ^ chunk_mac[0], file_mac[1] ^ chunk_mac[1], file_mac[2] ^ chunk_mac[2],
|
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[3] ^ chunk_mac[3]]
|
||||||
file_mac = aes_cbc_encrypt_a32(file_mac, ul_key[:4])
|
file_mac = aes_cbc_encrypt_a32(file_mac, ul_key[:4])
|
||||||
|
|
||||||
#encrypt file and upload
|
#encrypt file and upload
|
||||||
chunk = aes.encrypt(chunk)
|
chunk = aes.encrypt(chunk)
|
||||||
output_file = requests.post(ul_url + "/" + str(chunk_start), data=chunk, timeout=self.timeout)
|
output_file = requests.post(ul_url + "/" + str(chunk_start),
|
||||||
|
data=chunk, timeout=self.timeout)
|
||||||
completion_file_handle = output_file.text
|
completion_file_handle = output_file.text
|
||||||
|
|
||||||
#determine meta mac
|
#determine meta mac
|
||||||
|
@ -324,14 +335,16 @@ class Mega(object):
|
||||||
|
|
||||||
attribs = {'n': os.path.basename(filename)}
|
attribs = {'n': os.path.basename(filename)}
|
||||||
encrypt_attribs = base64_url_encode(encrypt_attr(attribs, ul_key[:4]))
|
encrypt_attribs = base64_url_encode(encrypt_attr(attribs, ul_key[:4]))
|
||||||
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],
|
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]]
|
ul_key[4], ul_key[5], meta_mac[0], meta_mac[1]]
|
||||||
encrypted_key = a32_to_base64(encrypt_key(key, self.master_key))
|
encrypted_key = a32_to_base64(encrypt_key(key, self.master_key))
|
||||||
#update attributes
|
#update attributes
|
||||||
data = self.api_request({'a': 'p', 't': dest, 'n': [
|
data = self.api_request({'a': 'p', 't': dest, 'n': [{
|
||||||
{'h': completion_file_handle, 't': 0, 'a': encrypt_attribs, 'k': encrypted_key}]}
|
'h': completion_file_handle,
|
||||||
)
|
't': 0,
|
||||||
|
'a': encrypt_attribs,
|
||||||
|
'k': encrypted_key}]})
|
||||||
#close input file and return API msg
|
#close input file and return API msg
|
||||||
input_file.close()
|
input_file.close()
|
||||||
return data
|
return data
|
||||||
|
@ -344,7 +357,8 @@ class Mega(object):
|
||||||
key = file['k'][file['k'].index(':') + 1:]
|
key = file['k'][file['k'].index(':') + 1:]
|
||||||
key = decrypt_key(base64_to_a32(key), self.master_key)
|
key = decrypt_key(base64_to_a32(key), self.master_key)
|
||||||
if file['t'] == 0:
|
if file['t'] == 0:
|
||||||
k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7])
|
k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6],
|
||||||
|
key[3] ^ key[7])
|
||||||
file['iv'] = key[4:6] + (0, 0)
|
file['iv'] = key[4:6] + (0, 0)
|
||||||
file['meta_mac'] = key[6:8]
|
file['meta_mac'] = key[6:8]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from mega import Mega
|
from mega import Mega
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
#user details
|
#user details
|
||||||
email = 'your@email.com'
|
email = 'your@email.com'
|
||||||
|
@ -8,7 +9,7 @@ def test():
|
||||||
mega = Mega()
|
mega = Mega()
|
||||||
|
|
||||||
##login
|
##login
|
||||||
m = mega.login(email, password)
|
m = mega.login(email, password)
|
||||||
|
|
||||||
##get user details
|
##get user details
|
||||||
details = m.get_user()
|
details = m.get_user()
|
||||||
|
@ -40,10 +41,11 @@ def test():
|
||||||
#trash a file by it's id
|
#trash a file by it's id
|
||||||
print(m.delete(file[1]['k']))
|
print(m.delete(file[1]['k']))
|
||||||
|
|
||||||
##download file, by id+key or url
|
##download file, by id+key or url
|
||||||
#file = m.find('myfile.doc')
|
#file = m.find('myfile.doc')
|
||||||
#m.download(file)
|
#m.download(file)
|
||||||
#m.download_url('https://mega.co.nz/#!6hBW0R4a!By7-Vjj5xal8K5w_IXH3PlGNyZ1VvIrjZkOmHGq1X00')
|
#m.download_url('https://mega.co.nz/#!6hBW0R4a!By7-Vjj5xal8K5w_IXH3PlGNyZ1VvIrjZkOmHGq1X00')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test()
|
test()
|
Loading…
Reference in a new issue