OneCompiler

xyzis

119

Q1) Caesar Cipher code:

def caesar_cipher_encrypt(message, shift):
encrypted = ""
for char in message:
if char.isalpha(): # Encrypt letters
base = ord('A') if char.isupper() else ord('a')
encrypted += chr((ord(char) - base + shift) % 26 + base)
elif char.isdigit(): # Encrypt digits
encrypted += chr((ord(char) - ord('0') + shift) % 10 + ord('0'))
else:
encrypted += char # Leave other characters unchanged
return encrypted

Get input from user

message = input("Enter the message to encrypt (letters and digits will be encrypted): ")
while True:
try:
shift = int(input("Enter the shift value (integer): "))
break
except ValueError:
print("Please enter a valid integer for the shift.")

Encrypt the message

encrypted_message = caesar_cipher_encrypt(message, shift)

Output the result

print(f"Encrypted message: {encrypted_message}")

Q2) playfair cipher:

def create_matrix(key):
key = key.upper().replace('J', 'I')
matrix = []
used = set()

for char in key:
    if char.isalpha() and char not in used:
        used.add(char)
        matrix.append(char)

for char in "ABCDEFGHIKLMNOPQRSTUVWXYZ":
    if char not in used:
        matrix.append(char)

return [matrix[i:i+5] for i in range(0, 25, 5)]

def find_position(matrix, char):
for row in range(5):
for col in range(5):
if matrix[row][col] == char:
return row, col
return None

def prepare_text(text):
text = text.upper().replace('J', 'I')
cleaned = ''.join(filter(str.isalpha, text))
i = 0
result = ''
while i < len(cleaned):
a = cleaned[i]
b = cleaned[i + 1] if i + 1 < len(cleaned) else 'X'
if a == b:
result += a + 'X'
i += 1
else:
result += a + b
i += 2
if len(result) % 2 != 0:
result += 'X'
return result

def encrypt_pair(a, b, matrix):
ra, ca = find_position(matrix, a)
rb, cb = find_position(matrix, b)

if ra == rb:
    return matrix[ra][(ca + 1) % 5] + matrix[rb][(cb + 1) % 5]
elif ca == cb:
    return matrix[(ra + 1) % 5][ca] + matrix[(rb + 1) % 5][cb]
else:
    return matrix[ra][cb] + matrix[rb][ca]

def decrypt_pair(a, b, matrix):
ra, ca = find_position(matrix, a)
rb, cb = find_position(matrix, b)

if ra == rb:
    return matrix[ra][(ca - 1) % 5] + matrix[rb][(cb - 1) % 5]
elif ca == cb:
    return matrix[(ra - 1) % 5][ca] + matrix[(rb - 1) % 5][cb]
else:
    return matrix[ra][cb] + matrix[rb][ca]

def encrypt_playfair(plaintext, key):
matrix = create_matrix(key)
prepared = prepare_text(plaintext)
ciphertext = ''
for i in range(0, len(prepared), 2):
ciphertext += encrypt_pair(prepared[i], prepared[i+1], matrix)
return ciphertext

def decrypt_playfair(ciphertext, key):
matrix = create_matrix(key)
plaintext = ''
for i in range(0, len(ciphertext), 2):
plaintext += decrypt_pair(ciphertext[i], ciphertext[i+1], matrix)
return plaintext

=== Main Program ===

key = input("Enter the key: ")
text = input("Enter the plaintext: ")

ciphertext = encrypt_playfair(text, key)
print(f"Encrypted text: {ciphertext}")

decrypted = decrypt_playfair(ciphertext, key)
print(f"Decrypted text: {decrypted}")

Q3) hill cipher (2x2):

import numpy as np

def mod_inverse(a, m):
a = a % m
for x in range(1, m):
if (a * x) % m == 1:
return x
return None

def matrix_mod_inv_2x2(matrix, modulus):
a, b = matrix[0]
c, d = matrix[1]
det = (a * d - b * c) % modulus
det_inv = mod_inverse(det, modulus)
if det_inv is None:
raise ValueError("Key matrix is not invertible modulo", modulus)

adjugate = np.array([[d, -b], [-c, a]]) % modulus
return (det_inv * adjugate) % modulus

