Experiment No.
01
AIM: Design and Implementation using Substitution ciphers:Caesar Cipher, Auto Key Cipher,
PlayFair Cipher
THEORY:
1. CAESAR CIPHER:
The Caesar cipher is a basic substitution cipher that shifts each letter in the plaintext by a
fixed number of positions down the alphabet. In a lab experiment, implementing the Caesar
cipher involves encoding and decoding messages by applying a consistent shift to the letters. One
advantage of the Caesar cipher is its simplicity, making it an accessible introduction to
cryptography for educational purposes. The algorithm is easy to understand and implement,
providing a hands-on experience for students to grasp fundamental cryptographic concepts.
However, its simplicity is also its primary disadvantage, as the Caesar cipher is susceptible to
brute-force attacks due to its limited key space.
With only 25 possible shifts in the English alphabet, attackers can easily try all
combinations to decipher the encrypted message. Additionally, frequency analysis can be
employed to identify common patterns in the ciphertext, further compromising its security.
Despite its vulnerabilities, the Caesar cipher serves as a valuable tool for teaching basic
encryption principles in a controlled laboratory setting.
Code:
def encrypt(text, s):
result = ""
for i in range(len(text)):
char = text[i]
if char.isupper():
result += chr((ord(char) + s - 65) % 26 + 65)
elif char.islower():
result += chr((ord(char) + s - 97) % 26 + 97)
else:
# If the character is a space, leave it unchanged
result += char
return result
def decrypt(text, s):
result = ""
for i in range(len(text)):
char = text[i]
if char.isupper():
result += chr((ord(char) - s - 65) % 26 + 65)
elif char.islower():
result += chr((ord(char) - s - 97) % 26 + 97)
else:
# If the character is a space, leave it unchanged
result += char
return result
text = input("Enter the text to be encrypted: ")
shift_value = int(input("Enter the shift value (key): "))
encrypted_text = encrypt(text, shift_value)
print("Encrypted Text:", encrypted_text)
decrypted_text = decrypt(encrypted_text, shift_value)
print("Decrypted Text:", decrypted_text)
Output:
2. AUTOKEY CIPHER:
The Autokey Cipher is a symmetric encryption algorithm that extends the Vigenè re
Cipher by using a key that is as long as the plaintext itself, typically generated from the original
message. In the Autokey Cipher, the key is appended to the plaintext, and this combined string is
used for encryption. The main advantage of the Autokey Cipher is that it eliminates the periodic
repetition seen in the Vigenè re Cipher, making it more secure. However, a disadvantage is that if
the key is not truly random or is too short, it may be susceptible to certain cryptanalysis methods.
In a lab experiment, students can explore the Autokey Cipher to understand its encryption and
decryption processes, implement it in a programming language, and analyze its strengths and
weaknesses in terms of security. The experiment provides valuable insights into historical
cryptographic techniques and encourages critical thinking about algorithm design and
vulnerabilities.
Code:
def print_credentials():
print("AutoKey Cipher Encryption and Decryption")
def convert_to_integer_values(message, alphabet):
values = []
for char in message:
if char.isalpha():
values.append(ord(char.lower()) - ord('a'))
else:
values.append(-1) # Use -1 to represent spaces
return values
def encode_message(original_values, key):
encoded_values = [key] + original_values[:-1]
return encoded_values
def decode_message(original_values, encoded_values):
decoded_array = [(o - e + 26) % 26 if o != -1 else -1 for o, e in
zip(original_values, encoded_values)]
return decoded_array
def calculate_encoded_array(original_values, encoded_values):
encoded_array = [(o + e) % 26 if o != -1 else -1 for o, e in
zip(original_values, encoded_values)]
return encoded_array
def array_to_string(array):
return "[" + ", ".join(map(str, array)) + "]"
def convert_to_letters(encoded_array):
letters = [chr(value + ord('a')) if 0 <= value <= 25 else ' ' for
value in encoded_array]
return "".join(letters)
def main():
print_credentials()
alphabet = list("abcdefghijklmnopqrstuvwxyz")
message = input("Enter the message to be encoded: ")
key = int(input("Enter the key: "))
original_values = convert_to_integer_values(message, alphabet)
encoded_values = encode_message(original_values, key)
encoded_array = calculate_encoded_array(original_values,
encoded_values)
encoded_message = convert_to_letters(encoded_array)
print(f"Message: {message}")
print(f"Key: {key}")
print("\nMessage Array:", array_to_string(original_values))
print("Encoding Array:", array_to_string(encoded_values))
print("Encoded Array:", array_to_string(encoded_array))
print("Encoded Message:", encoded_message)
decode_choice = input("Do you want to decode the message? (Y/N): ")
if decode_choice.lower() == 'y':
decoded_array = decode_message(encoded_array, encoded_values)
decoded_message = "".join([chr(value + ord('a')) if 0 <= value <=
25 else ' ' for value in decoded_array])
print("Decoded Array:", array_to_string(decoded_array))
print("Decoded Message:", decoded_message.lower())
else:
print("Program ended.")
if name == " main ":
main()
Output
3. PLAYFAIR CIPHER:
The Playfair cipher is a symmetric key encryption algorithm that operates on
pairs of letters (bigrams), making it more secure than simple substitution ciphers. It
uses a key matrix derived from a keyword to transform plaintext into ciphertext. In this
matrix, each letter is unique, and the encryption process involves mapping each pair of
plaintext letters to specific rules based on their positions in the matrix. An advantage of
the Playfair cipher is its resistance to frequency analysis due to the lack of a simple
letter-to-letter substitution pattern. Additionally, its key management is relatively
straightforward. However, a notable disadvantage is that it requires careful handling of
special cases, such as repeated letters in a digraph, which may affect the algorithm's
e ciency. The Playfair cipher is an excellent choice for educational purposes and lab
experiments as it provides hands-on experience in implementing a historical
encryption technique and understanding both its strengths and weaknesses in real-
world applications.
Code:
import numpy as np
def print_credentials():
print("Playfair Cipher Encryption and Decryption")
def prepare_message(message):
prepared_message = []
i = 0
while i < len(message) - 1:
char1 = message[i]
char2 = message[i + 1]
prepared_message.append(char1)
if char1 == char2:
prepared_message.append('X')
prepared_message.append(char2)
i += 2
if len(message) % 2 != 0:
prepared_message.append(message[-1] + 'X')
return ''.join(prepared_message)
def generate_key_square(key):
key_without_duplicates = remove_duplicates_and_j(key)
remaining_alphabets = get_remaining_alphabets(key_without_duplicates)
combined_key = key_without_duplicates + remaining_alphabets
key_square = np.array(list(combined_key)).reshape(5, 5)
return key_square
def print_key_square(key_square):
for row in key_square:
print(' '.join(row))
def print_pairs(message):
for i in range(0, len(message), 2):
if i + 1 < len(message):
print(message[i] + message[i + 1])
else:
print(message[i] + 'X')
def remove_duplicates_and_j(key):
unique_letters = []
seen_chars = set()
for ch in key:
if ch != 'P' and ch not in seen_chars:
unique_letters.append(ch)
seen_chars.add(ch)
return ''.join(unique_letters).upper()
def get_remaining_alphabets(key_without_j):
remaining_alphabets = []
for ch in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
if ch != 'P' and ch not in key_without_j:
remaining_alphabets.append(ch)
return ''.join(remaining_alphabets)
def find_position(key_square, ch):
for i in range(5):
for j in range(5):
if key_square[i, j] == ch:
return [i, j]
return [-1, -1]
def encrypt(message, key_square):
encrypted_message = []
if len(message) % 2 != 0:
message += 'Z'
for i in range(0, len(message), 2):
char1 = message[i]
char2 = message[i + 1]
pos1 = find_position(key_square, char1)
pos2 = find_position(key_square, char2)
# Check if positions are found
if pos1[0] == -1 or pos2[0] == -1:
print(f"Character not found in key square: {char1} or
{char2}")
continue
if pos1[1] == pos2[1]:
encrypted_char1 = key_square[(pos1[0] + 1) % 5, pos1[1]]
encrypted_char2 = key_square[(pos2[0] + 1) % 5, pos2[1]]
elif pos1[0] == pos2[0]:
encrypted_char1 = key_square[pos1[0], (pos1[1] + 1) % 5]
encrypted_char2 = key_square[pos2[0], (pos2[1] + 1) % 5]
else:
encrypted_char1 = key_square[pos1[0], pos2[1]]
encrypted_char2 = key_square[pos2[0], pos1[1]]
encrypted_message.append(encrypted_char1)
encrypted_message.append(encrypted_char2)
return ''.join(encrypted_message)
def decrypt(encrypted_message, key_square):
decrypted_message = []
for i in range(0, len(encrypted_message), 2):
char1 = encrypted_message[i]
char2 = encrypted_message[i + 1]
pos1 = find_position(key_square, char1)
pos2 = find_position(key_square, char2)
# Check if positions are found
if pos1[0] == -1 or pos2[0] == -1:
print(f"Character not found in key square: {char1} or
{char2}")
continue
if pos1[1] == pos2[1]:
decrypted_char1 = key_square[(pos1[0] - 1 + 5) % 5, pos1[1]]
decrypted_char2 = key_square[(pos2[0] - 1 + 5) % 5, pos2[1]]
elif pos1[0] == pos2[0]:
decrypted_char1 = key_square[pos1[0], (pos1[1] - 1 + 5) % 5]
decrypted_char2 = key_square[pos2[0], (pos2[1] - 1 + 5) % 5]
else:
decrypted_char1 = key_square[pos1[0], pos2[1]]
decrypted_char2 = key_square[pos2[0], pos1[1]]
decrypted_message.append(decrypted_char1)
decrypted_message.append(decrypted_char2)
return ''.join(decrypted_message)
def main():
print_credentials()
key = input("Enter the key: ").upper().replace("[^A-Z]", "")
key_square = generate_key_square(key)
print("Key Square:")
print_key_square(key_square)
message = input("Enter the message to be encrypted:
").upper().replace("[^A-Z]", "")
prepared_message = prepare_message(message)
print("Pairs of Letters:")
print_pairs(prepared_message)
encrypted_message = encrypt(prepared_message, key_square)
print("Encrypted Message:", encrypted_message)
choice = input("Do you want to decode the message? (Y/N): ")
if choice.upper() == 'Y':
decrypted_message = decrypt(encrypted_message, key_square)
print("Decrypted Message:", decrypted_message)
else:
print("Program ended.")
if name == " main ":
main()
Output:
CONCLUSION:
Thus, we understood the logics behind Caesars’, Autokey and Playfair Cipher and
implemented them in Python, learning the following drawbacks of each cipher - Caesars’ is the
easiest to crack in transmission due to easy logic, Autokey may result in erroneous decoding,
and Playfair may have bogus letters in decoding due to pairing of plaintext.