Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практическая криптография алгоритмы и их программирование
.pdf
Глава 6. Кpиптогpафическое сжатие 201
лать это — обеспечить шиpину рабочего интеpвала равной величине, не меньшей
max_aacfreq — максимальное значение суммы всех накапливаемых частот.
Если high_aac и low_aac располагаются настолько близко друг к другу, что
выполняются неравенства quarter1 <= low_aac < half <= high_aac < quarter3, то
следующие два бита вывода будут противоположны один другому — если первый
бит нулевой, то второй будет единичным, и наоборот. Это выполнено потому, что
вычисляемый интеpвал должен pасполагаться выше или соответственно ниже
сpедней точки pабочего интеpвала.
Запомнив в underflow_aac, что нам необходимо вывести дополнительный бит после текущего (постараемся запомнить также и его), мы можем безопасно pасшиpить
рабочий интервал впpаво. Если после этой сложной операции неравенства все равно
остаются верными, мы можем провести ее еще раз и т. д., пока не добьемся нужного
нам резльутата. Единственное, что нам нужно помнить в этой ситуации, — это количество дополнительных битов, которые мы должны вывести в поток.
Листинг 6.6
// обновление модели приближения текста
void updatemodel_aac(int ch)
{
int i;
// В случае переполнения значений частот необходимо
// провести масштабирование
if (accumulate_aac[0] >= maxaac_freq)
{
int cum;
cum=0;
for (i = 257; i >= 0; i--)
{
// мы просто делим все частоты пополам
aac_freq[i] = (aac_freq[i] + 1) >> 1;
accumulate_aac[i] = cum;
cum += aac_freq[i];
}
}
// перестановка символов с большими частотами в начало
// таблицы вероятностей
for (i = ch; aac_freq[i] == aac_freq[i - 1]; i--);
if (i < ch) {
int ch_i, ch_symbol;
ch_i = index2sym_aac[i];
ch_symbol = index2sym_aac[ch];
index2sym_aac[i] = ch_symbol;
index2sym_aac[ch] = ch_i;
sym2index_aac[ch_i] = ch;
sym2index_aac[ch_symbol] = i;
}
aac_freq[i]++;
while (i-- > 0) accumulate_aac[i]++;
}

202 Глава 6. Кpиптогpафическое сжатие
В режиме декодировщика все аналогично. Постепенный ввод битового потока
сжатого текста осуществляется с помощью специального регистра value. Обработанные биты постепенно перемещаются в его старшую часть, а в младшую постоянно поступают новые. После их обработки кодировщик избавляется от одинаковых битов, выводя их в поток и замещая их в регистре более младшими битами.
Листинг 6.7
// декомпрессия текста из битового потока
void decompress_aac(char *buf, int32_t max, BITFILE *f)
{
int32_t range, ofs = 0;
int i, cum, symbol;
uint16_t value;
if (!buf) return;
for (i = 0; i < 257; i++)
{
// заполняем единичками частоты, чтобы избежать
aac_freq[i] = 1;
accumulate_aac[i] = 257 - i;
}
aac_freq[0] = 0;
accumulate_aac[256] = 0;
// инициализация таблиц
for(i=257;i>0;i--)
accumulate_aac[i - 1] = accumulate_aac[i] + aac_freq[i - 1];
for (i = 0; i < 255; i++)
{
sym2index_aac[i] = i+1;
index2sym_aac[i+1] = i;
}
// заполняем рабочее значение
value = 0;
for (i = 0; i < 16; i++)
value = (uint16_t) ((value << 1) | bfreadbit(f));
low_aac = 0;
high_aac = highvalue_aac;
underflow_aac = 0;
// следим за тем, чтобы буфер при декомпрессии
// не переполнился
while (ofs < max)
{
// вычисляем ширину рабочего интервала
range = high_aac - low_aac + 1;
// вычисляем вспомогательные величины
cum = ( (value - low_aac + 1) * accumulate_aac[0] - 1) / range;
for (symbol = 1; accumulate_aac[symbol] > cum; symbol++);

Глава 6. Кpиптогpафическое сжатие 203
// вычисляем границы рабочего интервала
high_aac = (uint16_t) (low_aac +
(range * accumulate_aac[symbol - 1]) / accumulate_aac[0] - 1);
low_aac = (uint16_t) (low_aac +
(range * accumulate_aac[symbol]) / accumulate_aac[0]);
for (;;)
{
// если границы рабочего интервала слишком близки,
// масштабируем их, если все в порядке, то сужаем интервал
if (high_aac < quarter2_aac)
{
}
else if (low_aac >= quarter2_aac)
{
value -= (uint16_t) quarter2_aac;
low_aac -= (uint16_t) quarter2_aac;
high_aac -= (uint16_t) quarter2_aac;
}
else if (low_aac >= quarter1_aac && high_aac < quarter3_aac)
{
value -= (uint16_t) quarter1_aac;
low_aac -= (uint16_t) quarter1_aac;
high_aac -= (uint16_t) quarter1_aac;
}
else break;
// масштабируем рабочий интервал
low_aac <<= 1;
high_aac = (uint16_t) ((high_aac << 1) | 1);
value = (uint16_t) ((value << 1) | bfreadbit(f));
}
// если появился символ EOF, останавливаемся
if (symbol == 256) break;
buf[ofs++] = (INT8) index2sym_aac[symbol];
// обновляем модель приближения текста
updatemodel_aac(symbol);
}
}
Пpи завеpшении пpоцесса сжатия необходимо вывести уникальный завеpшающий символ, равный целой величине 256 (предыдущие 255 величин заняты для
обозначения байтов), а затем вывести достаточное количество битов underflow_aac, чтобы убедиться в том, что закодиpованная стpока попадет в итоговый
pабочий интеpвал. Декодировщик, обнаружив символ завершения процесса, также остановит свою работу.
Чтобы обеспечить более эффективную реализацию алгоритма арифметического кодирования, можно проделать ряд модификаций, начиная от оптимизации
алгоритма и заканчивая оптимизацией описанного исходного текста. Например,
более эффективный, хоть и сложный путь вычисления вероятностей символов лежит чеpез определение их зависимости от предыдущего символа. Подобная мар
-

