Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

C++ Program: Decode Playfair Cipher (Playfair Cipher Decryption)


Playfair Cipher เป็น Digraph Substitution Cipher เข้ารหัสคู่ตัวอักษร (Pairs) ใช้ Key Table 5×5 (อักขระ 25 ตัว ปกติละ J รวมกับ I)

Key Table Generation

  1. นำ Keyword มาเขียนซ้าย-ขวา บน-ล่าง ลบตัวซ้ำ
  2. เติมอักขระที่เหลือ A-Z (ยกเว้น J) ตามลำดับ

Example: Keyword "TUTORIALS"

T U O R I
A L S B C
D E F G H
K M N P Q
V W X Y Z

Encryption Rules (สำหรับ Pair ของตัวอักษร)

  1. Same Row: เลื่อยไปขวา 1 ตัว (Wrap around)
  2. Same Column: เลื่อนลง 1 ตัว (Wrap around)
  3. Rectangle: สลับ Column (Same Row, Other Corner)

Decryption Rules (ย้อนกลับ)

  1. Same Row: เลื่อนซ้าย 1 ตัว
  2. Same Column: เลื่อนขึ้น 1 ตัว
  3. Rectangle: สลับ Column (เหมือน Encryption)

โค้ด C++ สมบูรณ์ (Encrypt + Decrypt)

#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#include <algorithm>
using namespace std;

class PlayfairCipher {
    char table[5][5];
    // Position Cache: char -> (row, col)
    array, 26> posCache;
    bool cacheValid = false;
    
    void buildTable(const string& keyword, bool combineIJ = true) {
        string key = keyword;
        transform(key.begin(), key.end(), key.begin(), ::toupper);
        key.erase(remove_if(key.begin(), key.end(), 
            [](char c){ return !isalpha(c); }), key.end());
        
        string alphabet = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; // No J
        if (!combineIJ) alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXZ"; // No Q instead
        
        string used;
        for (char ch : key + alphabet) {
            char c = (combineIJ && ch == 'J') ? 'I' : ch;
            if (!combineIJ && ch == 'Q') continue;
            if (used.find(c) == string::npos) used += c;
        }
        
        for (int i = 0; i < 25; i++) {
            int r = i / 5, c = i % 5;
            table[r][c] = used[i];
            posCache[used[i] - 'A'] = {r, c};
        }
        cacheValid = true;
    }
    
    pair getPos(char ch) const {
        ch = toupper(ch);
        if (ch == 'J') ch = 'I';
        return posCache[ch - 'A'];
    }
    
    char getChar(int row, int col) const {
        return table[(row + 5) % 5][(col + 5) % 5];
    }
    
    string prepareText(const string& text, bool encrypt) {
        string result;
        for (char ch : text) {
            if (isalpha(ch)) {
                ch = toupper(ch);
                if (ch == 'J') ch = 'I';
                result += ch;
            }
        }
        if (encrypt) {
            string padded;
            for (size_t i = 0; i < result.length(); ) {
                padded += result[i];
                if (i + 1 < result.length() && result[i] == result[i+1]) {
                    padded += 'X'; // Insert X between doubles
                } else if (i + 1 < result.length()) {
                    padded += result[i+1];
                    i += 2;
                    continue;
                }
                i++;
            }
            if (padded.length() % 2 == 1) padded += 'X';
            return padded;
        }
        return result;
    }
    
    string process(const string& text, bool encrypt) {
        string prepared = prepareText(text, encrypt);
        string result;
        int dir = encrypt ? 1 : -1;
        
        for (size_t i = 0; i < prepared.length(); i += 2) {
            char a = prepared[i];
            char b = prepared[i+1];
            
            auto [r1, c1] = getPos(a);
            auto [r2, c2] = getPos(b);
            
            if (r1 == r2) { // Same Row
                result += getChar(r1, c1 + dir);
                result += getChar(r2, c2 + dir);
            } else if (c1 == c2) { // Same Column
                result += getChar(r1 + dir, c1);
                result += getChar(r2 + dir, c2);
            } else { // Rectangle
                result += getChar(r1, c2);
                result += getChar(r2, c1);
            }
        }
        return result;
    }
    
public:
    string encrypt(const string& plaintext, const string& keyword, bool combineIJ = true) {
        buildTable(keyword, combineIJ);
        return process(plaintext, true);
    }
    
    string decrypt(const string& ciphertext, const string& keyword, bool combineIJ = true) {
        buildTable(keyword, combineIJ);
        return process(ciphertext, false);
    }
    
    void printTable() const {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++)
                cout << table[i][j] << ' ';
            cout << '\n';
        }
    }
};

int main() {
    PlayfairCipher cipher;
    
    // Example from article
    string key = "tutorials";
    string plaintext = "hide money";
    
    cout << "Key Table:\n"; cipher.buildTable(key); cipher.printTable();
    cout << "\nPlaintext: " << plaintext << "\n";
    
    string encrypted = cipher.encrypt(plaintext, key);
    cout << "Encrypted: " << encrypted << "\n";
    
    string decrypted = cipher.decrypt(encrypted, key);
    cout << "Decrypted: " << decrypted << "\n";
    
    // Interactive Example
    cout << "\n--- Interactive ---\n";
    string k, msg;
    cout << "Enter key: "; getline(cin, k);
    cout << "Enter message: "; getline(cin, msg);
    cout << "Encrypt (e) or Decrypt (d)? ";
    char mode; cin >> mode; cin.ignore();
    
    if (mode == 'e' || mode == 'E') {
        cout << "Encrypted: " << cipher.encrypt(msg, k) << "\n";
    } else {
        cout << "Decrypted: " << cipher.decrypt(msg, k) << "\n";
    }
    
    return 0;
}

ผลลัพธ์ตัวอย่าง

Key Table:
T U O R I
A L S B C
D E F G H
K M N P Q
V W X Y Z

Plaintext: hide money
Encrypted: QCEFNUMFZV
Decrypted: HIDEMONEYX

--- Interactive ---
Enter key: players
Enter message: This is tutorialspoint
Encrypt (e) or Decrypt (d)? e
Encrypted: OKGCGCMZMQCFYA... (depends on key)

การจัดการ Double Letters และ Odd Length

  • Double Letters (เช่น "LL"): แทรก 'X' ระหว่าง → "LXL"
  • Odd Length: เติม 'X' ท้ายสุด
  • Decryption: ต้องลบ 'X' ที่แทรกด้วยมือ (Ambiguity)

Security

  • Key Space: 25! ≈ 1.55×10²⁵ (แต่ Keyword-based ลดลงมาก)
  • Frequency Analysis: ยากกว่า Monoalphabetic เพราะเป็น Digraph
  • Known Plaintext Attack: ได้ง่ายถ้ามี Crib
  • ไม่ปลอดภัยสำหรับการใช้งานจริง