CalculateHash

From SunFlurry wiki
Jump to: navigation, search
  CalculateHash (Функции криптографии)
Объект:Функции общего назначения
Статус разработки: Частичная реализация
Тип:Функция
Обращение к БД:Нет
Исключения:Невозможно превратить в строку, число, неверный аргумент
Визуальность:Нет

Функция подсчет контрольной суммы или хэша буфера или строки по указанному алгоритму, имеется также возможность подсчитать сумму нескольких блоков, как единого целого, вызывая эту функцию для каждого из блоков один раз. Хеш считается безопасным, если получить такой же хеш для массива байтов, содержащего другую информацию, путем подбора байтов, является практически невыполнимой задачей. Безопасные хеши, позволяют, к примеру, передавать информацию по сети Интернет без опасения, что она будет подделана. Для внутренних целей, не связанных с передачей информации по незащищенным каналам, нет смысла использовать более ресурсоемкие функции, когда можно обойтись более простыми.

Синтаксис

CalculateHash(<Информация для нахождения контрольной суммы (STRING,BUFFER)>,<Алгоритм (STRING)>="INTERNAL",<Завершающий блок (INT)>=1,<Сумма или состояние предыдущего блока (STRING,BUFFER)>,<Дополнительный параметр (STRING)>=""):<Сумма или текущее состояние (STRING,BUFFER)>

Аргументы

  • <Информация для нахождения контрольной суммы (STRING,BUFFER)> - Функция поддерживает строку (ANSI или UTF-16) или буфер для получения информации для (продолжения) нахождения хеша или контрольной суммы.
  • <Алгоритм (STRING)> - (необязательный аргумент) Строка, задающая используемый алгоритм. Поддерживаются следующие алгоритмы:
Алгоритм Длина хеша/суммы Описание
INTERNAL (по умолчанию) 64 бит / 16 символов Быстрый алгоритм, используемый системой при передаче пакетов. Не является безопасным.
CRC1..CRC64 1..64 бит / 1..16 символов Функция производит вычисление CRC по выбранному основанию от 1 до 64. Функция оптимизирована для подсчета CRC-16 и CRC-32, остальные CRC будут считаться быстрее для нескольких больших блоков данных, чем для большого количества маленьких, так как при каждом подсчете функция создает таблицу оптимизации CRC. Однако, такое поведение можно изменить, создав сначала таблицу для использования ее при каждом подсчете (см. примеры ниже). Вычислить CRC по определенному основанию можно большим количеством способов. Для вычисления CRC нужен определенный полином, число инициализации, число последней операции XOR, кроме того, байты при вычислении, как и сам полином могут быть обращены, результат также может быть обращен. Поэтому, чтобы получить корректное значение CRC необходимо знать всю дополнительную информацию по алгоритму его вычисления (смотрите комментарии к дополнительному параметру). Типы часто используемых контрольных сумм CRC:
  • CRC8 (8 бит / 2 символа) - используется в спутниковом вещании, беспроводных соединениях, радио и мобильных сетях и др. Не является безопасным.
  • CRC16 (16 бит / 4 символа) - используется в радио и мобильных сетях, USB, различных шинах данных и др. Не является безопасным.
  • CRC32 (32 бита / 8 символов) - используется в локальных сетях, архивах ZIP, граф. формате PNG, SCSI, авиации и др. Не является безопасным.
  • CRC64 (64 бита / 16 символов) - используется в алгоритме LZMA, базе данных UniProt и др. Считается слабым.
