Показаны сообщения с ярлыком affine transform. Показать все сообщения
Показаны сообщения с ярлыком affine transform. Показать все сообщения

среда, 21 сентября 2022 г.

GF2p8 Кодирование Рида-Соломона в изоморфном представлении поля

Коды Рида-Соломона считаются через экспоненты и логарифмы по таблице. А у нас с внедренеием инструкций Intel GFNI появилось две другие возможности: использовать инструкцию умножения и аффинные преобразования.

Для примера я взял коды РС из проекта библиотеки генеации QR-кодов. Там используется полином поля G(2^8) P = 0x11D. Кодирование производится в цикле. При переходе к операциям умножения в поле 0x11D, цикл кодрования выглядит так.


	for(i = 0; i < data_length; i++) {
		uint8_t fb =  data[i] ^ Z[0];
		for(j = 0; j < ecc_length-1; j++) {
			Z[j] = Z[j+1] ^ gf2p8_mul(fb, g[j], 0x11D);
		}
		Z[j] = gf2p8_mul(fb, g[j], 0x11D);
	}
	for(i = 0; i < ecc_length; i++) {
		ecc[i] = Z[i];
	}

Преобразование алгоритма для расчета в изоморфном представлении поля с образующим полиномом 0x11B:


	const uint64_t M = 0xFFAACC88F0A0C080;
	const uint64_t Mr= 0xFFAACC88F0A0C080;

	for(i = 0; i < data_length; i++) {
		uint8_t fb =  affine(M,data[i]) ^ Z[0];
		for(j = 0; j < ecc_length-1; j++) {
			Z[j] = Z[j+1] ^ gf2p8_mul(fb, g[j], 0x11B);
		}
		Z[j] = gf2p8_mul(fb, g[j], 0x11B);
	}
	for(i = 0; i < ecc_length; i++) {
		ecc[i] = affine(Mr, Z[i]);
	}

Данное преобразование предполагает так же изоморфные преобразования компонент вектора генератора кодов (g). Векторизация алгоритма может быть выполнена по вложенному циклу.

[1]The comeback of Reed Solomon codes N.Drucker, Sh.Gueron, V.Krasnov

Приложение

Довеском идет код для расчета остатка от деления на 255. Когда в контроллере нет полиномиального умножения, приходится использовать способ расчета умножения и инверсии с использованием таблиц логарифмов и экспонент.
// Multiplication in Galua Fields
int gf_mul(int x, int y) {
    if (x == 0 || y == 0) return 0;
    return exp_[(log_[x] + log_[y])%255];
}
// Inversion in Galua Fields
int gf_inv(int x) {
    return exp_[255 - log_[x]];
}
// Division in Galua Fields
int gf_div(int x, int y) {
    if (x == 0) return 0;
    return exp_[(log_[x] + 255 - log_[y]) % 255];
}
Самая неудобная операция в этом случае - вычисление остатка от деления на 255. Избавился от деления.
// Остаток от деления на 255
uint8_t mod255(unsigned v) {
	uint32_t q = (v*0x1010102UL)>>(24);
	return q;
}
// Остаток от деления на 255
uint8_t mod255(unsigned v) {
	return v + (v>>8);
}

А этот вариант самый простой, применим для сложения логарифмов.

Такая вот оптимизация.

[2] Исходники на Github

вторник, 20 сентября 2022 г.

Крипто-Рубль. Изоморфные преобразования при вычислении Хеш функции Stribog

Для расчета хеш функции ГОСТ Р 34.11-2012 "Stribog" выполняется табличная подстановка в цикле - это общепринятая практика.

Если вернуться к определению алгоритма, его описывают последовательностью матричных операций S, P, L, X:
S - подстановка Sbox для каждого элемента 8x8 байт.
P - транспонирование матрицы 8х8 байт
L - линейное преобразование.
X - исключающее ИЛИ.

Линейное преобразование может быть представлено операцией умножения матрицы коэффициентов 8x8 на матрицу состояния 8x8 в поле GF(2^8) с полиномом 0x171.