204 Глава 6. Кpиптогpафическое сжатие
ковская модель приближения текста даст лучшее распределение частот встречаемости символов в тексте, а вместе с ним и более эффективное сжатие.
Дополнительно к этому программа, приведенная в листингах этой главы, оперирует с 32-битовыми регистрами, что влечет за собой на необходимость довольно частого масштабирования и довольно грубый учет вероятностей появления
символов и соответственно на представление самих символов. А значит, снова
влияет на эффективность сжатия. Впрочем, за поиском существенных методов
оптимизации алгоритма отправим дотошного читателя в раздел списка литературы к данной главе, а сами снова обратимся к идее криптографического сжатия.
Криптографическое сжатие
The constraint of the integral number of bits had probably been
considered as obvious, in real applications, prior to the development of arithmetic coding, since the possibility of coding elements in fractional bits is quite surprising. Therefore Huffman
codes enjoyed widespread popularity in the four decades since
their invention.
A. Booksieinl, S. T. Klein, T. Raita.
Is Huffman Coding Dead?
Подойдя вплотную непосредственно к задаче формирования кодов и собственно кодирования-сжатия текста, не будем забывать, что нашей начальной целью было исследование алгоритмов сжатия с целью применения в них каких-либо
криптографических преобразований. Уже сейчас можно выделить моделирование-приближение текста как достаточно интересный объект именно для криптографического применения. Кодировщик использует модель для сжатия текста и
представления его в виде кодов сжатия. Очевидно, что для правильной декомпрессии декодировщик должен обладать точно такой же моделью текста. Совершенно очевидно, что в самом простом варианте мы можем использовать таблицу
вероятностей появления символов в качестве секретного ключа. Однако мы сразу
упираемся в следующие отрицательные стороны использования данного подхода:
•
довольно большой по объему ключ (в пересчете на биты это 2048-битовый
ключ), который не везде сможет использоваться;
•
к тому же на самом деле используется не все ключевое пространство,
например, использование ключа с нулевыми компонентами просто исключено;
•
использование случайного ключа сильно влияет на эффективность сжатия
в худшую сторону, сводя на нет преимущества алгоритма сжатия;
•
использование неслучайного ключа чревато возможностью создания быстрого алгоритма перебора для взлома.
Следовательно, использовать в качестве ключа таблицу распределения вероятностей символов или таблицу частот их появления нельзя. Один из выходов в
сложившейся ситуации заключается в модификации алгоритма сжатия привнесе
-

