Skip to content

Commit 8dfb528

Browse files
committed
finished command line arguments for decryption
1 parent 479e7c1 commit 8dfb528

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed

route-cipher.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
reoute-cipher.py
5+
"""
6+
7+
from pathlib import Path
8+
from copy import deepcopy
9+
from argparse import ArgumentParser
10+
import logging
11+
import sys
12+
13+
# TEST_CIPHERTEXT = '16 12 8 4 0 1 5 9 13 17 18 14 10 6 2 3 7 11 15 19'
14+
# TEST_COLS = 4
15+
# TEST_ROWS = 5
16+
# TEST_KEY = '-1 2 -3 4' # negative reads upwards, positive downwards
17+
18+
logger = None
19+
20+
def main():
21+
args = parse_arguments()
22+
logger = init_logger(args.verbose)
23+
24+
if args.file:
25+
logger.info(f'Reading from file at {args.file.resolve()}')
26+
with open(args.file) as f:
27+
ciphertext = f.read()
28+
else:
29+
ciphertext = args.message
30+
31+
validate_length(ciphertext, args.rows, args.columns)
32+
33+
translation_matrix = create_matrix(
34+
ciphertext,
35+
args.key,
36+
args.columns,
37+
args.rows
38+
)
39+
40+
print(logger.getEffectiveLevel())
41+
42+
if logger.getEffectiveLevel() >= 10:
43+
print_matrix(translation_matrix)
44+
45+
plaintext = decrypt(translation_matrix, args.rows)
46+
47+
print(plaintext)
48+
49+
50+
def validate_length(ciphertext, rows, cols):
51+
"""
52+
Checks that the input columns and rows are valid respective to the
53+
ciphertext length.
54+
"""
55+
factors = []
56+
len_cipher = len(ciphertext.split())
57+
58+
# Exclude 1x1 grids as that would be ciphertext
59+
for i in range(2, len_cipher):
60+
if len_cipher % i == 0:
61+
factors.append(i)
62+
63+
if rows * cols != len_cipher:
64+
print("Error - Input columns and rows not factors of length of ciphertext.")
65+
sys.exit(1)
66+
67+
68+
def validate_key(key, cols):
69+
"""Turn key into list of integers and checks it for validity"""
70+
71+
key_int = [int(k) for k in key.split()]
72+
lowest = min(key_int)
73+
highest = max(key_int)
74+
75+
if 0 in key_int:
76+
print('Invalid key - 0 is not a valid value')
77+
sys.exit(1)
78+
79+
if (
80+
len(key_int) != cols or \
81+
lowest < -cols or \
82+
highest > cols
83+
):
84+
print('Invalid key - Invalid number of of columns and/or rows')
85+
86+
return key_int
87+
88+
89+
def decrypt(matrix, rows):
90+
plaintext = ''
91+
matrix_copy = deepcopy(matrix)
92+
93+
for _ in range(rows):
94+
for col in matrix_copy:
95+
word = str(col.pop())
96+
plaintext += word + ' '
97+
98+
return plaintext
99+
100+
101+
def create_matrix(ciphertext, key, cols, rows):
102+
"""Turns a given ciphertext into a matrix of size cols x rows"""
103+
104+
ciphertext_list = ciphertext.split()
105+
translation_matrix = [None] * cols
106+
start = 0
107+
stop = rows
108+
109+
for k in validate_key(key, cols):
110+
111+
col_items = ciphertext_list[start:stop]
112+
113+
# Reverse order of ciphertext based on sign on key
114+
if int(k) > 0:
115+
col_items = list(reversed(col_items))
116+
117+
# Subtract one for python's 0 index
118+
translation_matrix[abs(int(k)) - 1] = col_items
119+
120+
# Increase counters for next iteration
121+
start += rows
122+
stop += rows
123+
124+
return translation_matrix
125+
126+
127+
def print_matrix(matrix):
128+
print('Translation Matrix', *matrix, sep='\n')
129+
130+
131+
def init_logger(verbosity):
132+
logger = logging.getLogger(__name__)
133+
if verbosity == 1:
134+
logger.setLevel(logging.INFO)
135+
if verbosity > 1:
136+
logger.setLevel(logging.DEBUG)
137+
return logger
138+
139+
140+
def parse_arguments():
141+
parser = ArgumentParser()
142+
143+
parser.add_argument('-k', '--key',
144+
help='key used to encrypt/decrypt the message'
145+
)
146+
parser.add_argument('-c', '--columns',
147+
help='Number of "columns" to use to create the route cipher',
148+
type=int
149+
)
150+
parser.add_argument('-r', '--rows',
151+
help='Number of "rows" to use to create the route cipher',
152+
type=int
153+
)
154+
155+
operations = parser.add_mutually_exclusive_group(required=True)
156+
operations.add_argument('-e', '--encrypt',
157+
help='run script with encryption instructions',
158+
action='store_true'
159+
)
160+
operations.add_argument('-d', '--decrypt',
161+
help='run script with decryption instructions',
162+
action='store_true'
163+
)
164+
165+
source = parser.add_mutually_exclusive_group(required=True)
166+
source.add_argument('-m', '--message',
167+
help='string to either encrypt or decrypt'
168+
)
169+
source.add_argument('-f', '--file',
170+
help='path to file containing the ciphertext',
171+
type=Path
172+
)
173+
174+
parser.add_argument('-v', '--verbose',
175+
help='prints additional information to terminal output',
176+
action='count'
177+
)
178+
return parser.parse_args()
179+
180+
181+
if __name__ == '__main__':
182+
main()

0 commit comments

Comments
 (0)