Beispiel #1
0
 def _process_file(self, file, shared_keys):
     if file['t'] == 0 or file['t'] == 1:
         keys = dict(
             keypart.split(':', 1) for keypart in file['k'].split('/')
             if ':' in keypart)
         uid = file['u']
         key = None
         # my objects
         if uid in keys:
             key = decrypt_key(base64_to_a32(keys[uid]), self.master_key)
         # shared folders
         elif 'su' in file and 'sk' in file and ':' in file['k']:
             shared_key = decrypt_key(base64_to_a32(file['sk']),
                                      self.master_key)
             key = decrypt_key(base64_to_a32(keys[file['h']]), shared_key)
             if file['su'] not in shared_keys:
                 shared_keys[file['su']] = {}
             shared_keys[file['su']][file['h']] = shared_key
         # shared files
         elif file['u'] and file['u'] in shared_keys:
             for hkey in shared_keys[file['u']]:
                 shared_key = shared_keys[file['u']][hkey]
                 if hkey in keys:
                     key = keys[hkey]
                     key = decrypt_key(base64_to_a32(key), shared_key)
                     break
         if file['h'] and file['h'] in shared_keys.get('EXP', ()):
             shared_key = shared_keys['EXP'][file['h']]
             encrypted_key = str_to_a32(
                 base64_url_decode(file['k'].split(':')[-1]))
             key = decrypt_key(encrypted_key, shared_key)
             file['shared_folder_key'] = shared_key
         if key is not None:
             # file
             if file['t'] == 0:
                 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['meta_mac'] = key[6:8]
             # folder
             else:
                 k = key
             file['key'] = key
             file['k'] = k
             attributes = base64_url_decode(file['a'])
             attributes = decrypt_attr(attributes, k)
             file['a'] = attributes
         # other => wrong object
         elif file['k'] == '':
             file['a'] = False
     elif file['t'] == 2:
         self.root_id = file['h']
         file['a'] = {'n': 'Cloud Drive'}
     elif file['t'] == 3:
         self.inbox_id = file['h']
         file['a'] = {'n': 'Inbox'}
     elif file['t'] == 4:
         self.trashbin_id = file['h']
         file['a'] = {'n': 'Rubbish Bin'}
     return file
Beispiel #2
0
    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])
Beispiel #3
0
    def _login_process(self, resp, password):
        encrypted_master_key = base64_to_a32(resp['k'])
        self.master_key = decrypt_key(encrypted_master_key, password)
        if 'tsid' in resp:
            tsid = base64_url_decode(resp['tsid'])
            key_encrypted = a32_to_str(
                encrypt_key(str_to_a32(tsid[:16]), self.master_key))
            if key_encrypted == tsid[-16:]:
                self.sid = resp['tsid']
        elif 'csid' in resp:
            encrypted_rsa_private_key = base64_to_a32(resp['privk'])
            rsa_private_key = decrypt_key(encrypted_rsa_private_key,
                                          self.master_key)

            private_key = a32_to_str(rsa_private_key)
            # The private_key contains 4 MPI integers concatenated together.
            rsa_private_key = [0, 0, 0, 0]
            for i in range(4):
                # An MPI integer has a 2-byte header which describes the number
                # of bits in the integer.
                bitlength = (private_key[0] * 256) + private_key[1]
                bytelength = math.ceil(bitlength / 8)
                # Add 2 bytes to accommodate the MPI header
                bytelength += 2
                rsa_private_key[i] = mpi_to_int(private_key[:bytelength])
                private_key = private_key[bytelength:]

            first_factor_p = rsa_private_key[0]
            second_factor_q = rsa_private_key[1]
            private_exponent_d = rsa_private_key[2]
            # In MEGA's webclient javascript, they assign [3] to a variable
            # called u, but I do not see how it corresponds to pycryptodome's
            # RSA.construct and it does not seem to be necessary.
            rsa_modulus_n = first_factor_p * second_factor_q
            phi = (first_factor_p - 1) * (second_factor_q - 1)
            public_exponent_e = modular_inverse(private_exponent_d, phi)

            rsa_components = (
                rsa_modulus_n,
                public_exponent_e,
                private_exponent_d,
                first_factor_p,
                second_factor_q,
            )
            rsa_decrypter = RSA.construct(rsa_components)

            encrypted_sid = mpi_to_int(base64_url_decode(resp['csid']))

            sid = '%x' % rsa_decrypter._decrypt(encrypted_sid)
            sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid)
            self.sid = base64_url_encode(sid[:43])
