Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C++

Mã hóa Playfair Cipher trong C++

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)

  1. Nhập từ khóa, loại bỏ ký tự trùng lặp.
  2. Đ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).
  3. 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

  1. 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.
  2. Nếu cặp có 2 ký tự trùng (ví dụ EE) → chèn X giữa: EXE.
  3. Nếu độ dài lẻ → thêm X (hoặc Z) ở cuối.
  4. 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ểmCaesarAffinePlayfair
Đơn vị mã hóa1 ký tự1 ký tự2 ký tự (digraph)
KhóaShift (0-25)(a,b), gcd(a,26)=1Từ khóa → bảng 5x5
LoạiMonoalphabeticMonoalphabeticPolygraphic
Kháng freq analysisKhôngKhôngMột phần (digraph)
Không gian khóa2631225! ≈ 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).