예제 #1
0
    def _mkdir(self, name, parent_node_id):
        # generate random aes key (128) for folder
        ul_key = [random.randint(0, 0xFFFFFFFF) for _ in range(6)]

        # encrypt attribs
        attribs = {'n': name}
        encrypt_attribs = base64_url_encode(encrypt_attr(attribs, ul_key[:4]))
        encrypted_key = a32_to_base64(encrypt_key(ul_key[:4], self.master_key))

        # update attributes
        data = self._api_request({
            'a':
            'p',
            't':
            parent_node_id,
            'n': [{
                'h': 'xxxxxxxx',
                't': 1,
                'a': encrypt_attribs,
                'k': encrypted_key
            }],
            'i':
            self.request_id
        })
        return data
예제 #2
0
 def get_folder_link(self, file):
     try:
         file = file[1]
     except (IndexError, KeyError):
         pass
     if 'h' in file and 'k' in file:
         public_handle = self._api_request({'a': 'l', 'n': file['h']})
         if public_handle == -11:
             raise RequestError("Can't get a public link from that file "
                                "(is this a shared file?)")
         decrypted_key = a32_to_base64(file['shared_folder_key'])
         return (f'{self.schema}://{self.domain}'
                 f'/#F!{public_handle}!{decrypted_key}')
     else:
         raise ValidationError('File id and key must be present')
예제 #3
0
 def get_link(self, file):
     """
     Get a file public link from given file object
     """
     file = file[1]
     if 'h' in file and 'k' in file:
         public_handle = self._api_request({'a': 'l', 'n': file['h']})
         if public_handle == -11:
             raise RequestError("Can't get a public link from that file "
                                "(is this a shared file?)")
         decrypted_key = a32_to_base64(file['key'])
         return (f'{self.schema}://{self.domain}'
                 f'/#!{public_handle}!{decrypted_key}')
     else:
         raise ValidationError('File id and key must be present')
예제 #4
0
 def rename(self, file, new_name):
     file = file[1]
     # create new attribs
     attribs = {'n': new_name}
     # encrypt attribs
     encrypt_attribs = base64_url_encode(encrypt_attr(attribs, file['k']))
     encrypted_key = a32_to_base64(encrypt_key(file['key'],
                                               self.master_key))
     # update attributes
     return self._api_request([{
         'a': 'a',
         'attr': encrypt_attribs,
         'key': encrypted_key,
         'n': file['h'],
         'i': self.request_id
     }])