def get_key_matrix_2x2():
print("Enter the 2x2 key matrix (integers only, space-separated):")
key = []
for i in range(2):
row = input(f"Row {i + 1}: ").split()
if len(row) != 2:
raise ValueError("Each row must have exactly 2 elements.")
key.append([int(x) % 26 for x in row])
return np.array(key)

def format_text_2(text):
text = ''.join(filter(str.isalpha, text.upper()))
if len(text) % 2 != 0:
text += 'X'
return text

def encrypt_hill_2x2(text, key_matrix):
text = format_text_2(text)
ciphertext = ""
for i in range(0, len(text), 2):
pair = text[i:i+2]
vector = np.array([[ord(pair[0]) - 65],
[ord(pair[1]) - 65]])
result = np.dot(key_matrix, vector) % 26
ciphertext += ''.join(chr(int(num) + 65) for num in result.flatten())
return ciphertext

def decrypt_hill_2x2(ciphertext, key_matrix):
inverse_key = matrix_mod_inv_2x2(key_matrix, 26)
plaintext = ""
for i in range(0, len(ciphertext), 2):
pair = ciphertext[i:i+2]
vector = np.array([[ord(pair[0]) - 65],
[ord(pair[1]) - 65]])
result = np.dot(inverse_key, vector) % 26
plaintext += ''.join(chr(int(round(val)) + 65) for val in result.flatten())
return plaintext

=== Main Program ===

try:
key_matrix = get_key_matrix_2x2()
det = int(round(np.linalg.det(key_matrix))) % 26
if np.gcd(det, 26) != 1:
raise ValueError("Key matrix is not invertible modulo 26.")

plaintext = input("Enter the plaintext to encrypt: ")
formatted = format_text_2(plaintext)
encrypted = encrypt_hill_2x2(formatted, key_matrix)
print(f"Encrypted text: {encrypted}")

decrypted = decrypt_hill_2x2(encrypted, key_matrix)
decrypted = decrypted[:len(formatted)]  # trim padding
print(f"Decrypted text: {decrypted}")

except Exception as e:
print(f"Error: {e}")

Q4) hill cipher (3x3):

import numpy as np

def mod_inverse(a, m):
a = a % m
for x in range(1, m):
if (a * x) % m == 1:
return x
return None

def matrix_mod_inv(matrix, modulus):
det = int(round(np.linalg.det(matrix))) % modulus
det_inv = mod_inverse(det, modulus)
if det_inv is None:
raise ValueError("Matrix is not invertible modulo", modulus)

# Calculate matrix of minors, then cofactors, then adjugate
minors = np.zeros((3, 3), dtype=int)
for i in range(3):
    for j in range(3):
        sub_matrix = np.delete(np.delete(matrix, i, axis=0), j, axis=1)
        minors[i][j] = int(round(np.linalg.det(sub_matrix)))

cofactors = np.zeros((3, 3), dtype=int)
for i in range(3):
    for j in range(3):
        cofactors[i][j] = minors[i][j] * ((-1) ** (i + j))

adjugate = np.transpose(cofactors) % modulus
inverse = (det_inv * adjugate) % modulus
return inverse

def get_key_matrix_3x3():
print("Enter the 3x3 key matrix (integers only, space-separated):")
key = []
for i in range(3):
row = input(f"Row {i + 1}: ").split()
if len(row) != 3:
raise ValueError("Each row must have exactly 3 elements.")
key.append([int(x) % 26 for x in row])
return np.array(key)

def format_text(text):
text = ''.join(filter(str.isalpha, text.upper()))
while len(text) % 3 != 0:
text += 'X'
return text

def encrypt_hill(text, key_matrix):
text = format_text(text)
ciphertext = ""
for i in range(0, len(text), 3):
chunk = text[i:i+3]
vector = np.array([[ord(chunk[0]) - 65],
[ord(chunk[1]) - 65],
[ord(chunk[2]) - 65]])
result = np.dot(key_matrix, vector) % 26
ciphertext += ''.join(chr(int(val) + 65) for val in result.flatten())
return ciphertext

