diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..f830b12
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,27 @@
+# License
+
+***ROTate*** - Encryption tool based on the ROT cipher
+
+Copyright © 2018 by Ralf Kilian
+
+Distributed under the *MIT License*:
+
+```
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cee5e83
--- /dev/null
+++ b/README.md
@@ -0,0 +1,67 @@
+# *ROTate*
+
+**Table of contents**
+
+* [Definition](#definition)
+* [Details](#details)
+* [Components](#components)
+* [Requirements](#requirements)
+* [Documentation](#documentation)
+* [Useless facts](#useless-facts)
+
+----
+
+## Definition
+
+The *ROTate* project is a collection of scripts to encrypt and decrypt files using various *ROT* cipher methods.
+
+[Top](#)
+
+## Details
+
+The project allows to encrypt and decrypt files using various ROT cipher methods such as *ROT13*, *ROT47*, *ROT128* as well as with enhanced variants based on each character set of these methods.
+
+It also comes with a tool to find out which variant and value has been used to encrypt a file.
+
+Due to the fact, that data encrypted with *ROT* methods can be cracked quite easily, they are **not** suitable for encrypting sensible data.
+
+[Top](#)
+
+## Components
+
+### *ROTate* variants
+
+There are three components to encrypt and decrypt files using the *ROT13*, *ROT47* and *ROT128* cipher method.
+
+They also allow using a user-defined rotation value (based on the character set of that cipher method) instead of the default rotation value.
+
+### *ROTate Cracker*
+
+As already mentioned above, data encrypted with *ROT* methods can be cracked quite easily. This brute force cracker helps to determine which *ROT* variant and rotation value has been used to encrypt a file or string by simply trying all supported variants with all rotation values available.
+
+[Top](#)
+
+## Requirements
+
+In order to use *ROTate*, the *Python* framework must be installed on the system.
+
+Depending on which version of the framework you are using:
+
+* *Python* 2.x (version 2.7 or higher is recommended, may also work with earlier versions)
+* *Python* 3.x (version 3.2 or higher is recommended, may also work with earlier versions)
+
+[Top](#)
+
+## Documentation
+
+In the corresponding `docs` sub-directories, there are plain text files containing a detailed documentation for each component with further information and usage examples.
+
+[Top](#)
+
+## Useless facts
+
+* The name *ROTate* stands for ***ROT*** *with* ***A**dditional* ***T**ools* *and* ***E**nhancements*.
+* The first version uploaded on *GitHub* was *ROTate* 3.0.6 built on March 13th, 2018.
+* Before uploading, the project has neither been changed nor even touched for more than three years.
+
+[Top](#)
diff --git a/python2/changelog.txt b/python2/changelog.txt
new file mode 100644
index 0000000..c5739f6
--- /dev/null
+++ b/python2/changelog.txt
@@ -0,0 +1,57 @@
+
+CHANGELOG (ROTate)
+
+ Version 3.0.6 (2018-03-13)
+
+ + Added new versions of the Clap and PaVal core modules (replaced the
+ existing ones).
+
+ * Revised (refurbished) all components of the project in general
+ (neglibible changes).
+
+ # Fixed the wildcard bug (certain characters inside the strings to
+ encrypt and decrypt and will no longer be interpreted as wildcards).
+
+ Version 3.0.5 (2015-01-03)
+
+ * Revised some code inside the ROTate Cracker core module (negligible
+ changes).
+
+ - Removed unnecessary module imports from the core modules.
+
+ Version 3.0.4 (2014-04-03)
+
+ + Added an optional command-line argument to the ROTate Cracker script
+ to write the integer ordinals of the decrypted string into the
+ output file.
+
+ Version 3.0.3 (2014-03-21)
+
+ * Revised (reduced) some code inside the ROTate Cracker core module.
+ * Revised the ROTate Cracker output file (non-printable characters are
+ now replaced either with whitespaces or a corresponding notice).
+
+ # Fixed the attribute error inside the ROTate Cracker core module when
+ reading out the major version of the Python framework using Python
+ version 2.6 or below.
+
+ Version 3.0.2 (2014-03-14)
+
+ + Added an error handler to the ROTate scripts in case no command-line
+ argument parser can be initialized.
+
+ * Revised (reduced) some code inside the ROTate Cracker core module.
+ * Revised the transform methods inside the ROT13, ROT47 and ROT128
+ core module (reduced some code for increased readability).
+
+ Version 3.0.1 (2014-03-07)
+
+ * Revised the description of some command-line arguments inside all
+ ROTate scripts.
+ * Revised the header of the ROTate Cracker output file (negligible
+ text changes).
+
+ Version 3.0.0 (2014-02-11)
+
+ * First official release of this major version.
+
diff --git a/python2/core/__init__.py b/python2/core/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/python2/core/clap.py b/python2/core/clap.py
new file mode 100755
index 0000000..03305c0
--- /dev/null
+++ b/python2/core/clap.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# Clap - Command-line argument parser module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/clap
+# ============================================================================
+
+__version__ = "1.1.10"
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+class Parser(object):
+ """
+ Project independent command-line argument parser class.
+ """
+ __arg_grp_opt = None
+ __arg_grp_req = None
+ __arg_parser = None
+ __is_argparser = False
+ __conflict_handler = "resolve" # used by OptionParser, only
+
+ def __init__(self):
+ try:
+ from argparse import ArgumentParser
+ self.__arg_parser = ArgumentParser(add_help=False)
+ self.__arg_grp_req = \
+ self.__arg_parser.add_argument_group("required arguments")
+ self.__arg_grp_opt = \
+ self.__arg_parser.add_argument_group("optional arguments")
+ self.__is_argparser = True
+ return
+ except ImportError:
+ # Ignore the exception and proceed with the fallback
+ pass
+
+ try:
+ from optparse import OptionParser
+ self.__arg_parser = \
+ OptionParser(conflict_handler=self.__conflict_handler)
+ self.__arg_grp_req = \
+ self.__arg_parser.add_option_group("Required arguments")
+ self.__arg_grp_opt = \
+ self.__arg_parser.add_option_group("Optional arguments")
+ return
+ except ImportError:
+ # This should never happen
+ raise ImportError("Failed to initialize an argument parser.")
+
+ def add_avalue(self, arg_short, arg_long, arg_help, arg_dest, arg_default,
+ arg_required):
+ """
+ Add an argument that expects a single user-defined value.
+ """
+ if arg_required:
+ obj = self.__arg_grp_req
+ else:
+ obj = self.__arg_grp_opt
+
+ if arg_default is not None:
+ # Enclose the value with quotes in case it is not an integer
+ quotes = "'"
+ try:
+ arg_default = int(arg_default)
+ quotes = ""
+ except ValueError:
+ pass
+
+ if arg_help.strip().endswith(")"):
+ arg_help = arg_help.rstrip(")")
+ arg_help += ", default is %s%s%s)" % \
+ (quotes, str(arg_default), quotes)
+ else:
+ arg_help += " (default is %s%s%s)" % \
+ (quotes, str(arg_default), quotes)
+
+ if self.__is_argparser:
+ if arg_short is None:
+ obj.add_argument(arg_long, help=arg_help, dest=arg_dest,
+ default=arg_default, required=arg_required)
+ else:
+ obj.add_argument(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, default=arg_default,
+ required=arg_required)
+ else:
+ if arg_short is None:
+ obj.add_option(arg_long, help=arg_help, dest=arg_dest,
+ default=arg_default)
+ else:
+ obj.add_option(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, default=arg_default)
+
+ def add_predef(self, arg_short, arg_long, arg_help, arg_dest, arg_choices,
+ arg_required):
+ """
+ Add an argument that expects a certain predefined value.
+ """
+ if arg_required:
+ obj = self.__arg_grp_req
+ else:
+ obj = self.__arg_grp_opt
+
+ if self.__is_argparser:
+ if arg_short is None:
+ obj.add_argument(arg_long, help=arg_help, dest=arg_dest,
+ choices=arg_choices, required=arg_required)
+ else:
+ obj.add_argument(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, choices=arg_choices,
+ required=arg_required)
+ else:
+ if arg_short is None:
+ obj.add_option(arg_long, help=arg_help, dest=arg_dest,
+ choices=arg_choices)
+ else:
+ # The OptionParser does not print the values to choose from,
+ # so these have to be added manually to the description of
+ # the argument first
+ arg_help += " (choose from "
+ for item in arg_choices:
+ arg_help += "'%s', " % item
+ arg_help = arg_help.rstrip(", ") + ")"
+
+ obj.add_option(arg_short, arg_long, help=arg_help,
+ dest=arg_dest)
+
+ def add_switch(self, arg_short, arg_long, arg_help, arg_dest, arg_store,
+ arg_required):
+ """
+ Add an argument that does not expect anything, but returns a
+ boolean value.
+ """
+ if arg_required:
+ obj = self.__arg_grp_req
+ else:
+ obj = self.__arg_grp_opt
+
+ if arg_store:
+ arg_store = "store_true"
+ else:
+ arg_store = "store_false"
+
+ if self.__is_argparser:
+ if arg_short is None:
+ obj.add_argument(arg_long, help=arg_help, dest=arg_dest,
+ action=arg_store, required=arg_required)
+ else:
+ obj.add_argument(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, action=arg_store,
+ required=arg_required)
+ else:
+ if arg_short is None:
+ obj.add_option(arg_long, help=arg_help, dest=arg_dest,
+ action=arg_store)
+ else:
+ obj.add_option(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, action=arg_store)
+
+ def dependency(self, arg_name, arg_value, dependency):
+ """
+ Check the dependency of a command-line argument.
+ """
+ if dependency is not None:
+ if arg_value is None or str(arg_value) == "":
+ raise Exception("The '%s' argument depends on %s'." %
+ (arg_name, dependency))
+
+ def error(self, obj):
+ """
+ Raise an error and cause the argument parser to print the error
+ message.
+ """
+ if type(obj) == str:
+ obj = obj.strip()
+
+ self.__arg_parser.error(obj)
+
+ def parse_args(self):
+ """
+ Parse and return the command-line arguments.
+ """
+ if self.__is_argparser:
+ args = self.__arg_parser.parse_args()
+ else:
+ (args, values) = self.__arg_parser.parse_args()
+ return args
+
+ def print_help(self):
+ """
+ Print the usage, description, argument details and epilog.
+ """
+ self.__arg_parser.print_help()
+
+ def set_description(self, string):
+ """
+ Set the description text.
+ """
+ self.__arg_parser.description = string.strip()
+
+ def set_epilog(self, string):
+ """
+ Set the epilog text.
+ """
+ self.__arg_parser.epilog = string.strip()
+
+# EOF
diff --git a/python2/core/common.py b/python2/core/common.py
new file mode 100755
index 0000000..4342ab4
--- /dev/null
+++ b/python2/core/common.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# Common core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+import paval as pv
+
+
+def get_file_size(file_path):
+ """
+ Get the size of a file in bytes.
+ """
+ pv.path(file_path, "", True, True)
+
+ f = open(file_path, "rb")
+ f.seek(0, 2)
+ file_size = f.tell()
+ f.close()
+
+ return int(file_size)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+# EOF
diff --git a/python2/core/cracker.py b/python2/core/cracker.py
new file mode 100755
index 0000000..6fd937f
--- /dev/null
+++ b/python2/core/cracker.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# Cracker core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+import os
+import paval as pv
+import rot13
+import rot47
+import rot128
+import string as st
+import sys
+
+from datetime import datetime as dt
+
+
+def brute_force(string, file_output, method=None, int_ordinals=False,
+ non_printable=False, limit=True):
+ """
+ Brute-force the given string to find out which ROT variant and
+ rotation value was used to encrypt.
+ """
+ pv.path(file_output, "output", True, False)
+ pv.string(string, "string to decrypt", True)
+ file_output = os.path.abspath(file_output)
+
+ if method is None:
+ method = "All"
+ else:
+ method = method.upper()
+ pv.compstr(method, "method", ["ROT13", "ROT47", "ROT128"])
+
+ list_values_rot13 = []
+ list_values_rot47 = []
+ list_values_rot128 = []
+ timestamp = str(dt.now())
+ output = "\r\n" + "=" * 78 + \
+ "\r\nFile type: ROTate Cracker output file" \
+ "\r\n" + "-" * 78 + \
+ "\r\nOutput file name: " + file_output + \
+ "\r\nString to decrypt: " + string + \
+ "\r\nMethods used: " + method + \
+ "\r\n" + "-" * 78 + \
+ "\r\nTimestamp: " + timestamp[:-7] + \
+ "\r\nROTate version: " + get_version() + \
+ "\r\n" + "=" * 78 + "\r\n\r\n"
+
+ if method == "All" or method == "ROT13":
+ list_values_rot13 = \
+ __get_values_rot13(string, int_ordinals, non_printable, limit)
+ if method == "All" or method == "ROT47":
+ list_values_rot47 = \
+ __get_values_rot47(string, int_ordinals, non_printable, limit)
+ if method == "All" or method == "ROT128":
+ list_values_rot128 = \
+ __get_values_rot128(string, int_ordinals, non_printable, limit)
+
+ if len(list_values_rot13) > 0:
+ output += "\r\n [ROT13]\r\n"
+ for value in list_values_rot13:
+ output += " - %s\r\n" % value
+ output += "\r\n"
+ if len(list_values_rot47) > 0:
+ output += "\r\n [ROT47]\r\n"
+ for value in list_values_rot47:
+ output += " - %s\r\n" % value
+ output += "\r\n"
+ if len(list_values_rot128) > 0:
+ output += "\r\n [ROT128]\r\n"
+ for value in list_values_rot128:
+ output += " - %s\r\n" % value
+ output += "\r\n"
+ fh_output = open(file_output, "wb")
+
+ # Run the appropriate code for the Python framework used
+ if sys.version_info[0] == 2:
+ fh_output.write(output)
+ elif sys.version_info[0] > 2:
+ fh_output.write(output.encode(sys.getdefaultencoding()))
+
+ fh_output.close()
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __get_int_ordinals(string):
+ """
+ Return the integer ordinals of a string.
+ """
+ output = ""
+
+ for char in string:
+ output += str(ord(char)).rjust(3, " ") + ", "
+ output = output.rstrip(", ")
+
+ return output
+
+
+def __get_printable(string):
+ """
+ Replace non-printable characters inside a string with whitespaces
+ """
+ output = ""
+
+ chars_remove = "\t\n\r\x0b\x0c"
+ for char in chars_remove:
+ string = string.replace(char, " ")
+
+ for char in string:
+ if char not in st.printable:
+ output += " "
+ else:
+ output += char
+
+ if output.strip() == "":
+ output = "(only non-printable characters or spaces)"
+
+ return output
+
+
+def __get_values_rot13(string, int_ordinals, non_printable, limit):
+ """
+ Core method to get all possible ROT13 values for the given string.
+ """
+ list_values = []
+ for i in range(1, 26):
+ output = __prepare_line(rot13.decrypt_string(string, i),
+ int_ordinals, non_printable, limit)
+ list_values.append("Value %s: %s" % (str(i).rjust(3, " "), output))
+
+ return list_values
+
+
+def __get_values_rot47(string, int_ordinals, non_printable, limit):
+ """
+ Core method to get all possible ROT47 values for the given string.
+ """
+ list_values = []
+ for i in range(1, 94):
+ output = __prepare_line(rot47.decrypt_string(string, i),
+ int_ordinals, non_printable, limit)
+ list_values.append("Value %s: %s" % (str(i).rjust(3, " "), output))
+
+ return list_values
+
+
+def __get_values_rot128(string, int_ordinals, non_printable, limit):
+ """
+ Core method to get all possible ROT128 values for the given string.
+ """
+ list_values = []
+ for i in range(1, 256):
+ output = __prepare_line(rot128.decrypt_string(string, i),
+ int_ordinals, non_printable, limit)
+ list_values.append("Value %s: %s" % (str(i).rjust(3, " "), output))
+
+ return list_values
+
+
+def __prepare_line(line, int_ordinals, non_printable, limit):
+ """
+ Prepare the line before it gets written into the file.
+ """
+ if int_ordinals:
+ line = __get_int_ordinals(line)
+ else:
+ if not non_printable:
+ line = __get_printable(line)
+
+ if limit:
+ if len(line) > 58:
+ line = line[:55] + "..."
+
+ return line
+
+# EOF
diff --git a/python2/core/paval.py b/python2/core/paval.py
new file mode 100755
index 0000000..a2ed54a
--- /dev/null
+++ b/python2/core/paval.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# PaVal - Parameter validation module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/paval
+# ============================================================================
+
+__version__ = "1.2.7"
+
+import filecmp
+import os
+
+
+def compfile(path, name="", list_files=[]):
+ """
+ Compare files to avoid that the same file is given multiple times or
+ in different ways (e. g. different name but same content).
+ """
+ __string(path, "%s path" % name, True)
+
+ if list_files is None:
+ list_files = []
+ elif len(list_files) == 0:
+ __ex("File list is empty (no files to compare with).", True)
+ else:
+ for item in list_files:
+ if not type(item) == list:
+ __ex("Every list item must be a sub-list.", True)
+ if not len(item) == 2:
+ __ex("Every sub-list must contain two items.", True)
+
+ path = os.path.abspath(path)
+ for item in list_files:
+ path_compare = os.path.abspath(str(item[0]))
+ name_compare = str(item[1])
+ if path == path_compare:
+ __ex("The %s and the %s file path must not be identical." %
+ (name, name_compare), False)
+ if os.path.exists(path) and os.path.exists(path_compare):
+ if filecmp.cmp(path, path_compare, 0):
+ __ex("The %s and %s file content must not be identical." %
+ (name, name_compare), False)
+
+
+def compstr(string, name="", list_strings=[]):
+ """
+ Compare a string with a list of strings and check if it is an item of
+ that list.
+ """
+ __string(string, name, False)
+ if len(list_strings) == 0:
+ __ex("No %s strings to compare with." % name, True)
+ if string not in list_strings:
+ __ex("The %s '%s' does not exist." % (name, string), False)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def intrange(value, name="", value_min=None, value_max=None, zero=False):
+ """
+ Validate an integer range.
+ """
+ value = __integer(value, "%s value" % name, False)
+ if value_min is not None:
+ value_min = __integer(value_min, "minimal %s value" % name, True)
+ intvalue(value_min, name, True, True, True)
+ if value_max is not None:
+ value_max = __integer(value_max, "maximal %s value" % name, True)
+ intvalue(value_max, name, True, True, True)
+ if not zero:
+ if value == 0:
+ __ex("The %s value must not be zero." % name, False)
+ if (value_min is not None) and (value_max is not None):
+ if value_min > value_max:
+ __ex("The maximal %s value must be greater than the minimal "
+ "value." % name, False)
+ if (value_min == value_max) and (not value == value_min):
+ __ex("The %s value can only be %s (depending on further range "
+ "further range arguments)." % (name, value_min), False)
+ if (value < value_min) or (value > value_max):
+ __ex("The %s value must be between %s and %s (depending on "
+ "further range arguments)." % (name, value_min, value_max),
+ False)
+ elif value_min is not None:
+ if value < value_min:
+ __ex("The %s value must not be less than %s." % (name, value_min),
+ False)
+ elif value_max is not None:
+ if value > value_max:
+ __ex("The %s value must not be greater than %s." %
+ (name, value_max), False)
+
+
+def intvalue(value, name="", positive=True, zero=False, negative=False):
+ """
+ Validate a single integer value.
+ """
+ value = __integer(value, "%s value" % name, False)
+ if not positive:
+ if value > 0:
+ __ex("The %s value must not be positive." % name, False)
+ if not zero:
+ if value == 0:
+ __ex("The %s value must not be zero." % name, False)
+ if not negative:
+ if value < 0:
+ __ex("The %s value must not be negative." % name, False)
+
+
+def path(path, name="", is_file=False, exists=False):
+ """
+ Validate a path of a file or directory.
+ """
+ string(path, "%s path" % name, False, None)
+ path = os.path.abspath(path)
+
+ if is_file:
+ path_type = "file"
+ else:
+ path_type = "directory"
+ if exists:
+ if not os.path.exists(path):
+ __ex("The given %s %s does not exist." % (name, path_type), False)
+ if (is_file and not os.path.isfile(path)) or \
+ (not is_file and not os.path.isdir(path)):
+ __ex("The given %s %s path is not a %s." % (name, path_type,
+ path_type), False)
+ else:
+ if os.path.exists(path):
+ __ex("The given %s %s path already exists." % (name, path_type),
+ False)
+
+
+def string(string, name="", wildcards=False, invalid_chars=None):
+ """
+ Validate a string.
+ """
+ __string(string, name, False)
+ if invalid_chars is None:
+ invalid_chars = ""
+ if not wildcards:
+ if ("*" in string) or ("?" in string):
+ __ex("The %s must not contain wildcards." % name, False)
+ if len(invalid_chars) > 0:
+ for char in invalid_chars:
+ if char in string:
+ # Use single quotes by default or double quotes in case the
+ # single quotes are the invalid character
+ quotes = "'"
+ if char == quotes:
+ quotes = "\""
+
+ __ex("The %s contains at least one invalid character "
+ "(%s%s%s)." % (name, quotes, char, quotes), False)
+
+
+def __ex(string, internal=False):
+ """
+ Internal method to raise an exception.
+ """
+ string = str(string).strip()
+ while (" " * 2) in string:
+ string = string.replace((" " * 2), " ")
+ if internal:
+ string = "PaVal: " + string
+ raise Exception(string)
+
+
+def __integer(value, name="", internal=False):
+ """
+ Internal method for basic integer validation.
+ """
+ if value is None:
+ __ex("The %s is missing." % name, internal)
+ if value == "":
+ __ex("The %s must not be empty." % name, internal)
+ try:
+ value = int(value)
+ except ValueError:
+ __ex("The %s must be an integer." % name, internal)
+ return int(value)
+
+
+def __string(string, name="", internal=False):
+ """
+ Internal method for basic string validation.
+ """
+ if string is None:
+ __ex("The %s is missing." % name, internal)
+ if string == "":
+ __ex("The %s must not be empty." % name, internal)
+
+# EOF
diff --git a/python2/core/rot128.py b/python2/core/rot128.py
new file mode 100755
index 0000000..b3a5265
--- /dev/null
+++ b/python2/core/rot128.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT128 encryption/decryption core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+import common
+import os
+import paval as pv
+
+
+def encrypt_file(file_input, file_output, buffer_size=4096, rot_value=128):
+ """
+ Encrypt a file using ROT128.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def encrypt_string(string, rot_value):
+ """
+ Encrypt a string using ROT128.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(True, string, rot_value)
+
+
+def decrypt_file(file_input, file_output, buffer_size=4096, rot_value=128):
+ """
+ Decrypt a file using ROT128.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def decrypt_string(string, rot_value):
+ """
+ Decrypt a string using ROT128.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(False, string, rot_value)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __transform_bytes(encrypt, data_input, rot_value):
+ """
+ Core method to manipulate bytes.
+ """
+ data_output = bytearray(b"")
+
+ for byte in data_input:
+ if encrypt:
+ data_output.append(((byte + rot_value) % 256))
+ else:
+ data_output.append(((byte - rot_value) % 256))
+
+ return data_output
+
+
+def __transform_string(encrypt, string, rot_value):
+ """
+ Core method to manipulate a string.
+ """
+ output = ""
+
+ for char in string:
+ if encrypt:
+ output += chr(((ord(char) + rot_value) % 256))
+ else:
+ output += chr(((ord(char) + rot_value) % 256))
+
+ return output
+
+# EOF
diff --git a/python2/core/rot13.py b/python2/core/rot13.py
new file mode 100755
index 0000000..ac0120b
--- /dev/null
+++ b/python2/core/rot13.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT13 encryption/decryption core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+import common
+import os
+import paval as pv
+
+
+def encrypt_file(file_input, file_output, buffer_size=4096, rot_value=13):
+ """
+ Encrypt a file using ROT13.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def encrypt_string(string, rot_value=13):
+ """
+ Encrypt a string using ROT13.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(True, string, rot_value)
+
+
+def decrypt_file(file_input, file_output, buffer_size=4096, rot_value=13):
+ """
+ Decrypt a file using ROT13.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def decrypt_string(string, rot_value=13):
+ """
+ Decrypt a string using ROT13.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(False, string, rot_value)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __transform_bytes(encrypt, data_input, rot_value):
+ """
+ Core method to manipulate bytes.
+ """
+ data_output = bytearray(b"")
+
+ for byte in data_input:
+ if byte in range(65, 91):
+ # Uppercase letters
+ if encrypt:
+ data_output.append((((byte - 65) + rot_value) % 26) + 65)
+ else:
+ data_output.append((((byte - 65) - rot_value) % 26) + 65)
+ elif byte in range(97, 123):
+ # Lowercase letters
+ if encrypt:
+ data_output.append((((byte - 97) + rot_value) % 26) + 97)
+ else:
+ data_output.append((((byte - 97) - rot_value) % 26) + 97)
+ else:
+ data_output.append(byte)
+
+ return data_output
+
+
+def __transform_string(encrypt, string, rot_value):
+ """
+ Core method to manipulate a string.
+ """
+ output = ""
+
+ for char in string:
+ if ord(char) in range(65, 91):
+ # Uppercase letters
+ if encrypt:
+ output += chr((((ord(char) - 65) + rot_value) % 26) + 65)
+ else:
+ output += chr((((ord(char) - 65) - rot_value) % 26) + 65)
+ elif ord(char) in range(97, 123):
+ # Lowercase letters
+ if encrypt:
+ output += chr((((ord(char) - 97) + rot_value) % 26) + 97)
+ else:
+ output += chr((((ord(char) - 97) - rot_value) % 26) + 97)
+ else:
+ output += char
+
+ return output
+
+# EOF
diff --git a/python2/core/rot47.py b/python2/core/rot47.py
new file mode 100755
index 0000000..ee71aef
--- /dev/null
+++ b/python2/core/rot47.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT47 encryption/decryption core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+import common
+import os
+import paval as pv
+
+
+def encrypt_file(file_input, file_output, buffer_size=4096, rot_value=47):
+ """
+ Encrypt a file using ROT47.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def encrypt_string(string, rot_value=47):
+ """
+ Encrypt a string using ROT47.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(True, string, rot_value)
+
+
+def decrypt_file(file_input, file_output, buffer_size=4096, rot_value=47):
+ """
+ Decrypt a file using ROT47.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def decrypt_string(string, rot_value=47):
+ """
+ Decrypt a string using ROT47.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(False, string, rot_value)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __transform_bytes(encrypt, data_input, rot_value):
+ """
+ Core method to manipulate bytes.
+ """
+ data_output = bytearray(b"")
+
+ for byte in data_input:
+ if byte in range(32, 126):
+ if encrypt:
+ data_output.append((((byte - 32) + rot_value) % 94) + 32)
+ else:
+ data_output.append((((byte - 32) - rot_value) % 94) + 32)
+ else:
+ data_output.append(byte)
+
+ return data_output
+
+
+def __transform_string(encrypt, string, rot_value):
+ """
+ Core method to manipulate a string.
+ """
+ output = ""
+
+ for char in string:
+ if ord(char) in range(32, 126):
+ if encrypt:
+ output += chr((((ord(char) - 32) + rot_value) % 94) + 32)
+ else:
+ output += chr((((ord(char) - 32) - rot_value) % 94) + 32)
+ else:
+ output += char
+
+ return output
+
+# EOF
diff --git a/python2/docs/usage_rotate-cracker.txt b/python2/docs/usage_rotate-cracker.txt
new file mode 100644
index 0000000..60c548f
--- /dev/null
+++ b/python2/docs/usage_rotate-cracker.txt
@@ -0,0 +1,83 @@
+
+USAGE (rotate-cracker.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Crack a ROT encrypted file
+
+ 1. Definition
+
+ The ROTate Cracker script helps to determine which ROT variant and
+ rotation value was used to encrypt a file or string by trying all
+ supported ROT variants with all rotation values available (brute
+ force).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-cracker.py --help
+
+ 3. Crack a ROT encrypted file
+
+ For example, you have the ROT encrypted file "secret.txt" containing
+ the line
+
+ Uryyb, Jbeyq!
+
+ and want to decrypt it. So, to find out which ROT method and rotation
+ value was used to encrypt the file, a string from the file (e. g. a
+ word) is required.
+
+ This example uses the string "Uryyb" and writes the decrypted values
+ into the file "/tmp/output.txt":
+
+ $ ./rotate-cracker.py -s "Uryyb" -o /tmp/output.txt
+
+ The output file shows that when using the ROT13 method, the string
+ "Uryyb" decrypted with value 13 returns the word "Hello":
+
+ (...)
+ [ROT13]
+ - Value 0: Uryyb
+ - Value 1: Tqxxa
+ - Value 2: Spwwz
+ - Value 3: Rovvy
+ - Value 4: Qnuux
+ - Value 5: Pmttw
+ - Value 6: Olssv
+ - Value 7: Nkrru
+ - Value 8: Mjqqt
+ - Value 9: Lipps
+ - Value 10: Khoor
+ - Value 11: Jgnnq
+ - Value 12: Ifmmp
+ - Value 13: Hello
+ (...)
+
+ So, the file or at least that string seems to be encrypted with that
+ method and value. Unfortunately, there is no analysis feature, which
+ means you will have to look at the returned value list to manually
+ check which entry makes most sense.
+
+ If there are non-printable characters inside the string, they will be
+ replaced by whitespaces. If the string only contains of whitespaces,
+ a notice will be written to the output file.
+
diff --git a/python2/docs/usage_rotate-rot128.txt b/python2/docs/usage_rotate-rot128.txt
new file mode 100644
index 0000000..53b835d
--- /dev/null
+++ b/python2/docs/usage_rotate-rot128.txt
@@ -0,0 +1,56 @@
+
+USAGE (rotate-rot128.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Encrypt a file
+ 4. Decrypt the file again
+ 5. User-defined rotation values
+
+ 1. Definition
+
+ The ROTate ROT128 script encrypts and decrypts a file using the ROT128
+ method with the default rotation value or a user-defined one (using
+ the ROT128 character set).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-rot128.py --help
+
+ 3. Encrypt a file
+
+ This works as described in section 3 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT128 instead of the
+ script used there.
+
+ 4. Decrypt the file again
+
+ This works as described in section 4 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT128 instead of the
+ script used there.
+
+ 5. User-defined rotation values
+
+ This works as described in section 5 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT128 instead of the
+ script used there.
+
diff --git a/python2/docs/usage_rotate-rot13.txt b/python2/docs/usage_rotate-rot13.txt
new file mode 100644
index 0000000..918322b
--- /dev/null
+++ b/python2/docs/usage_rotate-rot13.txt
@@ -0,0 +1,74 @@
+
+USAGE (rotate-rot13.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Encrypt a file
+ 4. Decrypt the file again
+ 5. User-defined rotation values
+
+ 1. Definition
+
+ The ROTate ROT13 script encrypts and decrypts a file using the ROT13
+ method with the default rotation value or a user-defined one (using
+ the ROT13 character set).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-rot13.py --help
+
+ 3. Encrypt a file
+
+ For example, if you have the input file "hello.txt" containing the
+ line
+
+ Hello, world!
+
+ and want to encrypt it using ROT13 (with its default value) and write
+ the encrypted data into "secret.txt", the command line would look like
+ this:
+
+ $ ./rotate-rot13.py -i hello.txt -o secret.txt -a encrypt
+
+ The encrypted data inside "secret.txt" should look like this:
+
+ Uryyb, Jbeyq!
+
+ 4. Decrypt the file again
+
+ To decrypt the file again and write the decrypted data into the file
+ "output.txt", type:
+
+ $ ./rotate-rot13.py -i secret.txt -o output.txt -a decrypt
+
+ 5. User-defined rotation values
+
+ You can also encrypt the file using the ROT13 method, but with
+ rotation value 19 instead of the default value:
+
+ $ ./rotate-rot13.py -i hello.txt -o secret.txt -a encrypt -v 19
+
+ The user-defined rotation value used to encrypt is also required to
+ properly decrypt the file again:
+
+ $ ./rotate-rot13.py -i secret.txt -o output.txt -a decrypt -v 19
+
diff --git a/python2/docs/usage_rotate-rot47.txt b/python2/docs/usage_rotate-rot47.txt
new file mode 100644
index 0000000..1498be1
--- /dev/null
+++ b/python2/docs/usage_rotate-rot47.txt
@@ -0,0 +1,56 @@
+
+USAGE (rotate-rot47.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Encrypt a file
+ 4. Decrypt the file again
+ 5. User-defined rotation values
+
+ 1. Definition
+
+ The ROTate ROT47 script encrypts and decrypts a file using the ROT47
+ method with the default rotation value or a user-defined one (using
+ the ROT47 character set).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-rot47.py --help
+
+ 3. Encrypt a file
+
+ This works as described in section 3 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT47 instead of the script
+ used there.
+
+ 4. Decrypt the file again
+
+ This works as described in section 4 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT47 instead of the script
+ used there.
+
+ 5. User-defined rotation values
+
+ This works as described in section 5 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT47 instead of the script
+ used there.
+
diff --git a/python2/license.txt b/python2/license.txt
new file mode 100644
index 0000000..6f15220
--- /dev/null
+++ b/python2/license.txt
@@ -0,0 +1,27 @@
+
+LICENSE (ROTate)
+
+ ROTate - Encryption tool based on the ROT cipher
+ Copyright (C) 2018 by Ralf Kilian
+
+ Distributed under the MIT License:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/python2/readme.txt b/python2/readme.txt
new file mode 100644
index 0000000..e643f41
--- /dev/null
+++ b/python2/readme.txt
@@ -0,0 +1,49 @@
+
+README (ROTate)
+
+ Project
+
+ ROTate
+ Version 3.0.6 (based on Python framework 2.x)
+ Copyright (C) 2018 by Ralf Kilian
+
+ Website: http://www.urbanware.org
+ GitHub: https://github.com/urbanware-org/rotate
+
+ Definition
+
+ The ROTate project is a collection of scripts to encrypt and decrypt
+ files using various ROT cipher methods.
+
+ License
+
+ This project is distributed under the MIT License. You should have
+ received a copy of the license along with this program (see the
+ 'license.txt' file). If not, you can find the license terms here:
+
+ https://opensource.org/licenses/MIT
+
+ Requirements
+
+ The requirements for this project can be found inside the included
+ file 'requirements.txt'.
+
+ In case this file is missing, you can also find the information on the
+ website of the project mentioned above.
+
+ Usage
+
+ For fundamental documentation as well as some usage examples for each
+ component of the project, you may have a look at the text files inside
+ the included 'docs' sub-directory.
+
+ Legal information
+
+ The project name is completely fictitious. Any correspondences with
+ existing websites, applications, companies and/or other projects are
+ purely coincidental.
+
+ All trademarks belong to their respective owners.
+
+ Errors and omissions excepted.
+
diff --git a/python2/requirements.txt b/python2/requirements.txt
new file mode 100644
index 0000000..3d50015
--- /dev/null
+++ b/python2/requirements.txt
@@ -0,0 +1,17 @@
+
+REQUIREMENTS (ROTate)
+
+ Notice
+
+ This version of ROTate was built for the Python 2.x framework. If you
+ need a version that works with Python 3.x, you may look here:
+
+ http://www.urbanware.org/rotate.html
+
+ General
+
+ Software requirements:
+
+ - Python 2.x (version 2.7 or higher is recommended, may also work
+ with earlier versions)
+
diff --git a/python2/rotate-cracker.py b/python2/rotate-cracker.py
new file mode 100755
index 0000000..ba99fb6
--- /dev/null
+++ b/python2/rotate-cracker.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# Cracker script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import cracker
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print "%s: error: %s" % (os.path.basename(sys.argv[0]), e)
+ sys.exit(1)
+
+ p.set_description("Determine which ROT variant and rotation value was "
+ "used to encrypt a file or string by trying all "
+ "supported ROT variants with all rotation values "
+ "available (brute force).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+ p.add_avalue("-s", "--string", "string to decrypt", "string", None, True)
+
+ # Define optional arguments
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_switch(None, "--int-ordinals", "write integer ordinals instead "
+ "of the characters into the output file", "int_ordinals",
+ True, False)
+ p.add_predef("-m", "--method", "use only one instead of all supported "
+ "methods to decrypt", "method", ["rot13", "rot47", "rot128"],
+ False)
+ p.add_switch(None, "--no-limit", "do not limit the length of the lines "
+ "inside the output file", "no_limit", False, False)
+ p.add_switch(None, "--non-printable", "write non-printable characters "
+ "into the output file", "non_printable", True, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print cracker.get_version()
+ sys.exit(0)
+
+ args = p.parse_args()
+ try:
+ cracker.brute_force(args.string, args.output_file, args.method,
+ args.int_ordinals, args.non_printable,
+ args.no_limit)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python2/rotate-rot128.py b/python2/rotate-rot128.py
new file mode 100755
index 0000000..69c7235
--- /dev/null
+++ b/python2/rotate-rot128.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT128 encryption/decryption script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import rot128
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print "%s: error: %s" % (os.path.basename(sys.argv[0]), e)
+ sys.exit(1)
+
+ p.set_description("Encrypt or decrypt a file using ROT128 with the "
+ "default rotation value or a user-defined one (using "
+ "the ROT128 character set).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_predef("-a", "--action", "action to perform", "action",
+ ["encrypt", "decrypt"], True)
+ p.add_avalue("-i", "--input-file", "input file path", "input_file", None,
+ True)
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+
+ # Define optional arguments
+ p.add_avalue("-b", "--buffer-size", "buffer size in bytes", "buffer_size",
+ 4096, False)
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_avalue("-v", "--value", "user-defined rotation value between 0 and "
+ "255", "value", 128, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print rot128.get_version()
+ sys.exit(0)
+
+ args = p.parse_args()
+ if args.action is None:
+ p.error("The required action argument is missing.")
+ elif args.action.lower() == ("encrypt"):
+ encrypt = True
+ elif args.action.lower() == ("decrypt"):
+ encrypt = False
+ else:
+ p.error("An unsupported action was given.")
+
+ try:
+ if encrypt:
+ rot128.encrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ else:
+ rot128.decrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python2/rotate-rot13.py b/python2/rotate-rot13.py
new file mode 100755
index 0000000..16af6d6
--- /dev/null
+++ b/python2/rotate-rot13.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT13 encryption/decryption script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import rot13
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print "%s: error: %s" % (os.path.basename(sys.argv[0]), e)
+ sys.exit(1)
+
+ p.set_description("Encrypt or decrypt a file using ROT13 with the "
+ "default rotation value or a user-defined one (using "
+ "the ROT13 character set).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_predef("-a", "--action", "action to perform", "action",
+ ["encrypt", "decrypt"], True)
+ p.add_avalue("-i", "--input-file", "input file path", "input_file", None,
+ True)
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+
+ # Define optional arguments
+ p.add_avalue("-b", "--buffer-size", "buffer size in bytes", "buffer_size",
+ 4096, False)
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_avalue("-v", "--value", "user-defined rotation value between 0 and "
+ "25", "value", 13, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print rot13.get_version()
+ sys.exit(0)
+
+ args = p.parse_args()
+ if args.action is None:
+ p.error("The required action argument is missing.")
+ elif args.action.lower() == ("encrypt"):
+ encrypt = True
+ elif args.action.lower() == ("decrypt"):
+ encrypt = False
+ else:
+ p.error("An unsupported action was given.")
+
+ try:
+ if encrypt:
+ rot13.encrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ else:
+ rot13.decrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python2/rotate-rot47.py b/python2/rotate-rot47.py
new file mode 100755
index 0000000..b5e3e4d
--- /dev/null
+++ b/python2/rotate-rot47.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT47 encryption/decryption script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import rot47
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print "%s: error: %s" % (os.path.basename(sys.argv[0]), e)
+ sys.exit(1)
+
+ p.set_description("Encrypt or decrypt a file using ROT47 with the "
+ "default rotation value or a user-defined one (using "
+ "the ROT47 character set).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_predef("-a", "--action", "action to perform", "action",
+ ["encrypt", "decrypt"], True)
+ p.add_avalue("-i", "--input-file", "input file path", "input_file", None,
+ True)
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+
+ # Define optional arguments
+ p.add_avalue("-b", "--buffer-size", "buffer size in bytes", "buffer_size",
+ 4096, False)
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_avalue("-v", "--value", "user-defined rotation value between 0 and "
+ "93", "value", 47, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print rot47.get_version()
+ sys.exit(0)
+
+ args = p.parse_args()
+ if args.action is None:
+ p.error("The required action argument is missing.")
+ elif args.action.lower() == ("encrypt"):
+ encrypt = True
+ elif args.action.lower() == ("decrypt"):
+ encrypt = False
+ else:
+ p.error("An unsupported action was given.")
+
+ try:
+ if encrypt:
+ rot47.encrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ else:
+ rot47.decrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python3/changelog.txt b/python3/changelog.txt
new file mode 100644
index 0000000..c5739f6
--- /dev/null
+++ b/python3/changelog.txt
@@ -0,0 +1,57 @@
+
+CHANGELOG (ROTate)
+
+ Version 3.0.6 (2018-03-13)
+
+ + Added new versions of the Clap and PaVal core modules (replaced the
+ existing ones).
+
+ * Revised (refurbished) all components of the project in general
+ (neglibible changes).
+
+ # Fixed the wildcard bug (certain characters inside the strings to
+ encrypt and decrypt and will no longer be interpreted as wildcards).
+
+ Version 3.0.5 (2015-01-03)
+
+ * Revised some code inside the ROTate Cracker core module (negligible
+ changes).
+
+ - Removed unnecessary module imports from the core modules.
+
+ Version 3.0.4 (2014-04-03)
+
+ + Added an optional command-line argument to the ROTate Cracker script
+ to write the integer ordinals of the decrypted string into the
+ output file.
+
+ Version 3.0.3 (2014-03-21)
+
+ * Revised (reduced) some code inside the ROTate Cracker core module.
+ * Revised the ROTate Cracker output file (non-printable characters are
+ now replaced either with whitespaces or a corresponding notice).
+
+ # Fixed the attribute error inside the ROTate Cracker core module when
+ reading out the major version of the Python framework using Python
+ version 2.6 or below.
+
+ Version 3.0.2 (2014-03-14)
+
+ + Added an error handler to the ROTate scripts in case no command-line
+ argument parser can be initialized.
+
+ * Revised (reduced) some code inside the ROTate Cracker core module.
+ * Revised the transform methods inside the ROT13, ROT47 and ROT128
+ core module (reduced some code for increased readability).
+
+ Version 3.0.1 (2014-03-07)
+
+ * Revised the description of some command-line arguments inside all
+ ROTate scripts.
+ * Revised the header of the ROTate Cracker output file (negligible
+ text changes).
+
+ Version 3.0.0 (2014-02-11)
+
+ * First official release of this major version.
+
diff --git a/python3/core/__init__.py b/python3/core/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/python3/core/clap.py b/python3/core/clap.py
new file mode 100755
index 0000000..e502975
--- /dev/null
+++ b/python3/core/clap.py
@@ -0,0 +1,216 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# Clap - Command-line argument parser module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/clap
+# ============================================================================
+
+__version__ = "1.1.10"
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+class Parser(object):
+ """
+ Project independent command-line argument parser class.
+ """
+ __arg_grp_opt = None
+ __arg_grp_req = None
+ __arg_parser = None
+ __is_argparser = False
+ __conflict_handler = "resolve" # used by OptionParser, only
+
+ def __init__(self):
+ try:
+ from argparse import ArgumentParser
+ self.__arg_parser = ArgumentParser(add_help=False)
+ self.__arg_grp_req = \
+ self.__arg_parser.add_argument_group("required arguments")
+ self.__arg_grp_opt = \
+ self.__arg_parser.add_argument_group("optional arguments")
+ self.__is_argparser = True
+ return
+ except ImportError:
+ # Ignore the exception and proceed with the fallback
+ pass
+
+ try:
+ from optparse import OptionParser
+ self.__arg_parser = \
+ OptionParser(conflict_handler=self.__conflict_handler)
+ self.__arg_grp_req = \
+ self.__arg_parser.add_option_group("Required arguments")
+ self.__arg_grp_opt = \
+ self.__arg_parser.add_option_group("Optional arguments")
+ return
+ except ImportError:
+ # This should never happen
+ raise ImportError("Failed to initialize an argument parser.")
+
+ def add_avalue(self, arg_short, arg_long, arg_help, arg_dest, arg_default,
+ arg_required):
+ """
+ Add an argument that expects a single user-defined value.
+ """
+ if arg_required:
+ obj = self.__arg_grp_req
+ else:
+ obj = self.__arg_grp_opt
+
+ if arg_default is not None:
+ # Enclose the value with quotes in case it is not an integer
+ quotes = "'"
+ try:
+ arg_default = int(arg_default)
+ quotes = ""
+ except ValueError:
+ pass
+
+ if arg_help.strip().endswith(")"):
+ arg_help = arg_help.rstrip(")")
+ arg_help += ", default is %s%s%s)" % \
+ (quotes, str(arg_default), quotes)
+ else:
+ arg_help += " (default is %s%s%s)" % \
+ (quotes, str(arg_default), quotes)
+
+ if self.__is_argparser:
+ if arg_short is None:
+ obj.add_argument(arg_long, help=arg_help, dest=arg_dest,
+ default=arg_default, required=arg_required)
+ else:
+ obj.add_argument(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, default=arg_default,
+ required=arg_required)
+ else:
+ if arg_short is None:
+ obj.add_option(arg_long, help=arg_help, dest=arg_dest,
+ default=arg_default)
+ else:
+ obj.add_option(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, default=arg_default)
+
+ def add_predef(self, arg_short, arg_long, arg_help, arg_dest, arg_choices,
+ arg_required):
+ """
+ Add an argument that expects a certain predefined value.
+ """
+ if arg_required:
+ obj = self.__arg_grp_req
+ else:
+ obj = self.__arg_grp_opt
+
+ if self.__is_argparser:
+ if arg_short is None:
+ obj.add_argument(arg_long, help=arg_help, dest=arg_dest,
+ choices=arg_choices, required=arg_required)
+ else:
+ obj.add_argument(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, choices=arg_choices,
+ required=arg_required)
+ else:
+ if arg_short is None:
+ obj.add_option(arg_long, help=arg_help, dest=arg_dest,
+ choices=arg_choices)
+ else:
+ # The OptionParser does not print the values to choose from,
+ # so these have to be added manually to the description of
+ # the argument first
+ arg_help += " (choose from "
+ for item in arg_choices:
+ arg_help += "'%s', " % item
+ arg_help = arg_help.rstrip(", ") + ")"
+
+ obj.add_option(arg_short, arg_long, help=arg_help,
+ dest=arg_dest)
+
+ def add_switch(self, arg_short, arg_long, arg_help, arg_dest, arg_store,
+ arg_required):
+ """
+ Add an argument that does not expect anything, but returns a
+ boolean value.
+ """
+ if arg_required:
+ obj = self.__arg_grp_req
+ else:
+ obj = self.__arg_grp_opt
+
+ if arg_store:
+ arg_store = "store_true"
+ else:
+ arg_store = "store_false"
+
+ if self.__is_argparser:
+ if arg_short is None:
+ obj.add_argument(arg_long, help=arg_help, dest=arg_dest,
+ action=arg_store, required=arg_required)
+ else:
+ obj.add_argument(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, action=arg_store,
+ required=arg_required)
+ else:
+ if arg_short is None:
+ obj.add_option(arg_long, help=arg_help, dest=arg_dest,
+ action=arg_store)
+ else:
+ obj.add_option(arg_short, arg_long, help=arg_help,
+ dest=arg_dest, action=arg_store)
+
+ def dependency(self, arg_name, arg_value, dependency):
+ """
+ Check the dependency of a command-line argument.
+ """
+ if dependency is not None:
+ if arg_value is None or str(arg_value) == "":
+ raise Exception("The '%s' argument depends on %s'." %
+ (arg_name, dependency))
+
+ def error(self, obj):
+ """
+ Raise an error and cause the argument parser to print the error
+ message.
+ """
+ if type(obj) == str:
+ obj = obj.strip()
+
+ self.__arg_parser.error(obj)
+
+ def parse_args(self):
+ """
+ Parse and return the command-line arguments.
+ """
+ if self.__is_argparser:
+ args = self.__arg_parser.parse_args()
+ else:
+ (args, values) = self.__arg_parser.parse_args()
+ return args
+
+ def print_help(self):
+ """
+ Print the usage, description, argument details and epilog.
+ """
+ self.__arg_parser.print_help()
+
+ def set_description(self, string):
+ """
+ Set the description text.
+ """
+ self.__arg_parser.description = string.strip()
+
+ def set_epilog(self, string):
+ """
+ Set the epilog text.
+ """
+ self.__arg_parser.epilog = string.strip()
+
+# EOF
diff --git a/python3/core/common.py b/python3/core/common.py
new file mode 100755
index 0000000..3a5194d
--- /dev/null
+++ b/python3/core/common.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# Common core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+from . import paval as pv
+
+
+def get_file_size(file_path):
+ """
+ Get the size of a file in bytes.
+ """
+ pv.path(file_path, "", True, True)
+
+ f = open(file_path, "rb")
+ f.seek(0, 2)
+ file_size = f.tell()
+ f.close()
+
+ return int(file_size)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+# EOF
diff --git a/python3/core/cracker.py b/python3/core/cracker.py
new file mode 100755
index 0000000..aa7f37b
--- /dev/null
+++ b/python3/core/cracker.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# Cracker core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+import os
+from . import paval as pv
+from . import rot13
+from . import rot47
+from . import rot128
+import string as st
+import sys
+
+from datetime import datetime as dt
+
+
+def brute_force(string, file_output, method=None, int_ordinals=False,
+ non_printable=False, limit=True):
+ """
+ Brute-force the given string to find out which ROT variant and
+ rotation value was used to encrypt.
+ """
+ pv.path(file_output, "output", True, False)
+ pv.string(string, "string to decrypt", True)
+ file_output = os.path.abspath(file_output)
+
+ if method is None:
+ method = "All"
+ else:
+ method = method.upper()
+ pv.compstr(method, "method", ["ROT13", "ROT47", "ROT128"])
+
+ list_values_rot13 = []
+ list_values_rot47 = []
+ list_values_rot128 = []
+ timestamp = str(dt.now())
+ output = "\r\n" + "=" * 78 + \
+ "\r\nFile type: ROTate Cracker output file" \
+ "\r\n" + "-" * 78 + \
+ "\r\nOutput file name: " + file_output + \
+ "\r\nString to decrypt: " + string + \
+ "\r\nMethods used: " + method + \
+ "\r\n" + "-" * 78 + \
+ "\r\nTimestamp: " + timestamp[:-7] + \
+ "\r\nROTate version: " + get_version() + \
+ "\r\n" + "=" * 78 + "\r\n\r\n"
+
+ if method == "All" or method == "ROT13":
+ list_values_rot13 = \
+ __get_values_rot13(string, int_ordinals, non_printable, limit)
+ if method == "All" or method == "ROT47":
+ list_values_rot47 = \
+ __get_values_rot47(string, int_ordinals, non_printable, limit)
+ if method == "All" or method == "ROT128":
+ list_values_rot128 = \
+ __get_values_rot128(string, int_ordinals, non_printable, limit)
+
+ if len(list_values_rot13) > 0:
+ output += "\r\n [ROT13]\r\n"
+ for value in list_values_rot13:
+ output += " - %s\r\n" % value
+ output += "\r\n"
+ if len(list_values_rot47) > 0:
+ output += "\r\n [ROT47]\r\n"
+ for value in list_values_rot47:
+ output += " - %s\r\n" % value
+ output += "\r\n"
+ if len(list_values_rot128) > 0:
+ output += "\r\n [ROT128]\r\n"
+ for value in list_values_rot128:
+ output += " - %s\r\n" % value
+ output += "\r\n"
+ fh_output = open(file_output, "wb")
+
+ # Run the appropriate code for the Python framework used
+ if sys.version_info[0] == 2:
+ fh_output.write(output)
+ elif sys.version_info[0] > 2:
+ fh_output.write(output.encode(sys.getdefaultencoding()))
+
+ fh_output.close()
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __get_int_ordinals(string):
+ """
+ Return the integer ordinals of a string.
+ """
+ output = ""
+
+ for char in string:
+ output += str(ord(char)).rjust(3, " ") + ", "
+ output = output.rstrip(", ")
+
+ return output
+
+
+def __get_printable(string):
+ """
+ Replace non-printable characters inside a string with whitespaces
+ """
+ output = ""
+
+ chars_remove = "\t\n\r\x0b\x0c"
+ for char in chars_remove:
+ string = string.replace(char, " ")
+
+ for char in string:
+ if char not in st.printable:
+ output += " "
+ else:
+ output += char
+
+ if output.strip() == "":
+ output = "(only non-printable characters or spaces)"
+
+ return output
+
+
+def __get_values_rot13(string, int_ordinals, non_printable, limit):
+ """
+ Core method to get all possible ROT13 values for the given string.
+ """
+ list_values = []
+ for i in range(1, 26):
+ output = __prepare_line(rot13.decrypt_string(string, i),
+ int_ordinals, non_printable, limit)
+ list_values.append("Value %s: %s" % (str(i).rjust(3, " "), output))
+
+ return list_values
+
+
+def __get_values_rot47(string, int_ordinals, non_printable, limit):
+ """
+ Core method to get all possible ROT47 values for the given string.
+ """
+ list_values = []
+ for i in range(1, 94):
+ output = __prepare_line(rot47.decrypt_string(string, i),
+ int_ordinals, non_printable, limit)
+ list_values.append("Value %s: %s" % (str(i).rjust(3, " "), output))
+
+ return list_values
+
+
+def __get_values_rot128(string, int_ordinals, non_printable, limit):
+ """
+ Core method to get all possible ROT128 values for the given string.
+ """
+ list_values = []
+ for i in range(1, 256):
+ output = __prepare_line(rot128.decrypt_string(string, i),
+ int_ordinals, non_printable, limit)
+ list_values.append("Value %s: %s" % (str(i).rjust(3, " "), output))
+
+ return list_values
+
+
+def __prepare_line(line, int_ordinals, non_printable, limit):
+ """
+ Prepare the line before it gets written into the file.
+ """
+ if int_ordinals:
+ line = __get_int_ordinals(line)
+ else:
+ if not non_printable:
+ line = __get_printable(line)
+
+ if limit:
+ if len(line) > 58:
+ line = line[:55] + "..."
+
+ return line
+
+# EOF
diff --git a/python3/core/paval.py b/python3/core/paval.py
new file mode 100755
index 0000000..159f357
--- /dev/null
+++ b/python3/core/paval.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# PaVal - Parameter validation module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/paval
+# ============================================================================
+
+__version__ = "1.2.7"
+
+import filecmp
+import os
+
+
+def compfile(path, name="", list_files=[]):
+ """
+ Compare files to avoid that the same file is given multiple times or
+ in different ways (e. g. different name but same content).
+ """
+ __string(path, "%s path" % name, True)
+
+ if list_files is None:
+ list_files = []
+ elif len(list_files) == 0:
+ __ex("File list is empty (no files to compare with).", True)
+ else:
+ for item in list_files:
+ if not type(item) == list:
+ __ex("Every list item must be a sub-list.", True)
+ if not len(item) == 2:
+ __ex("Every sub-list must contain two items.", True)
+
+ path = os.path.abspath(path)
+ for item in list_files:
+ path_compare = os.path.abspath(str(item[0]))
+ name_compare = str(item[1])
+ if path == path_compare:
+ __ex("The %s and the %s file path must not be identical." %
+ (name, name_compare), False)
+ if os.path.exists(path) and os.path.exists(path_compare):
+ if filecmp.cmp(path, path_compare, 0):
+ __ex("The %s and %s file content must not be identical." %
+ (name, name_compare), False)
+
+
+def compstr(string, name="", list_strings=[]):
+ """
+ Compare a string with a list of strings and check if it is an item of
+ that list.
+ """
+ __string(string, name, False)
+ if len(list_strings) == 0:
+ __ex("No %s strings to compare with." % name, True)
+ if string not in list_strings:
+ __ex("The %s '%s' does not exist." % (name, string), False)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def intrange(value, name="", value_min=None, value_max=None, zero=False):
+ """
+ Validate an integer range.
+ """
+ value = __integer(value, "%s value" % name, False)
+ if value_min is not None:
+ value_min = __integer(value_min, "minimal %s value" % name, True)
+ intvalue(value_min, name, True, True, True)
+ if value_max is not None:
+ value_max = __integer(value_max, "maximal %s value" % name, True)
+ intvalue(value_max, name, True, True, True)
+ if not zero:
+ if value == 0:
+ __ex("The %s value must not be zero." % name, False)
+ if (value_min is not None) and (value_max is not None):
+ if value_min > value_max:
+ __ex("The maximal %s value must be greater than the minimal "
+ "value." % name, False)
+ if (value_min == value_max) and (not value == value_min):
+ __ex("The %s value can only be %s (depending on further range "
+ "further range arguments)." % (name, value_min), False)
+ if (value < value_min) or (value > value_max):
+ __ex("The %s value must be between %s and %s (depending on "
+ "further range arguments)." % (name, value_min, value_max),
+ False)
+ elif value_min is not None:
+ if value < value_min:
+ __ex("The %s value must not be less than %s." % (name, value_min),
+ False)
+ elif value_max is not None:
+ if value > value_max:
+ __ex("The %s value must not be greater than %s." %
+ (name, value_max), False)
+
+
+def intvalue(value, name="", positive=True, zero=False, negative=False):
+ """
+ Validate a single integer value.
+ """
+ value = __integer(value, "%s value" % name, False)
+ if not positive:
+ if value > 0:
+ __ex("The %s value must not be positive." % name, False)
+ if not zero:
+ if value == 0:
+ __ex("The %s value must not be zero." % name, False)
+ if not negative:
+ if value < 0:
+ __ex("The %s value must not be negative." % name, False)
+
+
+def path(path, name="", is_file=False, exists=False):
+ """
+ Validate a path of a file or directory.
+ """
+ string(path, "%s path" % name, False, None)
+ path = os.path.abspath(path)
+
+ if is_file:
+ path_type = "file"
+ else:
+ path_type = "directory"
+ if exists:
+ if not os.path.exists(path):
+ __ex("The given %s %s does not exist." % (name, path_type), False)
+ if (is_file and not os.path.isfile(path)) or \
+ (not is_file and not os.path.isdir(path)):
+ __ex("The given %s %s path is not a %s." % (name, path_type,
+ path_type), False)
+ else:
+ if os.path.exists(path):
+ __ex("The given %s %s path already exists." % (name, path_type),
+ False)
+
+
+def string(string, name="", wildcards=False, invalid_chars=None):
+ """
+ Validate a string.
+ """
+ __string(string, name, False)
+ if invalid_chars is None:
+ invalid_chars = ""
+ if not wildcards:
+ if ("*" in string) or ("?" in string):
+ __ex("The %s must not contain wildcards." % name, False)
+ if len(invalid_chars) > 0:
+ for char in invalid_chars:
+ if char in string:
+ # Use single quotes by default or double quotes in case the
+ # single quotes are the invalid character
+ quotes = "'"
+ if char == quotes:
+ quotes = "\""
+
+ __ex("The %s contains at least one invalid character "
+ "(%s%s%s)." % (name, quotes, char, quotes), False)
+
+
+def __ex(string, internal=False):
+ """
+ Internal method to raise an exception.
+ """
+ string = str(string).strip()
+ while (" " * 2) in string:
+ string = string.replace((" " * 2), " ")
+ if internal:
+ string = "PaVal: " + string
+ raise Exception(string)
+
+
+def __integer(value, name="", internal=False):
+ """
+ Internal method for basic integer validation.
+ """
+ if value is None:
+ __ex("The %s is missing." % name, internal)
+ if value == "":
+ __ex("The %s must not be empty." % name, internal)
+ try:
+ value = int(value)
+ except ValueError:
+ __ex("The %s must be an integer." % name, internal)
+ return int(value)
+
+
+def __string(string, name="", internal=False):
+ """
+ Internal method for basic string validation.
+ """
+ if string is None:
+ __ex("The %s is missing." % name, internal)
+ if string == "":
+ __ex("The %s must not be empty." % name, internal)
+
+# EOF
diff --git a/python3/core/rot128.py b/python3/core/rot128.py
new file mode 100755
index 0000000..e295a54
--- /dev/null
+++ b/python3/core/rot128.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT128 encryption/decryption core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+from . import common
+import os
+from . import paval as pv
+
+
+def encrypt_file(file_input, file_output, buffer_size=4096, rot_value=128):
+ """
+ Encrypt a file using ROT128.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def encrypt_string(string, rot_value):
+ """
+ Encrypt a string using ROT128.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(True, string, rot_value)
+
+
+def decrypt_file(file_input, file_output, buffer_size=4096, rot_value=128):
+ """
+ Decrypt a file using ROT128.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def decrypt_string(string, rot_value):
+ """
+ Decrypt a string using ROT128.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(False, string, rot_value)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __transform_bytes(encrypt, data_input, rot_value):
+ """
+ Core method to manipulate bytes.
+ """
+ data_output = bytearray(b"")
+
+ for byte in data_input:
+ if encrypt:
+ data_output.append(((byte + rot_value) % 256))
+ else:
+ data_output.append(((byte - rot_value) % 256))
+
+ return data_output
+
+
+def __transform_string(encrypt, string, rot_value):
+ """
+ Core method to manipulate a string.
+ """
+ output = ""
+
+ for char in string:
+ if encrypt:
+ output += chr(((ord(char) + rot_value) % 256))
+ else:
+ output += chr(((ord(char) + rot_value) % 256))
+
+ return output
+
+# EOF
diff --git a/python3/core/rot13.py b/python3/core/rot13.py
new file mode 100755
index 0000000..03833b7
--- /dev/null
+++ b/python3/core/rot13.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT13 encryption/decryption core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+from . import common
+import os
+from . import paval as pv
+
+
+def encrypt_file(file_input, file_output, buffer_size=4096, rot_value=13):
+ """
+ Encrypt a file using ROT13.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(True, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def encrypt_string(string, rot_value=13):
+ """
+ Encrypt a string using ROT13.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(True, string, rot_value)
+
+
+def decrypt_file(file_input, file_output, buffer_size=4096, rot_value=13):
+ """
+ Decrypt a file using ROT13.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def decrypt_string(string, rot_value=13):
+ """
+ Decrypt a string using ROT13.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(False, string, rot_value)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __transform_bytes(encrypt, data_input, rot_value):
+ """
+ Core method to manipulate bytes.
+ """
+ data_output = bytearray(b"")
+
+ for byte in data_input:
+ if byte in range(65, 91):
+ # Uppercase letters
+ if encrypt:
+ data_output.append((((byte - 65) + rot_value) % 26) + 65)
+ else:
+ data_output.append((((byte - 65) - rot_value) % 26) + 65)
+ elif byte in range(97, 123):
+ # Lowercase letters
+ if encrypt:
+ data_output.append((((byte - 97) + rot_value) % 26) + 97)
+ else:
+ data_output.append((((byte - 97) - rot_value) % 26) + 97)
+ else:
+ data_output.append(byte)
+
+ return data_output
+
+
+def __transform_string(encrypt, string, rot_value):
+ """
+ Core method to manipulate a string.
+ """
+ output = ""
+
+ for char in string:
+ if ord(char) in range(65, 91):
+ # Uppercase letters
+ if encrypt:
+ output += chr((((ord(char) - 65) + rot_value) % 26) + 65)
+ else:
+ output += chr((((ord(char) - 65) - rot_value) % 26) + 65)
+ elif ord(char) in range(97, 123):
+ # Lowercase letters
+ if encrypt:
+ output += chr((((ord(char) - 97) + rot_value) % 26) + 97)
+ else:
+ output += chr((((ord(char) - 97) - rot_value) % 26) + 97)
+ else:
+ output += char
+
+ return output
+
+# EOF
diff --git a/python3/core/rot47.py b/python3/core/rot47.py
new file mode 100755
index 0000000..6ef3c47
--- /dev/null
+++ b/python3/core/rot47.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT47 encryption/decryption core module
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+__version__ = "3.0.6"
+
+from . import common
+import os
+from . import paval as pv
+
+
+def encrypt_file(file_input, file_output, buffer_size=4096, rot_value=47):
+ """
+ Encrypt a file using ROT47.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def encrypt_string(string, rot_value=47):
+ """
+ Encrypt a string using ROT47.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(True, string, rot_value)
+
+
+def decrypt_file(file_input, file_output, buffer_size=4096, rot_value=47):
+ """
+ Decrypt a file using ROT47.
+ """
+ pv.path(file_input, "input", True, True)
+ pv.path(file_output, "output", True, False)
+ pv.intvalue(buffer_size, "buffer size", True, False, False)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+ pv.compfile(file_input, "input", [[file_output, "output"]])
+
+ buffer_size = int(buffer_size)
+ rot_value = int(rot_value)
+ file_input = os.path.abspath(file_input)
+ file_output = os.path.abspath(file_output)
+
+ fh_input = open(file_input, "rb")
+ fh_output = open(file_output, "wb")
+
+ file_size = common.get_file_size(file_input)
+ byte_blocks = int(file_size / buffer_size)
+ byte_remainder = file_size % buffer_size
+
+ for block in range(byte_blocks):
+ data_input = bytearray(b"" + fh_input.read(buffer_size))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+ if byte_remainder > 0:
+ data_input = bytearray(b"" + fh_input.read(byte_remainder))
+ fh_output.write(__transform_bytes(False, data_input, rot_value))
+
+ fh_input.close()
+ fh_output.close()
+
+
+def decrypt_string(string, rot_value=47):
+ """
+ Decrypt a string using ROT47.
+ """
+ pv.string(string, "string", True)
+ pv.intvalue(rot_value, "ROT", True, False, False)
+
+ return __transform_string(False, string, rot_value)
+
+
+def get_version():
+ """
+ Return the version of this module.
+ """
+ return __version__
+
+
+def __transform_bytes(encrypt, data_input, rot_value):
+ """
+ Core method to manipulate bytes.
+ """
+ data_output = bytearray(b"")
+
+ for byte in data_input:
+ if byte in range(32, 126):
+ if encrypt:
+ data_output.append((((byte - 32) + rot_value) % 94) + 32)
+ else:
+ data_output.append((((byte - 32) - rot_value) % 94) + 32)
+ else:
+ data_output.append(byte)
+
+ return data_output
+
+
+def __transform_string(encrypt, string, rot_value):
+ """
+ Core method to manipulate a string.
+ """
+ output = ""
+
+ for char in string:
+ if ord(char) in range(32, 126):
+ if encrypt:
+ output += chr((((ord(char) - 32) + rot_value) % 94) + 32)
+ else:
+ output += chr((((ord(char) - 32) - rot_value) % 94) + 32)
+ else:
+ output += char
+
+ return output
+
+# EOF
diff --git a/python3/docs/usage_rotate-cracker.txt b/python3/docs/usage_rotate-cracker.txt
new file mode 100644
index 0000000..60c548f
--- /dev/null
+++ b/python3/docs/usage_rotate-cracker.txt
@@ -0,0 +1,83 @@
+
+USAGE (rotate-cracker.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Crack a ROT encrypted file
+
+ 1. Definition
+
+ The ROTate Cracker script helps to determine which ROT variant and
+ rotation value was used to encrypt a file or string by trying all
+ supported ROT variants with all rotation values available (brute
+ force).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-cracker.py --help
+
+ 3. Crack a ROT encrypted file
+
+ For example, you have the ROT encrypted file "secret.txt" containing
+ the line
+
+ Uryyb, Jbeyq!
+
+ and want to decrypt it. So, to find out which ROT method and rotation
+ value was used to encrypt the file, a string from the file (e. g. a
+ word) is required.
+
+ This example uses the string "Uryyb" and writes the decrypted values
+ into the file "/tmp/output.txt":
+
+ $ ./rotate-cracker.py -s "Uryyb" -o /tmp/output.txt
+
+ The output file shows that when using the ROT13 method, the string
+ "Uryyb" decrypted with value 13 returns the word "Hello":
+
+ (...)
+ [ROT13]
+ - Value 0: Uryyb
+ - Value 1: Tqxxa
+ - Value 2: Spwwz
+ - Value 3: Rovvy
+ - Value 4: Qnuux
+ - Value 5: Pmttw
+ - Value 6: Olssv
+ - Value 7: Nkrru
+ - Value 8: Mjqqt
+ - Value 9: Lipps
+ - Value 10: Khoor
+ - Value 11: Jgnnq
+ - Value 12: Ifmmp
+ - Value 13: Hello
+ (...)
+
+ So, the file or at least that string seems to be encrypted with that
+ method and value. Unfortunately, there is no analysis feature, which
+ means you will have to look at the returned value list to manually
+ check which entry makes most sense.
+
+ If there are non-printable characters inside the string, they will be
+ replaced by whitespaces. If the string only contains of whitespaces,
+ a notice will be written to the output file.
+
diff --git a/python3/docs/usage_rotate-rot128.txt b/python3/docs/usage_rotate-rot128.txt
new file mode 100644
index 0000000..53b835d
--- /dev/null
+++ b/python3/docs/usage_rotate-rot128.txt
@@ -0,0 +1,56 @@
+
+USAGE (rotate-rot128.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Encrypt a file
+ 4. Decrypt the file again
+ 5. User-defined rotation values
+
+ 1. Definition
+
+ The ROTate ROT128 script encrypts and decrypts a file using the ROT128
+ method with the default rotation value or a user-defined one (using
+ the ROT128 character set).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-rot128.py --help
+
+ 3. Encrypt a file
+
+ This works as described in section 3 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT128 instead of the
+ script used there.
+
+ 4. Decrypt the file again
+
+ This works as described in section 4 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT128 instead of the
+ script used there.
+
+ 5. User-defined rotation values
+
+ This works as described in section 5 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT128 instead of the
+ script used there.
+
diff --git a/python3/docs/usage_rotate-rot13.txt b/python3/docs/usage_rotate-rot13.txt
new file mode 100644
index 0000000..918322b
--- /dev/null
+++ b/python3/docs/usage_rotate-rot13.txt
@@ -0,0 +1,74 @@
+
+USAGE (rotate-rot13.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Encrypt a file
+ 4. Decrypt the file again
+ 5. User-defined rotation values
+
+ 1. Definition
+
+ The ROTate ROT13 script encrypts and decrypts a file using the ROT13
+ method with the default rotation value or a user-defined one (using
+ the ROT13 character set).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-rot13.py --help
+
+ 3. Encrypt a file
+
+ For example, if you have the input file "hello.txt" containing the
+ line
+
+ Hello, world!
+
+ and want to encrypt it using ROT13 (with its default value) and write
+ the encrypted data into "secret.txt", the command line would look like
+ this:
+
+ $ ./rotate-rot13.py -i hello.txt -o secret.txt -a encrypt
+
+ The encrypted data inside "secret.txt" should look like this:
+
+ Uryyb, Jbeyq!
+
+ 4. Decrypt the file again
+
+ To decrypt the file again and write the decrypted data into the file
+ "output.txt", type:
+
+ $ ./rotate-rot13.py -i secret.txt -o output.txt -a decrypt
+
+ 5. User-defined rotation values
+
+ You can also encrypt the file using the ROT13 method, but with
+ rotation value 19 instead of the default value:
+
+ $ ./rotate-rot13.py -i hello.txt -o secret.txt -a encrypt -v 19
+
+ The user-defined rotation value used to encrypt is also required to
+ properly decrypt the file again:
+
+ $ ./rotate-rot13.py -i secret.txt -o output.txt -a decrypt -v 19
+
diff --git a/python3/docs/usage_rotate-rot47.txt b/python3/docs/usage_rotate-rot47.txt
new file mode 100644
index 0000000..1498be1
--- /dev/null
+++ b/python3/docs/usage_rotate-rot47.txt
@@ -0,0 +1,56 @@
+
+USAGE (rotate-rot47.py)
+
+ Contents:
+
+ 1. Definition
+ 2. General stuff
+ 2.1 How to run Python scripts
+ 2.2 Overview of all command-line arguments
+ 3. Encrypt a file
+ 4. Decrypt the file again
+ 5. User-defined rotation values
+
+ 1. Definition
+
+ The ROTate ROT47 script encrypts and decrypts a file using the ROT47
+ method with the default rotation value or a user-defined one (using
+ the ROT47 character set).
+
+ 2. General stuff
+
+ 2.1 How to run Python scripts
+
+ All usage examples below show how to execute the Python scripts on
+ the shell of a Unix-like system. If you do not know, how to run
+ those scripts on your operating system, you may have a look at
+ this page:
+
+ http://www.urbanware.org/howto_python.html
+
+ 2.2 Overview of all command-line arguments
+
+ Usually, each script requires command-line arguments to operate.
+ So, to get an overview of all arguments available, simply run the
+ script with the "--help" argument. For example:
+
+ $ ./rotate-rot47.py --help
+
+ 3. Encrypt a file
+
+ This works as described in section 3 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT47 instead of the script
+ used there.
+
+ 4. Decrypt the file again
+
+ This works as described in section 4 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT47 instead of the script
+ used there.
+
+ 5. User-defined rotation values
+
+ This works as described in section 5 inside the ROT13 documentation,
+ but, of course, you need to run the ROTate ROT47 instead of the script
+ used there.
+
diff --git a/python3/license.txt b/python3/license.txt
new file mode 100644
index 0000000..d87dbbb
--- /dev/null
+++ b/python3/license.txt
@@ -0,0 +1,33 @@
+
+LICENSE (ROTate)
+
+ General
+
+ This project is distributed under the MIT License which can be found
+ below, inside the file "readme.txt" as well as in the header of each
+ script file. In case the license text is missing for some reason, you
+ can also find it here:
+
+ http://www.urbanware.org/license.html
+
+ License terms
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/python3/readme.txt b/python3/readme.txt
new file mode 100644
index 0000000..34bb176
--- /dev/null
+++ b/python3/readme.txt
@@ -0,0 +1,49 @@
+
+README (ROTate)
+
+ Project
+
+ ROTate
+ Version 3.0.6 (based on Python framework 3.x)
+ Copyright (C) 2018 by Ralf Kilian
+
+ Website: http://www.urbanware.org
+ GitHub: https://github.com/urbanware-org/rotate
+
+ Definition
+
+ The ROTate project is a collection of scripts to encrypt and decrypt
+ files using various ROT cipher methods.
+
+ License
+
+ This project is distributed under the MIT License. You should have
+ received a copy of the license along with this program (see the
+ 'license.txt' file). If not, you can find the license terms here:
+
+ https://opensource.org/licenses/MIT
+
+ Requirements
+
+ The requirements for this project can be found inside the included
+ file 'requirements.txt'.
+
+ In case this file is missing, you can also find the information on the
+ website of the project mentioned above.
+
+ Usage
+
+ For fundamental documentation as well as some usage examples for each
+ component of the project, you may have a look at the text files inside
+ the included 'docs' sub-directory.
+
+ Legal information
+
+ The project name is completely fictitious. Any correspondences with
+ existing websites, applications, companies and/or other projects are
+ purely coincidental.
+
+ All trademarks belong to their respective owners.
+
+ Errors and omissions excepted.
+
diff --git a/python3/requirements.txt b/python3/requirements.txt
new file mode 100644
index 0000000..5392db1
--- /dev/null
+++ b/python3/requirements.txt
@@ -0,0 +1,17 @@
+
+REQUIREMENTS (ROTate)
+
+ Notice
+
+ This version of ROTate was built for the Python 3.x framework. If you
+ need a version that works with Python 2.x, you may look here:
+
+ http://github.com/urbanware-org/rotate
+
+ General
+
+ Software requirements:
+
+ - Python 2.x (version 2.7 or higher is recommended, may also work
+ with earlier versions)
+
diff --git a/python3/rotate-cracker.py b/python3/rotate-cracker.py
new file mode 100755
index 0000000..e63915e
--- /dev/null
+++ b/python3/rotate-cracker.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# Cracker script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import cracker
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print("%s: error: %s" % (os.path.basename(sys.argv[0]), e))
+ sys.exit(1)
+
+ p.set_description("Determine which ROT variant and rotation value was "
+ "used to encrypt a file or string by trying all "
+ "supported ROT variants with all rotation values "
+ "available (brute force).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+ p.add_avalue("-s", "--string", "string to decrypt", "string", None, True)
+
+ # Define optional arguments
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_switch(None, "--int-ordinals", "write integer ordinals instead "
+ "of the characters into the output file", "int_ordinals",
+ True, False)
+ p.add_predef("-m", "--method", "use only one instead of all supported "
+ "methods to decrypt", "method", ["rot13", "rot47", "rot128"],
+ False)
+ p.add_switch(None, "--no-limit", "do not limit the length of the lines "
+ "inside the output file", "no_limit", False, False)
+ p.add_switch(None, "--non-printable", "write non-printable characters "
+ "into the output file", "non_printable", True, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print(cracker.get_version())
+ sys.exit(0)
+
+ args = p.parse_args()
+ try:
+ cracker.brute_force(args.string, args.output_file, args.method,
+ args.int_ordinals, args.non_printable,
+ args.no_limit)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python3/rotate-rot128.py b/python3/rotate-rot128.py
new file mode 100755
index 0000000..fddfbc2
--- /dev/null
+++ b/python3/rotate-rot128.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT128 encryption/decryption script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import rot128
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print("%s: error: %s" % (os.path.basename(sys.argv[0]), e))
+ sys.exit(1)
+
+ p.set_description("Encrypt or decrypt a file using ROT128 with the "
+ "default rotation value or a user-defined one (using "
+ "the ROT128 character set).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_predef("-a", "--action", "action to perform", "action",
+ ["encrypt", "decrypt"], True)
+ p.add_avalue("-i", "--input-file", "input file path", "input_file", None,
+ True)
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+
+ # Define optional arguments
+ p.add_avalue("-b", "--buffer-size", "buffer size in bytes", "buffer_size",
+ 4096, False)
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_avalue("-v", "--value", "user-defined rotation value between 0 and "
+ "255", "value", 128, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print(rot128.get_version())
+ sys.exit(0)
+
+ args = p.parse_args()
+ if args.action is None:
+ p.error("The required action argument is missing.")
+ elif args.action.lower() == ("encrypt"):
+ encrypt = True
+ elif args.action.lower() == ("decrypt"):
+ encrypt = False
+ else:
+ p.error("An unsupported action was given.")
+
+ try:
+ if encrypt:
+ rot128.encrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ else:
+ rot128.decrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python3/rotate-rot13.py b/python3/rotate-rot13.py
new file mode 100755
index 0000000..c14dfd9
--- /dev/null
+++ b/python3/rotate-rot13.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT13 encryption/decryption script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import rot13
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print("%s: error: %s" % (os.path.basename(sys.argv[0]), e))
+ sys.exit(1)
+
+ p.set_description("Encrypt or decrypt a file using ROT13 with the "
+ "default rotation value or a user-defined one (using "
+ "the ROT13 character set).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_predef("-a", "--action", "action to perform", "action",
+ ["encrypt", "decrypt"], True)
+ p.add_avalue("-i", "--input-file", "input file path", "input_file", None,
+ True)
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+
+ # Define optional arguments
+ p.add_avalue("-b", "--buffer-size", "buffer size in bytes", "buffer_size",
+ 4096, False)
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_avalue("-v", "--value", "user-defined rotation value between 0 and "
+ "25", "value", 13, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print(rot13.get_version())
+ sys.exit(0)
+
+ args = p.parse_args()
+ if args.action is None:
+ p.error("The required action argument is missing.")
+ elif args.action.lower() == ("encrypt"):
+ encrypt = True
+ elif args.action.lower() == ("decrypt"):
+ encrypt = False
+ else:
+ p.error("An unsupported action was given.")
+
+ try:
+ if encrypt:
+ rot13.encrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ else:
+ rot13.decrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/python3/rotate-rot47.py b/python3/rotate-rot47.py
new file mode 100755
index 0000000..78d6cd4
--- /dev/null
+++ b/python3/rotate-rot47.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# ============================================================================
+# ROTate - Encryption tool based on the ROT cipher
+# ROT47 encryption/decryption script
+# Copyright (C) 2018 by Ralf Kilian
+# Distributed under the MIT License (https://opensource.org/licenses/MIT)
+#
+# Website: http://www.urbanware.org
+# GitHub: https://github.com/urbanware-org/rotate
+# ============================================================================
+
+import os
+import sys
+
+
+def main():
+ from core import clap
+ from core import rot47
+
+ try:
+ p = clap.Parser()
+ except Exception as e:
+ print("%s: error: %s" % (os.path.basename(sys.argv[0]), e))
+ sys.exit(1)
+
+ p.set_description("Encrypt or decrypt a file using ROT47 with the "
+ "default rotation value or a user-defined one (using "
+ "the ROT47 character set).")
+ p.set_epilog("Further information and usage examples can be found inside "
+ "the documentation file for this script.")
+
+ # Define required arguments
+ p.add_predef("-a", "--action", "action to perform", "action",
+ ["encrypt", "decrypt"], True)
+ p.add_avalue("-i", "--input-file", "input file path", "input_file", None,
+ True)
+ p.add_avalue("-o", "--output-file", "output file path", "output_file",
+ None, True)
+
+ # Define optional arguments
+ p.add_avalue("-b", "--buffer-size", "buffer size in bytes", "buffer_size",
+ 4096, False)
+ p.add_switch("-h", "--help", "print this help message and exit", None,
+ True, False)
+ p.add_avalue("-v", "--value", "user-defined rotation value between 0 and "
+ "93", "value", 47, False)
+ p.add_switch(None, "--version", "print the version number and exit", None,
+ True, False)
+
+ if len(sys.argv) == 1:
+ p.error("At least one required argument is missing.")
+ elif ("-h" in sys.argv) or ("--help" in sys.argv):
+ p.print_help()
+ sys.exit(0)
+ elif "--version" in sys.argv:
+ print(rot47.get_version())
+ sys.exit(0)
+
+ args = p.parse_args()
+ if args.action is None:
+ p.error("The required action argument is missing.")
+ elif args.action.lower() == ("encrypt"):
+ encrypt = True
+ elif args.action.lower() == ("decrypt"):
+ encrypt = False
+ else:
+ p.error("An unsupported action was given.")
+
+ try:
+ if encrypt:
+ rot47.encrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ else:
+ rot47.decrypt_file(args.input_file, args.output_file,
+ args.buffer_size, args.value)
+ except Exception as e:
+ p.error(e)
+
+
+if __name__ == "__main__":
+ main()
+
+# EOF
diff --git a/rotate.png b/rotate.png
new file mode 100644
index 0000000..2f7d554
Binary files /dev/null and b/rotate.png differ