Playfair Cipher là mật mã thay thế theo cặp ký tự (digraph) sử dụng bảng khóa 5x5. Khác với các mật mã đơn bảng, nó che giấu được tần suất ký tự đơn lẻ, tăng độ an toàn so với Caesar/Affine.
Tạo bảng khóa (Key Square)
- Nhập từ khóa, loại bỏ ký tự trùng lặp.
- Điền tiếp các chữ cái còn lại của bảng chữ cái (A-Z, bỏ Q hoặc gộp I/J).
- Sắp vào lưới 5x5 theo hàng.
Ví dụ khóa TUTORIALS (I=J):
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
Quy tắc mã hóa
- Chuẩn hóa bản rõ: chỉ giữ chữ cái, chuyển uppercase, I→J (nếu chọn), chia thành cặp.
- Nếu cặp có 2 ký tự trùng (ví dụ
EE) → chènXgiữa:EXE. - Nếu độ dài lẻ → thêm
X(hoặcZ) ở cuối. - Với mỗi cặp
(c1, c2):- Cùng hàng: Lấy ký tự bên phải (quay vòng).
- Cùng cột: Lấy ký tự bên dưới (quay vòng).
- Khác hàng/cột: Tạo hình chữ nhật, lấy góc đối diện ngang hàng.
Chương trình hoàn chỉnh (Class Playfair)
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;
class Playfair {
char grid[5][5];
bool iEqualsJ;
void buildGrid(const string& key) {
string s;
// Xử lý khóa
for (char ch : key) {
ch = toupper(ch);
if (!isalpha(ch)) continue;
if (ch == 'J' && iEqualsJ) ch = 'I';
if (ch == 'Q' && !iEqualsJ) continue;
if (s.find(ch) == string::npos) s += ch;
}
// Bổ sung chữ cái còn lại
for (char ch = 'A'; ch <= 'Z'; ++ch) {
if (ch == 'J' && iEqualsJ) continue;
if (ch == 'Q' && !iEqualsJ) continue;
if (s.find(ch) == string::npos) s += ch;
}
// Điền lưới
for (int i = 0; i < 25; ++i) grid[i/5][i%5] = s[i];
}
bool findPos(char ch, int& r, int& c) const {
ch = toupper(ch);
if (ch == 'J' && iEqualsJ) ch = 'I';
for (r = 0; r < 5; ++r)
for (c = 0; c < 5; ++c)
if (grid[r][c] == ch) return true;
return false;
}
char getChar(int r, int c) const { return grid[(r+5)%5][(c+5)%5]; }
string prepare(const string& text) {
string filtered;
for (char ch : text) if (isalpha(ch)) {
ch = toupper(ch);
if (ch == 'J' && iEqualsJ) ch = 'I';
if (ch == 'Q' && !iEqualsJ) continue;
filtered += ch;
}
// Chèn X giữa cặp trùng
string paired;
for (size_t i = 0; i < filtered.size(); ) {
paired += filtered[i];
if (i+1 < filtered.size()) {
if (filtered[i] == filtered[i+1]) paired += 'X';
else paired += filtered[i+1];
} else paired += 'X';
i += (i+1 < filtered.size() && filtered[i] == filtered[i+1]) ? 1 : 2;
}
return paired;
}
public:
Playfair(string key, bool iEqJ = true) : iEqualsJ(iEqJ) { buildGrid(key); }
string encrypt(const string& plaintext) {
string prep = prepare(plaintext);
string cipher;
for (size_t i = 0; i < prep.size(); i += 2) {
char c1 = prep[i], c2 = prep[i+1];
int r1, c1_, r2, c2_;
findPos(c1, r1, c1_); findPos(c2, r2, c2_);
if (r1 == r2) { // Cùng hàng → phải
cipher += getChar(r1, c1_ + 1);
cipher += getChar(r2, c2_ + 1);
} else if (c1_ == c2_) { // Cùng cột → dưới
cipher += getChar(r1 + 1, c1_);
cipher += getChar(r2 + 1, c2_);
} else { // Hình chữ nhật
cipher += getChar(r1, c2_);
cipher += getChar(r2, c1_);
}
}
return cipher;
}
string decrypt(const string& ciphertext) {
string plain;
for (size_t i = 0; i < ciphertext.size(); i += 2) {
char c1 = ciphertext[i], c2 = ciphertext[i+1];
int r1, c1_, r2, c2_;
findPos(c1, r1, c1_); findPos(c2, r2, c2_);
if (r1 == r2) { // Cùng hàng → trái
plain += getChar(r1, c1_ - 1);
plain += getChar(r2, c2_ - 1);
} else if (c1_ == c2_) { // Cùng cột → trên
plain += getChar(r1 - 1, c1_);
plain += getChar(r2 - 1, c2_);
} else { // Hình chữ nhật (giống mã hóa)
plain += getChar(r1, c2_);
plain += getChar(r2, c1_);
}
}
return plain;
}
};
int main() {
Playfair pf("PLAYERS", true);
string msg = "This is TutorialsPoint";
cout << "Ban ro: " << msg << '\n';
string cipher = pf.encrypt(msg);
cout << "Ban ma: " << cipher << '\n';
string decrypted = pf.decrypt(cipher);
cout << "Giai ma: " << decrypted << '\n';
return 0;
}
Kết quả
Ban ro: This is TutorialsPoint Ban ma: OKGC GCMZ MQCF YARL QHOM Giai ma: THISISTUTORIALSPOINT
So sánh 3 mật mã cổ điển
| Đặc điểm | Caesar | Affine | Playfair |
|---|---|---|---|
| Đơn vị mã hóa | 1 ký tự | 1 ký tự | 2 ký tự (digraph) |
| Khóa | Shift (0-25) | (a,b), gcd(a,26)=1 | Từ khóa → bảng 5x5 |
| Loại | Monoalphabetic | Monoalphabetic | Polygraphic |
| Kháng freq analysis | Không | Không | Một phần (digraph) |
| Không gian khóa | 26 | 312 | 25! ≈ 1.5e25 |
Playfair là bước tiến quan trọng trong lịch sử mật mã học, nhưng hiện nay đã lỗi thời. Tuy nhiên, nó là bài tập tuyệt vời để hiểu: ma trận, modulo, xử lý chuỗi, và tư duy thiết kế mật mã khối (block cipher).