Глава 6. Кpиптогpафическое сжатие 205
нием в него таких криптографических примитивов, как подстановки и перестановки символов текста перед тем, как подать их на вход собственно кодировщику.
В этом случае псевдокод адаптивного алгоритма криптографического сжатия
получается из псевдокода обычного адаптивного алгоритма сжатия данных путем
несложных изменений (см. листинг 6.8).
Листинг 6.8
1. Взять некоторую начальную таблицу вероятностей P0.
2. Текущей моделью P сделать P0.
3. Получить символ C текста.
4. Применить подстановку или перестановку к символуC-получить новый символ C'.
5. Закодировать полученный символ C' текста в соответствии с моделью P.
6. Модифицировать модельPспомощьюсимвола C.
7. Перейти к п.3 или остановиться, если текст закончился.
В то же время из-за того, что мы изменяем символ C с помощью подстановки
или перестановки, кодировщик будет получать совсем не те символы, на которые
рассчитана модель, а следовательно, говорить о каком-либо сжатии уже попросту не приходится. Проблему можно решить, модифицируя модель в зависимости от результата подстановки/перестановки — символа C'. Но в этом случае
все криптографическое сжатие окажется ни чем иным, как сжатием текста, зашифрованного полиалфавитной заменой. То есть можно сказать, идея криптографического сжатия является более общим случаем алгоритма, шифрующего текст
с помощью какого-либо криптографического преобразования и «одновременно»
сжимающего его.
Вернемся, однако, к практике. Понятно, что предложенный в листинге 6.8 вариант криптографического сжатия не годен для практического применения. Мы,
конечно, можем немного модифицировать его, подействовав выбранной нами
криптографической функцией на код символа C, то есть уже после его обработки
кодировщиком (см. листинг 6.9).
Листинг 6.9
1. Взять некоторую начальную таблицу вероятностей P0.
2. Текущей моделью P сделать P0.
3. Получить символ C текста.
4. Применить подстановку или перестановку к символуC-получить новый символ C'.
5. Закодировать полученный символ C' текста в соответствии с моделью P.
6. Модифицировать модельPспомощьюсимвола C.
7. Перейти к п.3 или остановиться, если текст закончился.
Однако на этом пути, похоже, нет более эффективных решений, чем применение эффективного сжимающего преобразования, а затем заведомо стойкого
криптоалгоритма.
Очевидно, что все предложенные выше способы не являются достаточно эффективными, чтобы создавать ради их реализации дополнительные криптографические протоколы. Выигрыш от их использования, к сожалению, не велик, и овчинка, что называется, не стоит выделки.

206 Глава 6. Кpиптогpафическое сжатие
С другой стороны, мы властны внести существенные изменения в сам процесс кодирования-декодирования, поставив именно его в зависимость от ключа
системы. Можно попробовать модифицировать непосредственно сам алгоритм
сжатия. Посмотрим на его реализацию внимательнее. Массивы sym2index и
index2sym хранят информацию о том, какой символ исходного алфавита соответствует какому порядковому номеру отрезка кодового пространства. Они созданы
для того, чтобы наиболее часто используемые символы смещались в начало кодового пространства, а менее часто используемые — в конец. В этом случае декодировщик, чтобы найти нужный символ, будет совершать меньше операций за счет
того, что наиболее вероятные символы находятся ближе к месту начала поиска
декодировщиком соответствующего символа.
В процессе кодирования массивы изменяются, символы передвигаются по кодовому пространству, обеспечивая в будущем эффективное по времени декодирование
сжатого текста. Но вместе с изменением расположения отрезков, соответствующих
символам, в кодовом пространстве изменяются также и представления сжатых символов, то есть в наших терминах — шифробозначения. Например, передвинув символ «В» из конца кодового пространства в начало, кодирующее значение может выглядеть не как запланированное изначально 0.9568894, а 0.002389. Соответственно
меняются и биты, кодирующие наше результирующее значение. Вдобавок в адаптивном алгоритме все меняется динамически после изменения каждого символа.
Перед нами встает вопрос, который некогда задал себе Хорст Файстель: каким именно образом мы можем организовать зависимость алгоритма от некоторого ключа? Файстель воспользовался подстановками и перестановками, сделав
применение первых зависимым от значения очередного бита ключа. Он использовал две различные, заранее подготовленные таблицы подстановок S0 и S1. И в зависимости от того, равен ли очередной для итерации шифра и шифруемого блока
бит ключа единице или нулю, выбирал соответствующую таблицу и применял
подстановку к шифруемым данным.
Ничто не мешает поступить нам подобным же образом и в нашем случае. Мы
воспользуемся тем, что при изменении sym2index и index2sym изменяется также
и битовое представление кодов символов. Еще одним положительным моментом
этой модификации алгоритма является то, что, сколько бы ни переставляли символы внутри кодового пространства, мы никогда не увеличиваем длину кодов, а
значит, не ухудшаем сжимающие характеристики алгоритма. Таким образом, и
получаем самое настоящее криптографическое сжатие.
Дополнительно к этому можно «возмущать» модель приближения текста, модифицируя ее дополнительно в зависимости от ключа, а не от текста. Например,
мы можем приближать модель текста так, как будто каждый второй символ текста является символом из ключевой последовательности — правильно сформированная по ключу псевдослучайная последовательность символов будет довольно
сильно искажать модель приближения сжимаемого текста и как следствие менять
представление сжатого текста, то есть шифротекст.
Правда, в отличие от безобидной перетасовки отрезков в кодовом пространстве, изменение модели в зависимости не только от текста, но еще и от ключа делает сжатие менее эффективным, поскольку моделировщик может выделить до
-