def decrypt_hill(ciphertext, key_matrix):
inverse_key = matrix_mod_inv(key_matrix, 26)
plaintext = ""
for i in range(0, len(ciphertext), 3):
chunk = ciphertext[i:i+3]
vector = np.array([[ord(chunk[0]) - 65],
[ord(chunk[1]) - 65],
[ord(chunk[2]) - 65]])
result = np.dot(inverse_key, vector) % 26
plaintext += ''.join(chr(int(round(val)) + 65) for val in result.flatten())
return plaintext

=== Main Program ===

try:
key_matrix = get_key_matrix_3x3()
det = int(round(np.linalg.det(key_matrix))) % 26
if np.gcd(det, 26) != 1:
raise ValueError("Key matrix is not invertible modulo 26.")

plaintext = input("Enter the plaintext to encrypt: ")
encrypted = encrypt_hill(plaintext, key_matrix)
print(f"Encrypted text: {encrypted}")

decrypted = decrypt_hill(encrypted, key_matrix)
print(f"Decrypted text: {decrypted}")

except Exception as e:
print(f"Error: {e}")

Q5) Rail Fence cipher:

def rail_fence_encrypt(plaintext, rails):
# Create a grid with empty placeholders
fence = [['' for _ in range(len(plaintext))] for _ in range(rails)]

# Fill the fence grid in a zigzag pattern
direction_down = False
row, col = 0, 0
for char in plaintext:
    fence[row][col] = char
    col += 1

    # Change direction when we hit the top or bottom row
    if row == 0 or row == rails - 1:
        direction_down = not direction_down

    row = row + 1 if direction_down else row - 1

# Read the grid row by row to get the encrypted message
encrypted_text = ''.join(''.join(row) for row in fence)
return encrypted_text

def rail_fence_decrypt(ciphertext, rails):
# Create a grid to mark the zigzag pattern
fence = [['' for _ in range(len(ciphertext))] for _ in range(rails)]

# Mark the positions in the zigzag pattern
direction_down = False
row, col = 0, 0
for i in range(len(ciphertext)):
    fence[row][col] = '*'
    col += 1

    # Change direction when we hit the top or bottom row
    if row == 0 or row == rails - 1:
        direction_down = not direction_down

    row = row + 1 if direction_down else row - 1

# Fill the grid with the ciphertext characters row by row
index = 0
for r in range(rails):
    for c in range(len(ciphertext)):
        if fence[r][c] == '*' and index < len(ciphertext):
            fence[r][c] = ciphertext[index]
            index += 1

# Read the grid in the zigzag pattern to reconstruct the plaintext
decrypted_text = []
direction_down = False
row, col = 0, 0
for i in range(len(ciphertext)):
    decrypted_text.append(fence[row][col])
    col += 1

    # Change direction when we hit the top or bottom row
    if row == 0 or row == rails - 1:
        direction_down = not direction_down

    row = row + 1 if direction_down else row - 1

return ''.join(decrypted_text)

=== Main Program ===

try:
plaintext = input("Enter the plaintext: ").replace(' ', '') # Remove spaces from plaintext
rails = int(input("Enter the number of rails: "))

if rails <= 1:
    raise ValueError("The number of rails must be greater than 1.")

# Encrypt the plaintext
encrypted_text = rail_fence_encrypt(plaintext, rails)
print(f"Encrypted text: {encrypted_text}")

# Decrypt the ciphertext
decrypted_text = rail_fence_decrypt(encrypted_text, rails)
print(f"Decrypted text: {decrypted_text}")

except Exception as e:
print(f"Error: {e}")

Q6) Single columnar transposition:

def single_columnar_encrypt(plaintext, key):
# Remove spaces and convert to uppercase for consistency
plaintext = plaintext.replace(' ', '').upper()
key = key.replace(' ', '').upper()

# Calculate number of columns (length of key) and rows needed
num_columns = len(key)
num_rows = (len(plaintext) + num_columns - 1) // num_columns

# Create a grid and fill it with the plaintext
grid = [['' for _ in range(num_columns)] for _ in range(num_rows)]
index = 0
for row in range(num_rows):
    for col in range(num_columns):
        if index < len(plaintext):
            grid[row][col] = plaintext[index]
            index += 1
        else:
            grid[row][col] = 'X'  # Padding with 'X' if needed