Adler32 32 бита / 8 символов Использовался в сетевых протоколах передачи данных, используется в ZIP и Zlib и др. Не является безопасным.
MD5 128 битов / 32 символа Использовался для хешей паролей и др. Не является безопасным.
SHA-0 160 бит / 40 символов (Secure Hash Algorithm). SHA-0. Использовался для хешей паролей и др. Не является безопасным. Представляет только исторический интерес.
SHA-1 160 бит / 40 символов (Secure Hash Algorithm). SHA-1. Используется для хешей паролей и др. Не является безопасным.
SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256 224 бит, 256 бит, 384 бит, 512 бит (Secure Hash Algorithm). SHA-2. Используется для хешей паролей и др. Безопасен. Алгоритм делится на две семьи: 256-битная семья и 512-битная семья, отличающихся количеством циклов вычисления и размером переменных при вычислении (32 и 64 бит). Методы SHA-256, SHA-512 возвращают 32 и 64 байта (64 и 128 символов) соответственно. Остальные функции являются упрощениями указанных (имеют иные константы инициализации и результат усекается до требуемого размера в байтах). Доступны следующие упрощенные алгоритмы:
  • Семья 256-бит:
    • Метод SHA-224 возвращает 28 байт (56 символов).
  • Семья 512-бит:
    • Метод SHA-384 возвращает 48 байт (96 символов).
    • Метод SHA-512/256 возвращает 32 байта (64 символа).
    • Метод SHA-512/224 возвращает 28 байт (56 символов).
SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 (планируется к реализации) 224 бит, 256 бит, 384 бит, 512 бит (Secure Hash Algorithm). SHA-3. Используется для хешей паролей и др. Безопасен.
  • <Завершающий блок (INT)> - (необязательный аргумент) Является ли данный блок последним (по умолчанию 1 -- да). В зависимости от этого параметра, функция возвратит промежуточное состояние вычисления, которое может быть использовано для продолжения вычисления со следующим блоком данных (если этот параметр 0), либо результат -- контрольную сумму или хеш в виде шестнадцатеричной строки в верхнем регистре (если этот параметр 1).
  • <Сумма или состояние предыдущего блока (STRING,BUFFER)> - (необязательный аргумент) Если подсчет суммы продолжается и требуется подсчитать следующий блок данных, в данном аргументе передается промежуточное состояние вычисления, возвращенное функцией CalculateHash после предыдущего вызова (предыдущий вызов должен иметь аргумент <Завершающий блок> равным 0).
  • <Дополнительный параметр (STRING)> - (необязательный аргумент) Дополнительный параметр может использоваться в зависимости от алгоритма. Ниже дана таблица существующих параметров.
Алгоритм Значение Описание
CRC?? <Полином>[,<Значение инициализации>[,<Значение последней операции XOR>[,<Обратить полином>[,<Обратить результат>[,<Обратить байты данных>]]]]]
  • Некоторые значения полиномов CRC-16:
    • IBM,USB...: "8005" (по умолчанию)
    • CCITT: "1021"
    • ARINC: "A02B"
    • CDMA2000: "C867"
    • DECT: "0589"
    • T10-DIF: "8BB7"
    • DNP: "3D65"
    • OpenSafety-A: "5935"
    • OpenSafety-B: "755B"
    • Profibus: "1DCF"
  • Некоторые значения полиномов CRC-32:
    • ZIP,PNG,MP2,...: "04C11DB7" (по умолчанию)
    • iSCSI,SCTP,SSE4.2: "1EDC6F41" (CRC-32C)
    • Авиация: "814141AB" (CRC-32Q)
    • "A833982B" (CRC-32D)
  • Некоторые значения полиномов CRC-64:
    • ISO 3309: "1B" (по умолчанию)
    • ECMA-182: "42F0E1EBA9EA3693"
Задает параметры для вычисления CRC. Для дальнейшей информации по каждому из параметров ищите описание CRC в Интернете. Параметры <Обратить полином>, <Обратить результат> и <Обратить байты данных> могут принимать значения 1 или 0. Если доп. параметр опущен, будут использованы параметры по умолчанию (см. информацию ниже). Любой из параметров может быть опущен. При вычислении CRC с аргументом Завершающий блок равным 0, программа возвращает буфер с таблицей оптимизации значения CRC и промежуточным значением CRC. Для любого алгоритма CRC буфер имеет размер 2056 байт. Первые 2048 байт предназначены для таблицы оптимизации, последние 8 байт представляют промежуточное значение CRC. Таким буфером можно воспользоваться для ускорения расчета множества мелких блоков CRC, подставляя его для вычисления блока, при этом начальное значение вычисления (часто -1) должно быть записано в буфер с помощью функции SetQWord по адресу 2048.

