JH

Материал из testwiki
Перейти к навигации Перейти к поиску

Шаблон:Карточка хеш-функции

JH — семейство из четырёх криптографических хеш-функций: JH-224, JH-256, JH-384 и JH-512.

Алгоритмы этих хеш-функций отличаются только значением одного внутреннего параметра — длины (в битах) lhash выходного значения (которая и указана после дефиса в названии). Далее в статье при описании алгоритма я буду считать этот параметр частью входных данных для удобства, говоря о JH, как об одном алгоритме или одной хеш-функции.

Хеш-функция JH входит в пятёрку финалистов второго тура SHA-3. В процессе этого конкурса она была улучшена. В статье рассматривается последняя на данный момент версия, которую также можно назвать JH42 (так как главное изменение состояло в том, что число раундов в функции компрессии стало равно 42). Дата выхода документации по ней — 16 января 2011 года.

При хешировании входное сообщение дополняется и разделяется на части, которые далее последовательно обрабатываются так называемой функцией компрессии. Эта функция описана в спецификации в общем виде — то есть с переменным параметром d, меняя который можно конструировать JH-подобные хеш-функции (тем более криптостойкие, чем больше d). В JH исходно d=8.

При выборе финалиста в конкурсе SHA решающую роль играют не криптографические характеристики (они у всех функций отличные), а гибкость и оптимальность в программной и аппаратной реализации. На тему аппаратной реализации существует много исследований, например[1].

Алгоритм[2]

Уточнения

О названии элементов битовых векторов

Будем считать, что у всех обсуждаемых тут битовых векторов есть начало и конец, причём бит, расположенный в начале (слева) является первым, имеет позицию 0 и считается наиболее значимым, соответственно, бит, расположенный в конце (справа), является последним, имеет позицию с наибольшим номером, на один меньшим, чем число разрядов вектора, и считается наименее значимым.

То же самое, за исключением номера позиции, будем подразумевать для векторов, состоящих из битовых векторов, например, для сообщения, состоящего из блоков, или блока, состоящего из полубайтов. С номером же позиции какой-либо составной части битового вектора, состоящей из нескольких бит, будет путаница, создаваемая для удобства. Так, номера позиций полубайтов в блоке будут начинаться с нуля, а номера позиций блоков в сообщении — с единицы…

Шаблон:Скрытый

Обозначение конкатенации

Пусть вектор A состоит из последовательно идущих векторов A1,A2,,AN, тогда этот факт будет обозначаться так: A=A1||A2||||AN

Используемые функции — обобщённый случай

Здесь описаны функции, с помощью которых можно строить JH-подобные алгоритмы, меняя параметр d

Это функция, преобразующая s-блок (то есть размеры её входного и выходного значений одинаковы и равны 4 битам). В алгоритме используются 2 таких функции: S1 и S0. Их таблицы значений такие:

x 0 1 2 3 4 5 6 7 8 9 a b c d e f
S0(x) 9 0 4 b d c 3 f 1 a 2 6 7 5 8 e
S1(x) 3 c 6 d 5 7 1 9 f 2 0 4 b a e 8

Линейное преобразование пар ячеек — Шаблон:Math

Эта функция преобразует пару s-блоков (то есть размеры её входного и выходного значений одинаковы и равны 8 битам). Наиболее лаконичную запись она имеет в терминах конечных полей многочленов.

Рассмотрим конечное поле многочленов над GF(2) степени не выше 3-й. Оно изоморфно полю GF(24); установим стандартное для таких случаев соответствие межу полем многочленов и полем чисел: многочлен будет соответствовать числу, равному значению многочлена при x=2. Выберем для этого поля многочленов следующий примитивный многочлен:

x4+x+1.

Тогда, если рассматривать L(A,B), как функцию, преобразующую 2 многочлена, а числа и буквы — как многочлены, то

L(A,B)=((5A+2B),(2A+B)),

где «» и «+» — операции умножения и сложения в данном поле многочленов.

Перемешивание — Шаблон:Math

Функция Pd является композицией трёх более простых перемешиваний, преобразующих массив из 2d битовых векторов (то есть размеры их входных и выходных значений одинаковы и равны 2d×k битам, где k — число бит в одном элементе этого массива):

