def apply_punctured_enc(data, re_enc_length, seed, key, iv, init_val, debug=0): """ Apply punctured encryption to the given data: 're_enc_length' bytes are randomly selected using the seed and symmetrically encrypted using the given key and iv. :param data: data to re-encrypt :param re_enc_length: number of bytes to re-encrypt :param seed: seed to randomly select bytes to re-encrypt :param key: symmetric key to encrypt randomly selected bytes :param iv: initialisation vector for the symmetric encryption :param init_val: initial value (ONLY FOR CTR MODE) :param debug: if 1, prints will be shown during execution; default 0, no prints are shown :return: re-encrypted data """ # Get random bytes to re-encrypt and their positions in the data bytes_to_re_enc, re_enc_indexes = get_bytes_to_re_enc( data, re_enc_length, seed, debug) if debug: # ONLY USE FOR DEBUG print('BYTES TO RE-ENCRYPT = (%d) %s' % (len(bytes_to_re_enc), bytes_to_re_enc)) print('INDEX TO RE-ENCRYPT = (%d) %s' % (len(re_enc_indexes), re_enc_indexes)) # Re-encrypt random data bytes sym_cipher = sym.get_cipher(AES.MODE_CTR, init_val, None, key, iv) re_enc_bytes = sym.encrypt(sym_cipher, bytes_to_re_enc, debug) if debug: # ONLY USE FOR DEBUG print('RE-ENCRYPTED BYTES = (%s) (%d) %s' % (type(re_enc_bytes), len(re_enc_bytes), re_enc_bytes)) # Replace bytes with re-encrypted ones in data return replace_bytes(data, re_enc_bytes, re_enc_indexes, debug)
def re_enc_file(file, chunk_size, re_enc_length, seed, key, iv, debug=0): """ Split the file in chunks and apply re-encryption to each of them using the given parameters. :param file: file to re-encrypt :param chunk_size: chunk size in bytes :param re_enc_length: number of bytes to re-encrypt :param seed: seed to randomly select bytes to re-encrypt :param key: symmetric key to encrypt randomly selected bytes :param iv: initialisation vector for the symmetric encryption :param debug: if 1, prints will be shown during execution; default 0, no prints are shown """ # Read file in chunks and re-encrypt each of them with the given parameters with open(file, 'rb+') as f: # Compute cipher initial value (ONLY FOR CTR MODE) enc_block_size = sym.get_cipher(AES.MODE_CTR, None, None, key, iv).block_size init_val = 0 for file_chunk in iter(lambda: f.read(chunk_size), ''): if debug: # ONLY USE FOR DEBUG print('FILE CHUNK TO RE-ENC = (%s) (%d) %s' % (type(file_chunk), len(file_chunk), file_chunk)) # EOF if not len(file_chunk): break # Re-encrypt file chunk re_enc_file_chunk = re_enc.re_encrypt(file_chunk, re_enc_length, seed, key, iv, init_val, debug) if debug: # ONLY USE FOR DEBUG print('RE-ENCRYPTED FILE CHUNK = (%s) (%d) %s' % (type(re_enc_file_chunk), len(re_enc_file_chunk), re_enc_file_chunk)) # Check re-encryption correctness if len(re_enc_file_chunk) != len(file_chunk): logging.error( 'Re-encrypted file chunk length does not match file chunk length' ) print( '[ERROR] Re-encrypted file chunk length does not match file chunk length' ) raise Exception # Replace file chunk with re-encrypted one f.seek(-len(re_enc_file_chunk), os.SEEK_CUR) f.write(re_enc_file_chunk) # Update cipher initial value init_val = f.tell() // enc_block_size
def re_decrypt(data=None, re_enc_length=const.RE_ENC_LENGTH, seed=None, key=None, iv=None, init_val=None, debug=0): """ Re-decrypt data using the given parameters. :param data: data to re-decrypt :param re_enc_length: number of re-encrypted bytes :param seed: seed that randomly selected re-encrypted bytes :param key: symmetric key to decrypt randomly selected bytes :param iv: initialisation vector for the symmetric decryption :param init_val: initial value (ONLY FOR CTR MODE) :param debug: if 1, prints will be shown during execution; default 0, no prints are shown :return: re-decrypted data """ # Check if data is set if data is None: logging.error('re_decrypt data exception') # if debug: # ONLY USE FOR DEBUG # print('EXCEPTION in re_decrypt data') raise Exception # Check if key is set if key is None: logging.error('re_decrypt key exception') if debug: # ONLY USE FOR DEBUG print('EXCEPTION in re_decrypt key') raise Exception # Check if iv is set if iv is None: logging.error('re_decrypt iv exception') if debug: # ONLY USE FOR DEBUG print('EXCEPTION in re_decrypt iv') raise Exception # Check if the number of bytes to re-encrypt is lesser than the data length if re_enc_length < len(data): # Apply punctured encryption return remove_punctured_enc(data, re_enc_length, seed, key, iv, init_val, debug) else: # Re-encryption of the whole data sym_cipher = sym.get_cipher(AES.MODE_CTR, init_val, None, key, iv) return sym.decrypt(sym_cipher, data, debug)
def release(self, path, fh): #self.starting_time = time() * 1000.0 if self.debug: print("Releasing file ", path) # Create AES cipher # sym_cipher = AES.new(self.meta['sym_key'][:16], AES.MODE_CTR, nonce=self.meta['nonce']) # rewind file, adjust read_in_chucnk # print("Release: closing, removing and open again file ", self._full_path(path)) # os.close(fh) # print("close done") # os.remove(self._full_path(path)) # print("rm done") # fh = os.open(self._full_path(path), os.O_WRONLY | os.O_CREAT) # print("re-opening file") # fh = open(self._full_path(path), 'wb+') # print("re-opening file done") # print("seeking fh") # os.lseek(fh, 0, os.SEEK_SET) # Reset file pointer self.temp_fp.seek(0) if self.debug: print("Temporary file has size : ", self.temp_fp.seek(0, os.SEEK_END)) # self.temp_fp.seek(0) # Get re-encryptions parameters #re_enc_args = [] re_enc_ops_num = len(self.meta['re_encs']) #for re_enc_op in self.meta['re_encs']: # re_enc_args.append(self._create_re_enc_params(re_enc_op)) # Write only modified file chunks for chunk_num in self.file_written_chunks.keys(): if self.debug: print('CHUNK NUM # =', chunk_num, self.file_written_chunks[chunk_num]) if self.file_written_chunks[chunk_num]: if self.debug: print("release written chunk #", chunk_num) # print("release - read chunk #", chunk) # os.write(fh, sym_cipher.encrypt(chunk)) # fh.write(sym_cipher.encrypt(chunk)) # Read file chunk # Set file pointer to file chunk starting byte self.temp_fp.seek( int(chunk_num) * (self.meta['chunk_size'] - self.meta['random_size'])) # Read a file chunk from temporary file chunk = self.temp_fp.read(self.meta['chunk_size'] - self.meta['random_size']) if self.debug: print('in release, read %d bytes = %s' % (len(chunk), chunk)) # Compute offset on decoded file decoded_offset = self._get_decoded_offset(int(chunk_num)) # Compute initial value of cipher block counter init_val = self._get_cipher_initial_value(decoded_offset) # Create symmetric cipher with proper initial value sym_cipher = sym.get_cipher(AES.MODE_CTR, init_val, None, self.meta['sym_key'][:16], self.meta['nonce'], self.debug) #elapsed_time = (time() * 1000.0) - starting_time #elapsed_time_from_beginning = (time() * 1000.0) - self.starting_time #print('[{}] [{}] ** RELEASE - chunk {} after sym.get_cipher **'.format(elapsed_time_from_beginning, elapsed_time, chunk_num)) # Encrypt file chunk enc_chunk = sym.encrypt(sym_cipher, chunk, self.debug) # enc_chunk = chunk # Transform encrypted file chunk if self.debug: print("Applying AONT to newly encrypted chunk") # Get AONT parameters aont_args = self._create_aont_transf_params(len(enc_chunk)) # Apply AONT to the encrypted chunk transf_enc_chunk = aont.transform(enc_chunk, aont_args, self.debug) #elapsed_time = (time() * 1000.0) - starting_time #elapsed_time_from_beginning = (time() * 1000.0) - self.starting_time #print('[{}] [{}] ** RELEASE - chunk {} after aont.transform **'.format(elapsed_time_from_beginning, elapsed_time, chunk_num)) if self.debug: print("AONT successfully applied") # If previously applied, re-apply re-encryptions re_enc_transf_enc_chunk = transf_enc_chunk re_enc_init_val = self._get_cipher_initial_value( int(chunk_num) * self.meta['chunk_size']) if re_enc_ops_num: if self.debug: print("Re-applying re-encryptions to file chunk") for i in range(re_enc_ops_num): # Get re-encryptions parameters self.re_enc_args[i] = self._create_re_enc_params(i) # Add other params to re-encryption params self.re_enc_args[i]['init_val'] = re_enc_init_val # Re-encrypt transformed encrypted chunk re_enc_transf_enc_chunk = re_enc.apply_old_re_enc( re_enc_transf_enc_chunk, self.re_enc_args[i], self.debug) if self.debug: print("RE-ENCRYPTED CHUNK = (%d) %s" % (len(re_enc_transf_enc_chunk), re_enc_transf_enc_chunk)) print("Re-encryption successfully re-applied") #elapsed_time = (time() * 1000.0) - starting_time #elapsed_time_from_beginning = (time() * 1000.0) - self.starting_time #print('[{}] [{}] ** RELEASE - chunk {} after all re_enc_op **'.format(elapsed_time_from_beginning, elapsed_time, chunk_num)) if self.debug: print("Re-encryptions successfully re-applied") if self.debug: print('WRITING FH =', fh) print('CHUNK =', re_enc_transf_enc_chunk) # Compute offset on encoded file encoded_offset = self._get_encoded_offset(int(chunk_num)) # Write transformed encrypted chunk os.lseek(fh, encoded_offset, os.SEEK_SET) os.write(fh, re_enc_transf_enc_chunk) # os.write(fh, chunk + 32*b'a') if self.debug: print("chunk (%d) %s has been written on file %d" % (len(re_enc_transf_enc_chunk), re_enc_transf_enc_chunk, fh)) # with open(self._full_path(path), 'wb+') as enc_fp: # print("Release: file opened with fp ", enc_fp) # print("release: writing back from tempfile", self.temp_fp.file.name) # for chunk in self._read_in_chunks(self.temp_fp, self.CHUNK_SIZE): # print("release - read chunk" , chunk) # enc_fp.write(sym_cipher.encrypt(chunk)) if self.debug: print("Closing fs") os.close(fh) if self.debug: print("Closed") meta_directory = self.dirname + '/.abebox/' if not os.path.exists(meta_directory): os.makedirs(meta_directory) if self.debug: print("dumping meta on :", meta_directory + self.filename) if sum(self.file_written_chunks.values()): self._dump_meta(meta_directory + self.filename) #elapsed_time = (time() * 1000.0) - starting_time #elapsed_time_from_beginning = (time() * 1000.0) - self.starting_time #print('[{}] [{}] ** RELEASE END **'.format(elapsed_time_from_beginning, elapsed_time)) #print('RELEASE', time() * 1000.0 - self.starting_time) self.enc_fp.close() self.temp_fp.close() return
def write(self, path, buf, offset, fh): # self.enc_fp.close() # self.enc_fp = open(self._full_path(path), 'wb') full_path = self._full_path(path) if self.debug: print('PATH =', path) print('FULL PATH =', full_path) print('FH =', fh) print('OFFSET =', offset) print('BUFF =', buf) print('BUFF LEN =', len(buf)) # Compute file chunks involved in reading process starting_aont_chunk_num, ending_aont_chunk_num = self._get_aont_chunks_range( offset + len(buf), offset) # Check if those chunks have already been processed for chunk_num in range(starting_aont_chunk_num, ending_aont_chunk_num + 1): # Check if chunk is already in the list, otherwise add it and all previous ones not inside the list if str(chunk_num) not in self.file_written_chunks.keys(): if self.debug: print('Chunk not in already written list') for prev_chunk_num in range(chunk_num + 1): if str(prev_chunk_num ) not in self.file_written_chunks.keys(): self.file_written_chunks[str(prev_chunk_num)] = 1 self.file_read_chunks[str(prev_chunk_num)] = 1 if self.debug: print( 'Adding chunk #%d to the already written list with value %d' % (prev_chunk_num, self.file_written_chunks[ str(prev_chunk_num)])) # Check if chunk has already been processed elif not self.file_written_chunks[str(chunk_num)]: if self.debug: print('Chunk #%d needs to be processed' % chunk_num) # Compute offset on decoded file decoded_offset = self._get_decoded_offset(chunk_num) if self.debug: print('DECODED OFFSET =', decoded_offset) # Compute initial value of cipher block counter init_val = self._get_cipher_initial_value(decoded_offset) if self.debug: print('INITIAL VALUE =', init_val) # Create symmetric cipher with proper initial value sym_cipher = sym.get_cipher(AES.MODE_CTR, init_val, None, self.meta['sym_key'][:16], self.meta['nonce'], self.debug) # Get re-enc parameters for i in range(len(self.meta['re_encs'])): self.re_enc_args[i] = self._create_re_enc_params(i) # Anti-transform and decrypt chunk self._decode(full_path, chunk_num, decoded_offset, sym_cipher, self.re_enc_args) # Set relative array chunk position as read self.file_written_chunks[str(chunk_num)] = 1 self.file_read_chunks[str(chunk_num)] = 1 # Check if chunk is already in the list, otherwise add it and all previous ones not inside the list # if str(chunk_num) not in self.file_written_chunks.keys() or not self.file_written_chunks[str(chunk_num)]: # # if self.debug: # print('Chunk not in already written list') # print('Chunk #%d needs to be processed' % chunk_num) # # # Compute offset on decoded file # decoded_offset = self._get_decoded_offset(chunk_num) # # if self.debug: # print('DECODED OFFSET =', decoded_offset) # # # Compute initial value of cipher block counter # init_val = self._get_cipher_initial_value(decoded_offset) # # if self.debug: # print('INITIAL VALUE =', init_val) # # # Create symmetric cipher with proper initial value # sym_cipher = sym.get_cipher(AES.MODE_CTR, init_val, None, self.meta['sym_key'][:16], self.meta['nonce'], # self.debug) # # # Get re-encryptions parameters # for i in range(len(self.meta['re_encs'])): # self.re_enc_args[i] = self._create_re_enc_params(i) # # print('RE ENC ARGS', self.re_enc_args) # # # Anti-transform and decrypt chunk # self._decode(full_path, chunk_num, decoded_offset, sym_cipher, self.re_enc_args) # # print('WRITE: after decode') # # # Set relative array chunk position as read # self.file_written_chunks[str(chunk_num)] = 1 if self.debug: print("writing ", buf, " on ", path, " on tmp fs ", self.temp_fp) return super(Abebox, self).write(path, buf, offset, self.temp_fp.fileno())
def read(self, path, length, offset, fh): # self.enc_fp.close() # self.enc_fp = open(self._full_path(path), 'rb') full_path = self._full_path(path) if self.debug: print('READ ===============') print('FULL PATH =', full_path) print('PATH =', path) print('OFFSET =', offset) print('LENGTH =', length) # Compute length of the plaintext file and the related reading length file_size = os.path.getsize(full_path) temp_file_size = file_size - (math.ceil( file_size / self.meta['chunk_size']) * self.meta['random_size']) real_len = min(offset + length, temp_file_size) # Compute file chunks involved in reading process starting_aont_chunk_num, ending_aont_chunk_num = self._get_aont_chunks_range( real_len, offset) # Check if those chunks have already been processed for chunk_num in range(starting_aont_chunk_num, ending_aont_chunk_num + 1): # Check if chunk is already in the list, otherwise add it and all previous ones not inside the list if str(chunk_num) not in self.file_read_chunks.keys(): if self.debug: print('Chunk not in already read list') for prev_chunk_num in range(chunk_num + 1): if str(prev_chunk_num) not in self.file_read_chunks.keys(): self.file_read_chunks[str(prev_chunk_num)] = 0 if self.debug: print( 'Adding chunk #%d to the already read list with value %d' % (prev_chunk_num, self.file_read_chunks[str(prev_chunk_num)])) # Check if chunk has already been processed if not self.file_read_chunks[str(chunk_num)]: if self.debug: print('Chunk #%d needs to be processed' % chunk_num) # Compute offset on decoded file decoded_offset = self._get_decoded_offset(chunk_num) if self.debug: print('DECODED OFFSET =', decoded_offset) # Compute initial value of cipher block counter init_val = self._get_cipher_initial_value(decoded_offset) if self.debug: print('INITIAL VALUE =', init_val) # Create symmetric cipher with proper initial value sym_cipher = sym.get_cipher(AES.MODE_CTR, init_val, None, self.meta['sym_key'][:16], self.meta['nonce'], self.debug) # Get re-encryptions parameters for i in range(len(self.meta['re_encs'])): self.re_enc_args[i] = self._create_re_enc_params(i) # Anti-transform and decrypt chunk self._decode(full_path, chunk_num, decoded_offset, sym_cipher, self.re_enc_args) # Set relative array chunk position as read self.file_read_chunks[str(chunk_num)] = 1 if self.debug: print("reading ", length, " bytes on tmp fs ", self.temp_fp) #print('READ', time() * 1000.0 - self.starting_time) return super(Abebox, self).read(path, length, offset, self.temp_fp.fileno())
def _get_cipher_initial_value(self, offset): """ Return the initial value to set as cipher block counter for the given offset """ return offset // sym.get_cipher(AES.MODE_CTR, None, None, self.meta['sym_key'][:16], self.meta['nonce'])\ .block_size