Если подсчет большого объема данных происходит в несколько этапов, параметр достаточно указать только при первом вызове функции, после чего подставлять возвращенный функцией буфер для продолжения вычисления. Функция возвратит результат подсчета, как строку с CRC только в случае, когда аргумент Завершающий блок будет равен 1.
Преобразование полинома в числовое значение происходит по следующим правилам: Для примера возьмем полином x^16+x^15+x^2+1, который будет представлен числом "8005", шестнадцатеричное число 8005 создается из полинома следующим образом:

  1. Удаляется старший член x^16, он присутствует для всех полиномов
  2. Создается двоичное число с установленными битами 15, 2 и 0: 1000000000000101b = 8005h
SHA-X <Формат результата (INT)> Параметр задает формат результата. Если параметр равен 0 (по умолчанию), результат возвращается как строка в шестнадцатеричном формате, иначе как буфер, содержащий требуемое количество байт бинарной информации -- результат вычисления SHA-X (см. пример ниже).
Параметры по умолчанию при вычислении CRC??

При вычислении CRC дополнительный параметр записывается в виде: <Полином>[,<Значение инициализации>[,<Значение последней операции XOR>[,<Обратить полином>[,<Обратить результат>[,<Обратить байты данных>]]]]]. Ниже дана таблица значений параметра по умолчанию, если он опущен при вызове функции. Если для какого-либо CRC параметры по умолчанию не заданы (к примеру, для CRC-9), они должны быть заданы при вызове функции, иначе будет вызвано исключение):

  • CRC-1: 1,1,1,1,0,0
  • CRC-3: 3,3,3,1,0,0
  • CRC-4: 3,F,F,1,0,0
  • CRC-5: 9,1F,1F,1,0,0
  • CRC-6: 2F,3F,3F,1,0,0
  • CRC-7: 9,7F,7F,1,0,0
  • CRC-8: 7,0,0,1,0,0
  • CRC-10: 3D9,3FF,3FF,1,0,0
  • CRC-11: 385,7FF,7FF,1,0,0
  • CRC-12: F13,FFF,FFF,1,0,0
  • CRC-13: 1CF5,1FFF,1FFF,1,0,0
  • CRC-14: 202D,3FFF,3FFF,1,0,0
  • CRC-15: 4599,7FFF,7FFF,1,0,0
  • CRC-16: 8005,0,0,1,0,0
  • CRC-17: 1685B,1FFFF,1FFFF,1,0,0
  • CRC-21: 102899,1FFFFF,1FFFFF,1,0,0
  • CRC-24: 864CFB,FFFFFF,FFFFFF,1,0,0
  • CRC-30: 2030B9C7,3FFFFFFF,3FFFFFFF,1,0,0
  • CRC-32: 4C11DB7,FFFFFFFF,FFFFFFFF,1,0,0
  • CRC-40: 4820009,FFFFFFFFFF,FFFFFFFFFF,1,0,0
  • CRC-64: 1B,FFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFF,1,0,0

Возвращаемое значение

В зависимости от аргумента <Завершающий блок>, функция возвращает:

  • <Завершающий блок>=1: Результат подсчета хеша или контрольной суммы в виде строки представляющей шестнадцатеричное число, все буквенные составляющие представлены в верхнем регистре.
  • <Завершающий блок>=0: Промежуточный результат подсчета хеша или контрольной суммы в виде строки или буфера. Данное значение должно быть подставлено в аргумент <Сумма или состояние предыдущего блока> при следующем вызове функции для подсчета следующего блока.

Примеры

Пример вычисления MD5

//Пример показывает, как вычисляется MD5 ANSI строки и как можно вычислить MD5 строки, состоящей из двух частей

//Будет выведено: "9E107D9D372BB6826BD81D3542A419D6"
Message(CalculateHash("The quick brown fox jumps over the lazy dog","MD5"));

//Строка разделена на две части, первая функция считает MD5 первой части, при этом вычисление не завершается (<Завершающий блок>=0)
Буф:=CalculateHash("The quick brown fox jumps ","MD5",0);
//Вторая функция завершает вычисление и выдает результат (такой же, как и в первом примере с полным текстом): "9E107D9D372BB6826BD81D3542A419D6"
Message(CalculateHash("over the lazy dog","MD5",1,Буф));