LM = {
0x71, 0x05, 0x09, 0xB9, 0x61, 0xA2, 0x27, 0x0E,
0x04, 0x88, 0x5B, 0xB2, 0xE4, 0x36, 0x5F, 0x65,
0x5F, 0xCB, 0xAD, 0x0F, 0xBA, 0x2C, 0x04, 0xA5,
0xE5, 0x01, 0x54, 0xBA, 0x0F, 0x11, 0x2A, 0x76,
0xD4, 0x81, 0x1C, 0xFA, 0x39, 0x5E, 0x15, 0x24,
0x05, 0x71, 0x5E, 0x66, 0x17, 0x1C, 0xD0, 0x02,
0x2D, 0xF1, 0xE7, 0x28, 0x55, 0xA0, 0x4C, 0x9A,
0x0E, 0x02, 0xF6, 0x8A, 0x15, 0x9D, 0x39, 0x71,
Пояснение, как оно соотносится с с матрицей A. Колонка 1 матрицы M - это элемент A[0] = 0x8e20faa72ba0b470,
для которого выполнен разворот порядка следования бит.
8E=>71 20=>04 FA=>5F
Колонка 2 матрицы M - это элемент A[8],
. . .
Колонка 8 матрицы M - это элемент A[56],

Я независимо вывел эту таблицу, из представленнй таблицы подстановки. О возможности подобного представления читал в статье автора [1]. Продемонструю, как выполнить декомпозицию таблиц. Берем два числа: первоя строка, вторая и третья колонка:
A[1] = 0x47107ddd9b505a38
A[2] = 0xad08b0e0c3282d1c
Выделяем по маске 7 бит в каждом байте (A[1] & ~0x0101010101010101) >> 1)^A[2] видим числа 0x8E008E8E8E000000. В каждом байте стоит полином, по которуму производится редуцирование 0x171, с отраженным порядком бит будет 0x8E. Перед нами просто таблица умножения в поле GF(2^8)/[0x171] с отраженным порядком следования бит в байте.

Линейное преобразование может быть представлено таблично.
Cвойство L(a+b) = L(a) + L(b), как и свойство L(a*b) = L(a)*L(b) - действует и для таблицы.

Мы рассматриваем возможность реализации без таблицы, с использованием афинных преобазований и операци умножения в поле GF(2^8). Операция умножения может быть выполнена с использованием векторных инструкций умножения без переносов (Intel), полиномиального умножения (Arm Neon). Афинные преобразования могут выполняться с использованием векторных инструкций без обращения к таблицам.

Оптимизация под векторные инсрукции Arm Neon. 16 байт составляют вектор, используя вектора можно в два действия эмулировать работу операции умножения на константу. Для эмуляции используется свойство линейности преобразования.

Оптимизация под GF-NI:

Парадокс в том, что отечественные алгоритмы Stribog и Kuznyechik можно считать с использованием американской криптографии. Американская криптография использует умножение в поле GF(2^8) по полиному 0x11B. Отечетсвенная криптография использует умножение с редуцированием по полиному 0x171 в алгоритме Stribog, 0x1C3 в алгоритме Kuznyechik. Можно использовать афинные изоморфные преобразования, чтобы отобразить все матрицы и коэффициенты для использования умножения в поле 0x11b.

Матрицы изоморфного преобразования мы научились находить[2]. Для преобразования 0x171=> 0x11B можно использовать матрицы