# Create a list of (key_char, column_index) pairs and sort by key_char
key_order = [(char, i) for i, char in enumerate(key)]
key_order.sort()  # Sort by character to determine column order

# Read columns in the order defined by the sorted key
ciphertext = ''
for _, col_index in key_order:
    for row in range(num_rows):
        ciphertext += grid[row][col_index]

return ciphertext

def single_columnar_decrypt(ciphertext, key):
# Remove spaces and convert to uppercase for consistency
ciphertext = ciphertext.replace(' ', '').upper()
key = key.replace(' ', '').upper()

# Calculate number of columns (length of key) and rows
num_columns = len(key)
num_rows = len(ciphertext) // num_columns

# Create a list of (key_char, column_index) pairs and sort by key_char
key_order = [(char, i) for i, char in enumerate(key)]
key_order.sort()

# Calculate the position of each column in the sorted order
col_positions = [0] * num_columns
for i, (_, col_index) in enumerate(key_order):
    col_positions[col_index] = i

# Create a grid to reconstruct the plaintext
grid = [['' for _ in range(num_columns)] for _ in range(num_rows)]

# Fill the grid column by column based on the sorted key order
index = 0
for _, col_index in key_order:
    for row in range(num_rows):
        if index < len(ciphertext):
            grid[row][col_index] = ciphertext[index]
            index += 1

# Read the grid row by row to get the plaintext
plaintext = ''
for row in range(num_rows):
    for col in range(num_columns):
        plaintext += grid[row][col]

# Remove padding 'X' characters
return plaintext.rstrip('X')

=== Main Program ===

try:
plaintext = input("Enter the plaintext: ")
key = input("Enter the key: ")

if not key:
    raise ValueError("Key cannot be empty.")
if not plaintext:
    raise ValueError("Plaintext cannot be empty.")

# Encrypt the plaintext
encrypted_text = single_columnar_encrypt(plaintext, key)
print(f"Encrypted text: {encrypted_text}")

# Decrypt the ciphertext
decrypted_text = single_columnar_decrypt(encrypted_text, key)
print(f"Decrypted text: {decrypted_text}")

except Exception as e:
print(f"Error: {e}")

Q7) Double Columnar Transposition:

def single_columnar_encrypt(plaintext, key):
# Remove spaces and convert to uppercase for consistency
plaintext = plaintext.replace(' ', '').upper()
key = key.replace(' ', '').upper()

# Calculate number of columns (length of key) and rows needed
num_columns = len(key)
num_rows = (len(plaintext) + num_columns - 1) // num_columns

# Create a grid and fill it with the plaintext
grid = [['' for _ in range(num_columns)] for _ in range(num_rows)]
index = 0
for row in range(num_rows):
    for col in range(num_columns):
        if index < len(plaintext):
            grid[row][col] = plaintext[index]
            index += 1
        else:
            grid[row][col] = 'X'  # Padding with 'X' if needed

# Create a list of (key_char, column_index) pairs and sort by key_char
key_order = [(char, i) for i, char in enumerate(key)]
key_order.sort()  # Sort by character to determine column order

# Read columns in the order defined by the sorted key
ciphertext = ''
for _, col_index in key_order:
    for row in range(num_rows):
        ciphertext += grid[row][col_index]

return ciphertext

def single_columnar_decrypt(ciphertext, key):
# Remove spaces and convert to uppercase for consistency
ciphertext = ciphertext.replace(' ', '').upper()
key = key.replace(' ', '').upper()

# Calculate number of columns (length of key) and rows
num_columns = len(key)
num_rows = len(ciphertext) // num_columns

# Create a list of (key_char, column_index) pairs and sort by key_char
key_order = [(char, i) for i, char in enumerate(key)]
key_order.sort()

# Calculate the position of each column in the sorted order
col_positions = [0] * num_columns
for i, (_, col_index) in enumerate(key_order):
    col_positions[col_index] = i

# Create a grid to reconstruct the plaintext
grid = [['' for _ in range(num_columns)] for _ in range(num_rows)]

