import math
import random
MOD_ALL = 1114112
# Columnar Transposition
def columnar_encrypt(text, pad):
cols = random.randint(1, len(text) - 1)
rows = math.ceil(len(text) / cols)
padded = text.ljust((rows * cols), str(chr(pad)))
table = [padded[i:i+cols] for i in range(0, len(padded), cols)]
result = ""
for y in range(len(table[0])):
for r in range(len(table)):
result += table[r][y]
return result, rows
def columnar_decrypt(ciphertext, key, pad):
rows = key
table = [ciphertext[i:i+rows] for i in range(0, len(ciphertext), rows)]
res = ""
for a in range(rows):
for b in range(len(table)):
res += table[b][a]
return res.rstrip(str(chr(pad)))
# Hill Encrypt
def keygen(l):
while True:
if l % 2 == 0:
a, b, c, d = [random.randint(0, MOD_ALL) for _ in range(4)]
det = (a*d - b*c) % MOD_ALL
if det < MOD_ALL and math.gcd(det, MOD_ALL) == 1:
return f"{a}-{b}-{c}-{d}"
else:
a, b, c, d, e, f, g, h ,i = [random.randint(0, 99) for _ in range(9)]
det = (a*e*i + b*f*g + c*d*h - b*d*i - a*f*h - c*e*g) % MOD_ALL
if det < MOD_ALL and math.gcd(det, MOD_ALL) == 1:
return f"{a}-{b}-{c}-{d}-{e}-{f}-{g}-{h}-{i}"
def key_separator(k):
return k.split("-")
def hexcon(h):
return str(format(h, "06X"))
def hill_encrypt(pl, k):
cipher = ""
if len(pl) % 2 == 0:
for i in range(0, len(pl), 2):
vec = [int(ord(pl[i])), int(ord(pl[i+1]))]
bl1 = (int(k[0])*vec[0] + int(k[1])*vec[1]) % MOD_ALL
bl2 = (int(k[2])*vec[0] + int(k[3])*vec[1]) % MOD_ALL
cipher += hexcon(bl1) + hexcon(bl2)
else:
for i in range(0, len(pl), 3):
vec = [int(ord(pl[i])), int(ord(pl[i+1])), int(ord(pl[i+2]))]
bl1 = (int(k[0])*vec[0] + int(k[1])*vec[1] + int(k[2])*vec[2]) % MOD_ALL
bl2 = (int(k[3])*vec[0] + int(k[4])*vec[1] + int(k[5])*vec[2]) % MOD_ALL
bl3 = (int(k[6])*vec[0] + int(k[7])*vec[1] + int(k[8])*vec[2]) % MOD_ALL
cipher += hexcon(bl1) + hexcon(bl2) + hexcon(bl3)
return cipher
def matrix_inverse_2x2(k):
a, b, c, d = map(int, k)
det = (a*d - b*c) % MOD_ALL
inv_det = pow(det, -1, MOD_ALL)
return [( d*inv_det) % MOD_ALL,
(-b*inv_det) % MOD_ALL,
(-c*inv_det) % MOD_ALL,
( a*inv_det) % MOD_ALL]
def matrix_inverse_3x3(k):
a,b,c,d,e,f,g,h,i = map(int, k)
det = (a*e*i + b*f*g + c*d*h - b*d*i - a*f*h - c*e*g) % MOD_ALL
inv_det = pow(det, -1, MOD_ALL)
A = (e*i - f*h) % MOD_ALL
B = (-(b*i - c*h)) % MOD_ALL
C = (b*f - c*e) % MOD_ALL
D = (-(d*i - f*g)) % MOD_ALL
E = (a*i - c*g) % MOD_ALL
F = (-(a*f - c*d)) % MOD_ALL
G = (d*h - e*g) % MOD_ALL
H = (-(a*h - b*g)) % MOD_ALL
I = (a*e - b*d) % MOD_ALL
adj = [A, B, C,
D, E, F,
G, H, I]
return [(x * inv_det) % MOD_ALL for x in adj]
def hex_to_int_blocks(cipher, block_size):
values = [int(cipher[i:i+6], 16) for i in range(0, len(cipher), 6)]
return [values[i:i+block_size] for i in range(0, len(values), block_size)]
def hill_decrypt(cipher, k, pad):
plain = ""
if len(k) == 4:
invK = matrix_inverse_2x2(k)
blocks = hex_to_int_blocks(cipher, 2)
for vec in blocks:
x = (invK[0]*vec[0] + invK[1]*vec[1]) % MOD_ALL
y = (invK[2]*vec[0] + invK[3]*vec[1]) % MOD_ALL
plain += chr(x) + chr(y)
else:
invK = matrix_inverse_3x3(k)
blocks = hex_to_int_blocks(cipher, 3)
for vec in blocks:
x = (invK[0]*vec[0] + invK[1]*vec[1] + invK[2]*vec[2]) % MOD_ALL
y = (invK[3]*vec[0] + invK[4]*vec[1] + invK[5]*vec[2]) % MOD_ALL
z = (invK[6]*vec[0] + invK[7]*vec[1] + invK[8]*vec[2]) % MOD_ALL
plain += chr(x) + chr(y) + chr(z)
return plain.rstrip(str(chr(pad)))
if __name__ == "__main__":
print("="*100)
print("Welcome to Hill Fences Superencryption")
print("1.Encryption\n2.Decryption\n")
opt = int(input("Select an option: "))
print("="*100)
if opt == 1:
padding_random_hill = random.randint(0, MOD_ALL)
padding_random_columnar = random.randint(0, MOD_ALL)
text = input("Write your text: \n")
col_encrypted = columnar_encrypt(text, padding_random_columnar)
col_res, key = col_encrypted
padding_random_ = random.randint(0, MOD_ALL)
if len(col_res) % 2 != 0 and len(col_res) % 3 != 0:
col_res += str(chr(padding_random_hill))
hill_key_str = keygen(len(col_res))
hill_key = key_separator(hill_key_str)
final = hill_encrypt(col_res, hill_key)
print("\n"+ "="*100)
print("Final Cipher:\n" + final + "\n")
print(f"Key: \n{padding_random_columnar}-{padding_random_hill}-{key}-{hill_key_str}")
elif opt == 2:
text = input("Write your cipher: \n")
print()
key = input("Write your key: ")
splitted_key = key.split("-")
hill_key = [int(x) for x in splitted_key[3:]]
hill_decrypted = hill_decrypt(text, hill_key, int(splitted_key[1]))
final_plain = columnar_decrypt(hill_decrypted, int(splitted_key[2]), int(splitted_key[0]))
print()
print("Decrypted message: \n" + final_plain)
print("\n"+ "="*100)