Extra Systems

CYPHERNET

консольная система надежной коммерческой шифросвязи через сеть интернет

методы обработки длинных чисел


Для обработки длинных чисел Extra Systems Cypher Net использует массивы типа unsigned char такой длины, чтобы в них помещались числа необходимого размера. Существует два формата хранения: обычный и упакованный. В упакованном формате используются все биты, а в обычном - только половина. Таким образом, в обычном формате каждый байт содержит одну шестнадцатиричную цифру - четыре бита. Длина упакованных чисел определяется константой PACKED_VALUE_LENTH. Если мы работаем с 1024-битными ключами, то математика должна быть 2048-битная (поскольку произведение может содержать в два раза больше цифр, чем сомножители). В байте содержится 8 бит, следовательно значение параметра PACKED_VALUE_LENTH равно 256 (2048 делить на 8). А VALUE_LENTH будет в два раза больше, то есть 512.

Упаковку и распаковку длинных чисел производят процедуры pack_value и unpack_value:

void pack_value(unsigned char *value, unsigned char *packed_value)
{
	unsigned char x, y;
	unsigned int i = 0, j = 0;
	memset(packed_value, 0, PACKED_VALUE_LENTH);
	while (i < PACKED_VALUE_LENTH)
	{
		x = value[j++];
		y = value[j++];
		packed_value[i++] = x | (y << 4);
	}
}

void unpack_value(unsigned char *packed_value, unsigned char *value) 
{
	unsigned char cur_char;
	unsigned int i = 0, j = 0;
	clear_value(value);
	while (i < PACKED_VALUE_LENTH)
	{
		cur_char = packed_value[i++];
		value[j++] = cur_char & 0xF;
		value[j++] = cur_char >> 4;
	}
}

Также приведем в качестве примера процедуру вычитания длинных чисел:

void value_sub(signed char *operand1, signed char *operand2)
{				//operand1 -= operand2
	int i;
	for (i = 0; i < VALUE_LENTH; i++)
	{
		*(operand1 + i) -= *(operand2 + i);
		if (*(operand1 + i) < 0)
		{
			*(operand1 + i) += RADIX;
			if ((i + 1) < VALUE_LENTH) *(operand1 + i + 1) -= 1;
		}
	}
}

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

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

void pack_value(void *value, void *packed_value) { memcpy(packed_value, value, VALUE_LENTH); }
void unpack_value(void *packed_value, void *value) { memcpy(value, packed_value, VALUE_LENTH); }

А константы VALUE_LENTH и PACKED_VALUE_LENTH в нашей реализации длинной математики на языке ассемблера имеют одно и то же значение (256 при 1024-битных ключах). Это связано с тем, что ассемблерные модули не используют никакого "разрежения" битов, а хранение информации производится плотно, с использованием всех бит.

В настоящее время мы используем 64-битный код, поэтому единицей обработки длинных чисел является объект размером 8 байт. Фактически, наша ассемблерная арифметика использует 64-битные регистры в качестве цифр, так что "основание системы исчисления" у нас в ассемблере равно 2 в степени 64. В качестве примера продемонстрируем наш исходный код на ассемблере процедуры инкремента длинного числа (адрес операнда передается сюда в регистре rsi):

;======================================================
WORD_SIZE           EQU		8
LONG_INT_LENTH      EQU		VALUE_LENTH / WORD_SIZE
;======================================================
SECTION             .text
;======================================================
ii_inc_long_int:    mov		rcx,LONG_INT_LENTH
                    mov		rax,1
                    mov		rdx,0
.m10:               add		[rsi+rdx*WORD_SIZE],rax
                    jnc		.fin
                    inc		rdx
                    loop	.m10
.fin:               ret
;======================================================

Таким образом, на ассемблере, в случае 1024-битных ключей RSA, обрабатываются числа всего лишь из 32-х (параметр LONG_INT_LENTH) "знаков" (каждый из которых для данной архитектуры представляет собой машинное слово длиной 64 бита). Это, конечно, дает существенный прирост производительности по сравнению с модулями длинной математики на языке C (которые работают со словами длиной всего лишь 4 бита, и не имея при этом к тому же, в отличие от ассемблера, аппаратной поддержки контроля переполнения).


© Extra Systems, 2024 Extra Web Top