# Fill the grid column by column based on the sorted key order
index = 0
for _, col_index in key_order:
    for row in range(num_rows):
        if index < len(ciphertext):
            grid[row][col_index] = ciphertext[index]
            index += 1

# Read the grid row by row to get the plaintext
plaintext = ''
for row in range(num_rows):
    for col in range(num_columns):
        plaintext += grid[row][col]

return plaintext

def double_columnar_encrypt(plaintext, key1, key2):
# Perform single columnar transposition twice
intermediate_text = single_columnar_encrypt(plaintext, key1)
ciphertext = single_columnar_encrypt(intermediate_text, key2)
return ciphertext

def double_columnar_decrypt(ciphertext, key1, key2):
# Reverse the process: decrypt with key2 first, then key1
intermediate_text = single_columnar_decrypt(ciphertext, key2)
plaintext = single_columnar_decrypt(intermediate_text, key1)
# Remove padding 'X' characters
return plaintext.rstrip('X')

=== Main Program ===

try:
plaintext = input("Enter the plaintext: ")
key1 = input("Enter the first key: ")
key2 = input("Enter the second key: ")

if not key1 or not key2:
    raise ValueError("Keys cannot be empty.")
if not plaintext:
    raise ValueError("Plaintext cannot be empty.")

# Encrypt the plaintext
encrypted_text = double_columnar_encrypt(plaintext, key1, key2)
print(f"Encrypted text: {encrypted_text}")

# Decrypt the ciphertext
decrypted_text = double_columnar_decrypt(encrypted_text, key1, key2)
print(f"Decrypted text: {decrypted_text}")

except Exception as e:
print(f"Error: {e}")

Q8) AES encryption:

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64

def pad(text):
# Pad the text to be a multiple of 16 bytes (AES block size)
padding_length = 16 - (len(text) % 16)
padding = chr(padding_length) * padding_length
return text + padding

def unpad(text):
# Remove padding from the decrypted text
padding_length = ord(text[-1])
return text[:-padding_length]

def aes_encrypt(plaintext, key):
# Ensure key is 16 bytes (128 bits) for AES-128
key = key.ljust(16)[:16].encode('utf-8')

# Pad the plaintext and convert to bytes
plaintext = pad(plaintext)
plaintext_bytes = plaintext.encode('utf-8')

# Generate a random IV
iv = get_random_bytes(16)

# Create AES cipher object in CBC mode
cipher = AES.new(key, AES.MODE_CBC, iv)

# Encrypt the plaintext
ciphertext = cipher.encrypt(plaintext_bytes)

# Combine IV and ciphertext, encode in base64 for safe storage
return base64.b64encode(iv + ciphertext).decode('utf-8')

def aes_decrypt(ciphertext, key):
# Ensure key is 16 bytes (128 bits) for AES-128
key = key.ljust(16)[:16].encode('utf-8')

# Decode the base64 ciphertext
ciphertext_bytes = base64.b64decode(ciphertext)

# Extract IV (first 16 bytes) and actual ciphertext
iv = ciphertext_bytes[:16]
ciphertext = ciphertext_bytes[16:]

# Create AES cipher object in CBC mode
cipher = AES.new(key, AES.MODE_CBC, iv)

# Decrypt the ciphertext
decrypted_bytes = cipher.decrypt(ciphertext)

# Decode and remove padding
decrypted_text = decrypted_bytes.decode('utf-8')
return unpad(decrypted_text)

=== Main Program ===

try:
plaintext = input("Enter the plaintext: ")
key = input("Enter the key (16 characters or less, will be padded/truncated): ")

if not plaintext:
    raise ValueError("Plaintext cannot be empty.")

# Encrypt the plaintext
encrypted_text = aes_encrypt(plaintext, key)
print(f"Encrypted text (base64): {encrypted_text}")

# Decrypt the ciphertext
decrypted_text = aes_decrypt(encrypted_text, key)
print(f"Decrypted text: {decrypted_text}")

except Exception as e:
print(f"Error: {e}")

Q9) DES Algorithm:

from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
import base64

def pad(text):
# Pad the text to be a multiple of 8 bytes (DES block size)
padding_length = 8 - (len(text) % 8)
padding = chr(padding_length) * padding_length
return text + padding

