Очень интересно. Со временем ваша программа развивалась, вы обнаружили некоторые проблемы и пошагово исправили их. Иногда устраняя ошибки, делая одну и ту же ошибку дважды.
Тем не менее, есть некоторые критические ошибки, которые необходимо исправить. Это в основном несвязанные ошибки. Затем возникают ошибки стиля, такие как глобальные переменные и использование неправильных типов данных. Итак, большая проблема с дизайном программного обеспечения, которая должна быть подвергнута рефакторингу
И, наконец, что не менее важно, ваша идея о шифровании не будет работать, потому что вы храните в Key.txt «всегда первую букву вместе с ключом. В
sb = sb + (chars[(sb.length()) / 128]);
Вы всегда добавляете символ очистки, за которым следуют 128 байтов ключа. Таким образом, безопасность вашего подхода равна 0.
Затем, далее. Вы должен понимать индексы и границы массивов. Индекс массива начинается с 0, а последний допустимый индекс равен размеру массива минус 1. Для массива {1,2,3,4} размер равен 4, первый индекс равен 0 и последний индекс равен 3!
Вы всегда должны проверять, используете ли вы действительные индексы. Пример. В вашем утверждении:
chars[rand() % 95 + 1]
Вы создаете много проблем, выходящих за границы. rand
может выдавать числа от 0 до очень больших. Если остаток от такого большого числа равен 94, вы добавляете 1 к индексу и получаете неправильный 95. И потому в определении переменной
char chars[95];
int runs = 0;
выполняется определяется после символов, Вы получаете доступ к младшему байту переменной runs
. Вы можете проверить это. В первом создании файла ключа вы увидите много символов 0. Во втором случае, когда запуски будут равны единице, вы создадите символы \ 001.
Лучше всего использовать современные контейнеры C ++, которые помогут вам в таких проверках.
Далее, люди, которые читают вас сообщение не может понять, почему вы делаете определенные вещи. Итак, нам было интересно, почему вы пишете substr(1,128)
, а затем content = content.substr(130, content.size());
. После этого вы удалили первый чистый символ, а затем прочитали 128 байтов. И затем вы пропускаете "\ n", который вы добавили в ваш ключевой файл. Все это должно быть переработано.
Еще одна проблема. Для чтения вашего "chars.txt". Вы узнали, что не читали пробел '' в вашем l oop. Но вы не проверяли атрибут «skipws» IO-потока, а исправили его, добавив пробел в качестве последнего символа. Вы могли бы использовать функцию get
или getline
, чтобы решить эту проблему.
Теперь к вашему вопросу. В расшифровке вы использовали
for (int i = 0; i < 94; i++)
Итак, вы читаете все, кроме последней строки. Конец, то вы делаете
pass = pass.substr(129, pass.length());
129 неправильно. Это должно быть 128. Вы уже удалили первый чистый символ и "\ n". Затем он работает, я тестировал его в отладчике.
Однако все это не будет работать, потому что вы создаете огромный зашифрованный вывод для небольших входных строк. Таким образом, для 3 символов «ab c» вы создадите 384 байта вывода. Кто должен набрать это, и особенно с ошибочно создать 0 символов. Таким образом, вы даже не можете проверить код. Вам следует сохранить зашифрованные данные в файле, а для расшифровки снова прочитать их из файла.
Кстати, вы вызываете рекурсивный запуск вашей функции, что неправильно.
Есть еще много выводов. Следовательно, рекомендуется его рефакторинг. И, пожалуйста, добавьте тонны проверок ошибок. Особенно для любой IO-функции. Вы должны проверить, что это сработало.
Если у вас есть дополнительные вопросы, спросите.
Чтобы показать вам, как выполнять проверку ошибок, я создал для вас полное работоспособное решение. Пожалуйста, смотрите:
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <random>
#include <algorithm>
#include <iterator>
#include <numeric>
// Definitions ---------------------------------------------------------------------------------------------------------------------
// Filenames that we want to use in our software
const std::string fileNamePrintableCharacters{ "chars.txt" };
const std::string fileNameKey{ "Key.txt" };
// That many printable characters we will use. We will use the ASCII and hence have characters in the range 32-126, and that are 95
constexpr size_t NumberOfUsedPrintableCharacters = 95U;
// One character will be encrypted with this number of bytes
constexpr size_t NumberOfEncryptionCharacters = 128U;
// This is datatype for our key. For each printable character we use NumberOfEncryptionCharacters.
using KeyData = std::vector<std::string>;
// ----------------------------------------------------------------------------------------------------------------------------------
// Read the printable characters from the definition file
std::string getPrintableCharacters() {
// Here we sill store the result. Initialize all values with 0
std::string printableCharacters{};
// Open the file with the printable characters and check, if the file could be opened
if (std::ifstream printableCharactersFileStream{ fileNamePrintableCharacters }; printableCharactersFileStream) {
// Read the printable characters and check, if that worked
if (!std::getline(printableCharactersFileStream, printableCharacters) || (printableCharacters.size() != NumberOfUsedPrintableCharacters)) {
std::cerr << "\n\n*** Error: Could not read the expected data from ('" << fileNamePrintableCharacters << "')\n";
printableCharacters = {};
}
}
else { // In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: File with printable characters ('" << fileNamePrintableCharacters << "') could not be opened\n";
}
// Return the array with printable characters to the caller using Return Value Optimization / Copy Elision
return printableCharacters;
}
// ----------------------------------------------------------------------------------------------------------------------------------
std::string generateNewPrintableCharactersFile() {
// Here we will stor the resulting printabl characters string
std::string printableCharacters(NumberOfUsedPrintableCharacters, '\0');
// Open output file and chcke, if it could be opened
if (std::ofstream printableCharactersFileStream{ fileNamePrintableCharacters }; printableCharactersFileStream) {
// Create all printable characters
std::iota(printableCharacters.begin(), printableCharacters.end(), ' ');
if (!(printableCharactersFileStream << printableCharacters)) {
std::cerr << "\n\n*** Error: Could not create printable character file\n";
printableCharacters.clear();
}
}
return printableCharacters;
}
// ----------------------------------------------------------------------------------------------------------------------------------
KeyData getKeyData() {
// Here we will store all key data
KeyData keyData{};
// indicator for error. In case of error, we delete all key data
bool thereWasAnError = false;
// Open the file with the key data and check, if the file could be opened
if (std::ifstream keyDataFileStream{ fileNameKey }; keyDataFileStream) {
// Read NumberOfUsedPrintableCharacters lines from the key data file
for (size_t i{}; (i < NumberOfUsedPrintableCharacters) && keyDataFileStream && !thereWasAnError; ++i) {
// Read a line, and check, if that worked
if (std::string line{}; std::getline(keyDataFileStream, line)) {
// Sanity check. Check, if there were the expected number of characters in the key file
if (line.size() != NumberOfEncryptionCharacters) {
std::cerr << "\n\n*** Error: Invalid data in keyfile line " << i + 1 << "\n";
thereWasAnError = true;
}
else { // Save new read line with encryption characters
keyData.push_back(line);
}
}
}
// Next sanity check. Check, if we have read enough lines
if (keyData.size() != NumberOfUsedPrintableCharacters) {
std::cerr << "\n\n*** Error: Not enough data in key file\n";
thereWasAnError = true;
}
}
else { // In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: File with key data ('" << fileNameKey << "') could not be opened\n";
thereWasAnError = true;
}
// In case of any error, reset the key data
if (thereWasAnError) keyData.clear();
// Return the array with printable characters to the caller using Return Value Optimization / Copy Elision
return keyData;
}
// ----------------------------------------------------------------------------------------------------------------------------------
KeyData generateNewKeyFile(const std::string& printableCharacters) {
// Here we will store all key data
KeyData keyData{};
// Sanity Check. Do we have the expected number of printable characters
if (NumberOfUsedPrintableCharacters == printableCharacters.size()) {
// Open already now the output file, and check, if it could be opened.
if (std::ofstream keyDataFileStream{ fileNameKey }; keyDataFileStream) {
// Initialize random number generation. Lambda for generating randim values
std::random_device rd; //Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
std::uniform_int_distribution<size_t> distribution(0U, NumberOfUsedPrintableCharacters - 1);
auto randomCharacter = [&]() { return printableCharacters[distribution(gen)]; };
// We want to create a key string for all printable characters
for (size_t i{}; i < NumberOfUsedPrintableCharacters && keyDataFileStream; ++i) {
// Temporary for string with random chars
std::string key(NumberOfEncryptionCharacters, '\0');
// Genrate string with random content
std::generate_n(key.begin(), NumberOfEncryptionCharacters, randomCharacter);
// Write data to file and check for possible error
if (!(keyDataFileStream << key << "\n")) {
std::cerr << "\n\n*** Error: Could not write line to key file\n";
keyData = {}; break;
}
else {
// Store the next line in the file in in the internal vector
keyData.push_back(std::move(key));
}
}
if (keyData.size() != NumberOfUsedPrintableCharacters) std::cerr << "\n*** Geeneral error during creation of key file\n";
}
else {
// In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: File with key data ('" << fileNameKey << "') could not be opened for writing\n";
}
}
return keyData;
}
// ----------------------------------------------------------------------------------------------------------------------------------
std::string encrypt(const std::string& toBeEncrypted, const KeyData& keyData, const std::string& printableCharacters, const std::string& outputFileName) {
// Here we will store the encrypted result
std::string encryptedString{};
// First open output file, if this does not work, no need to do something else
if (std::ofstream outFileStream{ outputFileName }; outFileStream) {
// Then make some sanity checks
if (((keyData.size() != NumberOfUsedPrintableCharacters)) || (printableCharacters.size() != NumberOfUsedPrintableCharacters)) {
std::cerr << "\n\n*** Error: Preconditions for encryption not met\n";
}
else {
// Go through all data in source string that shall be encrypter
for (const char c : toBeEncrypted) {
// Search for the character in our printable character string and check, if it is available and if it is in the key data
if (size_t index{ printableCharacters.find(c) }; (index != std::string::npos) && (index < NumberOfUsedPrintableCharacters)) {
// Prepare output
encryptedString += keyData[index] + "\n";
outFileStream << keyData[index] << "\n";
}
else {
std::cerr << "\n\n*** Error: Invalid character '" << c << "' in input string\n";
}
}
}
}
else {
// In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: output file ('" << outputFileName << "') for encrypted data could not be opened\n";
}
return encryptedString;
}
// ----------------------------------------------------------------------------------------------------------------------------------
std::string decrypt(const KeyData& keyData, const std::string& printableCharacters, const std::string& inputFileName) {
// Here we will store the decrypted result
std::string decryptedString{};
// First open input file, if this does not work, no need to do something else
if (std::ifstream inFileStream{ inputFileName }; inFileStream) {
// Then make some sanity checks
if (((keyData.size() != NumberOfUsedPrintableCharacters)) || (printableCharacters.size() != NumberOfUsedPrintableCharacters)) {
std::cerr << "\n\n*** Error: Preconditions for encryption not met\n";
}
else {
// Read all lines of the file
for (std::string line{}; std::getline(inFileStream, line); ) {
// Search this line in the keydata
if (KeyData::const_iterator searchResult = std::find(keyData.begin(), keyData.end(), line); searchResult != keyData.cend()) {
// Calculate the distance
if (const int index{ std::distance(keyData.begin() , searchResult) }; index < printableCharacters.size()) {
decryptedString += printableCharacters[index];
}
else { // With all the checks, this error should not occur
std::cerr << "\n\n*** Unexpected error while decrypting data\n";
}
}
else {
std::cerr << "\n\n*** Error: Could not decrypt data\n";
}
}
}
}
else {
// In case that we could not open the file, show error message
std::cerr << "\n\n*** Error: input file ('" << inputFileName << "') with encrypted data could not be opened\n";
}
// Last sanity check
if (decryptedString.empty()) std::cerr << "\nGeneral Error during decryption\n";
return decryptedString;
}
int main() {
// First read the printable character string. Check, if that was successfull
if (std::string printableCharacters{ getPrintableCharacters() }; printableCharacters.length() == NumberOfUsedPrintableCharacters ||
(printableCharacters = generateNewPrintableCharactersFile()).size() == NumberOfUsedPrintableCharacters) {
// Next, try to read a KeyData file
if (KeyData keyData{ getKeyData() }; keyData.size() == NumberOfUsedPrintableCharacters ||
(keyData = generateNewKeyFile(printableCharacters)).size() == NumberOfUsedPrintableCharacters) {
// main programm loop
for (bool doRunMainLoop{ true }; doRunMainLoop; ) {
// Show menu to the user
std::cout << "\n\n\nPlease select, what to do. Press\n\n\t1\tfor encryption\n\t2\tfor decryption\n\t3\tto generate new key file\n\t4\tto exit\n\nSelection: ";
// Get input from user and check, if that worked
if (unsigned int selection{}; std::cin >> selection) {
switch (selection) {
case 1U:
// Encryption
std::cout << "\nEncryption\n\nEnter first a filename and then the word that you want to encrypt:\n";
if (std::string filename{}, word{}; std::cin >> filename >> word) {
std::cout << "\nEncrypted Data:\n\n" << encrypt(word, keyData, printableCharacters, filename) << "\n";
}
else std::cout << "\n\nError while reading your data. Please try again\n";
break;
case 2U:
// Decryption
std::cout << "\nDecryption\n\nEnter first a filename with the ecrypted information:\n";
if (std::string filename{}; std::cin >> filename) {
std::cout << "\nDecrypted Data:\n\n" << decrypt(keyData, printableCharacters, filename) << "\n";
}
else std::cout << "\n\nError while reading your data. Please try again\n";
break;
case 3U:
// Generate new keyfile
if ((keyData = generateNewKeyFile(printableCharacters)).size() != NumberOfUsedPrintableCharacters) {
std::cerr << "\n\n*** Error: New keyfile could not be generated. Exit\n";
doRunMainLoop = false;
}
break;
case 4U:
doRunMainLoop = false;
break;
default:
std::cout << "\nInvalid selection. Pleas try again\n";
} // End select
}
else std::cerr << "\n\n\nError: Unexpected error while reading from console\n";
} // End for
}
else std::cerr << "\n\n*** Error: Problem with key data generation\n";
}
else std::cerr << "\n\n*** Error: No printable character data present\n";
return 0;
}