матрицы изоморфного преобразования 171 => 11B "Stribog"
171=>11B 02 0E M =49A24EE22C984CF0 M-1 =FB44BAF0CC5A0A1C
171=>11B 02 17 M =C30E560C5240D828 M-1 =6D0A141C3A9C2046
171=>11B 02 52 M =B1A27CD0765CD234 M-1 =F5481A5CBE24D86E
171=>11B 02 54 M =29903A0892D47ABC M-1 =E5126608F2EC44F0
171=>11B 02 A0 M =C154448014AEDCC6 M-1 =1B8C164A06F81208
171=>11B 02 B4 M =1590FECC3E8E8CE6 M-1 =9960C6DA5E32485C
171=>11B 02 F6 M =090EFAA096722E1A M-1 =6FD8B46E36428C4A
171=>11B 02 FD M =A7541EDA2602426A M-1 =CB20B646D48660DA
Изоморфное представление матрицы LM в поле 0x11B:
unsigned char LM_11b = {
	0xF6,0x55,0x74,0xE4,0x56,0x3E,0xC1,0x2F,
	0x54,0xDF,0x17,0x9E,0xA9,0x60,0x43,0x02,
	0x43,0x1D,0x10,0x2E,0xEB,0xBB,0x54,0x65,
	0xA8,0x01,0x39,0xEB,0x2E,0xA1,0xE1,0xAD,
	0x93,0xAB,0x81,0x26,0x4E,0x42,0xF5,0xCE,
	0x55,0xF6,0x42,0x0D,0xFB,0x81,0xC7,0x0E,
	0xBA,0x5C,0xA6,0xEF,0x38,0x30,0xEC,0x71,
	0x2F,0x0E,0x07,0xD1,0xF5,0x2A,0x4E,0xF6,

Необходимо таже преобразовать таблицу Sbox в изоморфное представление Sbox_iso = MR·S·RM-1, где MR и RM-1 - матрицы прямого и обратного изоморфного преобразования из поля образованного полиномом 0x171 в поле 0x11b. R - отражение порядка следования бит.


void SPLX_11b(const uint8_t* s, uint8_t* r)
{
	int i,j,k;
	for (i=0; i<8; i++)
	for (j=0; j<8; j++) {
		uint8_t v= 0;
		for (k=0; k<8; k++) 
			v ^= gf2p8_mul(LM_11b[j*8+k], Sbox_iso[s[k*8+i]]);
		r[i*8+j] ^= v;
	}
}

Полученный алгоритм выполняет операции S,P,L,X. В цике выполняется операция умножения в поле GF(2^8)/0x11B. Оптимизация под ARM Neon может использовать полиномиальное умножение с последующим редуцированием.

Я озаглавил сообщение "Крипто-рубль..". Все алгоритмы добычи крипто-валют основаны на вычислении хеш-функций, этот алгоритм удалось выполнить исключительно в 8-битных числах. Это открывает возможность векторизовать алгоритм для параллельного вычисления до 64 хешей. Другая оптимизация - изоморфное преобразование в композитное поле позволяет получить эффективную реализацию алгоритма аппаратно в ASIC или на FPGA.


[1] Algebraic Aspects of the Russian Hash Standard GOST R 34.11-2012, O. Kazymyrov, V. Kazymyrova

воскресенье, 27 июня 2021 г.

GF2p8 Код Грея через аффинные преобразования

Задумался, что еще не высказанного осталось по аффинным преобразованиям. Нашел хороший пример приложения аффинных преобразований. Надо сказать, никто и не пытался выражать код Грея аффинными пребразованиями. Зачем нужны векторные преобразования из кода Грея, не скажу. Наверное не нужны. Но, пример интересный с точки зрения расширения области применения инструкции.

Прямое преобразование выражается битовой операцией:
Gi = Bi⊕Bi+1.
Обратное преобразование можно выразить точно так же, рекуррентной формулой:
Bi = Gi⊕Bi+1.

Из за рекурсии расчет можно считать неэффективным, требуется цикл.

Оба преобразования можно представить аффинными преобразованиями и расчет обратного преобразования будет занимать одну инструкцию gf2p8affine.

Преобразование в код Грея
M = 03060C183060C08016
10000000 =0x80
11000000 =0xC0
01100000 =0x60
00110000 =0x30
00011000 =0x18
00001100 =0x0C
00000110 =0x06
00000011 =0x03
Обратное преобразование из кода Грея
M-1 = FFFEFCF8F0E0C08016
10000000 =0x80
11000000 =0xC0
11100000 =0xE0
11110000 =0xF0
11111000 =0xF8
11111100 =0xFC
11111110 =0xFE
11111111 =0xFF

Путем перемножения матриц можно убедиться, что это обратные преобразования, произведение дает единичную диагональную матрицу

пятница, 25 июня 2021 г.

GF2p8 Нахождение корней образующего полинома

Понадобилось больше теории, чтобы перейти к композитным полям. Но все попадаются сатьи индусов с ведической алгеброй или индусов из исследовательского центра IBM в Индии. В единственном нормальном учебнике, который мне попался по теории Галуа и кодам Рида-Соломона, слово "корень" употребляется всего один раз, вообще один раз на все сто-пятьсот страниц, и без определения.

Допустим A -- один из корней образующего полинома p(x) = x8+x4+x3+x+1. При сложении коэффициентов при степенях используется арифметика по модулю 2. Степени A образуют полиномиальный базис [1,A,A2,A3,A4,A5,A6,A7], который далее может использоваться для вычисления матриц изоморфных преобразований и представления элементов поля.

Что значит корень? Это значит, что подстановка "корня" в образующий поле полином (8-ого порядка) дает ноль:
P(A) = с8A8 + с7A7 + ... + c2A2 + c1A1 + c0=0.
Здесь коэффициенты при степенях (ck) те же, что и в образующем полиноме.
Как найти корень? Чтобы найти в одно действие какой-то корень надо отбросить от образующего полинома степень 8, получим полином 7й степени – это один из корней (x4+x3+x+1). Отбрасывание старшего порядка равносильно операции A = x8 mod P(x). Остальные корни находим методом возведения полученного корня в квадрат, рекурсивно. Всего будет 8 корней для полинома 8 порядка.

// Проверка на корень
int gf2p8_is_root(uint8_t a, uint32_t Poly)
{
	uint8_t v=1, r=0;
	uint32_t p = Poly;
	while (p) {
		if(p&1) r ^= v;
		v = gf2p8_mul(v, a, Poly);
		p>>=1;
	}
	return (r==0);
}

Полином поля можно представить иначе, с учетом знания о корнях, как произведение:
P(x)= (x-A)(x-A2)(x-A4)(x-A8)(x-A16)(x-A32)(x-A64)(x-A128).

Все корни образующего полинома поля GF(2m)образуют циклическую группу по операции возведения в квадрат.

Для полинома восьмого порядка сразу можно выделить четыре корня: A =x1, x2, x4, x8 mod P(x). Остальные четыре корня надо считать.

Свойства поля Галуа GF(pm):

  1. Существует порождающий примитивный элемент поля (геренатор поля), такой что любой не нулевой элемент поля может быть выражен, как степень порождающего элемента поля. Мультипликативная группа циклична.
  2. Для любого элемента (g) поля GF(pm) справедливо: gpm-1 = 1, g-1 = gpm-2.
  3. Любой элемент поля может быть выражен суммой из m элементов поля. Для выражения элементов поля используются m элементов, составляющих полиномиальный базис. Элемент поля можно представить через полиномиальный базис: a0 +a1A +a2A2 +...+am−1Am−1.
  4. В поле GF(pm) выполняется равенство: (a + b)p = ap + bp (mod p)
  5. Если элемент A поля GF(pm) - корень нередуцируемого полинома P(x) степени m, то остальными корнями P(x) будут элементы: Ap, Ap2, . . . , Apm-1.
  6. Для любого простого числа p и любого нередуцируемого полинома P(x) степени m существует только одно поле Галуа GF(pm). Поля Галуа GF(pm), образованные различными нередуцируемыми полиномами степени m изоморфны.

Ниже привожу все корни от 30 нередуцируемых полиномов 8-ого порядка, и корни для полиномов 4-ого порядка.

Таблица корней полинома 8-ого порядка
11B: 02 04 10 1B 5E E4 4D FA
11D: 02 04 10 1D 4C 9D 5F 85
12B: 02 04 10 2B E9 EE FB C1
12D: 02 04 10 2D E5 5D F6 75
139: 02 04 10 39 9C 4E 8F 72
13F: 02 04 10 3F 96 91 84 AA
14D: 02 04 10 4D F8 E3 EB AB
15F: 02 04 10 5F 86 9A 95 C0
163: 02 04 10 63 F3 F4 E1 93
165: 02 04 10 65 8B 8C 99 ED
169: 02 04 10 69 03 05 11 68
171: 02 04 10 71 F3 2F E0 5B
177: 02 04 10 77 8B 3A 98 48
17B: 02 04 10 7B 03 05 11 7A
187: 02 04 10 87 6F 1D D6 34
18B: 02 04 10 8B AA 1A CF E3
18D: 02 04 10 8D 3D 7F 60 B8
19F: 02 04 10 9F 7D 59 94 38
1A3: 02 04 10 A3 70 CD 4A 43
1A9: 02 04 10 A9 18 E9 CD 82
1B1: 02 04 10 B1 66 5F B8 27
1BD: 02 04 10 BD 95 E6 A1 78
1C3: 02 04 10 C3 1F 96 A0 FD
1CF: 02 04 10 CF 76 A0 58 56
1D7: 02 04 10 D7 EB 9E CC 79
1DD: 02 04 10 DD 61 4D D3 35
1E7: 02 04 10 E7 7D 4B EA 2C
1F3: 02 04 10 F3 C1 1C A3 9A
1F5: 02 04 10 F5 28 7E 67 D3
1F9: 02 04 10 F9 6F 1B BC 26
Таблица корней полинома 4-ого порядка
13: 2 4 3 5
19: 2 4 9 E
1F: 2 4 F 8

пятница, 18 июня 2021 г.

GF2p8 Аффинные преобразования (ч. 6)

Продолжаю тему, понял что некоторые разделы не раскрыты..

Умножение на константу в поле GF(2^8)

Нужен шаблон для матрицы умножения на константу в поле. Причем, умножение на константу реализовать надо в виде матрицы аффинного преобразования, чтобы иметь возможность последовательно накладывать и перемножать матрицы.

Матрица умножения состоит из столбцов, в которых располагаются степени числа 2. При умножении констант, должно быть выполнено редуцирование по образующему полиному.
M = [δ·27,δ·26,δ·25,δ·24,δ·23,δ·22,δ·21,δ·20]T

Матрицу нужно транспонировать, если заполнение производится по строкам.

// Алгоритм формирования матрицы умножения на константу в поле GF(2^8)
uint64_t m_mulC(uint8_t c, uint32_t Poly){
	uint64_t m = c;
	int i;
	for (i=1;i<8;i++){
		uint8_t v = gf2p8_mul(c, (1<<i), Poly);
		m = (m<<8) ^ v;
	}
	return m_transpose(m);
}

Редуцирование в поле GF(2^8)

Надо уметь выполнять редуцирование в форме аффинных преобразований - в одно действие. Дана старшая часть умножения без переноса, требуется выполнить редуцирование, т.е. перевести разряды в младщую часть. Редуцирование можно выполнить методом умножения на х8 mod Poly. В данном контексте матрица аффинного преобразования получается из выражения m_mulC(Poly & 0xFF, Poly), редуцирование - это матрица умножения в поле на специальную константу.

Таблица матриц редуцирования
0x11B: M=0xB1D3A6FD4B962C58
0x11D: M=0x71E2B51B478E1C38
0x165: M=0xDDBAA952A495F7EE
0x171: M=0x8D1A34685D37E3C6
0x177: M=0x4DD7E3C6C1CFD3A6
0x1C3: M=0x5BEDDAB468D0FBAD
0x1F5: M=0x2346AF5E9F1D1911

Транспонирование матрицы 8х8 бит в поле GF(2^8)

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

// Транспонирование матриц 8x8 бит
__m128i transpose_8x8 (__m128i m){
    __m128i E = _mm_set1_epi64x(0x0102040810204080ULL);
    return _mm_gf2p8affine_epi64_epi8(E, m, 0);
}
Транспонирование матрицы M = 0x0123456789ABCDEF;
    M             MT
11101111 =EF   11110000 =F0
11001101 =CD   11001100 =CC
10101011 =AB   10101010 =AA
10001001 =89   00000000 =00
01100111 =67   11110000 =F0
01000101 =45   11001100 =CC
00100011 =23   10101010 =AA
00000001 =01   11111111 =FF

Чтобы убедиться, что это действительно транспонирование, применяем повторно. Повторное транспонирование возвращает матрицу к исходному виду. Транспонирование в таком виде нужно, чтобы динамически составлять матрицы умножения на константу. Ниже привожу функцию динамической генератции матрицы аффинного преобразования, которая выполняет умножение на константу в поле GF(2^8)/0x11B

// Составление Матрицы умножения на константу в поле 0x11B
__m128i m_mulC_11B(uint8_t c) {
    __m128i E = _mm_set1_epi64x(0x0102040810204080ULL);
    __m128i m = _mm_gf2p8mul_epi8(E, _mm_set1_epi8(c));
    return _mm_gf2p8affine_epi64_epi8(E, m, 0);// транспонирование 8x8
}
Матрица редуцирования [1B, 0x11B] A= 0xB1D3A6FD4B962C58
Матрица умножения на  [A5, 0x11B] A= 0xABFCF9581A356AD5

среда, 16 июня 2021 г.

GF2p8 Аффинные преобразования (ч. 4)

Вычисление CRC на операции умножения в изоморфном представлении поля GF(2^8)

Мы приблизились к тому, ради чего все затевали.. Нет, вычисление CRC-8 не самоцель. Это самый простой пример применения арифметики Галуа, на котором можно проверить методику. Наша цель - изоморфные преобразования поля, чтобы свести все варианты расчета к одному полиному 0x11B.

Хотелось бы донести хотя бы до одного человека, как выводить алгоритмы. На первом шаге берем операцию, на которой работает CRC:
a(x)×x8 mod P(x) -- операция '×'-умножение без переноса или сдвиг.
Это можно представить иначе:
a(x)⊗(x8 mod P(x)) -- операция '⊗'-умножение в поле P(x).
Для полинома 0x11D выражение принимает вид: a(x)⊗0x1D -- операция '⊗'-умножение в поле P(x)=0x11D.

// CRC-8 в поле 0x11D
uint8_t crc = 0xFD;// начальное значение
uint8_t x8  = 0x1D;
for (i=0; i<length; i++) {
    crc = gf2p8_mul(crc ^ data[i], x8, 0x11D);
}
return crc;

M-1·M(a(x)×0x1D mod 0x11D) = M-1((M·a(x)) × (M·0x1D) mod 0x11B) -- операция '×'-сначала выполняется в поле P(x)=0x11D, а после преобразования в поле P(x)=0x11B.

// CRC-8 в изоморфном представлении 0x11D=>0x11B
// матрицы изоморфного преобразования {11D, 02} =>{11B, FB}
uint64_t M =0x57EED8E22A228202;
uint64_t Mr=0x4F803A301CA0E8C0;

uint8_t crc = affine_byte(M, 0xFD);// начальное значение
uint8_t x8  = affine_byte(M, 0x1D);// преобразование 0x11D=>0x11B
for (i=0; i<length; i++) {
    crc^= affine_byte(M, data[i]);
    crc = gf2p8_mul(crc, x8, 0x11B);
}
return affine_byte(Mr, crc);// обратное преобразование 0x11B=>0x11D
FD => 6A        57EED8E22A228202
1D => 11        57EED8E22A228202
DE => 7E        4F803A301CA0E8C0
CRC8I=7E ..ok

Операция вычисляется на двух инструкциях в цикле: gf2p8_mul и affine. Работает!!