Classical Cryptography Suite

Advanced encryption and decryption tools with educational content for beginners and experts

Caesar Cipher

A substitution cipher where each letter is shifted by a fixed number of positions in the alphabet.

3

How it works:

Formula: E(x) = (x + n) mod 26 for encryption

Example: With shift=3, 'A' becomes 'D', 'B' becomes 'E', etc.

Security Demonstration:

Vigenère Cipher

A polyalphabetic substitution cipher using a keyword to vary the Caesar shift.

How it works:

Formula: E(i) = (P(i) + K(i mod m)) mod 26

Key repeating: The key repeats to match the text length

Security Demonstration:

Security Analysis & Cryptanalysis

Frequency Analysis

Analyze letter frequencies in your text to identify patterns

Vulnerability Comparison

Algorithm Key Space Security Level Main Vulnerabilities
Caesar 25 Very Low Brute Force, Frequency Analysis
Vigenère 26^m Low-Medium Kasiski, Index of Coincidence

Attack Methods

Brute Force Attack

Effectiveness: 100% against Caesar (25 keys), exponential time for Vigenère

Frequency Analysis

Most common letters: E(12.7%), T(9.1%), A(8.2%), O(7.5%)

Kasiski Examination

Finds repeated sequences to determine Vigenère key length

Implementation Code

Well-commented implementations for educational purposes

/**
 * Caesar Cipher Implementation
 * Simple substitution cipher with fixed shift
 * Time Complexity: O(n) where n is text length
 */

function caesarCipher(text, shift, decrypt = false) {
    // Adjust shift for decryption
    if (decrypt) shift = 26 - shift;
    
    return text.split('').map(char => {
        // Only process alphabetic characters
        if (/[A-Za-z]/.test(char)) {
            // Get character code and normalize to 0-25
            const code = char.toUpperCase().charCodeAt(0) - 65;
            
            // Apply Caesar shift with modulo wrap-around
            const shifted = (code + shift) % 26;
            
            // Convert back to character, preserving original case
            const newChar = String.fromCharCode(shifted + 65);
            return char === char.toLowerCase() ? newChar.toLowerCase() : newChar;
        }
        
        // Return non-alphabetic characters unchanged
        return char;
    }).join('');
}

// Brute force attack simulation
function bruteForceCaesar(ciphertext) {
    const results = [];
    
    // Try all possible shifts (1-25)
    for (let shift = 1; shift <= 25; shift++) {
        const decrypted = caesarCipher(ciphertext, shift, true);
        results.push({ shift, text: decrypted });
    }
    
    return results;
}
/**
 * Vigenère Cipher Implementation
 * Polyalphabetic substitution using keyword
 * Time Complexity: O(n) where n is text length
 */

function vigenereCipher(text, key, decrypt = false) {
    // Normalize key to uppercase
    key = key.toUpperCase().replace(/[^A-Z]/g, '');
    
    if (key.length === 0) return text;
    
    let keyIndex = 0;
    
    return text.split('').map(char => {
        // Only process alphabetic characters
        if (/[A-Za-z]/.test(char)) {
            const textCode = char.toUpperCase().charCodeAt(0) - 65;
            const keyCode = key[keyIndex % key.length].charCodeAt(0) - 65;
            
            // Apply Vigenère formula
            let shifted;
            if (decrypt) {
                shifted = (textCode - keyCode + 26) % 26;
            } else {
                shifted = (textCode + keyCode) % 26;
            }
            
            // Increment key position for alphabetic chars only
            keyIndex++;
            
            // Convert back to character, preserving case
            const newChar = String.fromCharCode(shifted + 65);
            return char === char.toLowerCase() ? newChar.toLowerCase() : newChar;
        }
        
        return char;
    }).join('');
}

// Key length analysis using Index of Coincidence
function indexOfCoincidence(text) {
    const counts = {};
    let total = 0;
    
    // Count letter frequencies
    for (const char of text.toUpperCase()) {
        if (/[A-Z]/.test(char)) {
            counts[char] = (counts[char] || 0) + 1;
            total++;
        }
    }
    
    // Calculate IC = Σ(ni * (ni-1)) / (N * (N-1))
    let sum = 0;
    for (const count of Object.values(counts)) {
        sum += count * (count - 1);
    }
    
    return total > 1 ? sum / (total * (total - 1)) : 0;
}
/**
 * Cryptanalysis Attack Methods
 * Educational implementations of common attacks
 */

// Frequency analysis for single substitution ciphers
function frequencyAnalysis(text) {
    const frequencies = {};
    let totalLetters = 0;
    
    // Count each letter
    for (const char of text.toUpperCase()) {
        if (/[A-Z]/.test(char)) {
            frequencies[char] = (frequencies[char] || 0) + 1;
            totalLetters++;
        }
    }
    
    // Convert to percentages
    const analysis = [];
    for (const [letter, count] of Object.entries(frequencies)) {
        analysis.push({
            letter,
            count,
            frequency: ((count / totalLetters) * 100).toFixed(2)
        });
    }
    
    // Sort by frequency (descending)
    return analysis.sort((a, b) => b.count - a.count);
}

// Kasiski Examination for Vigenère cipher
function kasiskiExamination(ciphertext) {
    const text = ciphertext.toUpperCase().replace(/[^A-Z]/g, '');
    const trigrams = {};
    const distances = [];
    
    // Find all trigrams and their positions
    for (let i = 0; i <= text.length - 3; i++) {
        const trigram = text.substr(i, 3);
        if (!trigrams[trigram]) {
            trigrams[trigram] = [];
        }
        trigrams[trigram].push(i);
    }
    
    // Calculate distances between repeated trigrams
    for (const [trigram, positions] of Object.entries(trigrams)) {
        if (positions.length > 1) {
            for (let i = 1; i < positions.length; i++) {
                distances.push(positions[i] - positions[i-1]);
            }
        }
    }
    
    // Find GCD of distances to estimate key length
    return distances.length > 0 ? gcd(...distances) : null;
}

// Greatest Common Divisor helper function
function gcd(a, b) {
    return b === 0 ? a : gcd(b, a % b);
}