Пример вычисления CRC16 с полиномом, отличающимся от "8005"

//Пример производит вычисление CRC16 ANSI строки для распространенного полинома CCITT "1021", отличающегося от встроенного полинома "8005":
Message(CalculateHash("Hello, World!","CRC16",1,,"1021"));
//Будет выведено: "9BD5"

Пример вычисления группы CRC с разными параметрами по любому из оснований

//Основание CRC:
Бит:=32;
//Полином CRC (16-ричная нотация)
Поли:="04C11DB7";
//Данные для вычисления CRC
Данные:="1234567890";
//Инициализация вычисления CRC (начальное значение), вместо этой конструкции можно просто указать "FFFFFFFFFFFFFFFF" для
//  CRC любого основания с инициализацией -1
Инит:=DecToBase(CutBitsMask(Бит),16);
//В конце вычисления будет выполнена операция XOR с данным числом
Конец:=DecToBase(CutBitsMask(Бит),16);

//Без обращения полинома и результата (обращение числа можно осуществить функцией ReflectBits(<Число>,Бит))
Message(CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",0,0")+" -> 0,0");
//С обращением полинома, но не результата (используется по умолчанию)
Message(CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",1,0")+" -> 1,0");
//Без обращения полинома, но с обращенным результатом
Message(CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",0,1")+" -> 0,1");
//С обращением полинома, и результата
Message(CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",1,1")+" -> 1,1");
//CRC по умолчанию
Message("Def:"+CalculateHash(Данные,"CRC"+Бит,1));

//Без обращения полинома и результата, но с обращенными данными
Message("R:"+CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",0,0,1")+" -> 0,0");
//С обращением полинома, но не результата, с обращенными данными
Message("R:"+CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",1,0,1")+" -> 1,0");
//Без обращения полинома, но с обращенным результатом, и с обращенными данными
Message("R:"+CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",0,1,1")+" -> 0,1");
//С обращением полинома, и результата и данных
Message("R:"+CalculateHash(Данные,"CRC"+Бит,1,,Поли+","+Инит+","+Конец+",1,1,1")+" -> 1,1");
//В результате выполнения функции, будет выведен следующий текст:
//FB624731 -> 0,0
//261DAEE5 -> 1,0
//8CE246DF -> 0,1
//A775B864 -> 1,1
//Def:261DAEE5
//R:FC098B17 -> 0,0
//R:6DCA160A -> 1,0
//R:E8D1903F -> 0,1
//R:506853B6 -> 1,1


Пример деления данных на блоки при вычисления множества небольших блоков CRC и сравнение с вычислением целого сообщения

//Вычисляем CRC-32 над каждым символом длинной строки и проверяем результат, вычислением CRC всей строки.
Randomize(1);
//Создаем строку
Стр:="";
For i:=1 To 100000 Do
  AppendStringToBufferedString(Стр,Chr(Random(256),1));
EndDo;

ProfilerClear();
ProfilerStart();
//Создаем таблицу оптимизации
Буф:=CalculateHash("","CRC32",0,,"04C11DB7");
//Устанавливаем начальное значение (это действие не обязательно, так как предыдущая операция уже становила корректное начальное значение)
Буф.SetQWord(2048,CutBitsMask(32));
//Считаем CRC для каждого символа с использованием таблицы Буф, хранящей предыдущее значение
For i:=1 To Length(Стр) Do
  CalculateHash(Mid(Стр,i,1),"CRC32",0,Буф);
EndDo;
//Находим результат
аРез1:=CalculateHash("","CRC32",1,Буф);

//Производим такой же подсчет всей строки сразу
аРез2:=CalculateHash(Стр,"CRC32",1,,"04C11DB7");

Message("Сравнение результатов: "+аРез1+" и "+аРез2);
//Будет выведено: "Сравнение результатов: 34A49BFF и 34A49BFF"

ProfilerStop();
Debugbreak;
Message("Выполнено!");
//Сравнение скорости показало, что для данного случая вычисление CRC единой строки быстрее примерно в 500 раз, чем вычисление 100000 строк, 
//  что является неплохим синтетическим результатом.

Пример вычисления MD5 для группы файлов