Pd(A)=ϕd(P'd(πd(A)))

Приведем алгоритмы этих перемешиваний, обозначив за A=A0||A1||||A2d1 и B=B0||B1||||B2d1 (где Ai и Bi — битовые векторы одинакового размера для всех i) — входной и выходной векторы соответственно:

  • B=πd(A):
fori=0to2d21begin
B4i+0=A4i+0
B4i+1=A4i+1
B4i+2=A4i+3
B4i+3=A4i+2
end
  • B=P'd(A):
fori=0to2d11begin
Bi=A2i
Bi+2d1=A2i+1
end
  • B=ϕd(A):
fori=0to2d21begin
B2i=A2i
B2i+1=A2i+1
B2d1+2i=A2d1+2i+1
B2d1+2i+1=A2d1+2i
end

Преобразование-раунд — Шаблон:Math

На вход подается 2d+2- мерный вектор A. Выход — 2d+2- мерный вектор. Так же на вход подается 2d-битная константа C.

Вектор A представляется в виде массива из 2d полубайт: A=A0||A1||||A2d1.

Потом над каждым полубайтом Ai производится преобразование S0 или S1 в зависимости от значения Ci (если Ci=0, то S0, иначе — S1)

Далее над каждой парой вида (SC2i(A2i),SC2i+1(A2i+1)) производится линейное преобразование L(SC2i(A2i),SC2i+1(A2i+1)).

И в конце концов результаты опять группируются в вектор, биты которого подвергаются перемешиванию Pd.

Это выражается в виде формулы:

Rd(A,C)=Pd(L(SC0(A0),SC1(A1))||L(SC2(A2),SC3(A3))||||L(SC2d2(A2d2),SC2d1(A2d1)))

Преобразование Шаблон:Math

На входе 2d+2 — мерный вектор A. Сначала происходит начальная группировка:

  • B=Gd(A):
fori=0to2d11begin
B8i+0=Ai+128×0
B8i+1=Ai+128×2
B8i+2=Ai+128×4
B8i+3=Ai+128×6
B8i+4=Ai+128×1
B8i+5=Ai+128×3
B8i+6=Ai+128×5
B8i+7=Ai+128×7
end

Далее к результату B этой группировки применяется 6×(d1) преобразований-раундов Rd(B,C) с константами, изменяющимися от раунда к раунду. Начальное значение переменной C задаётся, как целая часть числа (21)×22d, то есть

  • C=(21)×22d:
fori=1to6×(d1)begin
C=Rd2(C,0)
B=Rd(B,C)
end

Далее происходит конечная разгруппировка, обратная начальной:

  • Bfin=Gd1(B),

Где Gd1:Gd(Gd1(B))B

Шаблон:Скрытый

Таким образом,

Ed(A)=G1(Rd(Rd(Rd((Rd(Gd(A)),C1)),C6(d1)1),C6(d1)))

Ci=Rd2(Ci1,0),i=16(d1),C0=(21)×22d

Функция свёртки Шаблон:Math

На входе 2d+2-битный вектор H и 2d+1-битный вектор M. Сначала H преобразуется путём побитового сложения первой половины этого вектора с M, потом над результатом выполняется преобразование Ed и наконец результат преобразуется путём побитового сложения его второй половины с вектором M.

Запишем это в виде формул. Пусть Hleft — первая (старшая) половина вектора H, а Hright — вторая. Пусть также функции Edleft(A) и Edright(A) возвращают левую и правую половины Ed(A) соответственно. Тогда

Fd(H,M)=Edleft((HleftM)||Hright)||(Edright((HleftM)||Hright)M)

Используемые функции — адаптация к аппаратной реализации при d=8

Конкретная реализация во многом зависит от таких параметров, как

  1. Желательное быстродействие
  2. Желательный размер
  3. Желательная технология
  4. Желательное энергопотребление
  5. Желательная помехоустойчивость
  6. Желательная стоимость

Поэтому без задания этих параметров адаптация невозможна. Я дам описание преобразования L с помощью обычных для аппаратной разработки побитовых операций, а также некоторые константы, которые могут пригодиться, если нет существенного ограничения по размерам схемы.

Выражение преобразования Шаблон:Math через простые операции с битами

Пусть L(A,B)=C0||C1||C2||C3||D0||D1||D2||D3,, тогда

D0=B0A1;

D1=B1A2;

D2=B2A3A0;

D3=B3A0;

C0=A0D1;

C1=A1D2;

C2=A2D3D0;

C3=A3D0.

где «» — операция «исключающее или».

Пусть входной и выходной векторы lin_trans_in[0:7] и lin_trans_out[0:7] соответственно, тогда

Шаблон:Скрытый

Константы Шаблон:Math при разных Шаблон:Math

Для lhash=512,384,256,224 будем иметь соответственно:

Шаблон:Скрытый

Константы Шаблон:Math раундов Шаблон:Math

Ci=R6(Ci1,0),i=142,C0=(21)×2256

Представим их в виде массива, Ci+1=round_const[i][0:255]

Шаблон:Скрытый

Позиции полубайтов после перемешивания Шаблон:Math

Пусть на вход P8 поступил 1024-битный вектор — массив из 256-ти 4-битных векторов: A=A1||A2||||A256, а на выходе имеем B=B1||B2||||B256, тогда Bi=Apermut_pose[i*81:8]. Это означает, что первый полубайт выходного вектора B будет равен полубайту входного вектора A с номером позиции (от 0 до 255), содержащемся в первом байте константы permut_pose[0:2047], второй полубайт выходного вектора — полубайту входного вектора с номером позиции, содержащемся во втором байте permut_pose[0:2047], и т. д.

Шаблон:Скрытый

Используемые функции — адаптация к программной реализации при d=8

Суть этой адаптации заключается в минимизации числа операций путём использования операций с как можно более длинными операндами. Сделать это позволяют такие технологии, как, например, SIMD, SSE2, AVX.

примеры реализации на языке C

Для пояснения работы функций, а также для того, чтобы показать константы раундов, будут приводиться куски кода на C[3]. Будучи соединёнными в один файл и дополненными функцией main(), приведённой ниже, они компилируются[4]; полученная программа реализует функцию E8.

Шаблон:Скрытый
Шаблон:Скрытый

Функция Шаблон:Math

Преобразует четыре 128-битных вектора в зависимости от 128-битной константы. То есть

(x0,x1,x2,x3)=SBox(x00,x10,x20,x30,c)

Алгоритм таков. Введём ещё 128-битную переменную t и проинициализируем переменные начальными значениями

(x0,x1,x2,x3)=(x00,x10,x20,x30),

тогда последовательность присваиваний следующая:

  1. x3=¬x3
  2. x0=x0(c&(¬x2))
  3. t=c(x0&x1)
  4. x0=x0(x2&x3)
  5. x3=x3((¬x1)&x2)
  6. x1=x1(x0&x2)
  7. x2=x2(x0&(¬x3))
  8. x0=x0(x1|x3)
  9. x3=x3(x1&x2)
  10. x1=x1(t&x0)
  11. x2=x2t
Шаблон:Скрытый

Функция Шаблон:Math

Преобразует восемь 128-битных переменных. Пусть (b0,b1,b2,b3,b4,b5,b6,b7)=LinTrans(a0,a1,a2,a3,a4,a5,a6,a7), тогда

b4=a4a1

b5=a5a2

b6=a6a3a0

b7=a7a0

b0=a0b5

b1=a1b6

b2=a2b7b4

b3=a3b4

Шаблон:Скрытый

Функция Шаблон:Math

Преобразует 128-битную переменную в зависимости от целой константы n:6n0. Эта функция не оптимизируется для использования 128-битных переменных, однако для совместного использования с другими функциями из этого раздела она необходима.

Пусть b=Permutation(a,n), b=b0||b1||||b127,a=a0||a1||||a127 где. Алгоритм получения числа b таков:

  • b=a:
fori=0to1282×2n1begin
Swap((b2×2n×i+0||b2×2n×i+1||||b2×2n×i+2n1),(b2×2n×i+2n+0||b2×2n×i+2n+1||||b2×2n×i+2n+2n1))
end

Здесь запись Swap(p,q) означает такой участок алгоритма, после которого переменная p принимает значение, которое было у переменной q, а переменная q принимает значение, которое было у переменной p.

Шаблон:Скрытый

Функция Шаблон:Math, адаптированная к программной реализации

Преобразует 1024-битный вектор. Совпадает с функцией E8, описанной в обобщённом случае (в том смысле, что при совпадении значений аргументов совпадают значения функций). Пусть на вход поступил 1024-битный вектор. Представим его в виде набора 8-ми 128-битных переменных: (x0,x1,x2,x3,x4,x5,x6,x7). После следующих преобразований они будут представлять собой выходной вектор:

forr=0to41begin
(x0,x2,x4,x6)=SBox(x0,x2,x4,x6,Creven)
(x1,x3,x5,x7)=SBox(x1,x3,x5,x7,Crodd)
(x0,x2,x4,x6,x1,x3,x5,x7)=LinTrans(x0,x2,x4,x6,x1,x3,x5,x7)
x1=Permutation(x1,rmod7)
x3=Permutation(x3,rmod7)
x5=Permutation(x5,rmod7)
x7=Permutation(x7,rmod7)
end

Использующиеся 128-битные константы задаются следующим образом: Crodd=Cr1||Cr3||||Cr255,Creven=Cr0||Cr2||||Cr254,Cr=R6(Cr1,0),r=142,C0=(21)×2256

Шаблон:Скрытый

Исходные данные

Входной параметр

lhash — длина хэша (число бит в выходном векторе хеш-функции).

Может принимать только следующие значения:

  • 224, 256, 384 и 512;
напомним, что данная статья, строго говоря, описывает семейство из 4-х хеш-функций.

Входное сообщение

Представляет собой число — длину сообщения L и битовый вектор M0 (если L0). Даже если L=0 никаких трудностей для вычисления JH(M0) не возникает.

Алгоритм вычисления Шаблон:Math

1) Дополнение входного вектора

