Introduction to Cryptography
5 guided sections and curated resources to get you contest-ready.
Lesson Playbook
5 sectionsEncoding vs Encryption vs Hashing
These terms are often confused, but they serve very different purposes:
Encoding - Transforms data for compatibility (NOT security!)
- Base64, URL encoding, HTML entities
- Anyone can decode - no secret key needed
- Used for data transmission, not protection
Encryption - Transforms data to hide it from unauthorized users
- Requires a key to encrypt and decrypt
- Symmetric (same key) vs Asymmetric (public/private keys)
- Examples: AES, RSA, ChaCha20
Hashing - One-way transformation to verify integrity
- Cannot be reversed (in theory)
- Same input always produces same output
- Examples: MD5, SHA-256, bcrypt
Pro tip: In CTFs, if you see strings ending in '=' they're likely Base64. If it's 32 hex characters, probably MD5. 64 hex characters? Likely SHA-256.
Base64 Encoding
Base64 is everywhere in CTFs. It converts binary data to printable ASCII characters using a 64-character alphabet (A-Z, a-z, 0-9, +, /).
# Encoding
echo -n "Hello CTF" | base64
# Output: SGVsbG8gQ1RG
# Decoding
echo "SGVsbG8gQ1RG" | base64 -d
# Output: Hello CTF
# In Python
import base64
encoded = base64.b64encode(b"Hello CTF")
decoded = base64.b64decode(encoded)
# Recognizing Base64:
# - Length is multiple of 4 (padding with =)
# - Only contains A-Za-z0-9+/=
# - Often ends with = or ==Common Encodings in CTFs
You'll encounter these encodings constantly:
Hexadecimal - Base 16 (0-9, A-F) 48656c6c6f = "Hello"
Binary - Base 2 (0 and 1) 01001000 01100101 = "He"
ROT13 - Rotate each letter 13 positions Hello → Uryyb
URL Encoding - %XX format for special characters Hello World → Hello%20World
ASCII - Decimal representation 72 101 108 108 111 = "Hello"
# Python encoding toolkit
# Hex
"Hello".encode().hex() # '48656c6c6f'
bytes.fromhex('48656c6c6f').decode() # 'Hello'
# Binary
' '.join(format(ord(c), '08b') for c in "Hi")
# ROT13
import codecs
codecs.encode("Hello", 'rot_13') # 'Uryyb'
# URL
from urllib.parse import quote, unquote
quote("Hello World") # 'Hello%20World'Pro tip: CyberChef (gchq.github.io/CyberChef) is your best friend for encoding challenges. It can chain multiple operations and auto-detect encodings.
Hash Functions
Hashes are used for password storage, file integrity, and digital signatures. In CTFs, you'll often need to crack weak hashes.
MD5 (Broken, don't use for security)
- 32 hex characters (128 bits)
- Fast to compute = fast to crack
- "password" → 5f4dcc3b5aa765d61d8327deb882cf99
SHA-1 (Broken)
- 40 hex characters (160 bits)
- "password" → 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
SHA-256 (Currently secure)
- 64 hex characters (256 bits)
- Much slower to crack
# Generate hashes in terminal
echo -n "password" | md5sum
echo -n "password" | sha256sum
# Python hashing
import hashlib
hashlib.md5(b"password").hexdigest()
hashlib.sha256(b"password").hexdigest()
# Online cracking (for weak hashes)
# - crackstation.net
# - hashes.com
# - hashcat (local, GPU-accelerated)Classic Ciphers
Many beginner CTFs feature classical ciphers that you can break by hand or with simple tools:
Caesar Cipher - Shift each letter by N positions
- KHOOR (shift 3) → HELLO
- Try all 25 shifts to break it
Vigenère Cipher - Multiple Caesar shifts using a keyword
- More secure but still breakable
- Look for repeating patterns
Substitution Cipher - Each letter maps to another letter
- Use frequency analysis (E is most common in English)
- Look for common words like THE, AND, IS
# Caesar cipher in Python
def caesar(text, shift):
result = ""
for char in text:
if char.isalpha():
base = ord('A') if char.isupper() else ord('a')
result += chr((ord(char) - base + shift) % 26 + base)
else:
result += char
return result
# Brute force all shifts
cipher = "KHOOR"
for i in range(26):
print(f"Shift {i}: {caesar(cipher, -i)}")