//Данный пример производит подсчет MD5 сумм для каждой из верхних папок определенного пути, все файлы в папках сортируются по алфавиту и включаются в общую контрольную сумму как единый цифровой поток.
//После подсчета создается файл Folderhash.md5 содержащий подсчитанные суммы.

  Function _ПодсчитатьКонтрольнуюСуммуФайла(аФайл,ByRef аХеш)
    аРазмер:=File.GetFileSize(аФайл);
    If аРазмер=0 Then
      Exit;
    EndIf;
    аФайл2:=GetFileName(аФайл);
    
    аРазмер2:=аРазмер;
    аПоз:=0;
    While аРазмер>0 Do
      бРазмер:=Min(аРазмер,16777216);//16Mb
      аБуф:=Buffer.Create();
      аБуф.LoadFromFile(аФайл,аПоз,бРазмер);
      Form.StatusText(аФайл2+": "+(аПоз\1048576)+"Мб из "+(аРазмер2\1048576)+"Мб");
      аРазмер:=аРазмер-бРазмер;
      аПоз:=аПоз+бРазмер;
      if isEmpty(аХеш) Then
        аХеш:=CalculateHash(аБуф,"MD5",0);
      Else
        аХеш:=CalculateHash(аБуф,"MD5",0,аХеш);
      EndIf;
    EndDo;
    Message("  Подсчитан файл: "+аФайл2+" -> "+CalculateHash("","MD5",1,аХеш),"I");
  EndFunction

  Function _ПодсчитатьКонтрольныеСуммы(аПуть,флСоздаватьФайл=0,ByRef аХеш=0)
    T:=0;
    If флСоздаватьФайл Then
      T:=Text.Create(1);
    EndIf;
    Message("Подсчитывается "+аПуть+"...");
    aList:=File.LoadFileNames(аПуть+"*.*");
    For i:=1 To aList.Size() Do
      аФайл:=aList[i];
      аФайл:=TearStr(аФайл,Chr(1));
      aList.Set(i,Uppercase(аФайл),аФайл);
    EndDo;
    aList.Sort();
    
    For i:=1 To aList.Size() Do
      аФайл:=aList.GetName(i);
      If File.IsDirectory(аФайл) Then
        If флСоздаватьФайл Then
          аХеш:=0;
        EndIf;
        _ПодсчитатьКонтрольныеСуммы(AddBackslash(аФайл),0,аХеш);
        aList.SetName(i,"");
        If _And(флСоздаватьФайл,not isEmpty(аХеш)) Then
          T.AddString(CalculateHash("","MD5",1,аХеш)+": "+GetFileName(аФайл));
        EndIf;
      EndIf;
    EndDo;
    For i:=1 To aList.Size() Do
      аФайл:=aList.GetName(i);
      If _And(not isEmpty(аФайл),File.GetFileAttributes(аФайл) and _FILE_ATTRIBUTE_HIDDEN=0) Then
        аИмя:=Uppercase(GetFileName(аФайл));
        if аИмя<>"FOLDERHASH.MD5" then
          If флСоздаватьФайл Then
            аХеш:=0;
          EndIf;
          _ПодсчитатьКонтрольнуюСуммуФайла(аФайл,аХеш);
          If _And(флСоздаватьФайл,not isEmpty(аХеш)) Then
            T.AddString(CalculateHash("","MD5",1,аХеш)+"::: "+GetFileName(аФайл));
          EndIf;
        EndIf;
      EndIf;
    EndDo;
    If флСоздаватьФайл Then
      T.Save(аПуть+"Folderhash.md5",-1,1);
    EndIf;
  EndFunction

аПуть:="C:\";
_ПодсчитатьКонтрольныеСуммы(аПуть,1);

Пример вычисления SHA-1

//Пример показывает способ вычисления SHA-1 из строки и перекодирование бинарного результата с помощью Base64.

//Будет выведено: "2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12"
Message(CalculateHash("The quick brown fox jumps over the lazy dog","SHA-1"));

//Будет выведено: "L9ThxnotKPzthJ7hu3bnORuT6xI="
Буф:=CalculateHash("The quick brown fox jumps over the lazy dog","SHA-1",1,,1);
Message(EncodeString(Буф,"Base64"));