Encryption and Cryptography
What is Encryption?
Encryption is the process of converting plaintext (readable data) into ciphertext (unreadable data) to protect information from unauthorized access. Cryptography is the science of secure communication.
Key Concepts
- Plaintext - Original readable message
- Ciphertext - Encrypted unreadable message
- Key - Secret value used for encryption/decryption
- Algorithm - Mathematical process for encryption
- Decryption - Converting ciphertext back to plaintext
Caesar Cipher
The Caesar cipher is one of the oldest encryption methods, shifting each letter by a fixed number of positions in the alphabet.
PROCEDURE CAESAR_ENCRYPT(message, shift) {
alphabet <-- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
encrypted <-- ""
i <-- 1
REPEAT LENGTH(message) TIMES {
char <-- message[i]
char <-- UPPERCASE(char)
# Find position in alphabet
pos <-- 1
found <-- 0
REPEAT LENGTH(alphabet) TIMES {
IF (alphabet[pos] == char) {
found <-- 1
# Apply shift
new_pos <-- ((pos - 1 + shift) % 26) + 1
encrypted <-- encrypted + alphabet[new_pos]
}
pos <-- pos + 1
}
IF (found == 0) {
encrypted <-- encrypted + char
}
i <-- i + 1
}
RETURN encrypted
}
PROCEDURE CAESAR_DECRYPT(message, shift) {
RETURN CAESAR_ENCRYPT(message, 26 - shift)
}
# Testing Caesar cipher
original <-- "HELLO WORLD"
shift <-- 3
encrypted <-- CAESAR_ENCRYPT(original, shift)
decrypted <-- CAESAR_DECRYPT(encrypted, shift)
DISPLAY("Original: ")
DISPLAY(original)
DISPLAY("Encrypted: ")
DISPLAY(encrypted)
DISPLAY("Decrypted: ")
DISPLAY(decrypted)
Substitution Cipher
A substitution cipher replaces each letter with another letter according to a predefined mapping.
# Substitution Cipher in Spindle
# Simple substitution cipher implementation
# Create a simple substitution key (A->X, B->Y, C->Z, etc.)
key <-- ["X", "Y", "Z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W"]
PROCEDURE SUBSTITUTION_ENCRYPT(message) {
encrypted <-- ""
i <-- 1
REPEAT LENGTH(message) TIMES {
char <-- message[i]
# Convert character to position (A=1, B=2, etc.)
char_pos <-- char - 64 # ASCII A is 65, so subtract 64
IF (char_pos >= 1 AND char_pos <= 26) {
# Apply substitution
encrypted <-- encrypted + key[char_pos]
} ELSE {
# Keep non-alphabetic characters
encrypted <-- encrypted + char
}
i <-- i + 1
}
RETURN encrypted
}
PROCEDURE SUBSTITUTION_DECRYPT(message) {
decrypted <-- ""
i <-- 1
REPEAT LENGTH(message) TIMES {
char <-- message[i]
# Find position in key array
j <-- 1
found <-- 0
REPEAT 26 TIMES {
IF (key[j] == char) {
# Convert back to original character
decrypted <-- decrypted + (j + 64)
found <-- 1
BREAK
}
j <-- j + 1
}
IF (found == 0) {
# Keep non-alphabetic characters
decrypted <-- decrypted + char
}
i <-- i + 1
}
RETURN decrypted
}
# Using substitution cipher
DISPLAY("Substitution key: ")
DISPLAY(key)
message <-- "HELLO WORLD"
encrypted <-- SUBSTITUTION_ENCRYPT(message)
decrypted <-- SUBSTITUTION_DECRYPT(encrypted)
DISPLAY("Original: ")
DISPLAY(message)
DISPLAY("Encrypted: ")
DISPLAY(encrypted)
DISPLAY("Decrypted: ")
DISPLAY(decrypted)
Vigenère Cipher
The Vigenère cipher uses a keyword to shift letters, making it more secure than the Caesar cipher.
# Vigenère Cipher in Spindle
# Uses a keyword to shift letters
PROCEDURE VIGENERE_ENCRYPT(message, key) {
encrypted <-- ""
key_index <-- 1
i <-- 1
REPEAT LENGTH(message) TIMES {
char <-- message[i]
# Check if character is alphabetic
char_pos <-- char - 64 # ASCII A is 65
IF (char_pos >= 1 AND char_pos <= 26) {
# Get key character position
key_char <-- key[key_index]
key_pos <-- key_char - 64
# Apply shift
encrypted_pos <-- ((char_pos + key_pos - 1) % 26) + 1
encrypted <-- encrypted + (encrypted_pos + 64)
# Move to next key character
key_index <-- (key_index % LENGTH(key)) + 1
} ELSE {
# Keep non-alphabetic characters
encrypted <-- encrypted + char
}
i <-- i + 1
}
RETURN encrypted
}
PROCEDURE VIGENERE_DECRYPT(message, key) {
decrypted <-- ""
key_index <-- 1
i <-- 1
REPEAT LENGTH(message) TIMES {
char <-- message[i]
# Check if character is alphabetic
char_pos <-- char - 64 # ASCII A is 65
IF (char_pos >= 1 AND char_pos <= 26) {
# Get key character position
key_char <-- key[key_index]
key_pos <-- key_char - 64
# Apply reverse shift
decrypted_pos <-- ((char_pos - key_pos + 25) % 26) + 1
decrypted <-- decrypted + (decrypted_pos + 64)
# Move to next key character
key_index <-- (key_index % LENGTH(key)) + 1
} ELSE {
# Keep non-alphabetic characters
decrypted <-- decrypted + char
}
i <-- i + 1
}
RETURN decrypted
}
# Using Vigenère cipher
message <-- "HELLO WORLD"
key <-- "SECRET"
encrypted <-- VIGENERE_ENCRYPT(message, key)
decrypted <-- VIGENERE_DECRYPT(encrypted, key)
DISPLAY("Original: ")
DISPLAY(message)
DISPLAY("Key: ")
DISPLAY(key)
DISPLAY("Encrypted: ")
DISPLAY(encrypted)
DISPLAY("Decrypted: ")
DISPLAY(decrypted)
Hash Functions
Hash functions convert data of any size into a fixed-size string, commonly used for password storage and data integrity.
# Hash Functions in Spindle
# Simple hash function implementations
PROCEDURE SIMPLE_HASH(data) {
# Simple hash function for demonstration
hash_value <-- 0
i <-- 1
REPEAT LENGTH(data) TIMES {
char <-- data[i]
hash_value <-- ((hash_value * 31) + char) % 1000000
i <-- i + 1
}
RETURN hash_value
}
PROCEDURE MD5_HASH(data) {
# Simplified MD5-like hash (not actual MD5)
hash_value <-- 0
i <-- 1
REPEAT LENGTH(data) TIMES {
char <-- data[i]
hash_value <-- ((hash_value * 17) + char) % 1000000000
i <-- i + 1
}
RETURN "md5_" + hash_value
}
PROCEDURE SHA256_HASH(data) {
# Simplified SHA-256-like hash (not actual SHA-256)
hash_value <-- 0
i <-- 1
REPEAT LENGTH(data) TIMES {
char <-- data[i]
hash_value <-- ((hash_value * 23) + char) % 1000000000
i <-- i + 1
}
RETURN "sha256_" + hash_value
}
PROCEDURE VERIFY_PASSWORD(password, stored_hash) {
# Verify password against stored hash
password_hash <-- SHA256_HASH(password)
IF (password_hash == stored_hash) {
RETURN "True"
} ELSE {
RETURN "False"
}
}
# Testing hash functions
message <-- "Hello, World!"
password <-- "mypassword123"
DISPLAY("Original message: ")
DISPLAY(message)
DISPLAY("Simple hash: ")
DISPLAY(SIMPLE_HASH(message))
DISPLAY("MD5 hash: ")
DISPLAY(MD5_HASH(message))
DISPLAY("SHA-256 hash: ")
DISPLAY(SHA256_HASH(message))
# Password verification
stored_hash <-- SHA256_HASH(password)
DISPLAY("Stored hash: ")
DISPLAY(stored_hash)
DISPLAY("Password verification: ")
DISPLAY(VERIFY_PASSWORD(password, stored_hash))
DISPLAY("Wrong password verification: ")
DISPLAY(VERIFY_PASSWORD("wrongpass", stored_hash))
Symmetric vs Asymmetric Encryption
Symmetric Encryption
Uses the same key for encryption and decryption. Fast but requires secure key exchange.
Asymmetric Encryption
Uses public and private key pairs. Slower but provides better security for key exchange.
# Symmetric encryption example (AES-like)
def symmetric_encrypt(message, key):
# Simplified symmetric encryption
encrypted = ""
for i, char in enumerate(message):
key_char = key[i % len(key)]
encrypted_char = chr((ord(char) + ord(key_char)) % 256)
encrypted += encrypted_char
return encrypted
def symmetric_decrypt(encrypted, key):
# Simplified symmetric decryption
decrypted = ""
for i, char in enumerate(encrypted):
key_char = key[i % len(key)]
decrypted_char = chr((ord(char) - ord(key_char)) % 256)
decrypted += decrypted_char
return decrypted
# Asymmetric encryption simulation
class RSA:
def __init__(self):
# Simplified RSA with small numbers
self.public_key = (17, 3233) # (e, n)
self.private_key = (2753, 3233) # (d, n)
def encrypt(self, message):
e, n = self.public_key
encrypted = []
for char in message:
m = ord(char)
c = pow(m, e, n)
encrypted.append(c)
return encrypted
def decrypt(self, encrypted):
d, n = self.private_key
decrypted = ""
for c in encrypted:
m = pow(c, d, n)
decrypted += chr(m)
return decrypted
# Testing encryption types
message = "Hello"
symmetric_key = "SECRET"
# Symmetric encryption
sym_encrypted = symmetric_encrypt(message, symmetric_key)
sym_decrypted = symmetric_decrypt(sym_encrypted, symmetric_key)
print("Symmetric Encryption:")
print(f"Original: {message}")
print(f"Encrypted: {sym_encrypted}")
print(f"Decrypted: {sym_decrypted}")
# Asymmetric encryption
rsa = RSA()
asym_encrypted = rsa.encrypt(message)
asym_decrypted = rsa.decrypt(asym_encrypted)
print("\nAsymmetric Encryption:")
print(f"Original: {message}")
print(f"Encrypted: {asym_encrypted}")
print(f"Decrypted: {asym_decrypted}")
Digital Signatures
Digital signatures provide authentication and integrity verification for digital messages.
import hashlib
class DigitalSignature:
def __init__(self):
self.private_key = "my_private_key_123"
self.public_key = "my_public_key_456"
def sign_message(self, message):
"""Create a digital signature"""
# Combine message with private key
data_to_sign = message + self.private_key
# Create hash
signature = hashlib.sha256(data_to_sign.encode()).hexdigest()
return signature
def verify_signature(self, message, signature):
"""Verify a digital signature"""
# Recreate signature with public key
data_to_verify = message + self.public_key
expected_signature = hashlib.sha256(data_to_verify.encode()).hexdigest()
return signature == expected_signature
def create_signed_message(self, message):
"""Create a message with its signature"""
signature = self.sign_message(message)
return {
'message': message,
'signature': signature,
'sender': 'Alice'
}
# Using digital signatures
ds = DigitalSignature()
# Create and sign a message
original_message = "Hello, this is a secret message!"
signed_msg = ds.create_signed_message(original_message)
print("Digital Signature Example:")
print(f"Original message: {signed_msg['message']}")
print(f"Signature: {signed_msg['signature']}")
print(f"Sender: {signed_msg['sender']}")
# Verify the signature
is_valid = ds.verify_signature(signed_msg['message'], signed_msg['signature'])
print(f"Signature valid: {is_valid}")
# Try to verify with tampered message
tampered_message = "Hello, this is a tampered message!"
is_valid_tampered = ds.verify_signature(tampered_message, signed_msg['signature'])
print(f"Tampered message valid: {is_valid_tampered}")
SSL/TLS Protocol
SSL/TLS provides secure communication over networks, commonly used for HTTPS connections.
# Simplified SSL/TLS handshake simulation
import random
import hashlib
class SSLHandshake:
def __init__(self):
self.session_key = None
def client_hello(self):
"""Client initiates connection"""
client_random = random.getrandbits(128)
supported_ciphers = ['AES-256', 'AES-128', '3DES']
return {
'type': 'ClientHello',
'client_random': client_random,
'supported_ciphers': supported_ciphers
}
def server_hello(self, client_hello):
"""Server responds to client"""
server_random = random.getrandbits(128)
chosen_cipher = 'AES-256'
server_certificate = "Server's public certificate"
return {
'type': 'ServerHello',
'server_random': server_random,
'chosen_cipher': chosen_cipher,
'server_certificate': server_certificate
}
def key_exchange(self, client_random, server_random):
"""Generate session key"""
# Simplified key generation
combined = str(client_random) + str(server_random) + "session_secret"
self.session_key = hashlib.sha256(combined.encode()).hexdigest()[:32]
return self.session_key
def encrypt_message(self, message):
"""Encrypt message with session key"""
if not self.session_key:
return "No session key established"
# Simplified encryption
encrypted = ""
for i, char in enumerate(message):
key_char = self.session_key[i % len(self.session_key)]
encrypted_char = chr((ord(char) + ord(key_char)) % 256)
encrypted += encrypted_char
return encrypted
def decrypt_message(self, encrypted):
"""Decrypt message with session key"""
if not self.session_key:
return "No session key established"
# Simplified decryption
decrypted = ""
for i, char in enumerate(encrypted):
key_char = self.session_key[i % len(self.session_key)]
decrypted_char = chr((ord(char) - ord(key_char)) % 256)
decrypted += decrypted_char
return decrypted
# Simulating SSL/TLS handshake
ssl = SSLHandshake()
print("SSL/TLS Handshake Simulation:")
print("1. Client Hello...")
client_hello = ssl.client_hello()
print(f" Client Random: {client_hello['client_random']}")
print("2. Server Hello...")
server_hello = ssl.server_hello(client_hello)
print(f" Server Random: {server_hello['server_random']}")
print(f" Chosen Cipher: {server_hello['chosen_cipher']}")
print("3. Key Exchange...")
session_key = ssl.key_exchange(client_hello['client_random'], server_hello['server_random'])
print(f" Session Key: {session_key[:16]}...")
print("4. Secure Communication...")
message = "Hello, secure world!"
encrypted = ssl.encrypt_message(message)
decrypted = ssl.decrypt_message(encrypted)
print(f" Original: {message}")
print(f" Encrypted: {encrypted}")
print(f" Decrypted: {decrypted}")
Cryptographic Best Practices
Security Guidelines
- Use established cryptographic libraries
- Never implement your own crypto algorithms
- Use strong, random keys
- Keep private keys secure
- Use appropriate key lengths
- Regularly update cryptographic protocols
- Use salt for password hashing
- Implement proper key management
Practice Problems
Problem 1: Password Strength Checker
Create a function to check password strength based on various criteria.
Solution
def check_password_strength(password):
score = 0
feedback = []
# Length check
if len(password) >= 8:
score += 1
else:
feedback.append("Password should be at least 8 characters")
# Uppercase check
if any(c.isupper() for c in password):
score += 1
else:
feedback.append("Include uppercase letters")
# Lowercase check
if any(c.islower() for c in password):
score += 1
else:
feedback.append("Include lowercase letters")
# Digit check
if any(c.isdigit() for c in password):
score += 1
else:
feedback.append("Include numbers")
# Special character check
special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
if any(c in special_chars for c in password):
score += 1
else:
feedback.append("Include special characters")
# Strength rating
if score <= 2:
strength = "Weak"
elif score <= 4:
strength = "Medium"
else:
strength = "Strong"
return {
'score': score,
'strength': strength,
'feedback': feedback
}
# Testing password strength
passwords = ["password", "Password123", "P@ssw0rd!", "abc123"]
for pwd in passwords:
result = check_password_strength(pwd)
print(f"Password: {pwd}")
print(f"Score: {result['score']}/5")
print(f"Strength: {result['strength']}")
print(f"Feedback: {result['feedback']}")
print()