
МСЗИ_23_ИСТ_1_1_Какушкина_Ольга_Витальевна_ЛР_2
.docxМИНОБРНАУКИ РОССИИ
Ф
едеральное
государственное бюджетное образовательное
учреждение высшего образования
НИЖЕГОРОДСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ
УНИВЕРСИТЕТ им. Р.Е.АЛЕКСЕЕВА
Институт радиоэлектроники и информационных технологий
Кафедра информатики и систем управления
ОТЧЕТ по лабораторной работе №2
Стандарт шифрования данных DES в режиме «Обратная связь по шифру».
по дисциплине
Методы и средства защиты информации
(наименование дисциплины)
РУКОВОДИТЕЛЬ:
________________ Жуков М. С.
(подпись) (фамилия, и.,о.)
СТУДЕНТ:
________________ Какушкина О.В.
(подпись) (фамилия, и.,о.)
23-ИСТ-1-1
(шифр группы)
Работа защищена «___» ____________
С оценкой ________________________
Нижний Новгород 2025
Текст задания №2.3:
Реализовать стандарт шифрования данных DES в режиме «Обратная связь по шифру».
Данная программа реализует алгоритм шифрования DES (Data Encryption Standard) в режиме обратной связи по шифру (CFB). Это симметричный блочный шифр, работающий с 64-битными блоками данных.
Таблицы перестановок и преобразований
PC1 - Первоначальная перестановка ключа (64 бита → 56 бит)
PC2 - Перестановка для генерации подключей (56 бит → 48 бит)
IP - Начальная перестановка блока данных
IP_INV - Конечная перестановка (обратная IP)
E - Расширяющая перестановка (32 бита → 48 бит)
S_BOX - 8 S-блоков для нелинейного преобразования
P - Перестановка после S-блоков
SHIFT_SCHEDULE - График сдвигов для генерации ключей
Класс DES_CFB
Приватные методы
permutePC1(const bitset<64>& key)
Назначение: Первоначальная перестановка ключа по таблице PC1
Вход: 64-битный ключ
Выход: 56-битный ключ
permutePC2(const bitset<56>& key)
Назначение: Перестановка для генерации подключа по таблице PC2
Вход: 56-битный ключ
Выход: 48-битный подключ
generateRoundKeys(const bitset<64>& key)
Назначение: Генерация 16 раундовых ключей
Алгоритм:
Применяет PC1 к ключу
Разделяет на две 28-битные половины
Для каждого раунда:
Сдвигает половины согласно графику
Объединяет и применяет PC2
Сохраняет подключ
leftShift(bitset<28> bits, int shifts)
Назначение: Циклический сдвиг влево
Вход: 28 бит, количество сдвигов
Выход: Сдвинутые 28 бит
permuteIP(const bitset<64>& block)
Назначение: Начальная перестановка блока данных
Вход: 64-битный блок
Выход: Переставленный 64-битный блок
permuteIPInv(const bitset<64>& block)
Назначение: Конечная перестановка (обратная IP)
Вход: 64-битный блок
Выход: Переставленный 64-битный блок
feistel(const bitset<32>& right, const bitset<48>& roundKey)
Назначение: Функция Фейстеля для одного раунда
Алгоритм:
Расширение до 48 бит (E)
XOR с подключом
Проход через S-блоки
Перестановка P
processBlock(const bitset<64>& block, bool isDecrypt)
Назначение: Обработка одного блока (шифрование/дешифрование)
Алгоритм:
Начальная перестановка (IP)
16 раундов Фейстеля
Конечная перестановка (IP_INV)
stringToBitset(const string& s)
Назначение: Преобразование строки в 64-битный блок
Особенности: Дополняет нулями при необходимости
bitsetToString(const bitset<64>& bits)
Назначение: Преобразование 64-битного блока в строку
Публичные методы
Конструктор DES_CFB(const string& keyStr, const string& ivStr)
Инициализирует ключи и вектор инициализации
encrypt(const string& plaintext)
Назначение: Шифрование в режиме CFB
Алгоритм:
Начинает с IV
Для каждого блока:
Шифрует регистр обратной связи
XOR с блоком открытого текста
Результат становится новым регистром
decrypt(const string& ciphertext)
Назначение: Дешифрование в режиме CFB
Алгоритм:
Начинает с IV
Для каждого блока:
Шифрует регистр обратной связи
XOR с блоком шифртекста
Шифрблок становится новым регистром
Удаляет дополнение
Вспомогательные функции
enterText()
Запрашивает у пользователя текст для обработки
main()
Точка входа в программу
Реализует интерактивный интерфейс:
Ввод ключа и IV
Выбор операции (шифрование/дешифрование)
Обработка и вывод результатов
Примеры шифрования и расшифровывания:
Исходное сообщение: kakushkinaolga
Зашифрованные данные: 0dfbc78329f0de4c4cae48c27d4b8d2
Расшифрованные данные: kakushkinaolga
#include <iostream>
#include <string>
#include <bitset>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <stdexcept>
using namespace std;
// первоначальное преобразование
const int PC1[] = {
57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4
};
// второе преобразование
const int PC2[] = {
14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32
};
// таблица для определения количества сдвигов в каждом раунде
const int SHIFT_SCHEDULE[] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
// матрица начальной перестановки
const int IP[] = {
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7
};
// матрица конечной перестановки
const int IP_INV[] = {
40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25
};
//Функция расширитель E
const int E[] = {
32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1
};
// S-блоки
const int S_BOX[8][4][16] = {
// S1
{
{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
{0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
{4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}
},
// S2
{
{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
{3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
{0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}
},
// S3
{
{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
{1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}
},
// S4
{
{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
{3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}
},
// S5
{
{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
{4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}
},
// S6
{
{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
{10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
{9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
{4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}
},
// S7
{
{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
{13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
{1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
{6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}
},
// S8
{
{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
{1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
{7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
{2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}
}
};
// перестановка
const int P[] = {
16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25
};
class DES_CFB {
private:
vector<bitset<48>> roundKeys;
bitset<64> iv; // вектор инициализации
// выполняем начальную перестановку 64-битного ключа в 56-битный с использованием таблицы PC1
bitset<56> permutePC1(const bitset<64>& key) {
bitset<56> result;
for (int i = 0; i < 56; i++) {
result[55 - i] = key[64 - PC1[i]];
}
return result;
}
// Перестановка 56-битного ключа в 48-битный с использованием таблицы PC2
bitset<48> permutePC2(const bitset<56>& key) {
bitset<48> result;
for (int i = 0; i < 48; i++) {
result[47 - i] = key[56 - PC2[i]];
}
return result;
}
// Гнерация ключей
void generateRoundKeys(const bitset<64>& key) {
bitset<56> permutedKey = permutePC1(key); // началаьная перестановка PC1
bitset<28> left, right;
// Разделяет ключ на две 28-битные половины (left и right)
for (int i = 0; i < 28; i++) {
left[27 - i] = permutedKey[55 - i];
right[27 - i] = permutedKey[27 - i];
}
roundKeys.clear();
for (int round = 0; round < 16; round++) {
// Выполняет циклический сдвиг влево для обеих половин
left = leftShift(left, SHIFT_SCHEDULE[round]);
right = leftShift(right, SHIFT_SCHEDULE[round]);
// Объединяет половины
bitset<56> combined;
for (int i = 0; i < 28; i++) {
combined[55 - i] = left[27 - i];
combined[27 - i] = right[27 - i];
}
// Применяет перестановку PC2 для получения раундового ключа и сохраняет ключ в roundKeys
roundKeys.push_back(permutePC2(combined));
}
}
// Выполняет циклический сдвиг битов влево на указанное количество позиций
bitset<28> leftShift(bitset<28> bits, int shifts) {
bitset<28> result;
for (int i = 0; i < 28; i++) {
result[i] = bits[(i + shifts) % 28];
}
return result;
}
// Начальная перестановка блока данных (64 бита) с использованием таблицы IP
bitset<64> permuteIP(const bitset<64>& block) {
bitset<64> result;
for (int i = 0; i < 64; i++) {
result[63 - i] = block[64 - IP[i]];
}
return result;
}
// Обратная перестановка блока данных с использованием таблицы IP_INV
bitset<64> permuteIPInv(const bitset<64>& block) {
bitset<64> result;
for (int i = 0; i < 64; i++) {
result[63 - i] = block[64 - IP_INV[i]];
}
return result;
}
// основная функция преобразования - функция Фейстеля
bitset<32> feistel(const bitset<32>& right, const bitset<48>& roundKey) {
// 32-битный блок расширяется до 48 бит с использованием таблицы E
bitset<48> expanded;
for (int i = 0; i < 48; i++) {
expanded[47 - i] = right[32 - E[i]];
}
// XOR с раундовым ключом
expanded ^= roundKey;
// 48-битный блок разделяется на 8 6-битных частей, каждая преобразуется в 4 бита с помощью соответствующих S-боксов
bitset<32> substituted;
for (int i = 0; i < 8; i++) {
int row = expanded[47 - (i * 6)] * 2 + expanded[47 - (i * 6 + 5)];
int col = expanded[47 - (i * 6 + 1)] * 8 +
expanded[47 - (i * 6 + 2)] * 4 +
expanded[47 - (i * 6 + 3)] * 2 +
expanded[47 - (i * 6 + 4)];
int val = S_BOX[i][row][col];
for (int j = 0; j < 4; j++) {
substituted[31 - (i * 4 + j)] = (val >> (3 - j)) & 1;
}
}
// результат преобразуется с использованием таблицы P
bitset<32> result;
for (int i = 0; i < 32; i++) {
result[31 - i] = substituted[32 - P[i]];
}
return result;
}
// Обработка блока
bitset<64> processBlock(const bitset<64>& block, bool isDecrypt) {
bitset<64> permuted = permuteIP(block); // Применяет начальную перестановку IP
// Разделяет блок на две 32 - битные половины(left и right)
bitset<32> left, right;
for (int i = 0; i < 32; i++) {
left[31 - i] = permuted[63 - i];
right[31 - i] = permuted[31 - i];
}
// Выполняет 16 раундов Фейстеля
// для шифрования в прямом порядке 0-15, для дешифр в обратном 15 - 0
for (int round = 0; round < 16; round++) {
bitset<32> newRight = left ^ feistel(right, roundKeys[isDecrypt ? 15 - round : round]);
left = right;
right = newRight;
}
// объединяет половины (меняя лево и право местами)
bitset<64> combined;
for (int i = 0; i < 32; i++) {
combined[63 - i] = right[31 - i];
combined[31 - i] = left[31 - i];
}
// Применяет обратную перестановку IP_INV
return permuteIPInv(combined);
}
// Преобразует строку (до 8 символов) в 64-битный блок, каждый символ кодируется в 8 бит
bitset<64> stringToBitset(const string& s) {
if (s.length() > 8) {
throw runtime_error("Key must be 8 characters or less");
}
bitset<64> result;
for (size_t i = 0; i < s.length(); i++) {
for (int j = 0; j < 8; j++) {
result[i * 8 + j] = (s[i] >> (7 - j)) & 1;
}
}
return result;
}
// Преобразует 64-битный блок обратно в строку (8 символов)
string bitsetToString(const bitset<64>& bits) {
string result;
for (int i = 0; i < 8; i++) {
char c = 0;
for (int j = 0; j < 8; j++) {
c |= bits[i * 8 + j] << (7 - j);
}
result += c;
}
return result;
}
public:
DES_CFB(const string& keyStr, const string& ivStr = "defaultiv") {
// Преобразование строки ключа в битовый набор
bitset<64> key = stringToBitset(keyStr);
// Генерация раундовых ключей
generateRoundKeys(key);
// Установка вектора инициализации (IV)
iv = stringToBitset(ivStr.substr(0, 8));
}
// шифрование
string encrypt(const string& plaintext) {
string ciphertext;
bitset<64> feedback = iv; // Начинаем с IV
for (size_t i = 0; i < plaintext.length(); i += 8) {
// // Берём блок открытого текста (8 символов = 64 бита)
string blockStr = plaintext.substr(i, 8);
while (blockStr.length() < 8) blockStr += '\0'; // Дополнение нулями
// Шифруем регистр обратной связи (feedback)
bitset<64> encryptedFeedback = processBlock(feedback, false);
// XOR с блоком открытого текста → получаем шифрблок
bitset<64> plainBlock = stringToBitset(blockStr);
bitset<64> cipherBlock = plainBlock ^ encryptedFeedback;
// Обновляем feedback для следующего блока
feedback = cipherBlock;
// Добавляем шифрблок к результату
ciphertext += bitsetToString(cipherBlock);
}
return ciphertext;
}
// дешифрование
string decrypt(const string& ciphertext) {
string plaintext;
bitset<64> feedback = iv; // // Начинаем с IV
for (size_t i = 0; i < ciphertext.length(); i += 8) {
// Берём блок шифртекста (8 символов = 64 бита)
string blockStr = ciphertext.substr(i, 8);
// Шифруем регистр обратной связи (feedback)
bitset<64> encryptedFeedback = processBlock(feedback, false);
// XOR с блоком шифртекста → получаем открытый текст
bitset<64> cipherBlock = stringToBitset(blockStr);
bitset<64> plainBlock = cipherBlock ^ encryptedFeedback;
// Обновляем feedback для следующего блока
feedback = cipherBlock;
// Добавляем расшифрованный блок к результату
plaintext += bitsetToString(plainBlock);
}
// Удаляем дополнение (нули в конце)
size_t padPos = plaintext.find_last_not_of('\0');
if (padPos != string::npos) {
plaintext = plaintext.substr(0, padPos + 1);
}
else {
plaintext.clear();
}
return plaintext;
}
};
string enterText() {
string text;
cout << "Enter text: ";
getline(cin, text);
return text;
}
int main() {
setlocale(LC_ALL, "Russian");
cout << "DES in CFB mode implementation" << endl;
while (true) {
cout << "\nEnter 0 to exit, or any other key to continue: ";
string choice;
getline(cin, choice);
if (choice == "0") break;
// Get key from user
string key;
cout << "Enter key (up to 8 characters): ";
getline(cin, key);
if (key.empty()) {
cout << "Key cannot be empty!" << endl;
continue;
}
// Get IV from user
string iv;
cout << "Enter initialization vector (up to 8 characters, leave empty for default): ";
getline(cin, iv);
DES_CFB des(key, iv.empty() ? "defaultiv" : iv);
cout << "\n1. Encrypt\n2. Decrypt\nEnter choice: ";
string option;
getline(cin, option);
if (option == "1") {
string plaintext = enterText();
string ciphertext = des.encrypt(plaintext);
cout << "\nEncrypted text (hex): ";
for (char c : ciphertext) {
cout << hex << setw(2) << setfill('0') << (int)(unsigned char)c;
}
cout << endl;
}
else if (option == "2") {
string ciphertextHex;
cout << "Enter ciphertext in hex format: ";
getline(cin, ciphertextHex);
// Convert hex to string
string ciphertext;
for (size_t i = 0; i < ciphertextHex.length(); i += 2) {
string byteStr = ciphertextHex.substr(i, 2);
char byte = (char)stoi(byteStr, nullptr, 16);
ciphertext += byte;
}
string plaintext = des.decrypt(ciphertext);
cout << "\nDecrypted text: " << plaintext << endl;
}
else {
cout << "Invalid choice!" << endl;
}
}
return 0;
}
Вывод программы
Вывод:
В ходе лабораторной работы я работала с стандартом шифрования данных DES в режиме «Обратная связь по шифру». Научилась шифровать и расшифровывать текст данным методом.