Beispiel #4
0
 def get_files(self):
     files_data = self.api_req({'a': 'f', 'c': 1})
     for file in files_data['f']:
         if file['t'] in (0, 1):
             key = file['k'].split(':')[1]
             key = decrypt_key(base64_to_a32(key), self.master_key)
             # file
             if file['t'] == 0:
                 k = (key[0] ^ key[4],
                      key[1] ^ key[5],
                      key[2] ^ key[6],
                      key[3] ^ key[7])
             # directory
             else:
                 k = key
             attributes = base64urldecode(file['a'])
             attributes = dec_attr(attributes, k)
             file['a'] = attributes
             file['k'] = key
         # Root ("Cloud Drive")
         elif file['t'] == 2:
             self.root_id = file['h']
         # Inbox
         elif file['t'] == 3:
             self.inbox_id = file['h']
         # Trash Bin
         elif file['t'] == 4:
             self.trashbin_id = file['h']
     return files_data
def make_wallet(root_key, date, passphrase, fake_passphrase, kdf_type, salt):
	"""
	make_wallet(root_key, date, passphrase, fake_passphrase, kdf_type, salt)
	root_key :: 32 bytes (pseudo)-random data. 
	date :: Wallet creation date. Integer <= 65535. 
	   == ((creation day) - (2013-01-01)) / 7
	passphrase :: The passphrase to decrypt the wallet.
	fake_passphrase :: Alternate passphrase. Decrypts to a different wallet.
	kdf_type :: The numerical code for the desired KDF type.
	salt :: A 2-byte random value.
	Returns a wallet ciphertext, which can be decrypted with the passphrase.
	Format:
	[12 bit salt][4 bit KDF type][2 byte creation date][4 byte checksum][32 byte encrypted root_key]
	Salt goes first because SSSS works better with random most significant bits.
	"""
	assert(len(root_key) == 32)
	assert(date >= 0 and date <= 65535)
	assert(kdf_type in (0,1,2,8,9))
	assert(len(salt) == 2)
	big_endian_date = chr(date >> 8) + chr(date & 0xff)
	# Encode the KDF in the bottom 4 bits of the salt
	kdf_byte = chr( kdf_type + (ord(salt[1]) & 0xF0) )
	salt = salt[0] + kdf_byte
	# Use the provided salt value + the date as the salt
	new_salt = salt + big_endian_date # 4 byte salt

	kdf = crypto.kdfs[kdf_type]

	encrypted_root_key = crypto.encrypt_key(root_key, new_salt, passphrase, kdf)

	fake_root_key = crypto.decrypt_key(encrypted_root_key, new_salt, fake_passphrase, kdf)

	decryption_checksum = crypto.make_key_checksum(root_key, fake_root_key)

	return salt + big_endian_date + decryption_checksum + encrypted_root_key
Beispiel #6
0
 def get_upload_link(self, file):
     """
     Get a files public link inc. decrypted key
     Requires upload() response as input
     """
     if 'f' in file:
         file = file['f'][0]
         public_handle = self._api_request({'a': 'l', 'n': file['h']})
         file_key = file['k'][file['k'].index(':') + 1:]
         decrypted_key = a32_to_base64(
             decrypt_key(base64_to_a32(file_key), self.master_key))
         return (f'{self.schema}://{self.domain}'
                 f'/#!{public_handle}!{decrypted_key}')
     else:
         raise ValueError('''Upload() response required as input,
                         use get_link() for regular file input''')
Beispiel #7
0
 def _init_shared_keys(self, files, shared_keys):
     """
     Init shared key not associated with a user.
     Seems to happen when a folder is shared,
     some files are exchanged and then the
     folder is un-shared.
     Keys are stored in files['s'] and files['ok']
     """
     ok_dict = {}
     for ok_item in files['ok']:
         shared_key = decrypt_key(base64_to_a32(ok_item['k']),
                                  self.master_key)
         ok_dict[ok_item['h']] = shared_key
     for s_item in files['s']:
         if s_item['u'] not in shared_keys:
             shared_keys[s_item['u']] = {}
         if s_item['h'] in ok_dict:
             shared_keys[s_item['u']][s_item['h']] = ok_dict[s_item['h']]
     self.shared_keys = shared_keys
def decrypt_wallet(wallet, passphrase):
	assert(len(wallet) == 2 + 2 + 4 + 32)
	salt = wallet[0:2]
	big_endian_date = wallet[2:4]
	new_salt = salt + big_endian_date
	decryption_checksum = wallet[4:8]
	encrypted_root_key = wallet[8:40]

	kdf_type = ord(wallet[1]) & 0x0F
	assert(kdf_type in (0,1,2,8,9))
	kdf = crypto.kdfs[kdf_type]

	root_key = crypto.decrypt_key(encrypted_root_key, new_salt, passphrase, kdf)

	if not crypto.check_key_checksum(root_key, decryption_checksum):
		raise Exception("Decryption checksum is bad. Is password incorrect?")

	date = (ord(big_endian_date[0]) << 8) + ord(big_endian_date[1])

	return root_key, date