def prepareMetadataChunks(metadataTuple, privateHandle): """Encrypts, signs and adds the necessary flags to the metadata chunks. Arguments: metadataTuple {tuple of {bytes}} -- Output of squashAndSplitMetadataChunk(), where every element of the tuple is a metadata chunk. privateHandle {bytes} -- Self-explanatory. Returns: list -- List of metadata chunks - in order - ready to be encoded into trytes and sent in an iota tx. """ encryptionKey = encryption.getEncryptionKey(privateHandle) signingKey, verifyingKey = encryption.getKeypair(privateHandle) metadataChunkList = [] address_gen = datamap.createDatamapGenerator(verifyingKey, len(metadataTuple) + 2) next(address_gen) # first address is the treasure chunk's so we don't need it right now for i, metadataChunk in enumerate(metadataTuple): encryptedChunk, nonce, _ = encryption.encryptAES(metadataChunk, encryptionKey) preparedChunk = encryptedChunk+nonce address = encryption.trytesToBytes(next(address_gen)[:-1]) if i == 0: preparedChunk = addMetadataFlags(preparedChunk, len(metadataTuple)) signature = encryption.signChunk(preparedChunk + address, signingKey) finalMetadataChunk = b"".join([preparedChunk, signature]) metadataChunkList.append(finalMetadataChunk) return metadataChunkList
class TestChunkingFunctionsWithoutPassword(object): _path = "testfile_4" initialize_file(_path) privateHandle = encryption.getPrivateHandle() _, genesisHash = encryption.getKeypair(privateHandle) raw_file = read_binary_file(_path) chunkList = fileprocessor.fileToChunks(_path, privateHandle, genesisHash) def test_verify_chunk_signatures(self): _, verifyingKey = encryption.getKeypair(self.privateHandle) address_gen = datamap.createDatamapGenerator(verifyingKey, len(self.chunkList) + 1) for chunk in self.chunkList: datachunk, signature = encryption.splitChunkAndSignature(chunk) address = encryption.trytesToBytes(next(address_gen)[:-1]) encryption.verifyChunk(datachunk + address, signature, verifyingKey.hex()) assert True def test_decrypting_chunked_file(self): encryptionKey = encryption.getEncryptionKey(self.privateHandle) decrypted_path = self._path + "_decrypted" assert fileprocessor.chunksToFile(self.chunkList, encryptionKey, decrypted_path) decrypted_file = read_binary_file(decrypted_path) assert decrypted_file == self.raw_file #! Put functions here def test_clean_file(self): cleanup()
def test_End_to_End(): number_of_files = random.randint(4, 10) filenameList = ["testfile_" + str(i+1) for i in range(number_of_files)] initialize_multiple_files(filenameList) passwordFlagList = [bool(random.getrandbits(1)) for i in filenameList] passwordDict = {filename:"oyster" for filename, flag in zip(filenameList, passwordFlagList) if flag} privateHandle = encryption.getPrivateHandle() _, verifyingKey = encryption.getKeypair(privateHandle) encryptionKey = encryption.getEncryptionKey(privateHandle) rawMetadataChunk = fileprocessor.makeMetadataChunk(filenameList, passwordFlagList, verifyingKey) metadataChunkTuple = fileprocessor.squashAndSplitMetadataChunk(rawMetadataChunk) allChunksList = [b"TreasureChunkHere"] + fileprocessor.prepareMetadataChunks(metadataChunkTuple, privateHandle) for filename in filenameList: offsetHash = bytes.fromhex(rawMetadataChunk[filename]["offsetHash"]) if filename in passwordDict: # This statement is only True if filename has been added to the keys in the dict, which means it has a password fileChunks = fileprocessor.fileToChunks(filename, privateHandle, offsetHash, password=passwordDict[filename]) else: fileChunks = fileprocessor.fileToChunks(filename, privateHandle, offsetHash) allChunksList += fileChunks firstMetadataChunk = allChunksList[1] metadataChunkNumberFlag_bytes, _ = fileprocessor.stripMetadataFlags(firstMetadataChunk) numberFlag = int(np.fromstring(metadataChunkNumberFlag_bytes, dtype='uint8')) metadataArray = allChunksList[1:numberFlag+1] metadataJSON = fileprocessor.unpackMetadata(metadataArray, encryptionKey) decrypted_filenameList = list(metadataJSON.keys()) for filename in decrypted_filenameList: startIdx = metadataJSON[filename]["startIdx"] endIdx = startIdx + metadataJSON[filename]["chunkCount"] passwordFlag = bool(metadataJSON[filename]["password"]) decrypted_filename = "decrypted_"+filename fileList = allChunksList[startIdx:endIdx] if passwordFlag: fileprocessor.chunksToFile(fileList, encryptionKey, decrypted_filename, password=passwordDict[filename]) else: fileprocessor.chunksToFile(fileList, encryptionKey, decrypted_filename) assert read_binary_file(filename) == read_binary_file(decrypted_filename) assert SHA256.new(read_binary_file(filename)).digest() == SHA256.new(read_binary_file(decrypted_filename)).digest() address_gen = datamap.createDatamapGenerator(verifyingKey, None, 1) for chunk in allChunksList[1:]: #first chunk is the treasure chunk and doesn't get signed when doing local simulations data_chunk, signature = encryption.splitChunkAndSignature(chunk) address = encryption.trytesToBytes(next(address_gen)[:-1]) encryption.verifyChunk(data_chunk + address, signature, verifyingKey.hex()) assert True cleanup()
def test_verify_chunk_signatures(self): _, verifyingKey = encryption.getKeypair(self.privateHandle) address_gen = datamap.createDatamapGenerator(verifyingKey, len(self.chunkList) + 1) for chunk in self.chunkList: datachunk, signature = encryption.splitChunkAndSignature(chunk) address = encryption.trytesToBytes(next(address_gen)[:-1]) encryption.verifyChunk(datachunk + address, signature, verifyingKey.hex()) assert True
class TestEd25519Keys(object): signing_key, verifying_key = encryption.getKeypair( encryption.getPrivateHandle()) def test_keys_are_bytes(self): assert type(self.signing_key) is bytes assert type(self.verifying_key) is bytes def test_signing_key_is_32_bytes(self): assert len(self.signing_key) == 32
def fileToChunks(filename, privateHandle, startingHash, password = None): """Takes a filename and returns the file converted to rev2 compliant chunks, already signed. Arguments: filename {str} -- Self-explanatory. Also needs to include the extension. privateHandle {bytes} -- Bytestring to use as the private handle. Can and should be generated using encryption.getPrivateHandle(). startingHash {bytes} -- Hash on the main hashchain corresponding to the position of the first chunk of the file on the datamap. Keyword Arguments: password {str} -- Optional argument. Use it to password protect a particular file in a multi-file upload (default: {None}) Returns: list -- Every element is a chunk, in the same order as the bytes are read from the file. """ signingKey, _ = encryption.getKeypair(privateHandle) encryptionKey = encryption.getEncryptionKey(privateHandle) f = open(filename, mode="rb") filesize = os.path.getsize(filename) chunksize = 1013 numberOfChunks = math.ceil(filesize/chunksize) fileList = [] if password is not None: encryptionKey = encryption.getEncryptionKey(encryptionKey + password.encode("utf-8")) numberOfChunks = math.ceil((filesize - 997)/chunksize) + 1 chunksize = 997 address_gen = datamap.createDatamapGenerator(startingHash, numberOfChunks + 1) for i in range(0, numberOfChunks): chunk = f.read(chunksize) encrypted_chunk, nonce, tag = encryption.encryptAES(chunk, encryptionKey) address_trytes = next(address_gen)[:-1] #the real address is 81 chars, but the byte conversion only works with even numbers, so we use the first 80 chars of the address to sign and check address_bytes = encryption.trytesToBytes(address_trytes) if password is not None and i == 0: unsigned_chunk = b"".join([encrypted_chunk, nonce, tag]) chunksize = 1013 else: unsigned_chunk = b"".join([encrypted_chunk, nonce]) signature = encryption.signChunk(unsigned_chunk + address_bytes, signingKey) completed_chunk = unsigned_chunk + signature fileList.append(completed_chunk) f.close() return fileList
class TestChunkEncryption(object): #setup uncrypted_chunk = b"THIS COULD BE ANYTHING" private_handle = encryption.getPrivateHandle() signing_key, verifying_key = encryption.getKeypair(private_handle) encryption_key = encryption.getEncryptionKey(private_handle) #actually encrypt the chunk encrypted_chunk, nonce, tag = encryption.encryptAES( uncrypted_chunk, encryption_key) def test_length_encrypted_is_equal_to_length_uncrypted(self): assert len(self.encrypted_chunk) == len(self.uncrypted_chunk) def test_nonce_and_tag_lengths_are_16_bytes(self): assert len(self.nonce) == 16 and type(self.nonce) is bytes assert len(self.tag) == 16 and type(self.tag) is bytes def test_decryption_without_verification(self): completed_chunk = b''.join([self.encrypted_chunk, self.nonce]) decrypted_chunk = encryption.decryptAES(completed_chunk, self.encryption_key) assert decrypted_chunk == self.uncrypted_chunk def test_decryption_with_verification(self): completed_chunk = b''.join( [self.encrypted_chunk, self.nonce, self.tag]) decrypted_chunk = encryption.decryptAndVerifyAES( completed_chunk, self.encryption_key) assert decrypted_chunk == self.uncrypted_chunk def test_chunk_signing_and_validation(self): signature = encryption.signChunk(self.uncrypted_chunk, self.signing_key) assert len(signature) == 64 assert type(signature) is bytes encryption.verifyChunk(self.uncrypted_chunk, signature, self.verifying_key.hex())