Всем привет! Решил я сделать вторую версию своего "шифровальщика", если его можно так назвать. ШИФРЫ В данной версии поддерживаются только два метода шифрования: Цезаря (Просьба не писать про уязвимости этого шифра: частотный анализ, **** и т.д.), и шифр Вернама(Вижинера), хотел добавить RSA, но позже понял, что это не мой уровень. - Шифр Цезаря - это сдвиговый шифр, итоговый шифр складывается из того, что каждая буква сдвигается на несколько позиций (в зависимости от ключа), при этом ключ может находится в диапазоне от 1 до n - 1, где n - это мощность алфавита (в русском языке мощность алфавита 33). Этот шифр старый, из-за этого уязвимый, но в свои времена он был надежным. - Шифр Вернама - это современный шифр (был придуман в 1917 г.), суть в том, что есть ключ(гамма), он должен быть одноразовым и равным в длине тексту, есть текст. Алгоритм такой: посимвольно сложить текст и ключ по модулю мощности алфавита. СТРУКТУРА КОДА Учел замечания из предыдущей темы и постарался разбить код на функции, при этом минимизировать повторения. Условно разбил функции на 5 частей: - Генерирующие ключи - Сами шифры - Меню (выбор шифра и режима) - Основная функция (main) - Остальные функции (проверка дебагера, получение имени файла). САМ КОД Прилагаю код: #include <Windows.h> // В нём содержатся функции для работы с WinAPI #include <fstream> // Библиотека работы с файлами #include <iostream> // Библиотека потокового ввода-вывода #include <iomanip> // Функции работы с вводом-выводом #include <conio.h> // Из этой билиотеки нам нужна только функция _getch() #include <string> // Функции работы со строками #include <ctime> // Нужна нам, чтобы получить случайный seed using namespace std; enum status { ENCRYPT, DECRYPT }; // ----------- KEY-GENERATING FUNCTIONS ----------- int generateCaesarRandomKey() { srand(time(NULL)); return rand() % 25 + 1; } string generateVigVerRandomKey(string text) { srand(time(NULL)); string upperAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; string lowerAlphabet = "abcdefghijklmnopqrstuvwxyz"; string resultKey = ""; for (auto& i : text) { if (i >= 'a' && i <= 'z') { resultKey += lowerAlphabet[rand() % 26]; } else if (i >= 'A' && i <= 'Z') { resultKey += upperAlphabet[rand() % 26]; } } return resultKey; } // ----------- CIPHER FUNCTIONS ----------- string caesarCipher(string text, int key = 0, bool stat = 0) { if (stat == ENCRYPT) { if (key == 0) { key = generateCaesarRandomKey(); } for (auto &i : text) { if (i >= 'a' && i <= 'z') { i += key; if (i > 'z') { i = i - 'z' + 'a' - 1; } } else if (i >= 'A' && i <= 'Z') { i += key; if (i > 'Z') { i = i - 'Z' + 'A' - 1; } } } } else if (stat == DECRYPT) { for (auto& i : text) { if (i >= 'a' && i <= 'z') { i -= key; if (i < 'a') { i = i + 'z' - 'a' + 1; } } else if (i >= 'A' && i <= 'Z') { i -= key; if (i < 'A') { i = i + 'Z' - 'A' + 1; } } } } return text; } string vernamViginereCipher(string text, bool stat = 0, string keyword = "0") { int keyInd = 0; if (stat == ENCRYPT) { if (keyword == "0") { keyword = generateVigVerRandomKey(text); } for (auto& i : text) { if (i >= 'a' && i <= 'z') { i = 97 + ((i - 97) + (keyword[keyInd % keyword.size()] - 97)) % 26; keyInd++; } else if (i >= 'A' && i <= 'Z') { i = 65 + ((i - 65) + (keyword[keyInd % keyword.size()] - 65)) % 26; keyInd++; } } } else if (stat == DECRYPT) { if (keyword != "") { for (auto& i : text) { if (i >= 'a' && i <= 'z') { // Большие выражения с тернарным оператором из-за того, // что остаток от деления в C++ может быть отрицательным // Извиняюсь за такой ужасный фрагмент кода, вот формула которую можно запихнуть в функцию: // (a >= 0 ? a % b : (b - abs(a % b)) % b), где а - это ((i - 97) - (keyword[keyInd % keyword.size()] - 97)), b - 26 (мощность алфавита) i = 97 + (((i - 97) - (keyword[keyInd % keyword.size()] - 97)) >= 0 ? ((i - 97) - (keyword[keyInd % keyword.size()] - 97)) : 26 - abs(((i - 97) - (keyword[keyInd % keyword.size()] - 97)) % 26)) % 26; keyInd++; } else if (i >= 'A' && i <= 'Z') { i = 65 + ((i - 65) - (keyword[keyInd % keyword.size()] - 65)) % 26; keyInd++; } } } } if (stat == ENCRYPT) { string inserting = keyword + "\n"; text.insert(0, inserting); } return text; } // ----------- MENU ----------- int menu() { int chosen = 0; char key = '\0'; while (key != 13) { cout << "\t\tCIPHER: " << endl; cout << setw(30) << "\tCAESAR CIPHER " << right << setw(21) << (chosen == 0 ? "<--" : "") << endl; cout << setw(40) << "\tVERNAM (VIGINERE) CIPHER " << right << setw(10) << (chosen == 1 ? "<--" : "") << endl; key = _getch(); if (key != 13) { key = _getch(); if(key == 72 || key == 80) { if (chosen == 0) chosen = 1; else chosen = 0; } } system("cls"); } return chosen; } bool chooseStat() { bool chosen = 0; char key = '\0'; while (key != 13) { cout << "\t\tCHOOSE ACTION: " << endl; cout << setw(20) << "\tENCRYPT " << right << setw(10) << (chosen == 0 ? "<--" : "") << endl; cout << setw(20) << "\tDECRYPT " << right << setw(10) << (chosen == 1 ? "<--" : "") << endl; key = _getch(); if (key != 13) { key = _getch(); if (key == 72 || key == 80) { chosen = !chosen; } } system("cls"); } return chosen; } // ----------- OTHER FUNCTIONS ----------- void debCheck() { if (IsDebuggerPresent()) exit(0); } string getFileName(bool stat) { string res = ""; time_t now = time(0); tm* loc = localtime(&now); res = (stat == ENCRYPT ? "ENC-" : "DEC-") + to_string(loc->tm_mday) + "-" + to_string(loc->tm_mon) + "-" + to_string(loc->tm_year) + "-" + to_string(loc->tm_hour) + "-" + to_string(loc->tm_min) + (stat == ENCRYPT ? ".mp3" : ".txt"); return res; } // ----------- MAIN ----------- int main() { CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)debCheck, NULL, NULL, NULL); ifstream in; ofstream out; while (true) { cin.ignore(32767, '\n'); system("cls"); char path[MAX_PATH]; string keyword = "-1"; int key = -1; bool stat = chooseStat(); int method = menu(); system("cls"); cout << "\tENTER FILE PATH: "; cin.getline(path, MAX_PATH); system("cls"); switch (method) { case 0: cout << setw(30) << "CAESAR CIPPHER" << endl; cout << setw(10) << "ENTER THE KEY OR 0 TO " << (stat == ENCRYPT ? "GENERATE IT" : "READ IT FROM THE FIRST LINE") << ": " << endl; cin >> key; break; case 1: cout << setw(30) << "VERNAM (VIGINERE) CIPPHER" << endl; cout << setw(10) << "ENTER THE KEYWORD OR \"0\" TO " << (stat == ENCRYPT ? "GENERATE IT" : "READ IT FROM THE FIRST LINE") << ": " << endl; cin >> keyword; break; } if (stat == DECRYPT) { char tmp[1024]; in.open(path); in.getline(tmp, 1024); if (key == 0) { cout << "SET KEY WITH: " << (key = atoi(tmp)) << endl; } else if (keyword == "0") { cout << "SET KEYWORD WITH: " << (keyword = tmp) << endl; } in.close(); } else if (stat == ENCRYPT) { if (key == 0) { cout << "SET KEY WITH: " << (key = generateCaesarRandomKey()) << endl; } } in.open(path); string newFile = getFileName(stat); out.open(newFile); if (out.is_open()) { cout << "CREATED NEW FILE: " << newFile << endl; } else { cout << "ERROR CREATING FILE!" << endl; continue; } if (stat == ENCRYPT) { if (method == 0) { out << key << endl; } } char line[2048]; bool keyS = 1; long long linecnt = 0; while (in.getline(line, 2048)) { switch (method) { case 0: if(stat == DECRYPT && linecnt != 0 || stat == ENCRYPT) out << caesarCipher(line, key, stat) << endl; break; case 1: if (stat == DECRYPT) { if (keyS == 1) { keyword = line; } else { out << vernamViginereCipher(line, stat, keyword) << endl; } keyS = !keyS; } else { out << vernamViginereCipher(line, stat, keyword) << endl; } break; } linecnt++; } in.close(); out.close(); cout << "\tDONE!" << endl; cout << "PRESS \"X\" TO EXIT: "; char com = _getch(); if (com == 'x') break; } cout << "\tBYE!" << endl; return 0; } CPP #include <Windows.h> // В нём содержатся функции для работы с WinAPI #include <fstream> // Библиотека работы с файлами #include <iostream> // Библиотека потокового ввода-вывода #include <iomanip> // Функции работы с вводом-выводом #include <conio.h> // Из этой билиотеки нам нужна только функция _getch() #include <string> // Функции работы со строками #include <ctime> // Нужна нам, чтобы получить случайный seed using namespace std; enum status { ENCRYPT, DECRYPT }; // ----------- KEY-GENERATING FUNCTIONS ----------- int generateCaesarRandomKey() { srand(time(NULL)); return rand() % 25 + 1; } string generateVigVerRandomKey(string text) { srand(time(NULL)); string upperAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; string lowerAlphabet = "abcdefghijklmnopqrstuvwxyz"; string resultKey = ""; for (auto& i : text) { if (i >= 'a' && i <= 'z') { resultKey += lowerAlphabet[rand() % 26]; } else if (i >= 'A' && i <= 'Z') { resultKey += upperAlphabet[rand() % 26]; } } return resultKey; } // ----------- CIPHER FUNCTIONS ----------- string caesarCipher(string text, int key = 0, bool stat = 0) { if (stat == ENCRYPT) { if (key == 0) { key = generateCaesarRandomKey(); } for (auto &i : text) { if (i >= 'a' && i <= 'z') { i += key; if (i > 'z') { i = i - 'z' + 'a' - 1; } } else if (i >= 'A' && i <= 'Z') { i += key; if (i > 'Z') { i = i - 'Z' + 'A' - 1; } } } } else if (stat == DECRYPT) { for (auto& i : text) { if (i >= 'a' && i <= 'z') { i -= key; if (i < 'a') { i = i + 'z' - 'a' + 1; } } else if (i >= 'A' && i <= 'Z') { i -= key; if (i < 'A') { i = i + 'Z' - 'A' + 1; } } } } return text; } string vernamViginereCipher(string text, bool stat = 0, string keyword = "0") { int keyInd = 0; if (stat == ENCRYPT) { if (keyword == "0") { keyword = generateVigVerRandomKey(text); } for (auto& i : text) { if (i >= 'a' && i <= 'z') { i = 97 + ((i - 97) + (keyword[keyInd % keyword.size()] - 97)) % 26; keyInd++; } else if (i >= 'A' && i <= 'Z') { i = 65 + ((i - 65) + (keyword[keyInd % keyword.size()] - 65)) % 26; keyInd++; } } } else if (stat == DECRYPT) { if (keyword != "") { for (auto& i : text) { if (i >= 'a' && i <= 'z') { // Большие выражения с тернарным оператором из-за того, // что остаток от деления в C++ может быть отрицательным // Извиняюсь за такой ужасный фрагмент кода, вот формула которую можно запихнуть в функцию: // (a >= 0 ? a % b : (b - abs(a % b)) % b), где а - это ((i - 97) - (keyword[keyInd % keyword.size()] - 97)), b - 26 (мощность алфавита) i = 97 + (((i - 97) - (keyword[keyInd % keyword.size()] - 97)) >= 0 ? ((i - 97) - (keyword[keyInd % keyword.size()] - 97)) : 26 - abs(((i - 97) - (keyword[keyInd % keyword.size()] - 97)) % 26)) % 26; keyInd++; } else if (i >= 'A' && i <= 'Z') { i = 65 + ((i - 65) - (keyword[keyInd % keyword.size()] - 65)) % 26; keyInd++; } } } } if (stat == ENCRYPT) { string inserting = keyword + "\n"; text.insert(0, inserting); } return text; } // ----------- MENU ----------- int menu() { int chosen = 0; char key = '\0'; while (key != 13) { cout << "\t\tCIPHER: " << endl; cout << setw(30) << "\tCAESAR CIPHER " << right << setw(21) << (chosen == 0 ? "<--" : "") << endl; cout << setw(40) << "\tVERNAM (VIGINERE) CIPHER " << right << setw(10) << (chosen == 1 ? "<--" : "") << endl; key = _getch(); if (key != 13) { key = _getch(); if(key == 72 || key == 80) { if (chosen == 0) chosen = 1; else chosen = 0; } } system("cls"); } return chosen; } bool chooseStat() { bool chosen = 0; char key = '\0'; while (key != 13) { cout << "\t\tCHOOSE ACTION: " << endl; cout << setw(20) << "\tENCRYPT " << right << setw(10) << (chosen == 0 ? "<--" : "") << endl; cout << setw(20) << "\tDECRYPT " << right << setw(10) << (chosen == 1 ? "<--" : "") << endl; key = _getch(); if (key != 13) { key = _getch(); if (key == 72 || key == 80) { chosen = !chosen; } } system("cls"); } return chosen; } // ----------- OTHER FUNCTIONS ----------- void debCheck() { if (IsDebuggerPresent()) exit(0); } string getFileName(bool stat) { string res = ""; time_t now = time(0); tm* loc = localtime(&now); res = (stat == ENCRYPT ? "ENC-" : "DEC-") + to_string(loc->tm_mday) + "-" + to_string(loc->tm_mon) + "-" + to_string(loc->tm_year) + "-" + to_string(loc->tm_hour) + "-" + to_string(loc->tm_min) + (stat == ENCRYPT ? ".mp3" : ".txt"); return res; } // ----------- MAIN ----------- int main() { CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)debCheck, NULL, NULL, NULL); ifstream in; ofstream out; while (true) { cin.ignore(32767, '\n'); system("cls"); char path[MAX_PATH]; string keyword = "-1"; int key = -1; bool stat = chooseStat(); int method = menu(); system("cls"); cout << "\tENTER FILE PATH: "; cin.getline(path, MAX_PATH); system("cls"); switch (method) { case 0: cout << setw(30) << "CAESAR CIPPHER" << endl; cout << setw(10) << "ENTER THE KEY OR 0 TO " << (stat == ENCRYPT ? "GENERATE IT" : "READ IT FROM THE FIRST LINE") << ": " << endl; cin >> key; break; case 1: cout << setw(30) << "VERNAM (VIGINERE) CIPPHER" << endl; cout << setw(10) << "ENTER THE KEYWORD OR \"0\" TO " << (stat == ENCRYPT ? "GENERATE IT" : "READ IT FROM THE FIRST LINE") << ": " << endl; cin >> keyword; break; } if (stat == DECRYPT) { char tmp[1024]; in.open(path); in.getline(tmp, 1024); if (key == 0) { cout << "SET KEY WITH: " << (key = atoi(tmp)) << endl; } else if (keyword == "0") { cout << "SET KEYWORD WITH: " << (keyword = tmp) << endl; } in.close(); } else if (stat == ENCRYPT) { if (key == 0) { cout << "SET KEY WITH: " << (key = generateCaesarRandomKey()) << endl; } } in.open(path); string newFile = getFileName(stat); out.open(newFile); if (out.is_open()) { cout << "CREATED NEW FILE: " << newFile << endl; } else { cout << "ERROR CREATING FILE!" << endl; continue; } if (stat == ENCRYPT) { if (method == 0) { out << key << endl; } } char line[2048]; bool keyS = 1; long long linecnt = 0; while (in.getline(line, 2048)) { switch (method) { case 0: if(stat == DECRYPT && linecnt != 0 || stat == ENCRYPT) out << caesarCipher(line, key, stat) << endl; break; case 1: if (stat == DECRYPT) { if (keyS == 1) { keyword = line; } else { out << vernamViginereCipher(line, stat, keyword) << endl; } keyS = !keyS; } else { out << vernamViginereCipher(line, stat, keyword) << endl; } break; } linecnt++; } in.close(); out.close(); cout << "\tDONE!" << endl; cout << "PRESS \"X\" TO EXIT: "; char com = _getch(); if (com == 'x') break; } cout << "\tBYE!" << endl; return 0; } Если кто-то будет компилировать, необходимо добавить _CRT_SECURE_NO_WARNINGS Интересно ваше мнение.
Абстракции у тебя пока плохо получаются. И надо отходить от процедурного программирования в ООП. Вот некое подобие того что что я имел в виду под словом "стратегия" - прямо перед методом main в моём коде есть переменная EncryptionAlgorithms которая представляет собой массив алгоритмов шифрования. Сейчас в ней только одна реализация - шифр цезаря (абстракцию набросал я, а реализацию более или менее взял из твоего кода) - как первый шаг напиши класс VernamViginereCipherEncryptionAlgorithm и добавь его в этот массив. Прочувствуй насколько изолированно находится реализация алгоритма от кода в котором он используется. Реализуй остальную часть твое программы (меню выбора алгоритма и шифрование файлов) так что бы твой код опирался только на массив EncryptionAlgorithms (без знания того какие именно алгоритмы в этом массиве находятся). #include <iostream> #include <string> using namespace std; class IEncryptionAlgorithm { public: virtual string GetAlgorithmName() = 0; virtual bool ReadSecret(istream &in, ostream &out) = 0; virtual string *Encrypt(string *text) = 0; virtual string *Decrypt(string *text) = 0; }; class CaesarCipherEncryptionAlgorithm : public IEncryptionAlgorithm { private: const int UNDEFINED_KEY = 0; int key = UNDEFINED_KEY; void ValidateKey() { if (key == UNDEFINED_KEY) { throw std::runtime_error("The secred key is not configured. Invoke the `ReadSecret` method first."); } } public: virtual string GetAlgorithmName() { return "Caesar Сipher"; } virtual bool ReadSecret(istream &in, ostream &out) { out << "ENTER THE KEY OR 0 TO GENERATE IT: "; string input; in >> input; if (input == "0") { srand(time(NULL)); key = rand() % 25 + 1; } else { try { key = stoi(input); ValidateKey(); } catch (...) { out << "KEY MUST BE A NON-ZERO INTEGER" << endl; return false; } } out << "SET KEY WITH: " << key << endl; return true; } virtual string *Encrypt(string *text) { ValidateKey(); string* encrypted_text = new std::string(*text); for (auto &i : *encrypted_text) { if (i >= 'a' && i <= 'z') { i += key; if (i > 'z') { i = i - 'z' + 'a' - 1; } } else if (i >= 'A' && i <= 'Z') { i += key; if (i > 'Z') { i = i - 'Z' + 'A' - 1; } } } return encrypted_text; } virtual string *Decrypt(string *text) { ValidateKey(); string *decrypted_text = new std::string(*text); for (auto &i : *decrypted_text) { if (i >= 'a' && i <= 'z') { i -= key; if (i < 'a') { i = i + 'z' - 'a' + 1; } } else if (i >= 'A' && i <= 'Z') { i -= key; if (i < 'A') { i = i + 'Z' - 'A' + 1; } } } return decrypted_text; } }; IEncryptionAlgorithm* EncryptionAlgorithms[1] = { new CaesarCipherEncryptionAlgorithm(), }; int main() { for (IEncryptionAlgorithm* encryptionAlgorithm : EncryptionAlgorithms) { cout << "Configuring `" << encryptionAlgorithm->GetAlgorithmName() << "` encryption algorithm:" << endl; bool secretConfigured; do { secretConfigured = encryptionAlgorithm->ReadSecret(cin, cout); } while (!secretConfigured); } cout << endl << "Type the text to encrypt: "; string unencryptedText; cin >> unencryptedText; for (IEncryptionAlgorithm* encryptionAlgorithm : EncryptionAlgorithms) { cout << endl << encryptionAlgorithm->GetAlgorithmName() << ":" << endl; string* encryptedText = encryptionAlgorithm->Encrypt(&unencryptedText); cout << "\x1B[31mEncrypted text:\033[0m " << *encryptedText << endl; string* decryptedText = encryptionAlgorithm->Decrypt(encryptedText); cout << "\x1B[32mDecrypted text:\033[0m " << *decryptedText << endl; delete encryptedText; delete decryptedText; } return 0; } C #include <iostream> #include <string> using namespace std; class IEncryptionAlgorithm { public: virtual string GetAlgorithmName() = 0; virtual bool ReadSecret(istream &in, ostream &out) = 0; virtual string *Encrypt(string *text) = 0; virtual string *Decrypt(string *text) = 0; }; class CaesarCipherEncryptionAlgorithm : public IEncryptionAlgorithm { private: const int UNDEFINED_KEY = 0; int key = UNDEFINED_KEY; void ValidateKey() { if (key == UNDEFINED_KEY) { throw std::runtime_error("The secred key is not configured. Invoke the `ReadSecret` method first."); } } public: virtual string GetAlgorithmName() { return "Caesar Сipher"; } virtual bool ReadSecret(istream &in, ostream &out) { out << "ENTER THE KEY OR 0 TO GENERATE IT: "; string input; in >> input; if (input == "0") { srand(time(NULL)); key = rand() % 25 + 1; } else { try { key = stoi(input); ValidateKey(); } catch (...) { out << "KEY MUST BE A NON-ZERO INTEGER" << endl; return false; } } out << "SET KEY WITH: " << key << endl; return true; } virtual string *Encrypt(string *text) { ValidateKey(); string* encrypted_text = new std::string(*text); for (auto &i : *encrypted_text) { if (i >= 'a' && i <= 'z') { i += key; if (i > 'z') { i = i - 'z' + 'a' - 1; } } else if (i >= 'A' && i <= 'Z') { i += key; if (i > 'Z') { i = i - 'Z' + 'A' - 1; } } } return encrypted_text; } virtual string *Decrypt(string *text) { ValidateKey(); string *decrypted_text = new std::string(*text); for (auto &i : *decrypted_text) { if (i >= 'a' && i <= 'z') { i -= key; if (i < 'a') { i = i + 'z' - 'a' + 1; } } else if (i >= 'A' && i <= 'Z') { i -= key; if (i < 'A') { i = i + 'Z' - 'A' + 1; } } } return decrypted_text; } }; IEncryptionAlgorithm* EncryptionAlgorithms[1] = { new CaesarCipherEncryptionAlgorithm(), }; int main() { for (IEncryptionAlgorithm* encryptionAlgorithm : EncryptionAlgorithms) { cout << "Configuring `" << encryptionAlgorithm->GetAlgorithmName() << "` encryption algorithm:" << endl; bool secretConfigured; do { secretConfigured = encryptionAlgorithm->ReadSecret(cin, cout); } while (!secretConfigured); } cout << endl << "Type the text to encrypt: "; string unencryptedText; cin >> unencryptedText; for (IEncryptionAlgorithm* encryptionAlgorithm : EncryptionAlgorithms) { cout << endl << encryptionAlgorithm->GetAlgorithmName() << ":" << endl; string* encryptedText = encryptionAlgorithm->Encrypt(&unencryptedText); cout << "\x1B[31mEncrypted text:\033[0m " << *encryptedText << endl; string* decryptedText = encryptionAlgorithm->Decrypt(encryptedText); cout << "\x1B[32mDecrypted text:\033[0m " << *decryptedText << endl; delete encryptedText; delete decryptedText; } return 0; }