Playfair Cipher เป็น Digraph Substitution Cipher เข้ารหัสคู่ตัวอักษร (Pairs) ใช้ Key Table 5×5 (อักขระ 25 ตัว ปกติละ J รวมกับ I)
Key Table Generation
- นำ Keyword มาเขียนซ้าย-ขวา บน-ล่าง ลบตัวซ้ำ
- เติมอักขระที่เหลือ 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 ของตัวอักษร)
- Same Row: เลื่อยไปขวา 1 ตัว (Wrap around)
- Same Column: เลื่อนลง 1 ตัว (Wrap around)
- Rectangle: สลับ Column (Same Row, Other Corner)
Decryption Rules (ย้อนกลับ)
- Same Row: เลื่อนซ้าย 1 ตัว
- Same Column: เลื่อนขึ้น 1 ตัว
- 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
- ไม่ปลอดภัยสำหรับการใช้งานจริง