예제 #5
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''')
예제 #6
0
    def login_anonymous(self):
        logger.info('Logging in anonymous temporary user...')
        master_key = [random.randint(0, 0xFFFFFFFF)] * 4
        password_key = [random.randint(0, 0xFFFFFFFF)] * 4
        session_self_challenge = [random.randint(0, 0xFFFFFFFF)] * 4

        user = self._api_request({
            'a':
            'up',
            'k':
            a32_to_base64(encrypt_key(master_key, password_key)),
            'ts':
            base64_url_encode(
                a32_to_str(session_self_challenge) +
                a32_to_str(encrypt_key(session_self_challenge, master_key)))
        })

        resp = self._api_request({'a': 'us', 'user': user})
        if isinstance(resp, int):
            raise RequestError(resp)
        self._login_process(resp, password_key)
예제 #7
0
    def import_public_file(self,
                           file_handle,
                           file_key,
                           dest_node=None,
                           dest_name=None):
        """
        Import the public file into user account
        """
        # Providing dest_node spare an API call to retrieve it.
        if dest_node is None:
            # Get '/Cloud Drive' folder no dest node specified
            dest_node = self.get_node_by_type(2)[1]

        # Providing dest_name spares an API call to retrieve it.
        if dest_name is None:
            pl_info = self.get_public_file_info(file_handle, file_key)
            dest_name = pl_info['name']

        key = base64_to_a32(file_key)
        k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6],
             key[3] ^ key[7])

        encrypted_key = a32_to_base64(encrypt_key(key, self.master_key))
        encrypted_name = base64_url_encode(encrypt_attr({'n': dest_name}, k))
        return self._api_request({
            'a':
            'p',
            't':
            dest_node['h'],
            'n': [{
                'ph': file_handle,
                't': 0,
                'a': encrypted_name,
                'k': encrypted_key
            }]
        })
예제 #8
0
    def progress_upload(self, filename, dest=None, dest_filename=None):  # pylint: disable=R0914,R0915
        """
        Custom upload method with nicer progress
        """
        logger = logging.getLogger(__name__)
        # determine storage node
        if dest is None:
            # if none set, upload to cloud drive node
            if not hasattr(self, 'root_id'):
                self.get_files()
            dest = self.root_id

        # request upload url, call 'u' method
        with open(filename, 'rb') as input_file:
            file_size = os.path.getsize(filename)
            ul_url = self._api_request({'a': 'u', 's': file_size})['p']

            # generate random aes key (128) for file
            ul_key = [random.randint(0, 0xFFFFFFFF) for _ in range(6)]
            k_str = a32_to_str(ul_key[:4])
            count = Counter.new(
                128, initial_value=((ul_key[4] << 32) + ul_key[5]) << 64
            )
            aes = AES.new(k_str, AES.MODE_CTR, counter=count)

            upload_progress = 0
            completion_file_handle = None

            mac_str = '\0' * 16
            mac_encryptor = AES.new(
                k_str, AES.MODE_CBC, mac_str.encode("utf8")
            )
            iv_str = a32_to_str([ul_key[4], ul_key[5], ul_key[4], ul_key[5]])
            if file_size > 0:
                pbar = tqdm.tqdm(total=file_size, unit="b", unit_scale=True, unit_divisor=1024)
                for chunk_start, chunk_size in get_chunks(file_size):
                    chunk = input_file.read(chunk_size)
                    upload_progress += len(chunk)

                    encryptor = AES.new(k_str, AES.MODE_CBC, iv_str)
                    for i in range(0, len(chunk) - 16, 16):
                        block = chunk[i:i + 16]
                        encryptor.encrypt(block)

                    # fix for files under 16 bytes failing
                    if file_size > 16:
                        i += 16
                    else:
                        i = 0

                    block = chunk[i:i + 16]
                    if len(block) % 16:
                        block += makebyte('\0' * (16 - len(block) % 16))
                    mac_str = mac_encryptor.encrypt(encryptor.encrypt(block))

                    # encrypt file and upload
                    chunk = aes.encrypt(chunk)
                    output_file = requests.post(
                        ul_url + "/" + str(chunk_start),
                        data=chunk,
                        timeout=self.timeout
                    )
                    completion_file_handle = output_file.text
                    pbar.update(len(chunk))
                pbar.close()
            else:
                output_file = requests.post(
                    ul_url + "/0", data='', timeout=self.timeout
                )
                completion_file_handle = output_file.text

            logger.info('Chunks uploaded')
            logger.info('Setting attributes to complete upload')
            logger.info('Computing attributes')
            file_mac = str_to_a32(mac_str)

            # determine meta mac
            meta_mac = (file_mac[0] ^ file_mac[1], file_mac[2] ^ file_mac[3])

            dest_filename = dest_filename or os.path.basename(filename)
            attribs = {'n': dest_filename}

            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], ul_key[4],
                ul_key[5], meta_mac[0], meta_mac[1]
            ]
            encrypted_key = a32_to_base64(encrypt_key(key, self.master_key))
            logger.info('Sending request to update attributes')
            # update attributes
            data = self._api_request(
                {
                    'a': 'p',
                    't': dest,
                    'i': self.request_id,
                    'n': [
                        {
                            'h': completion_file_handle,
                            't': 0,
                            'a': encrypt_attribs,
                            'k': encrypted_key
                        }
                    ]
                }
            )
            logger.info('Upload complete')
            return data