Глава 6. Кpиптогpафическое сжатие 207
вольно много места совсем неиспользуемым символам, а кодировщик затем собьется при кодировании на лишних символах.
Листинг 6.10
#include "p_table.h"
void rotate_key(char *key) {
int z, i, s;
s=z=key[15];
for(i=15;i>0;i--) {
key[i] = key[i-1] ^ ((i*z) & 0xFF);
if (key[i] & 0x01 != 0) s += (s*i + key[i]) & 0xFF;
}
key[0] = (z+s+1)&0xFF;
}
void rotate_index(char *key) {
int i, b, k, q;
for (i = 0; i < 256; i++) {
b=i>>4;
k=i-(b<<4);
if ((key[b] >> k) & 0x01 != 0) {
if (key[b] > 128) {
q = sym2index_aac[P[i]];
sym2index_aac[P[i]] = sym2index_aac[i];
sym2index_aac[i] = q;
q = index2sym_aac[P[i]];
index2sym_aac[P[i]] = index2sym_aac[i];
index2sym_aac[i] = q;
}
else
updatemodel_aac(key[b]);
}
else {
updatemodel_aac(key[b]);
}
}
rotate_key(key);
}
Проиллюстрируем идею модификации алгоритма простым примером (см. листинг 6.10). Длину ключа для использования в нашей модифицированной версии
алгоритма сжатия возьмем равной 128 битам. При выборе длины ключа в нашем
случае можно руководствоваться общими соображениями о вычислительных
мощностях современных компьютеров и конкретными примерами взлома шифров
с длиной ключа меньше указанных 128 битов.
Основную работу по переразбиению кодового пространства выполняет функция rotate_index. Поскольку мы должны быть уверены в том, что после внесения
нашей зависимости изменятся кодовые представления всех символов, модификация соответствующих таблиц проводится целиком — по всем 256 элементам.

