Русская Википедия:Two-Track-MAC

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

Two-Track-MAC — код аутентификации сообщений, предназначенный для проверки подлинности и целостности передаваемых данных. Другими словами мы можем убедиться в том, что данные передавались от нужного источника и их содержание не менялось во время передачи.

Основная цель: предотвратить изменений сообщений третьей стороной во время передачи. Примером сообщений могут служить электронные банковские платежи или системы автоматизированного управления подвижными объектами.

Разработчики: Шаблон:Iw, Шаблон:Iw.

История создания

Алгоритм Two-Track-MAC был избран в проект NESSIE (Новые европейские алгоритмы для электронной подписи) в ноябре 2000 года и задумывался как более быстрый и надежный аналог имевшихся в то время алгоритмов MAC. В разработке принимали участие Шаблон:Iw из Лёвенского университета (Katholieke Universiteit Leuven) — Бельгия и Шаблон:Iw из Шаблон:Iw (Германия).

Описание

Файл:Ttmac-scheme.jpg
Схема работы алгоритма TTMAC

В основе Two-Track-MAC лежит хеш-функция RIPEMD-160. Особенность заключается в шифровании сообщения по двум независимым путям (на рисунке обозначены как L и R). Такой подход позволяет увеличить размер внутреннего состояния. В результате чего мы получаем больше возможных значений внутренней функции шифрования. Это позволяет усложнить атаки, основанные на переборе всевозможных значений.

По сравнению с MDx-MAC, который так же основан на RIPEMD-160 Two-Track-MAC намного эффективнее для коротких сообщений (512 или 1024 бит), и также эффективней на длинных сообщениях.

Другое важное преимущество TTMAC является возможность быстрого изменения ключа шифрования. Это позволяет увеличить стойкость системы, без снижения скорости. При довольно частой смене ключа злоумышленнику не удастся собрать большого количества пар сообщение-MAC-код, что сильно снижает вероятность подбора ключа или MAC-кода.

Принцип работы

Как уже отмечалось Two-Track-MAC имеет два параллельных блока преобразований L(K,M) и R(K,M), принимающие на вход сообщение M и ключ K. В результате каждый из блоков работает независимо друг от друга и создает два различных представления (А и В на рисунке) одних и тех же данных.

Предположим для начала, что размер сообщения M составляет 512 бит. Размер ключа K всегда фиксирован и составляет 160 бит. Для усложнения преобразований L(K,M) дает на выходе пять 32-битных слов (<math>A_0,A_1,A_2,A_3,A_4</math>). То есть формально разделяет пока ещё предварительный вариант ключа на 5 частей одинаковых размеров. Аналогично набор (<math>B_0,B_1,B_2,B_3,B_4</math>) дает на выходе функция R(K,M). Затем эти слова вычитаются по модулю <math> 2^{32} </math>. Это своего рода смешивание двух значений для приведения из к фиксированной длине 160 бит. Окончательный результат: <math>E = ~~(E_0,E_1,E_2,E_3,E_4</math>), где <math>E_i = A_i - B_i ~~ mod ~~2^{32}, ~~i = 0,1,2,3,4</math>. Собственно это и есть то, ради чего все делалось. E является кодом аутентичности сообщения M.

Если сообщение превышает 512 бит, то M разбивается на блоки <math>M = M_1 M_2 M_3...M_n</math>, где <math>M_i</math> имеет длину 512 бит. В результате процесс мы все те же операции проводим над каждым блоком, перемешивая их по очереди. Сообщение длиной не кратной 512 дополняется нулями и единицами до нужного нам размера.

Подробнее о смешивании результатов выполнения L и R

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

Введем обозначения: <math>A = (A_0,A_1,A_2,A_3,A_4) = L^(K,M_1)</math>

<math>B = (B_0,B_1,B_2,B_3,B_4) = R^(K,M_1)</math>

Далее создаем два 160-битных блоков <math>C = (C_0,C_1,C_2,C_3,C_4)</math> и <math>D = (D_0,D_1,D_2,D_3,D_4)</math>. Эти блоки заполняются различными комбинациями с выходными данными функций L и R. А именно:

<math>C_2 = A_3 - B_0</math>,

<math>C_3 = A_4 - B_1</math>,

<math>C_0 = (A_1 + A_4) - B_3</math>,

<math>C_1 = A_2 - B_4</math>,

<math>D_1 = (A_4 + A_2) - B_0</math>,

<math>D_2 = A_0 - B_1</math>,

