Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Практическая криптография алгоритмы и их программирование

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
Глава 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 величин заняты для обозначения байтов), а затем вывести достаточное количество битов underf­low_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 develop­ment of arithmetic coding, since the possibility of coding ele­ments 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 байтов свободно
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]