208 Глава 6. Кpиптогpафическое сжатие
На каждой итерации алгоритма изменения таблиц используется один бит
ключа. Таким образом, за 256 раз исполнения внешнего цикла в rotate_index все
128 битов ключа просматриваются два раза.
Вспомним алгоритм Файстеля с использованием двух таблиц подстановок S1
и S0. Наш алгоритм оказался организован похоже. В зависимости от того, нулевое или единичное значение принимает текущий бит ключа, используем либо перестановку отрезков без изменения их размеров внутри кодового пространства
(перестановка осуществляется с помощью таблицы перестановок P), либо изменяем саму модель с помощью байта ключа, в котором находится текущий бит.
Дополнительно после зашифрования каждого символа алгоритм предусматривает изменение ключа, с тем чтобы одинаковые символы не были представлены
одинаковой последовательностью битов и, следовательно, криптоанализ по открытому тексту был затруднен. За изменение ключа отвечает функция rotate_key.
Авторы тестировали первые несколько тысяч итераций ключа и пришли к выводу,
что предложенный алгоритм генерирует последовательность большей длины, чем
10
2
. Проверить это утверждение авторы предоставляют читателю.
Листинг 6.11
void crypto_compress(char *buf, int32_t len, char *key, BITFILE *f)
{
int i, ch;
for (i = 0; i < 257; i++)
{
aac_freq[i] = 1;
accumulate_aac[i] = 257 - i;
}
aac_freq[0] = 0;
accumulate_aac[256] = 0;
for(i=257;i>0;i--) accumulate_aac[i - 1] = accumulate_aac[i] + aac_freq[i -
1];
for (i = 0; i < 255; i++)
{
sym2index_aac[i] = i+1;
index2sym_aac[i+1] = i;
}
low_aac = 0;
high_aac = highvalue_aac;
underflow_aac = 0;
for (int32_t ofs = 0; ofs < len; ofs++)
{
rotate_index(key);
ch = sym2index_aac[ buf[ofs] ];
encodesymbol_aac(ch, f);
updatemodel_aac(ch);
}
encodesymbol_aac(256, f);

Глава 6. Кpиптогpафическое сжатие 209
underflow_aac++;
if (low_aac < quarter1_aac) {
bfwritebit(0, f);
while (underflow_aac)
{
bfwritebit(1, f);
underflow_aac--;
}
}
else
{
bfwritebit(1, f);
while (underflow_aac)
{
bfwritebit(0, f);
underflow_aac--;
}
}
}
void crypto_decompress(char *buf, int32_t max, BITFILE *f)
{
int32_t range, ofs = 0;
int i, cum, symbol;
uint16_t value;
if (!buf) return;
for (i = 0; i < 257; i++)
{
aac_freq[i] = 1;
accumulate_aac[i] = 257 - i;
}
aac_freq[0] = 0;
accumulate_aac[256] = 0;
for(i=257;i>0;i--)
accumulate_aac[i - 1] = accumulate_aac[i] + aac_freq[i - 1];
for (i = 0; i < 255; i++)
{
sym2index_aac[i] = i+1;
index2sym_aac[i+1] = i;
}
value = 0;
for (i = 0; i < 16; i++)
value = (uint16_t) ((value << 1) | bfreadbit(f));
low_aac = 0;
high_aac = highvalue_aac;
underflow_aac = 0;
while (ofs < max)
{
range = high_aac - low_aac + 1;
cum = ( (value - low_aac + 1) * accumulate_aac[0] - 1) / range;
for (symbol = 1; accumulate_aac[symbol] > cum; symbol++);

210 Глава 6. Кpиптогpафическое сжатие
high_aac = (uint16_t) (low_aac + (range * accumulate_aac[symbol - 1]) / accu
mulate_aac[0] - 1);
low_aac = (uint16_t) (low_aac + (range * accumulate_aac[symbol]) / accumula-
te_aac[0]);
for (;;)
{
if (high_aac < quarter2_aac)
{
}
else if (low_aac >= quarter2_aac)
{
high_aac -= (uint16_t) quarter2_aac;
}
else if (low_aac >= quarter1_aac && high_aac < quarter3_aac)
{
high_aac -= (uint16_t) quarter1_aac;
}
else break;
low_aac <<= 1;
}
if (symbol == 256) break;
buf[ofs++] = (uint8_t) index2sym_aac[symbol];
updatemodel_aac(symbol);
}
}
-
value -= (uint16_t) quarter2_aac;
low_aac -= (uint16_t) quarter2_aac;
value -= (uint16_t) quarter1_aac;
low_aac -= (uint16_t) quarter1_aac;
high_aac = (uint16_t) ((high_aac << 1) | 1);
value = (uint16_t) ((value << 1) | bfreadbit(f));
Соответственно проделанным изменениям функции криптографического сжатия и декомпрессии будут выглядеть так, как показано в листинге 6.11. Предъявленный алгоритм и является нашей искомой целью — действующим алгоритмом
криптографического сжатия. Алгоритм представляет собой реализацию достаточно эффективного способа сжатия данных и зависит от ключа, без которого невозможно будет восстановить исходный текст.
Листинг 6.12
X:\cryptobook\text\data>dir
Том в устройстве X имеет метку SECRETDISK
Серийный номер тома: 242F-24DA
Содержимое папки X:\cryptobook\text\src
26.02.2002 00:53 <DIR> .
26.02.2002 00:53 <DIR> ..
17.03.2002 01:36 521 text.txt
13 файлов 125 368 байтов
2 папок 151 248 896 байтов свободно
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
