Я возился с криптографией (для развлечения) и создал свой собственный скрипт шифрования одноразового блокнота на C. Теперь, с учетом сказанного, я свободно признаю, что я ни в коем случае не эксперт по криптографии. Я знаю, что Правило № 1 криптографии — не делать это самому. Однако меня искренне интересует, безопасен ли мой сценарий шифрования (теоретически).
Во-первых, вот базовый отчет о том, чего я пытаюсь достичь с помощью моего метода шифрования:
Цель состоит в том, чтобы обеспечить шифрование одноразовым блокнотом, при котором используются (1) хэш-таблица и (2) ключ шифрования. Хэш-таблица (в данном случае) предварительно закодирована со значениями от 01 до 98.
Ключ шифрования рассчитывается следующим образом:
- Получить случайный пользовательский ввод (как минимум той же длины, что и сообщение)
- Получить соответствующий индекс для char в validChars[] (исходный код ниже)
- Получите число в defaultHashTable, которое соответствует индексу #2.
Сообщение шифруется следующим образом:
- Возьмите сообщение и преобразуйте его в соответствующие значения defaultHashTable.
- Возьмите ранее сгенерированный ключ и добавьте его в преобразованное сообщение
- Теперь он зашифрован (при условии, что тот же ключ больше никогда не используется)
Например:
- Сообщение: привет
- Преобразование в соответствующие значения defaultHashTable: привет -> 0805121215
- Получить случайные символы для ключа: abkdh
- Преобразование в соответствующие значения defaultHashTable: abkdh -> 0102110408
- Добавляем ключ: 0805121215 + 0102110408 = 0907231623
- Зашифрованное сообщение: 0907231623
Вот исходный код (ПРИМЕЧАНИЕ: это комбинация функций, которые были в отдельных заголовочных файлах C, поэтому я не публикую функцию main()):
// Ключ (для сообщения) будет максимум [50,000][3] (таким образом, массив, где каждый состоит из 2 символов):
структура typedef {
символьный ключ[MAX_SIZE][3];
} Ключ;
ключ глобальный ключ;
// В этой структуре будет возвращено зашифрованное сообщение (поскольку это простой способ вернуть двумерный массив из функции на C):
структура typedef {
char зашифрованное сообщение[MAX_SIZE][3];
} ЗашифрованноеСообщение;
// Хеш-таблица представляет собой предварительно закодированный двумерный массив (который загружается в initDefaultHashTable()), который будет использоваться вместе с ключом для шифрования сообщений:
// ПРИМЕЧАНИЕ. Существует еще один режим шифрования, который я не упомянул (в попытке сделать это кратким), когда вам нужно вручную вводить случайные двузначные числа (97 из них) для хеш-таблицы.
структура typedef {
хеш-таблица символов[HASHTABLE_CAPACITY][3];
} DefaultHashTable;
DefaultHashTable defaultHashTable; // Объявить глобальную defaultHashTable, в которой будет храниться эта хэш-таблица
// Загружаем defaultHashTable с 1-98 соответственно:
недействительным initDefaultHashTable () {
for (int i = 0; i < HASHTABLE_CAPACITY; i++){
символ с[3];
sprintf(s, "%d", (i+1));
если (я < 10){
символ tmp = с [0];
с[0] = '0';
с[1] = тмп;
}
для (int j = 0; j < 2; j++) {
defaultHashTable.hashtable[i][j] = s[j];
}
}
}
// Допустимые символы, которые может содержать сообщение (из них 97):
char validChars[] = {'a','b','c','d','e','f','g','h','i','j','k', «л», «м», «н», «о», «р», «к», «р», «с», «т», «у», «в», «ш», «х». ','y','z','A','B','C','D','E','F','G','H','I','J', «К», «L», «М», «N», «О», «П», «Q», «R», «S», «Т», «U», «V», «W». ','X','Y','Z','!','@','#','$','%','^','&','*','(', ')','-','+',' ',',','.',':',';','\'','\"','[',']',' {','}','_','=','|','\','/','<','>','?','`','~','\ п','\т','0','1','2','3','4','5','6','7','8','9'};
// Для char возврат невозможен (я чувствую, что есть лучший способ сделать эту часть):
char FAILED = (char) 255;
// Находим индекс допустимого символа (из validChars) или FALSE, если он не существует:
int findChar (символ c) {
for (int i = 0; i < strlen(validChars); i++){
если (validChars[i] == c){
вернуть я;
}
}
вернуть ЛОЖЬ;
}
// Возвращаем char из validChars с заданным индексом:
char returnChar (индекс int) {
вернуть валидные символы [индекс];
}
// Получить индекс заданного значения хеш-таблицы (из defaultHashTable), а затем, если применимо, соответствующий символ в validChars:
char findHashTableValue(char n[3], хэш-таблица char[][3]){
for (int i = 0; i < HASHTABLE_CAPACITY; i++){
если (хэш-таблица[i][0] == n[0] && хэш-таблица[i][1] == n[1])
вернуть returnChar(i);
}
вернуть НЕУДАЧНО;
}
// ГЛАВНАЯ часть кода (основная функция c вызовет это): Шифрование с использованием одноразового шифрования блокнота, но с использованием хеш-таблицы по умолчанию для экономии времени:
void goThroughLightEnryptionProcess(char * str, char * write_to_file){
// Загружаем defaultHashTable:
initDefaultHashTable();
// Использует функцию для создания случайного ключа (на основе случайного пользовательского ввода):
generateRandomKey(strlen(str), MAX_SIZE, FALSE);
// Зашифровать сообщение:
EncryptedMessage encryptMsg = otpEncrypt (defaultHashTable.hashtable, str, globalKey.key);
// Прокручиваем и печатаем содержимое (если не записываем в файл):
если (write_to_file == NULL){
for (int i = 0; i < strlen(str); i++){
для (int j = 0; j < 2; j++) {
printf("%c", encryptMsg.encryptedMessage[i][j]);
}
}
printf("\n");
} еще {
// Записываем зашифрованное сообщение в файл:
// ПРИМЕЧАНИЕ: это еще один параметр, который вы можете передать в реальной программе (которая намного больше этой), где вы можете записать его в файл, а не отображать в терминале:
// ПРИМЕЧАНИЕ. Я не включил сюда этот код массива writeFileWithTwoDimensionalArray(), потому что он не имеет значения, так как он просто записывает в файл.
writeFileWithTwoDimensionalArray(encryptMsg.encryptedMessage, HASHTABLE_CAPACITY, write_to_file);
}
}
// (Вспомогательная функция) Загрузите массив из двух символов в ключ:
недействительным loadIntoKeyForRandoKey (целое число, char n [3]) {
для (целое я = 0; я < 2; я ++) {
globalKey.key[at][i] = n[i];
}
}
// Генерируем ключ на основе случайного пользовательского ввода:
void generateRandomKey (int password_length, int max_size, bool use_global) {
// @Использует globalHashTable | по умолчаниюHashTable
// @Использует глобальный ключ
символьный ответ[max_size];
printf("Введите случайные символы для ключа (a-z,A-Z,!@#$%%^&*()-+=, минимальная длина %d): ", password_length);
fgets(ответ, max_size, стандартный ввод);
// Удалить символ '\n' в конце:
символ * р;
если ((p = strchr(ответ, '\n')) != NULL){
*р = '\0';
} еще {
scanf("%*[^\n]");
сканф("%*с");
}
// Убедитесь, что пользовательский ввод >= password_length:
если (strlen(ответ) < длина_пароля){
printf("\n[ ОШИБКА ] : Случайные символы должны быть больше или равны %d.\n", password_length);
вернуть generateRandomKey (длина_пароля, максимальный_размер, использование_глобального);
}
// Преобразуем случайные символы в их эквиваленты в хеш-таблице соответственно:
for (int я = 0; я < длина_пароля; я ++) {
int getCharIndex = findChar (ответ [i]);
// Убедитесь, что он успешно найден:
если (getCharIndex == FALSE){
printf("\n[ ОШИБКА ] Символ '%c' недействителен. Повторите попытку.\n", response[i]);
вернуть generateRandomKey (длина_пароля, максимальный_размер, использование_глобального); // Делаем это снова
}
// Загрузить соответствующее значение хеш-таблицы в ключ:
если (use_global == ИСТИНА){
loadIntoKeyForRandoKey(i, globalHashTable.hashtable[getCharIndex]);
} еще {
loadIntoKeyForRandoKey(i, defaultHashTable.hashtable[getCharIndex]);
}
}
// Записываем случайный ключ в файл:
createFileWithTwoDimensionalArray(globalKey.key, длина_пароля, "ключ");
}
// (Вспомогательная функция) Для загрузки в структуру EncryptedMessage:
void loadIntoEncryptedMessage (int at, char n [3], EncryptedMessage * encryptedMsg) {
если (strlen(n) == 1){
// Добавляем '0':
символ tmp = n[0];
п[0] = '0';
п[1] = тмп;
}
для (целое я = 0; я < 2; я ++) {
зашифрованноесообщение->зашифрованноесообщение[at][i] = n[i];
}
}
/*
Шифрует сообщение с учетом допустимой хэш-таблицы и ключа
*/
EncryptedMessage otpEncrypt(хэш-таблица символов[][3], символ * сообщение, ключ символа[MAX_SIZE][3]){
Зашифрованное сообщение, зашифрованное сообщение;
for (int i = 0; i < strlen(msg); i++){
// Преобразование значения ключа в целое число:
int convertKeyValueIntoInt = safeConvertToInt(key[i]);
// Убедитесь, что он конвертирован правильно:
если (convertedKeyValueIntoInt == FALSE){
printf("[ ОШИБКА ] : Ключ поврежден в %d (значение = %s).\n", i, key[i]);
выход(1);
}
// Преобразуем сообщение пользователя в его эквивалент в хеш-таблице:
int indexOfMsgChar = findChar(msg[i]);
// Убедитесь, что findChar() правильно нашла значение:
если (indexOfMsgChar == FALSE){
printf("[ ОШИБКА ] : Пароль (msg) поврежден в %d (значение = %s). Это могло произойти из-за того, что символ '%c' не разрешен.\n", i, msg, msg[i ]);
выход(1);
}
char * соответствующийEncryptMsgChars = hashtable[indexOfMsgChar];
// Преобразование соответствующих символов encryptMsg в int:
int convertEncryptMsgCharsIntoInt = safeConvertToInt (соответствующий EncryptMsgChars);
// Убедитесь, что он конвертирован правильно:
если (convertedEncryptMsgCharsIntoInt == FALSE){
printf("[ ОШИБКА ] : Хэш-таблица повреждена в %d (значение = %s).\n", indexOfMsgChar, соответствующийEncryptMsgChars);
выход(1);
}
// Делаем расчет:
intcryptedFrag = otpeAdd(convertedEncryptMsgCharsIntoInt, convertKeyValueIntoInt);
// Преобразуем его в строку:
char зашифрованныйFragStr[3];
sprintf(encryptedFragStr, "%d",cryptedFrag);
loadIntoEncryptedMessage(i,cryptedFragStr, &encryptedMsg);
}
вернуть зашифрованное сообщение;
}
Мой непосредственный вопрос: если я использую предварительно закодированную хеш-таблицу (которую любой может сделать вывод), делает ли это шифрование небезопасным (даже если ключ, соответствующий значениям хеш-таблицы, полностью случайный при вводе пользователем)? Будет ли это безопасно только в том случае, если я рандомизирую номера хэш-таблицы (01-98) (и, возможно, рандомизирую validChars[])?
Мне искренне интересно, верна ли моя логика, поэтому любые комментарии, предложения или критика будут высоко оценены.