diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml new file mode 100644 index 0000000..661fb3f --- /dev/null +++ b/.github/workflows/lint-and-test.yml @@ -0,0 +1,39 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20939ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,140 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +cloakify-env/* \ No newline at end of file diff --git a/ciphers/dessertsSwedishChef b/ciphers/dessertsSwedishChef index 250bd5e..ee756c4 100644 --- a/ciphers/dessertsSwedishChef +++ b/ciphers/dessertsSwedishChef @@ -25,12 +25,10 @@ creme-a soondee-a streoosel creem -boottercreem ceremel cherry sooger shurtceke-a -boottermeelk hoockleberry hezelnoot geenger @@ -39,7 +37,6 @@ tuffffee-a muoosse-a cunnulee sherbet -muoosse-a jelly cheeseceke-a chuculete-a @@ -66,7 +63,6 @@ coord lulleepup bunbun dunoot -coord ceke-a breettle-a croonch @@ -79,7 +75,6 @@ pooffffs soocker blooeberry toornufer -frusteeng nootmeg broolee-a lemun @@ -87,7 +82,6 @@ cunffecshun terreene-a respberry elmund -pooddeeng turte-a velnoot mereeunberry @@ -98,17 +92,13 @@ trooffffle-a cumpute-a leecurice-a cucunoot -pooddeeng peeneepple-a jem geengerbreed iggs -boottercreem flun zest peestechiu strevberreees -sooger -pestry écleur funeella diff --git a/ciphers/evadeAV b/ciphers/evadeAV index 42857d5..2a9c4f5 100644 --- a/ciphers/evadeAV +++ b/ciphers/evadeAV @@ -34,7 +34,6 @@ A 5 G M -0 h s o diff --git a/cloakify.py b/cloakify.py index 93edc32..b18f35b 100644 --- a/cloakify.py +++ b/cloakify.py @@ -40,46 +40,70 @@ # $ ./cloakify.py payload.txt ciphers/desserts > exfiltrate.txt # -import os, sys, getopt, base64 +import base64 +import os +import random +import sys array64 = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+=") -def Cloakify( arg1, arg2, arg3 ): +def Cloakify(payloadPath:str, cipherPath:str, outputPath:str="", password:str=None): + """Payload file's binary contents will be read and converted into base64. + Cipher file will be read into a list that will be used for the payload's obfuscation. + If an output path is defined the obfuscated content will be written to that otherwise, + it will print it out to the console. - payloadFile = open( arg1, 'rb' ) - payloadRaw = payloadFile.read() - payloadB64 = base64.encodestring( payloadRaw ) + Args: + payloadPath (str): Path to the file that will be encoded + cipherPath (str): Path to the file used as the base64 cipher + outputPath (str): Path to write out the obfuscated payload + """ try: - with open( arg2 ) as file: - cipherArray = file.readlines() - except: - print "" - print "!!! Oh noes! Problem reading cipher '", arg2, "'" - print "!!! Verify the location of the cipher file" - print "" + with open(payloadPath, 'rb') as payloadFile: + payloadRaw = payloadFile.read() + payloadB64 = base64.encodebytes(payloadRaw) + payloadB64 = payloadB64.decode("ascii").replace("\n", "") + except Exception as e: + print("Error reading payload file {}: {}".format(payloadPath, e)) - if ( arg3 != "" ): + payloadOrdering = None + if password: + random.seed(password) + # Get a list of each line number in the cloaked file + payloadOrdering = [i for i in range(len(payloadB64))] + # Shuffle the order of the lines + random.shuffle(payloadOrdering) + + try: + with open(cipherPath, encoding="utf-8") as file: + cipherArray = file.readlines() + except Exception as e: + print("Error reading cipher file {}: {}".format(cipherPath, e)) + + if outputPath: try: - with open( arg3, "w+" ) as outFile: - for char in payloadB64: - if char != '\n': - outFile.write( cipherArray[ array64.index(char) ] ) - except: - print "" - print "!!! Oh noes! Problem opening or writing to file '", arg3, "'" - print "" + with open(outputPath, "w+", encoding="utf-8") as outFile: + if payloadOrdering: + # Iterate through the randomized line order and write each line to the file + for randomLoc in payloadOrdering: + outFile.write(cipherArray[array64.index(payloadB64[randomLoc])]) + else: + for char in payloadB64: + outFile.write(cipherArray[array64.index(char)]) + except Exception as e: + print("Error writing to output file {}: {}".format(outputPath, e)) + else: for char in payloadB64: - if char != '\n': - print cipherArray[ array64.index(char) ], + print(cipherArray[array64.index(char)].strip()) if __name__ == "__main__": - if ( len(sys.argv) != 3 ): - print "usage: cloakify.py " - exit - + if len(sys.argv) == 3: + Cloakify( sys.argv[1], sys.argv[2]) + elif len(sys.argv) == 4: + Cloakify(sys.argv[1], sys.argv[2], sys.argv[3]) else: - Cloakify( sys.argv[1], sys.argv[2], "" ) - + print("usage: cloakify.py ") + exit(-1) diff --git a/cloakifyFactory.py b/cloakifyFactory.py index afff0bf..a5659a9 100644 --- a/cloakifyFactory.py +++ b/cloakifyFactory.py @@ -39,7 +39,32 @@ # $ ./cloakifyFactory.py # -import os, sys, getopt, random, base64, cloakify, decloakify +# Standard Python Libraries +import base64 +from getpass import getpass +import os +import random +import sys + +# Local Python Imports +import cloakify +import decloakify +from removeNoise import removeNoise +from noiseTools import * + +# Dictionary to map the file selection options to the corresponding folder +NOISE_GENERATOR = 0 +CIPHER = 1 +optionFolders = { + NOISE_GENERATOR: "noiseTools", + CIPHER: "ciphers" +} +noiseFuncs = { + "prependEmoji.py": prependEmoji, + "prependID.py": prependID, + "prependLatLonCoords.py": prependLatLonCoords, + "prependTimestamps.py": prependTimestamps +} # Load list of ciphers gCipherFiles = next(os.walk("./ciphers/"))[2] @@ -48,77 +73,80 @@ gNoiseScripts = [] for root, dirs, files in os.walk( "./noiseTools" ): for file in files: - if file.endswith('.py'): - gNoiseScripts.append( file ) - + if file.endswith('.py') and "__init__" not in file: + gNoiseScripts.append(os.path.basename(file)) + def CloakifyFile(): - print "" - print "==== Cloakify a File ====" - print "" - sourceFile = raw_input("Enter filename to cloak (e.g. ImADolphin.exe or /foo/bar.zip): ") - print "" - cloakedFile = raw_input("Save cloaked data to filename (default: 'tempList.txt'): ") + print("") + print("==== Cloakify a File ====") + print("") + sourceFile = input("Enter filename to cloak (e.g. ImADolphin.exe or /foo/bar.zip): ") + print("") + cloakedFile = input("Save cloaked data to filename (default: 'tempList.txt'): ") if cloakedFile == "": cloakedFile = "tempList.txt" - cipherNum = SelectCipher() + cipherPath = SelectFile(gCipherFiles, CIPHER) - noiseNum = -1 - choice = raw_input("Add noise to cloaked file? (y/n): ") + choice = input("Add noise to cloaked file? (y/n): ") + if choice == "y": + noisePath = SelectFile(gNoiseScripts, NOISE_GENERATOR) + else: + noisePath = None + + choice = input("Protect cloaked file with a password: (y/n): ") + password = None if choice == "y": - noiseNum = SelectNoise() + password = getPassword(True) - print "" - print "Creating cloaked file using cipher:", gCipherFiles[ cipherNum ] + print("") + print(f"Creating cloaked file using cipher: {cipherPath}") try: - cloakify.Cloakify( sourceFile, "ciphers/" + gCipherFiles[ cipherNum ], cloakedFile ) + cloakify.Cloakify(sourceFile, os.path.join(".", "ciphers", cipherPath), cloakedFile, password) except: - print "" - print "!!! Well that didn't go well. Verify that your cipher is in the 'ciphers/' subdirectory." - print "" + print("") + print("!!! Well that didn't go well. Verify that your cipher is in the 'ciphers/' subdirectory.") + print("") - if noiseNum >=0: - print "Adding noise to cloaked file using noise generator:", gNoiseScripts[ noiseNum ] + if noisePath: + print(f"Adding noise to cloaked file using noise generator: {noisePath}") try: - os.system( "noiseTools/%s %s" % ( gNoiseScripts[ noiseNum ], cloakedFile )) + noiseFuncs[noisePath](cloakedFile) except: - print "" - print "!!! Well that didn't go well. Verify that '", cloakedFile, "'" - print "!!! is in the current working directory or try again giving full filepath." - print "" + print("") + print("!!! Well that didn't go well. Verify that '", cloakedFile, "'") + print("!!! is in the current working directory or try again giving full filepath.") + print("") - print "" - print "Cloaked file saved to:", cloakedFile - print "" + print("") + print("Cloaked file saved to:", cloakedFile) + print("") - choice = raw_input( "Preview cloaked file? (y/n): " ) + choice = input( "Preview cloaked file? (y/n): " ) if choice == "y": - print "" - with open( cloakedFile ) as file: - cloakedPreview = file.readlines() - i = 0; - while ( i<20 ): - print cloakedPreview[ i ], - i = i+1 - print "" + print("") + with open(cloakedFile, encoding="utf-8") as file: + for _ in range(20): + print(file.readline()) + print("") - choice = raw_input( "Press return to continue... " ) + choice = input( "Press return to continue... " ) def DecloakifyFile(): decloakTempFile = "decloakTempFile.txt" - print "" - print "==== Decloakify a Cloaked File ====" - print "" - sourceFile = raw_input( "Enter filename to decloakify (e.g. /foo/bar/MyBoringList.txt): " ) - print "" - decloakedFile = raw_input( "Save decloaked data to filename (default: 'decloaked.file'): " ) - print "" + print("") + print("==== Decloakify a Cloaked File ====") + print("") + sourceFile = input("Enter filename to decloakify (e.g. /foo/bar/MyBoringList.txt): ") + print("") + decloakedFile = input("Save decloaked data to filename (default: 'decloaked.file'): ") + print("") if decloakedFile == "": decloakedFile = "decloaked.file" @@ -126,372 +154,330 @@ def DecloakifyFile(): # Reviewing the cloaked file within cloakifyFactory will save a little time for those who # forgot the format of the cloaked file and don't want to hop into a new window just to look - choice = raw_input( "Preview cloaked file? (y/n default=n): " ) + choice = input("Preview cloaked file? (y/n default=n): ") if choice == "y": - print "" - + print("") try: - with open( sourceFile ) as file: - cloakedPreview = file.readlines() - i = 0; - while ( i<20 ): - print cloakedPreview[ i ], - i = i+1 - print "" + with open(sourceFile, encoding="utf-8") as file: + for _ in range(20): + print(file.readline()) + print("") except: - print "" - print "!!! Well that didn't go well. Verify that '", sourceFile, "'" - print "!!! is in the current working directory or the filepath you gave." - print "" + print("") + print("!!! Well that didn't go well. Verify that '", sourceFile, "'") + print("!!! is in the current working directory or the filepath you gave.") + print("") - choice = raw_input("Was noise added to the cloaked file? (y/n default=n): ") + choice = input("Was noise added to the cloaked file? (y/n default=n): ") if choice == "y": - noiseNum = SelectNoise() + noisePath = SelectFile(gNoiseScripts, NOISE_GENERATOR) stripColumns = 2 # No upper bound checking, relies on SelectNoise() returning valid value, fix in next release - if noiseNum >= 0: - try: - # Remove Noise, overwrite the source file with the stripped contents - print "Removing noise from noise generator:", gNoiseScripts[ noiseNum ] - os.system( "./removeNoise.py %s %s %s" % ( stripColumns, sourceFile, decloakTempFile )) + try: + # Remove Noise, overwrite the source file with the stripped contents + print(f"Removing noise from cloaked file.") + removeNoise(stripColumns, sourceFile, decloakTempFile) - # Copy decloak temp filename to sourceFile so that Decloakify() gets the right filename - sourceFile = decloakTempFile - except: - print "!!! Error while removing noise from file. Was calling 'removeNoise.py'.\n" + # Copy decloak temp filename to sourceFile so that Decloakify() gets the right filename + sourceFile = decloakTempFile + except: + print("!!! Error while removing noise from file.\n") - cipherNum = SelectCipher() + cipherPath = SelectFile(gCipherFiles, CIPHER) - print "Decloaking file using cipher: ", gCipherFiles[ cipherNum ] + choice = input("Was a password used for this file? (y/n default=n): ") + + password = None + if choice == "y": + password = getPassword() + + print(f"Decloaking file using cipher: {cipherPath}") # Call Decloakify() try: - decloakify.Decloakify( sourceFile, "ciphers/" + gCipherFiles[ cipherNum ], decloakedFile ) + decloakify.Decloakify(sourceFile, os.path.join(".", "ciphers", cipherPath), decloakedFile, password) - print "" - print "Decloaked file", sourceFile, ", saved to", decloakedFile + print("") + print(f"Decloaked file {sourceFile}, saved to {decloakedFile}") except: - print "" - print "!!! Oh noes! Error decloaking file (did you select the same cipher it was cloaked with?)" - print "" + print("") + print("!!! Oh noes! Error decloaking file (did you select the same cipher it was cloaked with?)") + print("") try: - os.system( "rm -f %s" % ( decloakTempFile )) + os.remove(decloakTempFile) except: - print "" - print "!!! Oh noes! Error while deleting temporary file:", decloakTempFile - print "" + print("") + print("!!! Oh noes! Error while deleting temporary file:", decloakTempFile) + print("") - choice = raw_input("Press return to continue... ") + choice = input("Press return to continue... ") -def SelectCipher(): - print "" - print "Ciphers:" - print "" - cipherCount = 1 - for cipherName in gCipherFiles: - print cipherCount, "-", cipherName - cipherCount = cipherCount + 1 - print "" +def SelectFile(files:list, option:str): + print("") + print(f"{option}:") + print("") + for fileCount, fileName in enumerate(files, start=1): + print(f"{fileCount}-{fileName}") + print("") selection = -1 - while ( selection < 0 or selection > (cipherCount - 2)): + while not(0 <= selection <= (fileCount-1)): try: - cipherNum = raw_input( "Enter cipher #: " ) + fileNum = input(f"Enter {option} #: " ) - selection = int ( cipherNum ) - 1 - - if ( cipherNum == "" or selection < 0 or selection > (cipherCount - 1)): - print "Invalid cipher number, try again..." - selection = -1 - + selection = int (fileNum) - 1 + if not(0 <= selection <= (fileCount-1)): + print(f"Invalid {option} number, try again...") except ValueError: - print "Invalid cipher number, try again..." - print "" - return selection + print(f"Invalid {option} number, try again...") + print("") + if option == NOISE_GENERATOR: + print(gNoiseScripts[selection]) + print(noiseFuncs[gNoiseScripts[selection]]) + return files[selection] if option == CIPHER else gNoiseScripts[selection] -def BrowseCiphers(): - print "" - print "======== Preview Ciphers ========" - cipherNum = SelectCipher() - print "===== Cipher:", gCipherFiles[ cipherNum ], " =====" - print "" +def BrowseFiles(files:list, option:str): + print("") + print(f"======== Preview {option} ========") + filePath = SelectFile(files, option) + print(f"===== {option}: {filePath} =====") + print("") try: - with open( "ciphers/"+gCipherFiles[ cipherNum ] ) as cipherList: - arrayCipher = cipherList.read() - print( arrayCipher ) + if option == NOISE_GENERATOR: + print(f"Sample output of prepended strings, using noise generator: {filePath}\n") + noiseFuncs[filePath](None) + else: + filePath = os.path.join(".", optionFolders[option], filePath) + with open(filePath, encoding="utf-8") as file: + cipher = file.read() + print(cipher) except: - print "!!! Error opening cipher file.\n" - - choice = raw_input( "Press return to continue... " ) - - -def SelectNoise(): - print "" - print "Noise Generators:" - print "" - - noiseCount = 1 - for noiseName in gNoiseScripts: - print noiseCount, "-", noiseName - noiseCount = noiseCount + 1 - print "" + print(f"!!! Error opening {option} file.\n") - selection = -1 - noiseTotal = noiseCount - 2 - - while ( selection < 0 or selection > noiseTotal ): - try: - noiseNum = raw_input( "Enter noise generator #: " ) + choice = input( "Press return to continue... " ) - selection = int ( noiseNum ) - 1 - if ( selection == "" or selection < 0 or selection > noiseTotal ): - print "Invalid generator number, try again..." - selection = -1 +def getPassword(confirm=False): + password = getpass(prompt="Enter password: ") - except ValueError: - print "Invalid generator number, try again..." - - return selection + if confirm: + confirm_password = getpass(prompt="Confirm password: ") - -def BrowseNoise(): - print "" - print "======== Preview Noise Generators ========" - noiseNum = SelectNoise() - print "" - - # No upper bounds checking, relies on SelectNoise() to return a valid value, fix in next update - if noiseNum >= 0: - - try: - print "Sample output of prepended strings, using noise generator:", gNoiseScripts[ noiseNum ], "\n" - os.system( "noiseTools/%s" % ( gNoiseScripts[ noiseNum ] )) - except: - print "!!! Error while generating noise preview.\n" - - print "" - choice = raw_input( "Press return to continue... " ) + while password != confirm_password: + print("Passwords do not match...please retry") + password = getpass(prompt="Enter password: ") + confirm_password = getpass(prompt="Confirm password: ") + + return password def Help(): - print "" - print "===================== Using Cloakify Factory =====================" - print "" - print "For background and full tutorial, see the presentation slides at" - print "https://github.com/TryCatchHCF/Cloakify" - print "" - print "WHAT IT DOES:" - print "" - print "Cloakify Factory transforms any filetype (e.g. .zip, .exe, .xls, etc.) into" - print "a list of harmless-looking strings. This lets you hide the file in plain sight," - print "and transfer the file without triggering alerts. The fancy term for this is" - print "'text-based steganography', hiding data by making it look like other data." - print "" - print "For example, you can transform a .zip file into a list made of Pokemon creatures" - print "or Top 100 Websites. You then transfer the cloaked file however you choose," - print "and then decloak the exfiltrated file back into its original form. The ciphers" - print "are designed to appear like harmless / ignorable lists, though some (like MD5" - print "password hashes) are specifically meant as distracting bait." - print "" - print "BASIC USE:" - print "" - print "Cloakify Factory will guide you through each step. Follow the prompts and" - print "it will show you the way." - print "" - print "Cloakify a Payload:" - print "- Select 'Cloakify a File' (any filetype will work - zip, binaries, etc.)" - print "- Enter filename that you want to Cloakify (can be filename or filepath)" - print "- Enter filename that you want to save the cloaked file as" - print "- Select the cipher you want to use" - print "- Select a Noise Generator if desired" - print "- Preview cloaked file if you want to check the results" - print "- Transfer cloaked file via whatever method you prefer" - print "" - print "Decloakify a Payload:" - print "- Receive cloaked file via whatever method you prefer" - print "- Select 'Decloakify a File'" - print "- Enter filename of cloaked file (can be filename or filepath)" - print "- Enter filename to save decloaked file to" - print "- Preview cloaked file to review which Noise Generator and Cipher you used" - print "- If Noise Generator was used, select matching Generator to remove noise" - print "- Select the cipher used to cloak the file" - print "- Your decloaked file is ready to go!" - print "" - print "You can browse the ciphers and outputs of the Noise Generators to get" - print "an idea of how to cloak files for your own needs." - print "" - print "Anyone using the same cipher can decloak your cloaked file, but you can" - print "randomize (scramble) the preinstalled ciphers. See 'randomizeCipherExample.txt'" - print "in the Cloakify directory for an example." - print "" - print "NOTE: Cloakify is not a secure encryption scheme. It's vulnerable to" - print "frequency analysis attacks. Use the 'Add Noise' option to add entropy when" - print "cloaking a payload to help degrade frequency analysis attacks. Be sure to" - print "encrypt the file prior to cloaking if secrecy is needed." + print("") + print("===================== Using Cloakify Factory =====================") + print("") + print("For background and full tutorial, see the presentation slides at") + print("https://github.com/TryCatchHCF/Cloakify") + print("") + print("WHAT IT DOES:") + print("") + print("Cloakify Factory transforms any filetype (e.g. .zip, .exe, .xls, etc.) into") + print("a list of harmless-looking strings. This lets you hide the file in plain sight,") + print("and transfer the file without triggering alerts. The fancy term for this is") + print("'text-based steganography', hiding data by making it look like other data.") + print("") + print("For example, you can transform a .zip file into a list made of Pokemon creatures") + print("or Top 100 Websites. You then transfer the cloaked file however you choose,") + print("and then decloak the exfiltrated file back into its original form. The ciphers") + print("are designed to appear like harmless / ignorable lists, though some (like MD5") + print("password hashes) are specifically meant as distracting bait.") + print("") + print("BASIC USE:") + print("") + print("Cloakify Factory will guide you through each step. Follow the prompts and") + print("it will show you the way.") + print("") + print("Cloakify a Payload:") + print("- Select 'Cloakify a File' (any filetype will work - zip, binaries, etc.)") + print("- Enter filename that you want to Cloakify (can be filename or filepath)") + print("- Enter filename that you want to save the cloaked file as") + print("- Select the cipher you want to use") + print("- Select a Noise Generator if desired") + print("- Preview cloaked file if you want to check the results") + print("- Transfer cloaked file via whatever method you prefer") + print("") + print("Decloakify a Payload:") + print("- Receive cloaked file via whatever method you prefer") + print("- Select 'Decloakify a File'") + print("- Enter filename of cloaked file (can be filename or filepath)") + print("- Enter filename to save decloaked file to") + print("- Preview cloaked file to review which Noise Generator and Cipher you used") + print("- If Noise Generator was used, select matching Generator to remove noise") + print("- Select the cipher used to cloak the file") + print("- Your decloaked file is ready to go!") + print("") + print("You can browse the ciphers and outputs of the Noise Generators to get") + print("an idea of how to cloak files for your own needs.") + print("") + print("Anyone using the same cipher can decloak your cloaked file, but you can") + print("randomize (scramble) the preinstalled ciphers. See 'randomizeCipherExample.txt'") + print("in the Cloakify directory for an example.") + print("") + print("NOTE: Cloakify is not a secure encryption scheme. It's vulnerable to") + print("frequency analysis attacks. Use the 'Add Noise' option to add entropy when") + print("cloaking a payload to help degrade frequency analysis attacks. Be sure to") + print("encrypt the file prior to cloaking if secrecy is needed.") def About(): - print "" - print "===================== About Cloakify Factory =====================" - print "" - print " \"Hide & Exfiltrate Any Filetype in Plain Sight\"" - print "" - print " Written by TryCatchHCF" - print " https://github.com/TryCatchHCF/Cloakify" - print "" - print "Data Exfiltration In Plain Sight; Evade DLP/MLS Devices; Social Engineering" - print "of Analysts; Defeat Data Whitelisting Controls; Evade AV Detection. Text-based" - print "steganography usings lists. Convert any file type (e.g. executables, Office," - print "Zip, images) into a list of everyday strings. Very simple tools, powerful" - print "concept, limited only by your imagination." - print "" - print "Cloakify Factory uses Python scripts to cloak / uncloak any file type using" - print "list-based ciphers (text-based steganography). Allows you to transfer data" - print "across a secure network's perimeter without triggering alerts, defeating data" - print "whitelisting controls, and derailing analyst's review via social engineering" - print "attacks against their workflows. As a bonus, cloaked files defeat signature-" - print "based malware detection tools." - print "" - print "NOTE: Cloakify is not a secure encryption scheme. It's vulnerable to" - print "frequency analysis attacks. Use the 'Add Noise' option to add entropy when" - print "cloaking a payload to help degrade frequency analysis attacks. Be sure to" - print "encrypt the file prior to cloaking if secrecy is needed." - print "" - print "DETAILS:" - print "" - print "Cloakify first Base64-encodes the payload, then applies a cipher to generate" - print "a list of strings that encodes the Base64 payload. Once exfiltrated, use" - print "Decloakify with the same cipher to decode the payload. The ciphers are" - print "designed to appear like harmless / ingorable lists, though some (like MD5" - print "password hashes) are specifically meant as distracting bait." - print "" - print "Prepackaged ciphers include lists of:" - print "" - print "- Amphibians (scientific names)" - print "- Belgian Beers" - print "- Desserts in English, Arabic, Thai, Russian, Hindi, Chinese, Persian, and" - print " Muppet (Swedish Chef)" - print "- Emoji" - print "- evadeAV (smallest cipher space, x3 payload size)" - print "- GeoCoords World Capitals (Lat/Lon)" - print "- GeoCaching Coordinates (w/ Site Names)" - print "- IPv4 Addresses of Popular Websites" - print "- MD5 Password Hashes" - print "- PokemonGo Monsters" - print "- Top 100 Websites" - print "- Ski Resorts" - print "- Status Codes (generic)" - print "- Star Trek characters" - print "- World Beaches" - print "- World Cup Teams" - print "" - print "Prepackaged scripts for adding noise / entropy to your cloaked payloads:" - print "" - print "- prependEmoji.py: Adds a randomized emoji to each line" - print "- prependID.py: Adds a randomized ID tag to each line" - print "- prependLatLonCoords.py: Adds random LatLong coordinates to each line" - print "- prependTimestamps.py: Adds timestamps (log file style) to each line" - print "" - print "CREATE YOUR OWN CIPHERS:" - print "" - print "Cloakify Factory is at its best when you're using your own customized" - print "ciphers. The default ciphers may work for most needs, but in a unique" - print "exfiltration scenario you may need to build your own." - print "" - print "Creating a Cipher:" - print "" - print "- Create a list of at least 66 unique words/phrases/symbols (Unicode accepted)" - print "- Randomize the list order" - print "- Remove all duplicate entries and all blank lines" - print "- Place cipher file in the 'ciphers/' subdirectory" - print "- Re-run Cloakify Factory to automatically load the new cipher" - print "- Test cloaking / decloaking with new cipher before using operationally" - print "" + print("") + print("===================== About Cloakify Factory =====================") + print("") + print(" \"Hide & Exfiltrate Any Filetype in Plain Sight\"") + print("") + print(" Written by TryCatchHCF") + print(" https://github.com/TryCatchHCF/Cloakify") + print("") + print("Data Exfiltration In Plain Sight; Evade DLP/MLS Devices; Social Engineering") + print("of Analysts; Defeat Data Whitelisting Controls; Evade AV Detection. Text-based") + print("steganography usings lists. Convert any file type (e.g. executables, Office,") + print("Zip, images) into a list of everyday strings. Very simple tools, powerful") + print("concept, limited only by your imagination.") + print("") + print("Cloakify Factory uses Python scripts to cloak / uncloak any file type using") + print("list-based ciphers (text-based steganography). Allows you to transfer data") + print("across a secure network's perimeter without triggering alerts, defeating data") + print("whitelisting controls, and derailing analyst's review via social engineering") + print("attacks against their workflows. As a bonus, cloaked files defeat signature-") + print("based malware detection tools.") + print("") + print("NOTE: Cloakify is not a secure encryption scheme. It's vulnerable to") + print("frequency analysis attacks. Use the 'Add Noise' option to add entropy when") + print("cloaking a payload to help degrade frequency analysis attacks. Be sure to") + print("encrypt the file prior to cloaking if secrecy is needed.") + print("") + print("DETAILS:") + print("") + print("Cloakify first Base64-encodes the payload, then applies a cipher to generate") + print("a list of strings that encodes the Base64 payload. Once exfiltrated, use") + print("Decloakify with the same cipher to decode the payload. The ciphers are") + print("designed to appear like harmless / ingorable lists, though some (like MD5") + print("password hashes) are specifically meant as distracting bait.") + print("") + print("Prepackaged ciphers include lists of:") + print("") + print("- Amphibians (scientific names)") + print("- Belgian Beers") + print("- Desserts in English, Arabic, Thai, Russian, Hindi, Chinese, Persian, and") + print(" Muppet (Swedish Chef)") + print("- Emoji") + print("- evadeAV (smallest cipher space, x3 payload size)") + print("- GeoCoords World Capitals (Lat/Lon)") + print("- GeoCaching Coordinates (w/ Site Names)") + print("- IPv4 Addresses of Popular Websites") + print("- MD5 Password Hashes") + print("- PokemonGo Monsters") + print("- Top 100 Websites") + print("- Ski Resorts") + print("- Status Codes (generic)") + print("- Star Trek characters") + print("- World Beaches") + print("- World Cup Teams") + print("") + print("Prepackaged scripts for adding noise / entropy to your cloaked payloads:") + print("") + print("- prependEmoji.py: Adds a randomized emoji to each line") + print("- prependID.py: Adds a randomized ID tag to each line") + print("- prependLatLonCoords.py: Adds random LatLong coordinates to each line") + print("- prependTimestamps.py: Adds timestamps (log file style) to each line") + print("") + print("CREATE YOUR OWN CIPHERS:") + print("") + print("Cloakify Factory is at its best when you're using your own customized") + print("ciphers. The default ciphers may work for most needs, but in a unique") + print("exfiltration scenario you may need to build your own.") + print("") + print("Creating a Cipher:") + print("") + print("- Create a list of at least 66 unique words/phrases/symbols (Unicode accepted)") + print("- Randomize the list order") + print("- Remove all duplicate entries and all blank lines") + print("- Place cipher file in the 'ciphers/' subdirectory") + print("- Re-run Cloakify Factory to automatically load the new cipher") + print("- Test cloaking / decloaking with new cipher before using operationally") + print("") def MainMenu(): - print " ____ _ _ _ __ ______ _ " - print " / __ \ | | | |_|/ _| | ___| | | " - print "| / \/ | ___ __ _| | ___| |_ _ _ | |_ __ _ ___| |_ ___ _ __ _ _ " - print "| | | |/ _ \ / _` | |/ / | _| | | | | _/ _` |/ __| __/ _ \| '__| | | |" - print "| \__/\ | |_| | |_| | <| | | | |_| | | || |_| | |__| || |_| | | | |_| |" - print " \____/_|\___/ \__,_|_|\_\_|_| \__, | \_| \__,_|\___|\__\___/|_| \__, |" - print " __/ | __/ |" - print " |___/ |___/ " - print "" - print " \"Hide & Exfiltrate Any Filetype in Plain Sight\"" - print "" - print " Written by TryCatchHCF" - print " https://github.com/TryCatchHCF" - print " (\~---." - print " / (\-`-/)" - print " ( ' ' ) data.xls image.jpg \\ List of emoji, IP addresses," - print " \ ( \_Y_/\\ ImADolphin.exe backup.zip --> sports teams, desserts," - print " \"\"\ \___// LoadMe.war file.doc / beers, anything you imagine" - print " `w \"" + print(" ____ _ _ _ __ ______ _ ") + print(" / __ \ | | | |_|/ _| | ___| | | ") + print("| / \/ | ___ __ _| | ___| |_ _ _ | |_ __ _ ___| |_ ___ _ __ _ _ ") + print("| | | |/ _ \ / _` | |/ / | _| | | | | _/ _` |/ __| __/ _ \| '__| | | |") + print("| \__/\ | |_| | |_| | <| | | | |_| | | || |_| | |__| || |_| | | | |_| |") + print(" \____/_|\___/ \__,_|_|\_\_|_| \__, | \_| \__,_|\___|\__\___/|_| \__, |") + print(" __/ | __/ |") + print(" |___/ |___/ ") + print("") + print(" \"Hide & Exfiltrate Any Filetype in Plain Sight\"") + print("") + print(" Written by TryCatchHCF") + print(" https://github.com/TryCatchHCF") + print(" (\~---.") + print(" / (\-`-/)") + print(" ( ' ' ) data.xls image.jpg \\ List of emoji, IP addresses,") + print(" \ ( \_Y_/\\ ImADolphin.exe backup.zip --> sports teams, desserts,") + print(" \"\"\ \___// LoadMe.war file.doc / beers, anything you imagine") + print(" `w \"") selectionErrorMsg = "1-7 are your options. Try again." - notDone = 1 - - while ( notDone ): - - print "" - print "==== Cloakify Factory Main Menu ====" - print "" - print "1) Cloakify a File" - print "2) Decloakify a File" - print "3) Browse Ciphers" - print "4) Browse Noise Generators" - print "5) Help / Basic Usage" - print "6) About Cloakify Factory" - print "7) Exit" - print "" - - invalidSelection = 1 - - while ( invalidSelection ): - try: - choice = int( raw_input( "Selection: " )) - - if ( choice > 0 and choice < 8 ): - invalidSelection = 0 - else: - print selectionErrorMsg - - except ValueError: - print selectionErrorMsg - - if choice == 1: - CloakifyFile() - elif choice == 2: - DecloakifyFile() - elif choice == 3: - BrowseCiphers() - elif choice == 4: - BrowseNoise() - elif choice == 5: - Help() - elif choice == 6: - About() - elif choice == 7: - notDone = 0 - else: - print selectionErrorMsg + + while True: + try: + print("") + print("==== Cloakify Factory Main Menu ====") + print("") + print("1) Cloakify a File") + print("2) Decloakify a File") + print("3) Browse Ciphers") + print("4) Browse Noise Generators") + print("5) Help / Basic Usage") + print("6) About Cloakify Factory") + print("7) Exit") + print("") + + choice = int(input( "Selection: ")) + + if choice == 1: + CloakifyFile() + elif choice == 2: + DecloakifyFile() + elif choice == 3: + BrowseFiles(gCipherFiles, CIPHER) + elif choice == 4: + BrowseFiles(gNoiseScripts, NOISE_GENERATOR) + elif choice == 5: + Help() + elif choice == 6: + About() + elif choice == 7: + break + else: + print(selectionErrorMsg) + except ValueError: + print(selectionErrorMsg) byeArray = ("Bye!", "Ciao!", "Adios!", "Aloha!", "Hei hei!", "Bless bless!", "Hej da!", "Tschuss!", "Adieu!", "Cheers!") - print "" - print random.choice( byeArray ) - print "" + print("") + print(random.choice( byeArray )) + print("") -# ============================== Main Loop ================================ -# -MainMenu() + +if __name__ == "__main__": + MainMenu() diff --git a/decloakify.py b/decloakify.py index 8cb895d..2584917 100644 --- a/decloakify.py +++ b/decloakify.py @@ -26,36 +26,57 @@ # # $ ./decloakify.py cloakedPayload.txt ciphers/desserts.ciph - -import sys, getopt, base64 +import base64 +import random +import sys array64 = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+=") -def Decloakify( arg1, arg2, arg3 ): - - with open( arg1 ) as file: - listExfiltrated = file.readlines() - - with open( arg2) as file: - arrayCipher = file.readlines() +def Decloakify(cloakedPath:str, cipherPath:str, outputPath:str="", password:str=""): + """Cipher file will be read into a list that will be used for the payload's deobfuscation. + Cloaked file's contents will be read in line by line mapping the line to a base64 character. + If an output path is defined the base64 contents will be decoded and written to the output + file otherwise it will be written to the console. + Args: + cloakedPath (str): Path to the file that is encoded + cipherPath (str): Path to the file used as the base64 cipher + outputPath (str): Path to write out the decoded + """ + with open(cipherPath, encoding="utf-8") as file: + arrayCipher = file.readlines() + clear64 = "" + with open(cloakedPath, encoding="utf-8") as file: + if password: + random.seed(password) + lines = file.readlines() + # Get a list of each line number in the cloaked file + decodeOrdering = [i for i in range(len(lines))] + # Shuffle the order of the lines to what they were during encoding + random.shuffle(decodeOrdering) + # Map the index of the original payload to the index in the cloaked file + decodeOrdering = {k: v for v, k in enumerate(decodeOrdering)} + # Iterate through the proper line order and reconstruct the unshuffled base64 payload + for i in range(len(lines)): + clear64 += array64[arrayCipher.index(lines[decodeOrdering[i]])] + else: + for line in file: + clear64 += array64[arrayCipher.index(line)] - for word in listExfiltrated: - clear64 += array64[ arrayCipher.index(word) ] - - if ( arg3 != "" ): - with open( arg3, "w" ) as outFile: - outFile.write( base64.b64decode( clear64 )) - + payload = base64.b64decode(clear64) + if outputPath: + with open(outputPath, "wb") as outFile: + outFile.write(payload) else: - print base64.b64decode( clear64 ), + print(payload) if __name__ == "__main__": - if (len(sys.argv) != 3): - print "usage: decloakify.py " - exit + if len(sys.argv) == 3: + Decloakify(sys.argv[1], sys.argv[2]) + elif len(sys.argv) == 4: + Decloakify(sys.argv[1], sys.argv[2], sys.argv[3]) else: - Decloakify( sys.argv[1], sys.argv[2], "" ) - + print("usage: decloakify.py ") + exit(-1) diff --git a/noiseTools/__init__.py b/noiseTools/__init__.py new file mode 100644 index 0000000..74da5bf --- /dev/null +++ b/noiseTools/__init__.py @@ -0,0 +1,4 @@ +from .prependEmoji import prependEmoji +from .prependID import prependID +from .prependLatLonCoords import prependLatLonCoords +from .prependTimestamps import prependTimestamps \ No newline at end of file diff --git a/noiseTools/prependEmoji.py b/noiseTools/prependEmoji.py index d19b89d..5c301c2 100644 --- a/noiseTools/prependEmoji.py +++ b/noiseTools/prependEmoji.py @@ -22,32 +22,32 @@ import os, sys, getopt, random -if ( len(sys.argv) > 2 ): - print "usage: prependEmoji.py " - print - print "Strip leading emoji prior to decloaking the cloaked file." - print - exit -else: - - # FIX PENDING - Relative pathing is for cloakifyFactory.py - with open( "ciphers/emoji" ) as file: +def prependEmoji(cloakedFilename, emojiCipherPath="ciphers/emoji"): + with open(emojiCipherPath, encoding="utf-8") as file: arrayCipher = file.read().splitlines() - if ( len(sys.argv) == 1): - # Generate sample of noise generator output - i = 0 - while ( i<20 ): - print( random.choice(arrayCipher) + "\n" ), - i = i+1 - - else: - # Prepend noise generator output to file - with open ( sys.argv[1] ) as file: + if cloakedFilename: + # Prepend noise generator output to file + with open(cloakedFilename, encoding="utf-8") as file: cloakedFile = file.readlines() - with open ( sys.argv[1], "w" ) as file: - for i in cloakedFile: - file.write( random.choice(arrayCipher) + " " + i ), + with open(cloakedFilename, "w", encoding="utf-8") as file: + for line in cloakedFile: + file.write(f"{random.choice(arrayCipher)} {line}"), + else: + # Generate sample of noise generator output + for _ in range(20): + print(random.choice(arrayCipher)) + + +if __name__ == "__main__": + if len(sys.argv) == 2: + emojiCipherPath = os.path.abspath(os.path.join("../", "ciphers", "emoji")) + prependEmoji(sys.argv[1], emojiCipherPath) + else: + print("usage: prependEmoji.py ") + print() + print("Strip leading emoji prior to decloaking the cloaked file.") + print() diff --git a/noiseTools/prependID.py b/noiseTools/prependID.py index 9179ca4..91e752c 100644 --- a/noiseTools/prependID.py +++ b/noiseTools/prependID.py @@ -27,37 +27,28 @@ arrayCode = list ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") -if ( len(sys.argv) > 2 ): - print "usage: prepend4digitID.py " - print - print "Strip tag prior to decloaking the cloaked file." - print - exit -else: - - if ( len(sys.argv) == 1): - - i = 0 - while ( i<20 ): +def prependID(cloakedFilename:str): + if cloakedFilename: + # Prepend noise generator output to file + with open(cloakedFilename, encoding="utf-8") as file: + cloakedFile = file.readlines() + + with open(cloakedFilename, "w", encoding="utf-8") as file: + for line in cloakedFile: + file.write(f"Tag: {random.choice(arrayCode)}{random.choice(arrayCode)}{random.choice(arrayCode)}{random.choice(arrayCode)} {line}"), + else: + # Generate sample of noise generator output + for _ in range(20): + print(f"Tag: {random.choice(arrayCode)}{random.choice(arrayCode)}{random.choice(arrayCode)}{random.choice(arrayCode)}") - print( "Tag: " + - random.choice(arrayCode) + - random.choice(arrayCode) + - random.choice(arrayCode) + - random.choice(arrayCode)) - i = i+1 +if __name__ == "__main__": + if len(sys.argv) == 2: + prependID(sys.argv[1]) else: - with open( sys.argv[1] ) as file: - exfilFile = file.read().splitlines() + print("usage: prependID.py ") + print() + print("Strip leading ID prior to decloaking the cloaked file.") + print() - with open( sys.argv[1], "w" ) as file: - for i in exfilFile: - if i != '\n': - file.write( "Tag: " + - random.choice(arrayCode) + - random.choice(arrayCode) + - random.choice(arrayCode) + - random.choice(arrayCode) + - " " + i + "\n" ) diff --git a/noiseTools/prependLatLonCoords.py b/noiseTools/prependLatLonCoords.py index 52de70c..b98b58f 100644 --- a/noiseTools/prependLatLonCoords.py +++ b/noiseTools/prependLatLonCoords.py @@ -27,43 +27,42 @@ import os, sys, getopt, random -if ( len(sys.argv) > 2 ): - print "usage: prependLatLonCoords.py " - print - print "Strip the coordinates prior to decloaking the cloaked file." - print - exit +# Geocoords for Denver, USA. Replace with whatever is best for your needs +BASE_LAT = 39.739236 +BASE_LON = -104.990251 -else: - # Geocoords for Denver, USA. Replace with whatever is best for your needs - baseLat = 39.739236 - baseLon = -104.990251 +# AT LATITUDE 40 DEGREES (NORTH OR SOUTH) +# One minute of latitude = 1.85 km or 1.15 mi +# One minute of longitude = 1.42 km or 0.88 mi +SIZE_LAT = 0.0002 +SIZE_LON = 0.0002 - # AT LATITUDE 40 DEGREES (NORTH OR SOUTH) - # One minute of latitude = 1.85 km or 1.15 mi - # One minute of longitude = 1.42 km or 0.88 mi - - sizeLat = 0.0002 - sizeLon = 0.0002 - - if ( len(sys.argv) == 1): - i = 0 - while (i<20): - lat = baseLat + (sizeLat * random.randint(0,2000)) - lon = baseLon + (sizeLon * random.randint(0,2000)) - print( str( lat ) + " " + str( lon )) - i = i+1 +def prependLatLonCoords(cloakedFilename:str): + if cloakedFilename: + # Prepend noise generator output to file + with open(cloakedFilename, encoding="utf-8") as file: + cloakedFile = file.readlines() + + with open(cloakedFilename, "w", encoding="utf-8") as file: + for line in cloakedFile: + lat = BASE_LAT + (SIZE_LAT * random.randint(0,2000)) + lon = BASE_LON + (SIZE_LON * random.randint(0,2000)) + file.write(f"{lat} {lon} {line}"), else: - with open( sys.argv[1], "r" ) as file: - cloakedFile = file.readlines() + # Generate sample of noise generator output + for _ in range(20): + lat = BASE_LAT + (SIZE_LAT * random.randint(0,2000)) + lon = BASE_LON + (SIZE_LON * random.randint(0,2000)) + print(f"{lat} {lon}") - with open( sys.argv[1], "w" ) as file: - # Generate a random with enough range to look good, scale with vals above - for i in cloakedFile: - lat = baseLat + (sizeLat * random.randint(0,2000)) - lon = baseLon + (sizeLon * random.randint(0,2000)) - - file.write( str( lat ) + " " + str( lon ) + " " + i ) +if __name__ == "__main__": + if len(sys.argv) == 2: + prependLatLonCoords(sys.argv[1]) + else: + print("usage: prependLatLonCoords.py ") + print() + print("Strip leading coordinates prior to decloaking the cloaked file.") + print() diff --git a/noiseTools/prependTimestamps.py b/noiseTools/prependTimestamps.py index c966ddb..59073d7 100644 --- a/noiseTools/prependTimestamps.py +++ b/noiseTools/prependTimestamps.py @@ -27,50 +27,46 @@ import os, sys, getopt, datetime, random -minDaysBack = 1011 -maxDaysBack = 1104 +MIN_DAYS_BACK = 1011 +MAX_DAYS_BACK = 1104 -minSecondsStep = 0 -maxSecondsStep = 664 +MIN_SECONDS_STEP = 0 +MAX_SECONDS_STEP = 664 -if ( len(sys.argv) > 2 ): - print "usage: prependTimestamps.py " - print - print "Strip timestamps prior to decloaking the cloaked file." - print - exit -else: - # Set the start date back around 2 years from today (give or take) for entropy range - # Randomize a little for each run to avoid a pattern in the first line of each file +TODAY = datetime.date.today() +START_DATE = TODAY - datetime.timedelta(days=random.randint(MIN_DAYS_BACK, MAX_DAYS_BACK)) +STEP = datetime.timedelta(seconds=random.randint(MIN_SECONDS_STEP, MAX_SECONDS_STEP)) +T = datetime.time( random.randint(0,23),random.randint(0,59),random.randint(0,59) ) - today = datetime.date.today() - startDate = today - datetime.timedelta(days=random.randint(minDaysBack,maxDaysBack)) - step = datetime.timedelta(seconds=random.randint(minSecondsStep,maxSecondsStep)) - t = datetime.time( random.randint(0,23),random.randint(0,59),random.randint(0,59) ) - fakeDate = datetime.datetime.combine( startDate, t ) - if ( len(sys.argv) == 1 ): - # Generate sample of noise generator output - i = 0; - while (i<20): - print( str( fakeDate )) - step = datetime.timedelta(seconds=random.randint(minSecondsStep,maxSecondsStep)) +def prependTimestamps(cloakedFilename:str): + fakeDate = datetime.datetime.combine(START_DATE, T) + if cloakedFilename: + # Prepend noise generator output to file + with open(cloakedFilename, encoding="utf-8") as file: + cloakedFile = file.readlines() + + with open(cloakedFilename, "w", encoding="utf-8") as file: + for line in cloakedFile: + file.write(f"{fakeDate} {line}"), + step = datetime.timedelta(seconds=random.randint(MIN_SECONDS_STEP, MAX_SECONDS_STEP)) + fakeDate += step + else: + # Generate sample of noise generator output + for _ in range(20): + print(f"{fakeDate}") + step = datetime.timedelta(seconds=random.randint(MIN_SECONDS_STEP, MAX_SECONDS_STEP)) fakeDate += step - i = i+1 - + +if __name__ == "__main__": + if len(sys.argv) == 2: + prependTimestamps(sys.argv[1]) else: - # Prepend noise generator output to file - with open( sys.argv[1] ) as file: - cloakedFile = file.readlines() + print("usage: prependTimestamps.py ") + print() + print("Strip leading timestamps prior to decloaking the cloaked file.") + print() - with open( sys.argv[1], "w" ) as file: - for i in cloakedFile: - file.write( str( fakeDate ) + " " + i ) - step = datetime.timedelta(seconds=random.randint(minSecondsStep,maxSecondsStep)) - fakeDate += step - if fakeDate.date() > today: - startDate = today - datetime.timedelta(days=random.randint(minDaysBack,maxDaysBack)) - fakeDate = datetime.datetime.combine( startDate, datetime.time( random.randint(0,23),random.randint(0,59),random.randint(0,59) ) ) diff --git a/removeNoise.py b/removeNoise.py index 2d8fc63..63c89e6 100644 --- a/removeNoise.py +++ b/removeNoise.py @@ -19,20 +19,16 @@ import os, sys, getopt -if ( len(sys.argv) != 4 ): - print "usage: removeNoise.py " - print - exit +def removeNoise(numberOfColumnsToStrip:str, noisyCloakedPath:str, outputPath:str): + numberOfColumnsToStrip = int(numberOfColumnsToStrip) -else: - numberOfColumnsToStrip = int( sys.argv[1] ) + with open(noisyCloakedPath, encoding="utf-8") as noisyFile, open(outputPath, "w", encoding="utf-8") as outputFile: + for line in noisyFile: + if line != "\n": + outputFile.write( ' '.join(line.split(' ')[numberOfColumnsToStrip:])) - with open( sys.argv[2] ) as file: - noisyFile = file.readlines() - file.close() - with open( sys.argv[3], "w" ) as file: - for line in noisyFile: - if line != '\n': - file.write( ' '.join(line.split(' ')[numberOfColumnsToStrip:])) - file.close() +if __name__ == "__main__": + if len(sys.argv) != 4 : + print("usage: removeNoise.py ") + exit(-1) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_ciphers.py b/tests/test_ciphers.py new file mode 100644 index 0000000..f4d3ca4 --- /dev/null +++ b/tests/test_ciphers.py @@ -0,0 +1,52 @@ +import os +from tempfile import TemporaryDirectory + +import pytest + +from cloakify import Cloakify +from decloakify import Decloakify + +ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +CIPHER_DIR = os.path.join(ROOT_DIR, "ciphers") + + +def test_duplicate_cipher_lines(): + for cipher_name in os.listdir(CIPHER_DIR): + cipher_path = os.path.join(CIPHER_DIR, cipher_name) + seen_lines = set() + with open(cipher_path, 'r', encoding="utf-8") as fp: + for line in fp: + assert line not in seen_lines + seen_lines.add(line) + + +def test_ciphers(): + payload_path = os.path.join(ROOT_DIR, "README.md") + + with TemporaryDirectory() as temp_dir: + cloaked_path = os.path.join(temp_dir, "cloaked.file") + decloaked_path = os.path.join(temp_dir, "decloaked.file") + for cipher_name in os.listdir(CIPHER_DIR): + cipher_path = os.path.join(CIPHER_DIR, cipher_name) + Cloakify(payload_path, cipher_path, cloaked_path) + Decloakify(cloaked_path, cipher_path, decloaked_path) + + with open(payload_path, "r") as payload_fp, open(decloaked_path, "r") as decloaked_fp: + assert payload_fp.read() == decloaked_fp.read() + + +def test_ciphers_with_password(): + password = "test1234567890" + payload_path = os.path.join(ROOT_DIR, "README.md") + + with TemporaryDirectory() as temp_dir: + cloaked_path = os.path.join(temp_dir, "cloaked.file") + decloaked_path = os.path.join(temp_dir, "decloaked.file") + for cipher_name in os.listdir(CIPHER_DIR): + cipher_path = os.path.join(CIPHER_DIR, cipher_name) + Cloakify(payload_path, cipher_path, cloaked_path, password) + Decloakify(cloaked_path, cipher_path, decloaked_path, password) + + with open(payload_path, "r") as payload_fp, open(decloaked_path, "r") as decloaked_fp: + assert payload_fp.read() == decloaked_fp.read() +