def _login_common(self, res, password): if res in (-2, -9): raise MegaIncorrectPasswordExcetion("Incorrect e-mail and/or password.") enc_master_key = base64_to_a32(res['k']) self.master_key = decrypt_key(enc_master_key, password) if 'tsid' in res: tsid = base64urldecode(res['tsid']) key_encrypted = a32_to_str( encrypt_key(str_to_a32(tsid[:16]), self.master_key)) if key_encrypted == tsid[-16:]: self.sid = res['tsid'] elif 'csid' in res: enc_rsa_priv_key = base64_to_a32(res['privk']) rsa_priv_key = decrypt_key(enc_rsa_priv_key, self.master_key) privk = a32_to_str(rsa_priv_key) self.rsa_priv_key = [0, 0, 0, 0] for i in range(4): l = ((ord(privk[0]) * 256 + ord(privk[1]) + 7) / 8) + 2 self.rsa_priv_key[i] = mpi2int(privk[:l]) privk = privk[l:] enc_sid = mpi2int(base64urldecode(res['csid'])) decrypter = RSA.construct( (self.rsa_priv_key[0] * self.rsa_priv_key[1], 0, self.rsa_priv_key[2], self.rsa_priv_key[0], self.rsa_priv_key[1])) sid = '%x' % decrypter.key._decrypt(enc_sid) sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid) self.sid = base64urlencode(sid[:43])
def login_ephemeral(self): random_master_key = [random.randint(0, 0xFFFFFFFF)] * 4 random_password_key = [random.randint(0, 0xFFFFFFFF)] * 4 random_session_self_challenge = [random.randint(0, 0xFFFFFFFF)] * 4 user_handle = self.api_req({ 'a': 'up', 'k': a32_to_base64(encrypt_key(random_master_key, random_password_key)), 'ts': base64urlencode(a32_to_str(random_session_self_challenge) + a32_to_str(encrypt_key( random_session_self_challenge, random_master_key))) }) res = self.api_req({'a': 'us', 'user': user_handle}) self._login_common(res, random_password_key)
def download_file(self, file_id, file_key, public=False): if public: file_key = base64_to_a32(file_key) file_data = self.api_req({'a': 'g', 'g': 1, 'p': file_id}) else: file_data = self.api_req({'a': 'g', 'g': 1, 'n': file_id}) k = (file_key[0] ^ file_key[4], file_key[1] ^ file_key[5], file_key[2] ^ file_key[6], file_key[3] ^ file_key[7]) iv = file_key[4:6] + (0, 0) meta_mac = file_key[6:8] file_url = file_data['g'] file_size = file_data['s'] attributes = base64urldecode(file_data['at']) attributes = dec_attr(attributes, k) file_name = attributes['n'] infile = requests.get(file_url, stream=True).raw outfile = open(file_name, 'wb') counter = Counter.new( 128, initial_value=((iv[0] << 32) + iv[1]) << 64) decryptor = AES.new(a32_to_str(k), AES.MODE_CTR, counter=counter) file_mac = (0, 0, 0, 0) for chunk_start, chunk_size in sorted(get_chunks(file_size).items()): chunk = infile.read(chunk_size) chunk = decryptor.decrypt(chunk) outfile.write(chunk) chunk_mac = [iv[0], iv[1], iv[0], iv[1]] for i in range(0, len(chunk), 16): block = chunk[i:i+16] if len(block) % 16: block += '\0' * (16 - (len(block) % 16)) block = str_to_a32(block) chunk_mac = [ chunk_mac[0] ^ block[0], chunk_mac[1] ^ block[1], chunk_mac[2] ^ block[2], chunk_mac[3] ^ block[3]] chunk_mac = aes_cbc_encrypt_a32(chunk_mac, k) 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 = aes_cbc_encrypt_a32(file_mac, k) outfile.close() # Integrity check if (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]) != meta_mac: raise ValueError('MAC mismatch')
def dec_attr(attr, key): attr = aes_cbc_decrypt(attr, a32_to_str(key)).rstrip('\0') return json.loads(attr[4:])
def enc_attr(attr, key): attr = 'MEGA' + json.dumps(attr) if len(attr) % 16: attr += '\0' * (16 - len(attr) % 16) return aes_cbc_encrypt(attr, a32_to_str(key))
def aes_cbc_decrypt_a32(data, key): return str_to_a32(aes_cbc_decrypt(a32_to_str(data), a32_to_str(key)))
def uploadfile(self, filename, dst=None): if not dst: root_id = getattr(self, 'root_id', None) if root_id == None: self.get_files() dst = self.root_id infile = open(filename, 'rb') size = os.path.getsize(filename) ul_url = self.api_req({'a': 'u', 's': size})['p'] ul_key = [random.randint(0, 0xFFFFFFFF) for _ in range(6)] counter = Counter.new( 128, initial_value=((ul_key[4] << 32) + ul_key[5]) << 64) encryptor = AES.new( a32_to_str(ul_key[:4]), AES.MODE_CTR, counter=counter) file_mac = [0, 0, 0, 0] for chunk_start, chunk_size in sorted(get_chunks(size).items()): chunk = infile.read(chunk_size) chunk_mac = [ul_key[4], ul_key[5], ul_key[4], ul_key[5]] for i in range(0, len(chunk), 16): block = chunk[i:i+16] if len(block) % 16: block += '\0' * (16 - len(block) % 16) block = str_to_a32(block) chunk_mac = [chunk_mac[0] ^ block[0], chunk_mac[1] ^ block[1], chunk_mac[2] ^ block[2], chunk_mac[3] ^ block[3]] chunk_mac = aes_cbc_encrypt_a32(chunk_mac, ul_key[:4]) 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 = aes_cbc_encrypt_a32(file_mac, ul_key[:4]) chunk = encryptor.encrypt(chunk) url = '%s/%s' % (ul_url, str(chunk_start)) outfile = requests.post(url, data=chunk, stream=True).raw completion_handle = outfile.read() infile.close() meta_mac = (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3]) attributes = {'n': os.path.basename(filename)} enc_attributes = base64urlencode(enc_attr(attributes, 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], ul_key[4], ul_key[5], meta_mac[0], meta_mac[1]] encrypted_key = a32_to_base64(encrypt_key(key, self.master_key)) data = self.api_req({'a': 'p', 't': dst, 'n': [ {'h': completion_handle, 't': 0, 'a': enc_attributes, 'k': encrypted_key}]}) return data