using System; using System.Text; // Оставляем пространство имен LabRab2, т.к. хеш-алгоритм из Лаб. 2 namespace LabRab2 { // ПЕРЕИМЕНОВАЛИ КЛАСС! Теперь он отражает суть алгоритма (G/T хеш) public class GTHashAlgorithm { // Константы G/T алгоритма (остаются без изменений) private const byte T_PLUS = 165; private const byte G_PLUS = 23; private const byte T_SHIFT = 3; private const byte G_SHIFT = 3; private const int TARGET_LENGTH = 128; // Длина для расширения данных // Метод для вычисления хеша из строки и получения СУММЫ первых 24 байт // Именно этот метод вызывается для генерации ключа из пароля public int ComputeHash(string data) { if (string.IsNullOrEmpty(data)) { throw new ArgumentException("Input string cannot be null or empty.", nameof(data)); } Console.WriteLine($"Hashing string: \"{data}\""); // Логгирование // Шаг 1а: Добавляем длину string processedString = data + data.Length; Console.WriteLine($"String with length appended: \"{processedString}\""); // Шаг 1б: Расширяем до 128 символов processedString = MultiplyString(processedString); Console.WriteLine($"String multiplied/truncated to {TARGET_LENGTH} chars: \"{processedString.Substring(0, Math.Min(30, processedString.Length))}...\""); // Показываем начало // Кодировка должна быть зарегистрирована в Main byte[] strBytes; try { // Используем Windows-1251, как и раньше strBytes = Encoding.GetEncoding("Windows-1251", new EncoderExceptionFallback(), new DecoderExceptionFallback()) .GetBytes(processedString); } catch (EncoderFallbackException e) { Console.WriteLine($"FATAL: Error encoding the processed string to Windows-1251: {e.Message}"); throw; // Без байтов хешировать нечего } Console.WriteLine($"String as Windows-1251 bytes ({strBytes.Length}) ready for hashing."); // Шаг 1в: Вычисляем 128-байтный хеш var hashBytes = Hash(strBytes); Console.WriteLine($"Generated 128-byte hash. First few bytes (hex): {BitConverter.ToString(hashBytes, 0, Math.Min(16, hashBytes.Length)).Replace("-", "")}"); // Суммируем первые 24 байта для получения итогового значения (ключа) int resultSum = 0; int bytesToSum = Math.Min(24, hashBytes.Length); // Берем 24 или меньше, если хеш короче for (int i = 0; i < bytesToSum; i++) { resultSum += hashBytes[i]; } Console.WriteLine($"Sum of first {bytesToSum} hash bytes: {resultSum}"); // Возвращаем именно СУММУ (как int). Приведение к uint будет в Main. return resultSum; } // Функция расширения строки (без изменений) private string MultiplyString(string s) { StringBuilder sb = new StringBuilder(s); while (sb.Length < TARGET_LENGTH) { // Добавляем исходную строку (s), а не то, что уже накоплено в sb, // чтобы точно соответствовать описанию циклического повтора. sb.Append(s); } return sb.ToString(0, TARGET_LENGTH); // Обрезаем до 128 } // Основная функция G/T хеширования (128 байт) (без изменений) private byte[] Hash(byte[] msgBytes) { if (msgBytes == null) throw new ArgumentNullException(nameof(msgBytes)); // Добавим проверку на соответствие длины ожиданиям if (msgBytes.Length != TARGET_LENGTH) { // Это не должно происходить, если MultiplyString работает верно Console.WriteLine($"Warning: Hash function received {msgBytes.Length} bytes, expected {TARGET_LENGTH}. Algorithm might behave unexpectedly."); } if (msgBytes.Length == 0) throw new ArgumentException("Input byte array cannot be empty.", nameof(msgBytes)); int hashLength = msgBytes.Length; // Длина будет 128 var hash = new byte[hashLength]; // TArray будет 128 x 128 x 2 var TArray = new byte[hashLength, hashLength, 2]; var GArray = new byte[hashLength]; // Вычисляем GArray for (int i = 0; i < hashLength; i++) { G(i, GArray); } // Вычисляем TArray и собираем хеш for (int i = 0; i < hashLength; i++) // Цикл по строкам { for (int j = 0; j < hashLength; j++) // Цикл по столбцам { T(i, j, msgBytes, TArray, GArray); } // Берем результат из самого правого столбца hash[i] = TArray[i, hashLength - 1, 1]; } return hash; // Возвращаем 128 байт } // Функция G (без изменений) private void G(int index, byte[] GArray) { if (index == 0) GArray[index] = CircularShiftLeft((byte)(G_PLUS + 1), G_SHIFT); else GArray[index] = CircularShiftLeft((byte)(G_PLUS + GArray[index - 1]), G_SHIFT); } // Функция T (без изменений) private void T(int row, int col, byte[] msgBytes, byte[,,] TArray, byte[] GArray) { byte res1; byte res2; // Логика T остается прежней if (row == 0) { if (col == 0) { res1 = (byte)(GArray[row] ^ CircularShiftLeft(msgBytes[0], T_SHIFT)); res2 = (byte)(T_PLUS ^ 1); } else { res1 = (byte)(TArray[row, col - 1, 1] ^ CircularShiftLeft(msgBytes[col], T_SHIFT)); res2 = (byte)(T_PLUS ^ msgBytes[col - 1]); } } else { if (col == 0) { res1 = (byte)(GArray[row] ^ CircularShiftLeft(TArray[row - 1, col, 0], T_SHIFT)); res2 = (byte)(T_PLUS ^ GArray[row - 1]); } else { res1 = (byte)(TArray[row, col - 1, 1] ^ CircularShiftLeft(TArray[row - 1, col, 0], T_SHIFT)); res2 = (byte)(T_PLUS ^ TArray[row - 1, col - 1, 0]); } } TArray[row, col, 0] = res1; TArray[row, col, 1] = (byte)(res1 + res2); // Сложение по модулю 256 } // Циклический сдвиг влево (без изменений) private byte CircularShiftLeft(byte value, byte count) { count %= 8; if (count == 0) return value; return (byte)((value << count) | (value >> (8 - count))); } } }