class TestChunkEncryption(object): # Setup uncrypted_chunk = get_random_bytes(1013) 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())
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 = iota_utils.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 = iota_utils.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 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 = iota_utils.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
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 protocol chunk and doesn't get signed when doing local simulations data_chunk, signature = encryption.splitChunkAndSignature(chunk) address = iota_utils.trytesToBytes(next(address_gen)[:-1]) encryption.verifyChunk(data_chunk + address, signature, verifyingKey.hex()) assert True cleanup()
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 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 = iota_utils.trytesToBytes(next(address_gen)[:-1]) encryption.verifyChunk(datachunk + address, signature, verifyingKey.hex()) assert True
import iota from disperpy import encryption, fileprocessor, datamap, iota_utils ### IMPORTANT TEST PARAMETERS filename = "Install.txt" #must be in the same dir as this script node_url = "https://piota-node.com:443" ### ------------------------- # Generate handle and keys privateHandle = encryption.getPrivateHandle() _, verifyingKey = encryption.getKeypair(privateHandle) encryptionKey = encryption.getEncryptionKey(privateHandle) # Show handle on terminal print("Private Handle:", privateHandle.hex()) # Generate metadata chunk rawMetadataChunk = fileprocessor.makeMetadataChunk([filename], [False], verifyingKey) metadataChunkTuple = fileprocessor.squashAndSplitMetadataChunk( rawMetadataChunk) allChunksList = [b"Protocol Chunk Here"] + fileprocessor.prepareMetadataChunks( metadataChunkTuple, privateHandle) # Split, encrypt and sign the file as chunks offsetHash = bytes.fromhex(rawMetadataChunk[filename]["offsetHash"]) fileChunks = fileprocessor.fileToChunks(filename, privateHandle, offsetHash) allChunksList += fileChunks
def test_End_to_End(): filenameList, passwordFlagList, _ = generate_fake_input() privateHandle = encryption.getPrivateHandle() _, verifyingKey = encryption.getKeypair(privateHandle) encryptionKey = encryption.getEncryptionKey(privateHandle) rawMetadataChunk = fileprocessor.makeMetadataChunk(filenameList, passwordFlagList, verifyingKey) metadataChunkTuple = fileprocessor.squashAndSplitMetadataChunk( rawMetadataChunk) allChunksList = [b"Protocol Chunk Here" ] + fileprocessor.prepareMetadataChunks( metadataChunkTuple, privateHandle) offsetHash = bytes.fromhex(rawMetadataChunk[filenameList[0]]["offsetHash"]) fileChunks = fileprocessor.fileToChunks(filenameList[0], privateHandle, offsetHash) allChunksList += fileChunks """ Just use non-password files for now 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 """ ### Now the file is prepared to be sent. Every element in allChunksList is the message part of an iota tx in bytes #Initialize iota api and generate a random seed. api = iota.Iota(node_url) # Check node connection try: api.get_node_info() except: print("Connection to node failed:", node_url) exit else: print("Connection to node established:", node_url) # Send chunked file to the Tangle iota_utils.send_file(api, verifyingKey, allChunksList) # Wait 5s to give the node time to publish the tx time.sleep(5) # Retrieve transactions metadataJSON = iota_utils.retrieve_metadata(api, verifyingKey, encryptionKey) filename = list(metadataJSON.keys())[0] chunk_list = iota_utils.retrieve_file(api, metadataJSON[filename]) # Decrypt and rebuild file decrypted_filename = "decrypted_" + filename fileprocessor.chunksToFile(chunk_list, encryptionKey, decrypted_filename) # Asserts 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() """ Just a single file for now for filename in list(metadata.keys()): decrypted_filename = "decrypted_"+filename fileList = iota_utils.retrieve_file(api, metadata[filename]) passwordFlag = bool(metadata[filename]["password"]) if passwordFlag: fileprocessor.chunksToFile(fileList, encryptionKey, decrypted_filename, password=passwordDict[filename]) else: fileprocessor.chunksToFile(fileList, encryptionKey, decrypted_filename) print(read_binary_file(filename) == read_binary_file(decrypted_filename)) """ cleanup()