Присоединение к сообщению M0 дополнительных бит в конце. Происходит в три этапа:
1.1)Дополнение единицей.
Присоединение к концу сообщения единичного бита.
1.2)Дополнение нулями.
Присоединение к концу сообщения, дополненного единицей, нулевых бит в количестве 383+(Lmod512) штук.
1.3)Дополнение длиной сообщения.
Присоединение к концу сообщения, дополненного единицей и нулями, 128 бит, в которых записана длина исходного сообщения (например, если L=2, то добавка будет выглядеть так: 0010).
В итоге получится дополненное сообщение M с длиной, кратной 512.

2) Свёртка дополненного входного вектора функцией F8

M разбивается на блоки по 512 бит. Обозначим за N число таких блоков.
Свёртка происходит за N итераций. На i-той итерации на вход F8 поступает i512-битный блок Mi сообщения M и значение Hi1=F8(Hi2,Mi1), вычисленное на предыдущей итерации. Имеется также нулевая итерация, на которой вычисляется H0 из H1 и M0. Таким образом имеем:
H=HN=F8(HN1,MN)=F8(F8(HN2,MN1),MN)==F8((H1,M0)).
поясняющая схема
H1 и M0 выбираются так: первые 16 бит H1 равны входному параметру lhash — размеру выходного хэша (для lhash, равных 512,324,256 или 224 это соответственно 0200h, 0180h, 0100h или 00e0h), а остальные биты H1 и все биты M0 задаются равными 0.

3) Выборка хэша из выхода функции F8

Из 1024-битного вектора HN=HN0||HN1||||HN1023, полученного на выходе F8 на последней итерации свёртки дополненного входного сообщения, выбираются последние lhash бит:
JH(M0)=HN1023+1lhash||HN1023+2lhash||||HN1023

См. также

Примечания

Шаблон:Примечания

Ссылки

Шаблон:Хеш-алгоритмы

  1. сравнение финалистов второго тура SHA по параметрам реализации на различных ПЛИС http://www.ecrypt.eu.org/hash2011/proceedings/hash2011_07.pdf Шаблон:Wayback
  2. алгоритм взят здесь: http://www3.ntu.edu.sg/home/wuhj/research/jh/jh_round3.pdf Шаблон:Wayback
  3. Эти куски взяты по адресу http://www3.ntu.edu.sg/home/wuhj/research/jh/jh_sse2_opt64.h Шаблон:Wayback и изменены для ясности и простоты.
  4. При использовании компилятора gcc для того, чтобы он подразумевал возможность использования дополнительных командных наборов, поддерживаемых процессором, типа SSE2, в командную строку при компиляции можно добавить опцию -march=native (например "gcc -o prog prog.c -Wall -march=native").