import sys
import argparse
import math
ADFGVX = 'ADFGVX'
def build_polybius_square(key):
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
seen = set()
square_key = ''.join([c for c in key.upper() if c not in seen and not seen.add(c)])
remaining = ''.join([c for c in alphabet if c not in square_key])
full_key = square_key + remaining
square = {}
rev_square = {}
i = 0
for r in ADFGVX:
for c in ADFGVX:
digraph = r + c
char = full_key[i]
square[char] = digraph
rev_square[digraph] = char
i += 1
return square, rev_square
def columnar_transposition(text, key):
num_cols = len(key)
num_rows = math.ceil(len(text) / num_cols)
padded_text = text.ljust(num_cols * num_rows, 'X')
matrix = [padded_text[i:i+num_cols] for i in range(0, len(padded_text), num_cols)]
key_order = sorted(list(enumerate(key)), key=lambda x: x[1])
result = ''
for idx, _ in key_order:
result += ''.join(row[idx] for row in matrix)
return result
def inverse_columnar_transposition(ciphertext, keyword):
num_cols = len(keyword)
num_rows = math.ceil(len(ciphertext) / num_cols)
padded_len = num_rows * num_cols
padded_cipher = ciphertext.ljust(padded_len, 'X')
sorted_key = sorted(list(enumerate(keyword)), key=lambda x: x[1])
col_order = [idx for idx, _ in sorted_key]
columns = [''] * num_cols
k = 0
for col in col_order:
col_len = num_rows
columns[col] = padded_cipher[k:k+col_len]
k += col_len
plaintext = ''
for i in range(num_rows):
for col in range(num_cols):
if i < len(columns[col]):
plaintext += columns[col][i]
return plaintext
def adfgvx_encrypt(plaintext, transposition_key, square_key):
plaintext = ''.join(plaintext.strip().upper().replace(' ', '').replace('\n', ''))
square, _ = build_polybius_square(square_key)
polybius_text = ''.join(square[c] for c in plaintext if c in square)
return columnar_transposition(polybius_text, transposition_key)
def adfgvx_decrypt(ciphertext, transposition_key, square_key):
ciphertext = ''.join(ciphertext.strip().upper().split())
_, rev_square = build_polybius_square(square_key)
intermediate = inverse_columnar_transposition(ciphertext, transposition_key)
plaintext = ''
for i in range(0, len(intermediate), 2):
digraph = intermediate[i:i+2]
if len(digraph) == 2 and digraph in rev_square:
plaintext += rev_square[digraph]
return plaintext
def main():
parser = argparse.ArgumentParser(description="ADFGVX encryptor/decryptor")
parser.add_argument('-k', '--key', required=False, help='Transposition key')
parser.add_argument('-s', '--square', required=False, help='Polybius square key')
parser.add_argument('-m', '--mode', choices=['encrypt', 'decrypt'], required=True, help='Mode')
parser.add_argument('file', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help='Input file or STDIN')
args = parser.parse_args()
data = args.file.read()
key="ADFGVX"
square="ADFGVX"
try:
key = args.key.upper()
except:
pass
try:
square = args.square.upper()
except:
pass
if args.mode == 'encrypt':
result = adfgvx_encrypt(data, key.upper(), square.upper())
else:
result = adfgvx_decrypt(data, key.upper(), square.upper())
print(result)
if __name__ == "__main__":
main()