def unpad(text):
# Remove padding from the decrypted text
padding_length = ord(text[-1])
return text[:-padding_length]

def des_encrypt(plaintext, key):
# Ensure key is 8 bytes (64 bits) for DES
key = key.ljust(8)[:8].encode('utf-8')

# Pad the plaintext and convert to bytes
plaintext = pad(plaintext)
plaintext_bytes = plaintext.encode('utf-8')

# Generate a random IV
iv = get_random_bytes(8)

# Create DES cipher object in CBC mode
cipher = DES.new(key, DES.MODE_CBC, iv)

# Encrypt the plaintext
ciphertext = cipher.encrypt(plaintext_bytes)

# Combine IV and ciphertext, encode in base64 for safe storage
return base64.b64encode(iv + ciphertext).decode('utf-8')

def des_decrypt(ciphertext, key):
# Ensure key is 8 bytes (64 bits) for DES
key = key.ljust(8)[:8].encode('utf-8')

# Decode the base64 ciphertext
ciphertext_bytes = base64.b64decode(ciphertext)

# Extract IV (first 8 bytes) and actual ciphertext
iv = ciphertext_bytes[:8]
ciphertext = ciphertext_bytes[8:]

# Create DES cipher object in CBC mode
cipher = DES.new(key, DES.MODE_CBC, iv)

# Decrypt the ciphertext
decrypted_bytes = cipher.decrypt(ciphertext)

# Decode and remove padding
decrypted_text = decrypted_bytes.decode('utf-8')
return unpad(decrypted_text)

=== Main Program ===

try:
plaintext = input("Enter the plaintext: ")
key = input("Enter the key (8 characters or less, will be padded/truncated): ")

if not plaintext:
    raise ValueError("Plaintext cannot be empty.")

# Encrypt the plaintext
encrypted_text = des_encrypt(plaintext, key)
print(f"Encrypted text (base64): {encrypted_text}")

# Decrypt the ciphertext
decrypted_text = des_decrypt(encrypted_text, key)
print(f"Decrypted text: {decrypted_text}")

except Exception as e:
print(f"Error: {e}")

Q10) RSA for single character:
def gcd(a, b):
# Calculate GCD using Euclidean algorithm
while b:
a, b = b, a % b
return a

def mod_inverse(e, phi):
# Calculate modular multiplicative inverse of e mod phi
for d in range(1, phi):
if (e * d) % phi == 1:
return d
raise ValueError("Modular inverse does not exist")

def rsa_encrypt(message, e, n):
# Convert message (character) to number and encrypt: c = m^e mod n
m = ord(message) # Convert character to ASCII number
c = pow(m, e, n) # Modular exponentiation
return c

def rsa_decrypt(ciphertext, d, n):
# Decrypt: m = c^d mod n and convert back to character
m = pow(ciphertext, d, n) # Modular exponentiation
return chr(m) # Convert number back to character

=== Main Program ===

try:
# Step 1: Choose two small prime numbers
p = 11
q = 13

# Step 2: Calculate n (modulus) and phi (totient)
n = p * q  # n = 11 * 13 = 143
phi = (p - 1) * (q - 1)  # phi = (11-1) * (13-1) = 10 * 12 = 120

# Step 3: Choose public key e (coprime with phi)
e = 7  # 7 is coprime with 120 (gcd(7, 120) = 1)
if gcd(e, phi) != 1:
    raise ValueError("e is not coprime with phi")

# Step 4: Calculate private key d (modular inverse of e mod phi)
d = mod_inverse(e, phi)  # d such that (e * d) % phi = 1

# Step 5: Get user input for message (single character)
message = input("Enter a single character to encrypt: ")
if len(message) != 1:
    raise ValueError("Please enter exactly one character")

# Step 6: Encrypt the message
ciphertext = rsa_encrypt(message, e, n)
print(f"Encrypted ciphertext: {ciphertext}")

# Step 7: Decrypt the ciphertext
decrypted_message = rsa_decrypt(ciphertext, d, n)
print(f"Decrypted message: {decrypted_message}")

except Exception as e:
print(f"Error: {e}")