Во встроенном языке текущей версии платформы 1С:Предприятие нет реализации алгоритма Keccak и основанных на нём SHA-3.
В статье приведен текст алгоритма расчёта Хеша по стандартам:
- SHA3-128
- SHA3-224
- SHA3-256
- SHA3-384
- SHA3-512
- Keccak-256
- Keccak-512
- SHAKE128
- SHAKE256
- cSHAKE128
- cSHAKE256
Приложенная обработка содержит в себе реализацию представленного алгоритма с возможностью запуска функции со стандартными значениями параметров, а также с произвольными значениями параметров. Проверки на корректность произвольных значений параметров нет.
В обработке также реализован алгоритм расчета HMAC
Указанные стандарты получаются выполнением алгоритма Keccak с различными параметрами
Так, для SHA3-512 размер блока 1024 бита, размер выходной строки 512 бит, первая часть дополнения входных данных 0x06, вторая часть дополнения входных данных 0x80. Между первой и второй частью добавляются нулевые байты, с таким расчётом, чтобы длина входных данных стала кратной (1600 - размер блока) - 576 бит.
Описание алгоритма SHA-3 можно найти в википедии.
В приведённом примере функция SHA3 предназначена для расчета хеша по алгоритму Keccak с параметрами, указанными в стандарте, соответствие варианты стандартов и параметров формируются в функции ПолучитьСоответствияСтандартов3
Функция HMACSHA3 производит расчет хеша с ключом.
Функция ПодготовитьИВыполнитьКечак выполняет расчёт хеша с указанными параметрами, в неё передается входное сообщение в виде двоичных данных, размер блока, длина выходной строки, первый и второй суффиксы. Функция вызывает подготовку входных данных, подготавливает переменные и запускает функцию хеширования Кечак.
Текст кода имплементации:
#Область ВспомогательныеФункции
Функция ПолучитьСоответствияСтандартов3()
лСоответствиеСтандартаИАлгоритма = Новый Соответствие;
лСоответствиеСтандартаИАлгоритма.Вставить("SHA3-128",Новый Структура("Битность,Суффикс,Размер",256,"06",128));
лСоответствиеСтандартаИАлгоритма.Вставить("SHA3-224",Новый Структура("Битность,Суффикс,Размер",448,"06",224));
лСоответствиеСтандартаИАлгоритма.Вставить("SHA3-256",Новый Структура("Битность,Суффикс,Размер",512,"06",256));
лСоответствиеСтандартаИАлгоритма.Вставить("SHA3-384",Новый Структура("Битность,Суффикс,Размер",768,"06",384));
лСоответствиеСтандартаИАлгоритма.Вставить("SHA3-512",Новый Структура("Битность,Суффикс,Размер",1024,"06",512));
лСоответствиеСтандартаИАлгоритма.Вставить("Keccak-256",Новый Структура("Битность,Суффикс,Размер",512,"01",256));
лСоответствиеСтандартаИАлгоритма.Вставить("Keccak-512",Новый Структура("Битность,Суффикс,Размер",1024,"01",512));
лСоответствиеСтандартаИАлгоритма.Вставить("SHAKE128",Новый Структура("Битность,Суффикс,Размер",256,"1f","256"));
лСоответствиеСтандартаИАлгоритма.Вставить("SHAKE256",Новый Структура("Битность,Суффикс,Размер",512,"1f","512"));
лСоответствиеСтандартаИАлгоритма.Вставить("cSHAKE128",Новый Структура("Битность,Суффикс,Размер",256,"00","256"));
лСоответствиеСтандартаИАлгоритма.Вставить("cSHAKE256",Новый Структура("Битность,Суффикс,Размер",512,"00","512"));
Возврат лСоответствиеСтандартаИАлгоритма;
КонецФункции // ПолучитьСоответствияСтандартов3(пКлюч)
#КонецОбласти
#Область ПобитовыеОперации
Функция ПовторитьСтроку(пСтрока, пКоличество)
лЧасти = Новый Массив;
Для к = 1 По пКоличество Цикл
лЧасти.Добавить(пСтрока);
КонецЦикла;
Возврат СтрСоединить(лЧасти, "");
КонецФункции // ПовторитьСтроку(пСтрока, пКоличество)
Функция СклеитьДвоичныеДанные(пДвоичныеДанные1, пДвоичныеДанные2)
лМассивДвоичныхДанных = Новый Массив;
лМассивДвоичныхДанных.Добавить(пДвоичныеДанные1);
лМассивДвоичныхДанных.Добавить(пДвоичныеДанные2);
Возврат СоединитьДвоичныеДанные(лМассивДвоичныхДанных);
КонецФункции // СклеитьДвоичныеДанные(пДвоичныеДанные1, пДвоичныеДанные2)
Функция ПолучитьМассивЧиселИзHexСтроки(Знач пСтрока)
лПоз = Найти(пСтрока,",");
лМассив = Новый Массив;
Пока лПоз>1 Цикл
лПодстрока = Сред(пСтрока,1,лПоз-1);
пСтрока = Сред(пСтрока,лПоз+1);
лПоз = Найти(пСтрока,",");
лМассив.Добавить(ПолучитьБуферДвоичныхДанныхИзHexСтроки(лПодстрока).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian));
КонецЦикла;
лМассив.Добавить(ПолучитьБуферДвоичныхДанныхИзHexСтроки(пСтрока).ПрочитатьЦелое64(0,ПорядокБайтов.BigEndian));
Возврат лМассив;
КонецФункции // ПолучитьМассивБДДИзHexСтроки(Знач пСтрока)
Функция ПобитовыйСдвигВлево64(пЧисло, пСмещение)
лЧисло0 = Цел(пЧисло/4294967296);
лЧисло1 = пЧисло%4294967296;
Если пСмещение<32 Тогда
Возврат (ПобитовыйСдвигВлево(лЧисло0, пСмещение)+ПобитовыйСдвигВправо(лЧисло1, 32-пСмещение))*4294967296+
ПобитовыйСдвигВлево(лЧисло1, пСмещение);
ИНаче
Возврат ПобитовыйСдвигВлево(лЧисло1, пСмещение - 32)*4294967296;
КонецЕсли;
КонецФункции // ПобитовыйСдвигВлево64(пЧисло, пСмещение)
Функция ПобитовыйСдвигВправо64(пЧисло, пСмещение)
лЧисло0 = Цел(пЧисло/4294967296);
лЧисло1 = пЧисло%4294967296;
Если пСмещение<32 Тогда
Возврат (ПобитовыйСдвигВправо(лЧисло0, пСмещение))*4294967296+
ПобитовыйСдвигВправо(лЧисло1, пСмещение)+ПобитовыйСдвигВлево(лЧисло0, 32-пСмещение);
ИНаче
Возврат ПобитовыйСдвигВправо(лЧисло0, пСмещение - 32);
КонецЕсли;
КонецФункции // ПобитовыйСдвигВправо64(пЧисло, пСмещение)
// функция осуществляет циклический сдвиг влево
//
Функция ЦиклическийСдвигВлево64(пЧисло, пСмещение)
Возврат(ПобитовыйСдвигВправо64(пЧисло, 64-пСмещение) + ПобитовыйСдвигВлево64(пЧисло, пСмещение));
КонецФункции // ЦиклическийСдвигВлево64(пЧисло, пСмещение)
Функция ПобитовоеИсключительноеИли64(пЧисло1, пЧисло2)
лЧисло10 = Цел(пЧисло1/4294967296);
лЧисло11 = пЧисло1%4294967296;
лЧисло20 = Цел(пЧисло2/4294967296);
лЧисло21 = пЧисло2%4294967296;
Возврат (ПобитовоеИсключительноеИли(лЧисло10,лЧисло20)*4294967296+ПобитовоеИсключительноеИли(лЧисло11,лЧисло21));
КонецФункции // ПобитовоеИсключительноеИли64(пЧисло1, пЧисло2)
Функция ПобитовоеИ64(пЧисло1, пЧисло2)
лЧисло10 = Цел(пЧисло1/4294967296);
лЧисло11 = пЧисло1%4294967296;
лЧисло20 = Цел(пЧисло2/4294967296);
лЧисло21 = пЧисло2%4294967296;
Возврат (ПобитовоеИ(лЧисло10,лЧисло20)*4294967296+ПобитовоеИ(лЧисло11,лЧисло21));
КонецФункции // ПобитовоеИ64(пЧисло1, пЧисло2)
Функция ПобитовоеНе64(пЧисло1)
лЧисло10 = Цел(пЧисло1/4294967296);
лЧисло11 = пЧисло1%4294967296;
Возврат (ПобитовоеНе(лЧисло10)*4294967296+ПобитовоеНе(лЧисло11));
КонецФункции // ПобитовоеНе64(пЧисло1)
Функция СложитьHex(Знач пСуффикс1, Знач пСуффикс2)
лСтрокаHex = "0123456789abcdef";
пСуффикс1 = нРег(пСуффикс1);
пСуффикс2 = НРег(пСуффикс2);
н1=0;
н2=0;
Колво1 = СтрДлина(пСуффикс1);
Колво2 = СтрДлина(пСуффикс2);
лВыхСтрока = "";
лПереносРазряда = 0;
лФл = Истина;
Пока лФл Цикл
лФл = Ложь;
лЧисло1 = 0;
лЧисло2 = 0;
Если н1<Колво1 Тогда
лСимв1 = Сред(пСуффикс1,Колво1-н1,1);
лЧисло1 = СтрНайти(лСтрокаHex,лСимв1)-1;
лФл = Истина;
КонецЕсли;
Если н2<Колво2 Тогда
лСимв2 = Сред(пСуффикс2,Колво2-н2,1);
лЧисло2 = СтрНайти(лСтрокаHex,лСимв2)-1;
лФл = Истина;
КонецЕсли;
лЧисло3 = лЧисло1+лЧисло2+лПереносРазряда;
лПереносРазряда = 0;
Если лЧисло3>15 Тогда
лЧисло3 = 0;
лПереносРазряда = 1;
КонецЕсли;
Если лФл Тогда
лВыхСтрока = Сред(лСтрокаHex,лЧисло3+1,1) + лВыхСтрока;
КонецЕсли;
н1 = н1 + 1;
н2 = н2 + 1;
КонецЦикла;
Если лПереносРазряда>0 Тогда
лВыхСтрока = Сред(лСтрокаHex,лПереносРазряда+1,1) + лВыхСтрока;
КонецЕсли;
Если СтрДлина(лВыхСтрока)%2<>0 Тогда
лВыхСтрока = "0" + лВыхСтрока;
КонецЕсли;
Возврат лВыхСтрока;
КонецФункции
#КонецОбласти
#Область SHA3
// Функция расчета Хеша по алгоритму SHA3 с ключом
// Возвращает hex строку
// Входные параметры:
// пКлюч - строка с ключом, неограниченная, приводится к длине блока - 128
// пДанные - строка с данными, неограниченная
// пСтандарт - строка с описанием стандарта, варианты: "SHA3-128", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", "Keccak-256", "Keccak-512", "SHAKE128", "SHAKE256", "cSHAKE128", "cSHAKE256"
// Возвращаяет Hex строку с хешем
//
Функция HMACSHA3(пДанные, пКлюч="", пСтандарт="SHA3-512") Экспорт
ДанныеДв = ПолучитьДвоичныеДанныеИзСтроки(пДанные);
КлючДв = ПолучитьДвоичныеДанныеИзСтроки(пКлюч);
СоответствиеСтандартаИАлгоритма = ПолучитьСоответствияСтандартов3();
лСтандарт = СоответствиеСтандартаИАлгоритма[пСтандарт];
РазмерБлока = (1600-лСтандарт.Битность)/8; // Размер блока для HMAC512 - 128
Если КлючДв.Размер() > РазмерБлока Тогда
КлючHex = SHA3(КлючДв, пСтандарт);
ИНаче
КлючHex = ПолучитьHexСтрокуИзДвоичныхДанных(КлючДв);
КонецЕсли;
Если СтрДлина(КлючHex)/2 < РазмерБлока Тогда
КлючHex = Лев(КлючHex + ПовторитьСтроку("00", РазмерБлока-СтрДлина(КлючHex)/2), РазмерБлока);
КонецЕсли;
КлючБуфер = ПолучитьБуферДвоичныхДанныхИзHexСтроки(КлючHex);
opad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("5c", РазмерБлока));
ipad = ПолучитьБуферДвоичныхДанныхИзHexСтроки(ПовторитьСтроку("36", РазмерБлока));
ipad.ЗаписатьПобитовоеИсключительноеИли(0, КлючБуфер);
ikeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ipad);
opad.ЗаписатьПобитовоеИсключительноеИли(0, КлючБуфер);
okeypad = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(opad);
Возврат SHA3(СклеитьДвоичныеДанные(okeypad, ПолучитьДвоичныеДанныеИзHexСтроки(SHA3(СклеитьДвоичныеДанные(ikeypad, ДанныеДв),пСтандарт))),пСтандарт);
КонецФункции
// Функция расчёта хеша по алгоритму SHA3, Keccak (кечак)
// Возвращает Hex строку
// Параметры:
// Данные - дв.данные с данными
// пСтандарт - стандрат по которому производится расчёт.
// Варианты: "SHA3-128", "SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512", "Keccak-256", "Keccak-512", "SHAKE128", "SHAKE256", "cSHAKE128", "cSHAKE256"
//
Функция SHA3(пДанные, пСтандарт="SHA3-512") Экспорт
СоответствиеСтандартаИАлгоритма = ПолучитьСоответствияСтандартов3();
лСтандарт = СоответствиеСтандартаИАлгоритма[пСтандарт];
Возврат ПодготовитьИВыполнитьКечак(пДанные, лСтандарт.Битность, лСтандарт.Размер, лСтандарт.Суффикс, "80");
КонецФункции // SHA3(пДанные, пСтандарт="SHA3-512") Экспорт
// Функция подготавливает данные, дополняя их суффиксами между которыми нулевые байты, возвращает двоичные данные
// Возвращает двоичные данные
// Параметры:
// пДанные - Двоичные данные, которые нужно подготовить
// пБитность - Количество бит, кратно которому должен получиться результат
// пСуффикс1 - Hex строка с суффиксом 1 по умолчанию "06" для SHA3
// пСуффикс2 - Hex строка с суффиксом 2 по уиолчанию "80"
//
Функция ПодготовитьДанные(пДанные, пБитность, пСуффикс1="06", пСуффикс2="80")
лРазмер = пДанные.Размер();
лКолвБайт = пБитность/8;
лРазница = лРазмер%лКолвБайт;
лКолВоКДобавке = (лКолвБайт - лРазница);
Если лКолВоКДобавке = 1 Тогда
лСтрокаДополнения = СложитьHex(пСуффикс1,пСуффикс2);// "9f";//"86"; //пСуффикс1 + ПовторитьСтроку("00", лКолвБайт-1) + пСуффикс2;;
Иначе
лСтрокаДополнения = пСуффикс1 + ПовторитьСтроку("00", лКолВоКДобавке-2) + пСуффикс2;
//лСтрокаДополнения = "01" + ПовторитьСтроку("00", лКолВоКДобавке-2) + "80"; // keccak
//лСтрокаДополнения = "06" + ПовторитьСтроку("00", лКолВоКДобавке-2) + "80"; // sha3
КонецЕсли;
Возврат СклеитьДвоичныеДанные(пДанные, ПолучитьДвоичныеДанныеИзHexСтроки(лСтрокаДополнения));
КонецФункции // ПодготовитьДанные(пДанные, пБитность, пСуффикс1, пСуффикс2)
// Функция подготавливает данные и выполняет расчет хеша
// Возвращаяет hex строку
// Параметры:
// пДанные - двоичные данные
// пБитность - размер блока алгоритма в битах
// пДлинаВыхода - размер выхода функции в битах
// пСуффикс1 - суффикс добавляемый к входным данным, hex строка
// пСуффикс2 - суффикс добавляемый к выходным данным дополненным первым суффиксом и нулевыми данными, hex строка
// заданы значения по умолчанию для стандарта SHA3-512
//
Функция ПодготовитьИВыполнитьКечак(пДанные, пБитность=1024, пДлинаВыхода=512, пСуффикс1="06", пСуффикс2="80") Экспорт
b = 1600; // размер буфера бит
c = пБитность;
d = пДлинаВыхода;
l = 6; // в SHA-3 приняты такие параметры Keccak
r = b - c; // (200-(c/8))*8; // 576 для sha3-512
пДанные2 = ПодготовитьДанные(пДанные,r, пСуффикс1, пСуффикс2);
msg = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(пДанные2); // Message
Возврат Кечак(l,r,msg,d);
КонецФункции // ПодготовитьИВыполнитьКечак(пДанные, пБитность, пДлинаВыхода=0, пСуффикс1, пСуффикс2) Экспорт
// Собственно сам алгоритм
// Возвращает hex строку указанной длины d
// Параметры:
// l - задаёт длину обрабатываемого слова в битах, реализован алгоритм для l=6
// r - размер обрабатываемого блока в битах
// msg - данные в виде буфера двоичных данных
// d - длина выхода в битах
//
Функция Кечак(l,r,msg,d)
w = Pow(2,l); // 64 бита слово лДлинаСлова = 64; // Бит
m_blockSize = r / w * 8; // 72 = 200-2*(лРазмер/8);
wb = w/8;
// Инициализация массива 5 х 5 х w
state = Новый Массив;
Для x = 0 по 4 Цикл
state.Добавить(Новый Массив);
Для y = 0 по 4 Цикл
state[x].Добавить(0); // Число будет обрабатываться как 64-х разрядное 2^_l
КонецЦикла;
КонецЦикла;
// фаза впитывания
i = 0;
Пока i<msg.Размер Цикл
j = 0;
Пока j < r/w Цикл
лТекБлок = msg.ПрочитатьЦелое64(i+j*wb,ПорядокБайтов.LittleEndian);
x = j % 5;
y = Цел(j / 5);
state[x][y] = ПобитовоеИсключительноеИли64(state[x][y],лТекБлок);
j = j + 1;
КонецЦикла;
ФункцияПерестановокКечак(state,l,w);
i = i + m_blockSize;
КонецЦикла;
// Фаза выжимки
Z0 = "";
лБуферZ = Новый БуферДвоичныхДанных(25*wb); // 200
лДлинаВыхСтр = d/4; // d/8*2 d/8 - байт и * 2 - символа hex на байт
Пока СтрДлина(Z0) < лДлинаВыхСтр Цикл // для кечак и sha3 цикл проходит один раз
k = 0;
Для i=0 По 4 Цикл
Для j=0 По 4 Цикл
лБуферZ.ЗаписатьЦелое64(k,state[j][i],ПорядокБайтов.LittleEndian);
k = k + 8;
КонецЦикла;
КонецЦикла;
ФункцияПерестановокКечак(state,l,w);
Z0 = Z0 + ПолучитьHexСтрокуИзБуфераДвоичныхДанных(лБуферZ);
КонецЦикла;
Z = Лев(Z0,лДлинаВыхСтр);
Возврат Нрег(Z);
КонецФункции // Кечак(l,r,msg,d)
Функция ФункцияПерестановокКечак(a,l,w)
nRounds = 12 + 2*l;
лСтрока = "
|0000000000000001,0000000000008082,800000000000808a,8000000080008000,
|000000000000808b,0000000080000001,8000000080008081,8000000000008009,
|000000000000008a,0000000000000088,0000000080008009,000000008000000a,
|000000008000808b,800000000000008b,8000000000008089,8000000000008003,
|8000000000008002,8000000000000080,000000000000800a,800000008000000a,
|8000000080008081,8000000000008080,0000000080000001,8000000080008008";
RC = ПолучитьМассивЧиселИзHexСтроки(лСтрока);
Для r=0 По nRounds-1 Цикл
// _2; [Keccak §2.3.2]
C = Новый Массив(5);
D = Новый Массив(5);
Для x=0 По 4 Цикл
C[x] = a[x][0];
Для y=1 По 4 Цикл
C[x] = ПобитовоеИсключительноеИли64(C[x], a[x][y]);
КонецЦикла;
КонецЦикла;
Для x=0 По 4 Цикл
D[x] = ПобитовоеИсключительноеИли64(C[((x+4)%5)],ЦиклическийСдвигВлево64(C[((x+1)%5)], 1));
// a[x,y] = a[x,y] X53; D[x]
Для y=0 По 4 Цикл
a[x][y] = ПобитовоеИсключительноеИли64(a[x][y], D[x]);
КонецЦикла;
КонецЦикла;
// `1; + `0; [Keccak §2.3.4]
x = 1;
y = 0;
current = a[x][y];
Для t=0 По nRounds-1 Цикл
X1 = y;
Y1 = (2*x + 3*y)%5;
tmp = a[X1][Y1];
a[X1][Y1] = ЦиклическийСдвигВлево64(current, ((t+1)*(t+2)/2) % w);
current = tmp;
x = X1;
y = Y1;
КонецЦикла;
// `7; [Keccak §2.3.1]
Для y=0 По 4 Цикл
C = Новый Массив;
Для x=0 По 4 Цикл
C.Добавить(a[x][y]);
КонецЦикла;
Для x=0 По 4 Цикл
a[x][y] = ПобитовоеИсключительноеИли64(C[x], ПобитовоеИ64(ПобитовоеНе64(C[(x+1)%5]), C[(x+2)%5]));
КонецЦикла;
КонецЦикла;
// _3; [Keccak §2.3.5]
a[0][0] = ПобитовоеИсключительноеИли64(a[0][0], RC[r]);
КонецЦикла;
Возврат Неопределено;
КонецФункции // ФункцияПерестановокКечак(a,l,w)
#КонецОбласти
В обработке имеется поле для ввода входной строки, ключа и настроек алгоритма. Для расчёта хеша или HMAC по стандартам нужно выбрать стандарт в соответствующем поле ввода и нажать кнопку "Рассчитатьхеш3" или "Получитьхмак3". Для расчёта хеша по произвольным параметрам достаточно указать битность, префикс, суффикс и длину выхода и нажать кнопку "кечак по параметрам".