Модуль перетасовки алфавита предназначен для дополнительной защиты передаваемой по каналу связи секретной информации. Хотя применяемое нами в Extra Systems Cypher Net потоковое шифрование RC4 и не имеет в нашей реализации никаких уязвимостей, тем не менее сохраняется теоретическая возможность перебора ключей с последующим лексическим анализом попыток расшифровки передаваемой информации. Речь, конечно, не идет о тех возможностях, которыми человечество располагает сегодня. Для современных систем время перебора ключей RC4 в нашем случае на много порядков превышает время существования Вселенной. Речь идет о тех гипотетических возможностях, которые могут появиться в будущем. Имея намерение застраховаться от любых мыслимых вариантов, мы и добавили к Extra Systems Cypher Net данный модуль перетасовки алфавита, который делает в принципе невозможным лексический анализ попыток расшифровки, и таким образом лишает злоумышленника в принципе какой-либо возможности взломать данную систему шифрованной связи.
Суть этого метода заключается в том, что коды 256 символов (количество вариантов для одного байта из восьми бит) совершенно случайным образом перемещаются в таблице, так что текст на любом языке превращается в полную абракадабру, состоящую из печатных и непечатных символов, перемешанных между собой совершенно непредсказуемым образом. В результате этого метод взлома путем полного перебора паролей шифрования, суть которого заключается в поиске вариантов, когда шифротекст превращается в набор неких печатных символов, полностью теряет свою состоятельность, поскольку правильный пароль, при нашем подходе, который здесь описывается, превращается, как уже было указано, в абракадабру, которую никак не возможно принять за нормальный текст.
Таким образом, взломщик Extra Systems Cypher Net не имеет возможности подбирать пароль, ориентируясь на то, чтобы получить некий осмысленный текст.
Изначально в системе создается таблица, которая соответствует тождественному преобразованию (то есть, она ничего не делает - не меняет информацию о кодировке символов). Это делает модуль shuffle_init:
int main(void) { int i,file_handle; unsigned char data[256]; for (i=0; i<256; i++) data[i] = i; file_handle = creat("shuffle.encrypt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); write(file_handle, data, sizeof(data)); close(file_handle); return 0; }
Затем информация в этой таблице случайным образом тасуется (данную процедуру можно, и даже нужно, делать не один раз). Этим занимается модуль shuffle_randomize:
int main(void) { unsigned char src, dst; unsigned char shuffle_encrypt_data[256]; unsigned char random_src[256], random_dst[256]; int i, file_handle; file_handle = open("shuffle.encrypt", O_RDONLY); read(file_handle, shuffle_encrypt_data, sizeof(shuffle_encrypt_data)); close(file_handle); fill_random_buffer(random_src, sizeof(random_src)); fill_random_buffer(random_dst, sizeof(random_dst)); for(i = 0; i < 256; i++) { src = shuffle_encrypt_data[random_src[i]]; dst = shuffle_encrypt_data[random_dst[i]]; shuffle_encrypt_data[random_src[i]] = dst; shuffle_encrypt_data[random_dst[i]] = src; } file_handle = creat("shuffle.encrypt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); write(file_handle, shuffle_encrypt_data, sizeof(shuffle_encrypt_data)); close(file_handle); return 0; }
Следует особо подчеркнуть, что функция fill_random_buffer (используемая в этом и ряде других модулей данной системы) всегда возвращает именно криптографически стойкую последовательность случайных байт. В разных операционных системах она опирается на вызовы различных специально предназначенных для этого и официально документированных системных сервисов (специально предназначенных и официально объявленных как таковые, что служат для генерации криптографически стойких последовательностей случайных байт). В каждой из поддерживаемых нами операционных систем функция fill_random_buffer фактически лишь переадресовывает обращение к такой системной функции (и просто возвращает своему клиенту результаты ее работы).
Применением этого ключа в системе Extra Systems Cypher Net занимается модуль shuffle (обратите внимание, что shuffle_decrypt_data создается "на лету" на базе shuffle_encrypt_data сразу после ее загрузки - этот факт автоматически обеспечивает полную согласованность алгоритмов кодирования и декодирования передаваемой информации), код которого на языке C выглядит следующим образом:
unsigned char shuffle_encrypt_data[256], shuffle_decrypt_data[256]; int load_shuffle_data(void) { unsigned char src; int i, file_handle; if((file_handle = open("shuffle.encrypt", O_RDONLY)) == -1) return 0; read(file_handle, shuffle_encrypt_data, sizeof(shuffle_encrypt_data)); close(file_handle); for(i = 0; i < 256; i++) { src = shuffle_encrypt_data[i]; shuffle_decrypt_data[src] = i; } return 1; } void shuffle_rotate_l(unsigned char *buffer_ptr, int buffer_len) { int i; unsigned char x; if (!buffer_len) return; x = buffer_ptr[0]; for(i = 0; i < (buffer_len - 1); i++) buffer_ptr[i] = (buffer_ptr[i] << 4) | (buffer_ptr[i + 1] >> 4); buffer_ptr[buffer_len - 1] = (buffer_ptr[buffer_len - 1] << 4) | (x >> 4); } void shuffle_rotate_r(unsigned char *buffer_ptr, int buffer_len) { int i; unsigned char x; if (!buffer_len) return; x = buffer_ptr[buffer_len - 1]; for(i = (buffer_len - 1); i > 0; i--) buffer_ptr[i] = (buffer_ptr[i] >> 4) | (buffer_ptr[i - 1] << 4); buffer_ptr[0] = (buffer_ptr[0] >> 4) | (x << 4); } void shuffle_twist(unsigned char *buffer_ptr, int buffer_len) { int i; unsigned char x, y; if (buffer_len < 2) return; for(i = 1; i < buffer_len; i++) { x = buffer_ptr[i - 1]; y = buffer_ptr[i]; buffer_ptr[i - 1] = (x & 0xF0) | (y >> 4); buffer_ptr[i] = (y & 0xF) | (x << 4); } } void shuffle_buffer(unsigned char *buffer_ptr, int buffer_len, unsigned char *shuffle_data) { short counter; for(counter = 0; counter < buffer_len; counter++) buffer_ptr[counter] = shuffle_data[buffer_ptr[counter]]; } void shuffle_swap_l(unsigned char *buffer_ptr, int buffer_len, unsigned char swap_old) { int i; unsigned char x, y, swap_new; if (buffer_len < 2) return; swap_new = ~swap_old; for(i = 1; i < buffer_len; i++) { x = buffer_ptr[i - 1]; y = buffer_ptr[i]; buffer_ptr[i - 1] = (x & swap_old) | (y & swap_new); buffer_ptr[i] = (y & swap_old) | (x & swap_new); } } void shuffle_swap_r(unsigned char *buffer_ptr, int buffer_len, unsigned char swap_old) { int i; unsigned char x, y, swap_new; if (buffer_len < 2) return; swap_new = ~swap_old; for(i = (buffer_len - 1); i > 0; i--) { x = buffer_ptr[i - 1]; y = buffer_ptr[i]; buffer_ptr[i - 1] = (x & swap_old) | (y & swap_new); buffer_ptr[i] = (y & swap_old) | (x & swap_new); } } unsigned char i_shuffle_rol(unsigned char x, unsigned char y) { unsigned char a, b; a = ((x >> 1) & 0xF0) | ((y << 3) & 0x80); b = ((x << 1) & 0x0F) | ((y >> 3) & 0x01); return a | b; } void shuffle_rol(unsigned char *buffer_ptr, int buffer_len) { int i; unsigned char x, y; if (buffer_len < 2) return; for(i = 1; i < buffer_len; i++) { x = buffer_ptr[i - 1]; y = buffer_ptr[i]; buffer_ptr[i - 1] = i_shuffle_rol(x, y); buffer_ptr[i] = i_shuffle_rol(y, x); } } unsigned char i_shuffle_ror(unsigned char x, unsigned char y) { unsigned char a, b; a = ((x & 0xF0) << 1) | ((y >> 3) & 0x10); b = ((x & 0x0F) >> 1) | ((y << 3) & 0x08); return a | b; } void shuffle_ror(unsigned char *buffer_ptr, int buffer_len) { int i; unsigned char x, y; if (buffer_len < 2) return; for(i = (buffer_len - 1); i > 0; i--) { x = buffer_ptr[i - 1]; y = buffer_ptr[i]; buffer_ptr[i - 1] = i_shuffle_ror(x, y); buffer_ptr[i] = i_shuffle_ror(y, x); } } void shuffle_encrypt_buffer(unsigned char *buffer_ptr, int buffer_len) { shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_rol(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_swap_l(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_1); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_rotate_l(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_twist(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_rotate_l(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_swap_l(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_2); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); shuffle_rol(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data); } void shuffle_decrypt_buffer(unsigned char *buffer_ptr, int buffer_len) { shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_ror(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_swap_r(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_2); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_rotate_r(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_twist(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_rotate_r(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_swap_r(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_1); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); shuffle_ror(buffer_ptr, buffer_len); shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data); }
Благодаря этой хаотической перестановке букв злоумышленник полностью лишается возможности анализировать текст, который получается у него при попытках расшифровки по методу перебора ключей. Файл "shuffle.encrypt" формируется для каждого клиента свой, и кроме того, каждый клиент, при желании, снабжается программой shuffle_randomize, с помощью которой он сам может произвести любое количество дополнительных перестановок в этом своем ключевом файле.
Назначение процедур shuffle_rotate_l и shuffle_rotate_r заключается в том, чтобы исключить регулярные повторения ключевых байт кодировки UTF8 (которые возникают при употреблении всех языков, кроме английского — и особенно это характерно для кириллицы) — поскольку это обстоятельство могло бы служить зацепкой для подбора ключа RC4 методом «грубой силы». Применение нами этих процедур в данном модуле полностью исключает в нашем продукте и такую опасность.
Аналогичную задачу решает процедура shuffle_swap — ее работа заключается в перестановке битов в соседних байтах по определенной маске (SHUFFLE_SWAP_STEP). Конкретный вид этой маски мы подбираем исходя из критерия достижения максимального уровня энтропии итоговой последовательности байт (предварительно зашифрованных данным модулем). Для разных языков нами используются различные маски. Эффективность их работы, как правило, превышает показатель 7.9 бит энтропии на один байт. Мы не публикуем конкретные значения этих битовых масок, поскольку это никак не влияет на криптографическую стойкость нашей системы. Такое наше решение не нарушает принцип Керкгоффса, поскольку коды алгоритмов нами здесь полностью опубликованы, а конкретные значения SHUFFLE_SWAP_STEP относятся не к кодам, а скорее — к ключевым данным, сохранять которые в секрете принцип Керкгоффса никому не запрещает.
Что касается процедуры shuffle_rol (ее обратимость обеспечивает shuffle_ror), то ее добавление в шифр Павленко было вызвано необходимостью нахождения оптимальных параметров для увеличения энтропии абстрактной последовательности байтов на любом европейском языке. Наши предварительные исследования выявили ряд оптимальных значений SHUFFLE_SWAP_STEP_1 и SHUFFLE_SWAP_STEP_2, которые создавали максимальные значения энтропии для итогового текста (подвергнутого воздействию шифра Павленко) в зависимости от языка. Но, оптимальные для одного языка параметры, были не оптимальными для другого. Именно для решения этой проблемы мы и добавили в наш алгоритм эти процедуры (shuffle_rol и shuffle_ror). В результате этого, конечная энтропия любого текста на любом языке (после обработки шифром Павленко) при любых параметрах, оптимальных для другого языка, ощутимо превысила показатель 7.9 бит на байт (что практически идентично чисто случайной последовательности). И, в данной ситуации, после добавления этих процедур, стало ясно, что практического значения выбор специфических для конкретного языка значений параметров SHUFFLE_SWAP_STEP_1 и SHUFFLE_SWAP_STEP_2 уже не имеет практического значения. В частности, значения подобранные нами ранее для английского языка — великолепно работают и для всех остальных европейских языков, включая кириллицу (русский и украинский).
Желающие проверить работу этого алгоритма могут, например, воспользоваться такими значениями SHUFFLE_SWAP_STEP_1 и SHUFFLE_SWAP_STEP_2 как 0xC3 и 0x3C. В наших экспериментах эти (чисто случайные, и отнюдь не самые оптимальные) параметры обеспечили энтропию 7.97 бит на байт для текстов на всех языках данного сайта (русский, украинский, английский и т. д.) и стандартное среднеквадратичное отклонение меньше 7 для текста длиной 8192 байта (средняя частота встречаемости байт в этом случае 8192:256=32). Это действительно можно назвать превращением осмысленного текста на любом европейском языке в последовательность абсолютно случайных символов.
Естественно, что у любой пары (или компании) абонентов, общающихся между собой через Extra Systems Cypher Net, файл "shuffle.encrypt" должен быть идентичным. Изначально абоненты получают эти файлы от нас, но, как уже отмечалось выше, могут и самостоятельно их создавать и дополнительно перетасовывать. Важно лишь, чтобы они в этом случае, для обеспечения идентичности всех комплектов, физически их передали своим партнерам (при личной встрече или через доверенного курьера).
Контент этой страницы доступен также на английском, французском, немецком, португальском, испанском, итальянском и украинском языке.
© Extra Systems, 2024 |
|