<math>D_3 = A_1 - B_2</math>,

<math>D_4 = A_2 - B_3</math>,

<math>D_0 = A_3 - B_4</math>.

Не стоит забывать, что все сложения и вычитания производятся по модулю <math>2^{32}</math>.

Когда сообщение больше 512 бит, полученные блоки C и D будут подаваться на вход вместо ключа для следующего блока сообщения. Так происходит пока мы не пройдем все сообщение.

Условно весь процесс создания создания кода аутентичности можно представляется как :

<math>HL(1) = A = L^{*}(K,M1)</math>

<math>HR(1) = B = R^{*}(K,M1)</math>

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

<math>(A,B)->(C,D)</math>

<math>HL(i) = A = L^{*}(C,M_i)</math>

<math>HR(i) = B = R^{*}(D,M_i)</math>

На последней итерации получается мы меняем местами входные данные для R и L. Это делается для повышения стойкости кода аутентификации. Окончательные <math>HL(n)</math> и <math>HR(n)</math> представляют собой Two-track-MAC.

Псевдокод

Ниже представлен псевдокод алгоритма.

<math>C_0:=K_0;C_1:=K1;C_2:=K_2;C_3:=K_3;C_4:=K_4;D_0:=K_0;D_1:=K_1;D_2:=K_2;D_3:=K_3;D_4:=K_4;</math>
FOR i:= 0 TO n-1 {
  <math>A_0:=C_0;A_1:=C_1;A_2:=C_2;A_3:=C_3;A_4:=C_4;</math>
  <math>B_0:=D_0;B_1:=D_1;B_2:=D_2;B_3:=D_3;B_4:=D_4;</math>
  IF (i!=n-1) FOR j:= 0 TO 79 {
     <math>T:=rol_{s(j)}(A_0 \oplus f(j,A_1,A_2,A_3) \oplus M_{i}[r(j)] \oplus c(j)) \oplus A_4</math>;
     <math>A_0:=A_4;A_4:=A_3;A_3:=rol_{10}(A_2); A_2:=A_1;A_1:=T;</math>
     <math>T:=rol_{s^{'}(j)}(B_0 \oplus f(79 - j,B_1,B_2,B_3) \oplus M_i[r^{'}(j)] \oplus c^{'}(j)) \oplus B_4;</math>
     <math>B_0:=B_4;B_4:=B_3;B_3:=rol_{10}(B_2);B_2:=B_1;B_1:=T;</math>
  }
  else FOR j:= 0 TO 79 {
     <math>T:=rol_{s(j)}(A_0 \oplus f(j,A_1,A_2,A_3) \oplus M_i[r(j)] \oplus c(j)) \oplus A_4</math>
     <math>A_0:=A_4; A_4:=A_3;A_3:=rol_{10}(A_2);A_2:=A_1;A_1:=T;</math>
     <math>T:=rol_{s^{'}(j)}(B_0 \oplus 79 -j,B_1,B_2,B_3) \oplus M_{i}[r^{'}(j)] \oplus c^{'}(j))</math>
     <math>B_0:=B_4; B_4:=B_3;B_3:=rol_{10}(B_2); B_2:=B_1;B_1:=T;</math>
  }
  <math>A_0:=A_0 \ominus C_0; A_1:=A_1 \ominus C_1;A_2:= A_2 \ominus C_2; A_3:= A_3 \ominus C_3</math>
  <math>A_4:=A_4 \ominus C_4</math>
  <math>B_0:=B_0 \ominus D_0; B_1:=B_1 \ominus D_1;B_2:= B_2 \ominus D_2; B_3:= B_3 \ominus D_3</math>
  <math>B_4:=B_4 \ominus D_4;</math>
  IF (i != n-1) {
     <math>C_2:=A_3 \ominus B_0; C_3:=A_4 \ominus B_1; C_4:=A_0 \ominus B_2; C_0:= (A_1 /oplus A_4) \ominus B_3</math>
     <math>C_1:=A_2 \ominus B_4</math>
     <math>D_1:=(A_4 \oplus A_2) \ominus B_0; D_2:=A_0 \ominus B_1; D_3:=A_1 \ominus B_2</math>
     <math>D_4:=A_2 \ominus B_3; D_0:=A_3 \ominus B_4</math>
 }
}
<math>E_0:=A_0 \ominus B_0; E_1:=A_1 \ominus B_1; E_2:=A_2 \ominus B_2; E_3:=A_3 \ominus B_3;</math>
<math>E_4:=A_4 \ominus B_4</math>;

Пояснения и возможные реализации

Здесь даются пояснения к обозначениям, используемым в псевдокоде TTMAC, а также обсуждается возможность их реализации.

<math>f(j,x,y,z)</math> — нелинейная функция, работающая с битами. Для различных j производит различные операции над x, y,z. А именно:

  • <math>f(j; x; y; z) = x \otimes y \otimes z 02:42, 18 июля 2023 (+04)02:42, 18 июля 2023 (+04)EducationBot (обсуждение) (0 \le j \le 15)</math>
  • <math>f(j; x; y; z) = (x \wedge y) \lor (\neg x \wedge z)EducationBot (обсуждение) (16 \le j \le 31)</math>
  • <math>f(j; x; y; z) = (x \lor \neg y) \otimes z 02:42, 18 июля 2023 (+04)02:42, 18 июля 2023 (+04) (32 \le j \le 47 )</math>
  • <math>f(j; x; y; z) = (x \land z) \lor (y \land \neg z)EducationBot (обсуждение) 02:42, 18 июля 2023 (+04) (48 \le j \le 63)</math>
  • <math>f(j; x; y; z) = x \otimes (y \lor \neg z)02:42, 18 июля 2023 (+04)EducationBot (обсуждение) 02:42, 18 июля 2023 (+04) (64 \le j \le 79)</math>

Например на языке C удобно представить эти функции в виде макросов[1]:

#define F1(x, y, z)	(x ^ y ^ z)
#define F2(x, y, z)	(z ^ (x & (y^z)))
#define F3(x, y, z)	(z ^ (x | ~y))
#define F4(x, y, z)	(y ^ (z & (x^y)))
#define F5(x, y, z)	(x ^ (y | ~z))

Символами <math> c(j), c^{'}(j) </math> обозначены константы, значения которых определяется диапазоном j:

  • с(j) = 00000000x <math>(0 \le j \le 15)</math>
  • с(j) = 5A827999x <math>(16 \le j \le 31)</math>
  • с(j) = 6ED9EBA1x <math>(32 \le j \le 47)</math>
  • с(j) = 8F1BBCDCx <math>(48 \le j \le 63)</math>
  • с(j) = A953FD4Ex <math>(64 \le j \le 79)</math>
  • с'(j) = 50A28BE6x <math>(0 \le j \le 15)</math>
  • с'(j) = 5C4DD124x <math>(16 \le j \le 31)</math>
  • с'(j) = 6D703EF3x <math>(32 \le j \le 47 )</math>
  • с'(j) = 7A6D76E9x <math>(48 \le j \le 63)</math>
  • с'(j) = 00000000x <math>(64 \le j \le 79)</math>

Функции r(j) и r' дают один из 16 возможных блоков, на которые делится исходное сообщение. Так как блоки имеют размер 512 бит, то r(j) может принимать значения от 0 до 15. Где r(j) = 0 (r'(j) = 0) означает биты 0…15, а r(j) = 15 (r'(j) = 15) — биты 495…511 соответственно.

  • r(j) = j при <math> (0 \le j \le 15)</math>
  • r(16..31) = 7; 4; 13; 1; 10; 6; 15; 3; 12; 0; 9; 5; 2; 14; 11; 8
  • r(32..47) = 3; 10; 14; 4; 9; 15; 8; 1; 2; 7; 0; 6; 13; 11; 5; 12
  • r(48..63) = 1; 9; 11; 10; 0; 8; 12; 4; 13; 3; 7; 15; 14; 5; 6; 2
  • r(64..79) = 4; 0; 5; 9; 7; 12; 2; 10; 14; 1; 3; 8; 11; 6; 15; 13
  • r'(0..15) = 5; 14; 7; 0; 9; 2; 11; 4; 13; 6; 15; 8; 1; 10; 3; 12
  • r'(16..31) = 6; 11; 3; 7; 0; 13; 5; 10; 14; 15; 8; 12; 4; 9; 1; 2
  • r'(32..47) = 15; 5; 1; 3; 7; 14; 6; 9; 11; 8; 12; 2; 10; 0; 4; 13
  • r'(48..63) = 8; 6; 4; 1; 3; 11; 15; 0; 5; 12; 2; 13; 9; 7; 10; 14
  • r'(64..79) = 12; 15; 10; 4; 1; 5; 8; 7; 6; 2; 13; 14; 0; 3; 9; 11

Пример: Пусть сообщение:

M = "Software-optimized universal hashing and message authentication."

Поставим в соответствие каждому символу определённый код:

"S", "o", "f", "t", "w", "a", "r", "e", "-", "o", "p", "t", "i", "m", "i", "z"
 83, 111, 102, 116, 119, 97,  114, 101, 45,  111, 112, 116, 105, 109, 105, 122
"e", "d", " ", "u", "n", "i", "v", "e", "r", "s", "a", "l", " ", "h", "a", "s"
101, 100,  32, 117, 110, 105, 118, 101, 114, 115,  97, 108,  32, 104,  97, 115
"h", "i", "n", "g", " ", "a", "n", "d", " ", "m", "e", "s", "s", "a", "g", "e"
104, 105, 110, 103, 32,  97, 110,  100, 32,  109, 101, 115, 115,  97, 103, 101
" ", "a", "u", "t", "h", "e", "n", "t", "i", "c", "a", "t", "i", "o", "n", "."
 32,  97, 117, 116, 104, 101, 110, 116, 105,  99,  97, 116, 105, 111, 110,  46

В двоичном представлении текст сообщение будет содержать 512 нулей и единиц. Затем оно разбивается на 16 блоков по 32 бита:

<math>M_0 = </math> = (01010011 01101111 01100110 01110100) 
<math>M_1 = </math> = (01110111 01100001 01110010 01100101)
<math>M_2 = </math> = (00101101 01101111 01110000 01110100)
<math>M_3 = </math> = (01101001 01101101 01101001 01111010) 
<math>M_4 = </math> = (01100101 01100100 00100000 01110101) 
<math>M_5 = </math> = (01101110 01101001 01110110 01100101)
<math>M_6 = </math> = (01110010 01110011 01100001 01101100) 
<math>M_7 = </math> = (00100000 01101000 01100001 01110011)
<math>M_8 = </math> = (01101000 01101001 01101110 01100111) 
<math>M_9 = </math> = (00100000 01100001 01101110 01100100)
<math>M_{10} = </math> = (00100000 01101101 01100101 01110011) 
<math>M_{11} = </math> = (01110011 01100001 01100111 01100101)
<math>M_{12} = </math> = (00100000 01100001 01110101 01110100) 
<math>M_{13} = </math> = (01101000 01100101 01101110 01110100)
<math>M_{14} = </math> = (01101001 01100011 01100001 01110100) 
<math>M_{15} = </math> = (01101001 01101111 01101110 00101110)

В результате функция <math> M(r(16))</math> выдаст: (00100000 01101000 01100001 01110011). А <math> M_{2}(r(16))</math> даст третий бит, т.е 1.

s(j) и <math>s^{'}(j) </math> — дают номер бита для операции битового поворота rol.

  • s(0..15) = 11; 14; 15; 12; 5; 8; 7; 9; 11; 13; 14; 15; 6; 7; 9; 8
  • s(16..31) = 7; 6; 8; 13; 11; 9; 7; 15; 7; 12; 15; 9; 11; 7; 13; 12
  • s(32..47) = 11; 13; 6; 7; 14; 9; 13; 15; 14; 8; 13; 6; 5; 12; 7; 5
  • s(48..63) = 11; 12; 14; 15; 14; 15; 9; 8; 9; 14; 5; 6; 8; 6; 5; 12
  • s(64..79) = 9; 15; 5; 11; 6; 8; 13; 12; 5; 12; 13; 14; 11; 8; 5; 6
  • s'(0..15) = 8; 9; 9; 11; 13; 15; 15; 5; 7; 7; 8; 11; 14; 14; 12; 6
  • s'(16..31) = 9; 13; 15; 7; 12; 8; 9; 11; 7; 7; 12; 7; 6; 15; 13; 11
  • s'(32..47) = 9; 7; 15; 11; 8; 6; 6; 14; 12; 13; 5; 14; 13; 13; 7; 5
  • s'(48..63) = 15; 5; 8; 11; 14; 14; 6; 14; 6; 9; 12; 9; 12; 5; 15; 8
  • s'(64..79) = 8; 5; 12; 9; 12; 5; 14; 6; 8; 13; 6; 5; 15; 13; 11; 11

На самом деле все выше описанные выражения заимствованны из алгоритма хеш-функции RIPEMD-160. Собственно поэтому RIPEMD-160 и является основой для Two-Track-MAC.

Реализация алгоритма TTMAC была включена в криптографическую библиотеку Crypto++[2].

Примеры

Продемонстрируем пример работы алгоритма для различных входных данных. Первый параметр — 160-битный ключ. Второй параметр — передаваемое сообщение. На выходе мы получаем 160 битный код аутентификации.

TTMAC(K,M) = TTMAC(0000000000000000000000000000000000000000,"") = 46724343BDDF4A150009046D4CBF16F2A6378073
TTMAC(K,M) = TTMAC(0000000000000000000000000000000000000000,"Hello World") = 5C8350FEEA6922C4E79F262A72603AA7F99C381D
TTMAC(K,M) = TTMAC(0000000000000000000000000000000000000001,"1") = 8B91136B35096C30C58FA17D7ADE882DBC1CC482

Вопросы использования

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

Например две доверяющие друг другу стороны заранее договорились об использовании секретного ключа, который известен только им. Тем самым гарантируется аутентичность источника и сообщения. Недостаток такого подхода очевиден. Возникает необходимость наличия двух доверяющих друг другу сторон.

Возможно так же совместное использование кода аутентификации сообщения совместно с функцией симметричного шифрования по одной из схем: <math> E_{k_{1}}(M,TTMAC_{k_2}(M))</math>

<math> (E_{k_{1}}(M),TTMAC_{k_2}(M))</math>

<math> (E_{k_{1}}(M),TTMAC_{k_2}(E_{k_{1}}(M)))</math>

Такой подход предполагает различие в ключах <math>k_2 </math> и <math>k_1 </math>,а также различие в алгоритмах шифрования и вычисления MAC. Иначе есть вероятность возникновения дополнительных соотношений, которые можно использовать для отбраковки ключей.

В частности TTMAC может использоваться для «аутентификации транзакций». Это означает, что сообщение подтверждается единственностью и своевременной передачей. Такой тип аутентификации предоставляет возможность защиты от повторного использования ранее переданных сообщений, что необходимо в тех случаях, когда угроза может привести к нежелательным последствиям.

Безопасность

Успешность атак на Two-Track-MAC зависит от сложности ключа k, длина которого должна составляет 160 бит, длины выходного кода m, которая может составлять 32,64,.. и 160 бит и длины l внутреннего состояния, которая составляет 320 бит.

Первая возможность — перебор всех возможных ключей. Если удается подобрать ключ, то злоумышленник имеет возможность подделать любое сообщение. Для ключа длины k и выходного кода длины m, такая атака требует <math> 2^{k} </math> попыток и <math>\frac{k}{m} </math> известных пар (текст, MAC) для проверки. Так как размер внутреннего состояния TTMAC составляет 360 бит, то вероятность вычислить значение кода аутентичности снижается до <math> 2^{160} </math>, по сравнению с <math> 2^{80} </math> для RIPEMD-160.

Поиск так называемых [Коллизия|коллизий] требует знания <math>2^{\frac{l}{2}}</math> пар (текст,MAC), где l — длина внутреннего состояния. В самом деле это результат следует из парадокса «дней рождений». Для обнаружения внутренних коллизий с помощью исследования внешний коллизий, требуется порядка <math> 2^{l - m}</math> наборов текст-MAC. Кроме того риск может быть уменьшен уменьшением времени жизни ключа.

Результаты приведены таблице:

Типы атак Попытки Возможный успех Известные пары
Поиск ключа <math>2^{160}</math> <math> \frac{160}{m} </math>
Угадывание MAC <math>\frac{1}{2^{m}}</math>
Атака основанная на парадоксе дней рождения <math>2^{160} </math>

Вычислительная эффективность

Число операции для TTMAC всего лишь на несколько процентов превосходит число операций требуемых для вычисление RIPEMD-160. Сравним высоко оптимизированный код RIPEMD-160 и TTMAC. Первый требует 1013 циклов на блок, а для TTMAC такой же степени оптимизации понадобится около 1044 цикла на блок. Это достигается благодаря изначальному проектированию алгоритма для быстрой работы на вычислительных устройствах.

См. также

Примечания

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

Литература

  • А. П. Алферов, А. Ю. Зубов, А. С. Кузьмин, А. В. Черемушкин Основы криптографии: Учебное пособие. 3-е изд. — М.: Гелиос APB,2005 — 480c.,ил.
  • Stinson, D.R. (Douglas Robert), 1956 — Cryptography: theory and practice /D.R. Stinson. p. cm. — (Discreete mathematics and its application) Includes bibliographical references and index. ISBN 0-8493-8521-0.

Ссылки