Skip to content

Commit

Permalink
docopt rules! except that it adds a dependency :(
Browse files Browse the repository at this point in the history
  • Loading branch information
hellman committed Apr 23, 2014
1 parent 1a28d41 commit 74891d6
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 119 deletions.
3 changes: 2 additions & 1 deletion setup.py
Expand Up @@ -19,6 +19,7 @@

packages=['xortool'],
provides=['xortool'],
install_requires=['docopt>=0.6.1'],
scripts=["xortool/xortool", "xortool/xortool-xor"],

classifiers=['Development Status :: 4 - Beta',
Expand All @@ -30,5 +31,5 @@
'License :: OSI Approved :: MIT License',
'Topic :: Scientific/Engineering :: Mathematics',
'Topic :: Security :: Cryptography',
],
],
)
110 changes: 15 additions & 95 deletions xortool/args.py
@@ -1,108 +1,28 @@
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import getopt
from docopt import docopt

from routine import *
from routine import parse_char


class ArgError(Exception):
pass


PARAMETERS = {
"input_is_hex": 0,
"max_key_length": 65,
"known_key_length": None,
"most_frequent_char": None,
"brute_chars": None,
"brute_printable": None,
"frequency_spread": 0,
"filename": "-", # stdin by default
}


def show_usage_and_exit():
print """xortool.py
A tool to do some xor analysis:
- guess the key length (based on count of equal chars)
- guess the key (base on knowledge of most probable char)
Usage:
{} [-h|--help] [OPTIONS] [<filename>]
Options:
-l,--key-length length of the key (integer)
-c,--char most possible char (one char or hex code)
-m,--max-keylen=32 maximum key length to probe (integer)
-x,--hex input is hex-encoded str
-b,--brute-chars brute force all possible characters
-o,--brute-printable same as -b but will only use printable
characters for keys
""".format(os.path.basename(sys.argv[0]))
sys.exit(1)


def parse_parameters():
"""
Parse arguments and update PARAMETERS if needed
"""
options, arguments = get_options_and_arguments(sys.argv[1:])
update_parameters(options, arguments)
return PARAMETERS


def get_options_and_arguments(program_arguments):
options, arguments = [], []
def parse_parameters(doc, version):
p = docopt(doc, version=version)
p = {k.lstrip("-"): v for k, v in p.items()}
try:
options, arguments = getopt.gnu_getopt(program_arguments,
"l:c:s:m:xbo",
["key-length=",
"char=",
"spread=",
"max-keylen=",
"hex",
"help",
"usage",
"brute-chars",
"brute-printable"])

except getopt.GetoptError:
show_usage_and_exit()
return options, arguments


def update_parameters(options, arguments):
global PARAMETERS
try:
for option, value in options:
if option in ("-x", "--hex"):
PARAMETERS["input_is_hex"] = 1
elif option in ("-c", "--char"):
PARAMETERS["most_frequent_char"] = parse_char(value)
elif option in ("-l", "--key-length"):
PARAMETERS["known_key_length"] = int(value)
elif option in ("-b", "--brute-chars"):
PARAMETERS["brute_chars"] = True
elif option in ("-o", "--brute-printable"):
PARAMETERS["brute_printable"] = True
elif option in ("-s", "--spread"):
PARAMETERS["frequency_spread"] = int(value)
elif option in ("-m", "--max-keylen"):
PARAMETERS["max_key_length"] = int(value)
elif option in ("-h", "--help", "--usage"):
show_usage_and_exit()
else:
raise ArgError("Unknown argument: {0}".format(option))
return {
"input_is_hex": bool(p["hex"]),
"max_key_length": int(p["max-keylen"]),
"known_key_length": int(p["key-length"]) if p["key-length"] else None,
"most_frequent_char": parse_char(p["char"]) if p["char"] else None,
"brute_chars": bool(p["brute-chars"]),
"brute_printable": bool(p["brute-printable"]),
"frequency_spread": 0, # to be removed
"filename": p["FILE"] if p["FILE"] else "-", # stdin by default
}
except ValueError as err:
raise ArgError(str(err))

if ((PARAMETERS["most_frequent_char"] and PARAMETERS["brute_printable"]
or
PARAMETERS["most_frequent_char"] and PARAMETERS["brute_chars"]
or
PARAMETERS["brute_printable"] and PARAMETERS["brute_chars"])):
raise ArgError("Only one out of -c, -b or -o should be used")

if len(arguments) == 1:
PARAMETERS["filename"] = arguments[0]

return
56 changes: 33 additions & 23 deletions xortool/xortool
@@ -1,28 +1,38 @@
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# ---------------------------------------------------------------
# xortool.py
# A tool to do some xor analysis:
# - guess the key length (based on count of equal chars)
# - guess the key (base on knowledge of most frequent char)
# Usage:
# xortool [-h|--help] [OPTIONS] [<filename>]
# Options:
# -l,--key-length length of the key (integer)
# -c,--char most frequent char (one char or hex code)
# -m,--max-keylen=65 maximum key length to probe (integer)
# -x,--hex input is hex-encoded str
# Examples:
# xortool file.bin
# xortool -x -l 4 -c ' ' file.hex
# ---------------------------------------------------------------
# Author: hellman ( hellman1908@gmail.com )
# License: MIT License ( http://opensource.org/licenses/MIT )
# ---------------------------------------------------------------
"""
xortool
A tool to do some xor analysis:
- guess the key length (based on count of equal chars)
- guess the key (base on knowledge of most frequent char)
Usage:
xortool [-x] [-m MAX-LEN] [FILE]
xortool [-x] [-l LEN] [-c CHAR | -b | -o] [FILE]
xortool [-x] [-m MAX-LEN| -l LEN] [-c CHAR | -b | -o] [FILE]
xortool [-h | --help]
xortool --version
Options:
-x --hex input is hex-encoded str
-l LEN, --key-length=LEN length of the key
-m MAX-LEN, --max-keylen=MAX-LEN maximum key length to probe [default: 65]
-c CHAR, --char=CHAR most frequent char (one char or hex code)
-b --brute-chars brute force all possible most frequent chars
-o --brute-printable same as -b but will only check printable chars
-h --help show this help
Examples:
xortool file.bin
xortool -l 11 -c 20 file.bin
xortool -x -c ' ' file.hex
"""

from operator import itemgetter

import os
import string
import xortool
from xortool.colors import *

from xortool.routine import *
Expand All @@ -35,15 +45,15 @@ PARAMETERS = dict()
def main():
global PARAMETERS
try:
PARAMETERS = parse_parameters()
PARAMETERS = parse_parameters(__doc__, xortool.__version__)
ciphertext = get_ciphertext()
update_key_length(ciphertext)

if PARAMETERS["brute_chars"] is not None:
if PARAMETERS["brute_chars"]:
try_chars = range(256)
elif PARAMETERS["brute_printable"] is not None:
elif PARAMETERS["brute_printable"]:
try_chars = map(ord, string.printable)
elif PARAMETERS["most_frequent_char"] is not None:
elif PARAMETERS["most_frequent_char"]:
try_chars = [PARAMETERS["most_frequent_char"]]
else:
die(C_WARN +
Expand Down

0 comments on commit 74891d6

Please sign in to comment.