Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практическая криптография алгоритмы и их программирование
.pdf
Глава 7. Прикладные задачи шифрования 231
long cipher_type;
long body_size;
uchar rc4_key[RC4_KEYSIZE];
} SCR_HEAD;
typedef struct RC4_KEY_t {
uchar state[256];
uchar a_reg;
uchar b_reg;
} RC4_KEY;
static void xchg_bytes(uchar *a, uchar *b) {
register uchar temp_reg;
temp_reg = *a;
*a = *b;
*b = temp_reg;
}
, алгоритм RC4 - разворачивание ключей
void setup_rc4_key(RC4_KEY *key, uchar *pass, int passlen) {
uchar *state = &key->state[0];
uchara=0,b=0;
int i;
for (i = 0; i < 256; i++) key->state[i] = i;
key->a_reg = 0;
key->b_reg = 0;
for (i = 0; i < 256; i++) {
b = pass[a] + state[i] + b;
xchg_bytes(state+i, state+b);
a = (a + 1) % passlen;
}
}
, алгоритм RC4 - шифрование
void encode_rc4(uchar *buf, int buflen, RC4_KEY *key) {
uchar a_reg = key->a_reg, b_reg = key->b_reg;
uchar xor, *state = &key->state[0];
int i;
for (i = 0; i < buflen; i++) {
a_reg++;
b_reg = (state[a_reg] + b_reg);
xchg_bytes(&state[a_reg], &state[b_reg]);
xor = state[a_reg] + state[b_reg];
buf[i] ^= state[xor];
}
key->a_reg = a_reg;
key->b_reg = b_reg;
}
, собственно, процедура расшифрования и обработки исходного текста
static I32 decipher(int idx, SV *buf_sv, int maxlen) {
char *nl = "\n";
static first_time = 1;

232 Глава 7. Прикладные задачи шифрования
static int cur_pos = 0;
static int body_size = 0;
static SV *script_sv = NULL;
RC4_KEY rc4_key;
SCR_HEAD *header;
intn=1;
char *outs, *p;
if (first_time) {
, расшифровываем только в первый раз,
, затем только обрабатываем
first_time--;
, инициализируем поток
script_sv = FILTER_DATA(idx);
SvPVX(script_sv)[0] = '\0';
SvCUR_set(script_sv, 0);
, читаем и проверяем заголовок
n = FILTER_READ(idx + 1, script_sv, HEADER_SIZE);
if (n < HEADER_SIZE) croak("Data error");
header = (SCR_HEAD *) SvPVX(script_sv);
if (header->ID != RIGHT_ID || header->cipher_type > RC4_CIPHER)
croak("Data incorrect type");
body_size = header->body_size;
, готовим ключ RC4
setup_rc4_key(&rc4_key, header->rc4_key, RC4_KEYSIZE);
, загружаем данные из потока
SvPVX(script_sv)[0] = '\0';
SvCUR_set(script_sv, 0);
while (n > 0) {
, читаем, пока есть силы
n = FILTER_READ(idx + 1, script_sv, SEGMENT_SIZE);
}
, расшифровываем
decode_rc4((uchar *) SvPVX(script_sv), body_size, &rc4_key);
SvPVX(script_sv)[body_size] = '\0';
SvCUR_set(script_sv, body_size);
}
outs = SvPVX(script_sv) + cur_pos;
SvPVX(buf_sv)[0] = '\0';
SvCUR_set(buf_sv, 0);
n = body_size - cur_pos;
if (n <= 0) return 0;
if (maxlen) {
sv_catpvn(buf_sv, outs, maxlen>n?n:maxlen);
cur_pos += maxlen;
return SvCUR(buf_sv);
}
else {
if (p = ninstr(outs, outs+n-1,nl,nl)) {

Глава 7. Прикладные задачи шифрования 233
, catched EOL
sv_catpvn(buf_sv, outs,p-outs+1);
cur_pos += (p - outs + 1);
return SvCUR(buf_sv);
}
else
sv_catpvn(buf_sv, outs, n);
}
filter_del(decipher);, отсоединяемся
return SvCUR(buf_sv);, возвращаем исходный текст интерпретатору
}
MODULE = scripher PACKAGE = scripher
PROTOTYPES: DISABLE
BOOT:
, проверка, есть ли другие модули...
if (gv_stashpvn("B", 1, FALSE)) croak("core dumped");
, ...если есть, сделать вид, что произошла ошибка
return;
void
import(module)
SV *module = NO_INIT
PPCODE:
{
SV *sv = newSV(SEGMENT_SIZE);
, проверка, работает ли отладчик...
if (PL_perldb) croak("There is a memory leak because of perl debugger.");
, ...если есть, сделать вид, что произошла ошибка из-за него
else filter_add(decipher, sv);
}
void
unimport(...)
PPCODE:
{
filter_del(decipher);
}
Встроенные средства Perl API позволяют также определить, запущен ли
скрипт под отладчиком в целях исследования его кода, а также наличие и количество других фильтров исходных текстов (см. завершающую часть листинга 7.16).
Это позволяет в какой-то степени отслеживать попытки прогона программы под
отладчиком PerlDB и попытки создания копий исходных текстов с помощью дополнительных модулей.
В результате компиляции программы из листинга 7.16 получается семикилобайтовый модуль scripher.so с реализованным алгоритмом RC4 и необходимыми
средствами обработки исходного текста. Для работы он размещается в том же каталоге, что и зашифрованный файл сценария. Таким образом, для передачи сценария
необходимо передать два дополнительных файла (которые являются общими для
всех зашифрованных сценариев этого типа), а именно scripher.pm и scripher.so.

234 Глава 7. Прикладные задачи шифрования
Для создания скрипта, содержащего зашифрованный сценарий-скрипт и
ключ, можно воспользоваться утилитой из листинга 7.17, которая просто соединяет различные файлы вместе.
Листинг 7.17
#!/usr/bin/perl -w
$RC4_KEYSIZE = 32;
print "Scripher Enveloper\n\n";
$HEADER = "L L L";
$ID = 0xDEADFACE;
$VERSION = 0xC4C4C401;
if (@ARGV < 2) {
print "Usage: $0 envelope_file path_to_scripher_files\n\n";
exit;
}
$SCRIPT_HEADER = "#!/usr/bin/perl -w\nuse lib '$ARGV[1]';\nuse scripher;\n";
unless (-s $ARGV[0]) {
print "Can't find or zeroed file $ARGV[0]";
exit;
}
open F, "<$ARGV[0].coded";
binmode(F);
read(F, $DATA, (-s $ARGV[0]));
close(F);
open F, "<$ARGV[0].pass";
binmode(F);
read(F, $KEY, $RC4_KEYSIZE);
close(F);
$BODY_SIZE = length($DATA);
open F, ">$ARGV[0].pl";
print F $SCRIPT_HEADER;
print F pack($HEADER, $ID, $VERSION, $BODY_SIZE);
print F $KEY;
print F $DATA;
close F;
print "Well done.\n\n";
Использовать модуль защиты можно следующим образом. На языке Perl пи-
шем произвольный сценарий-скрипт под именем, например, test (см. листинг
7.18), в который íå включаем первую стандартную строчку #/path/perl -options.
Она будет создана автоматически при зашифровании исходного текста.
Листинг 7.18
print "OK-HELLO\n" for (1..10);
print "HELLO!\n";
print øpsø;

Глава 7. Прикладные задачи шифрования 235
После работы программы зашифрования исходного текста с алгоритмом RC4
получим несколько файлов: test.pass è test.coded. Используя программу из листинга 7.17, получим готовый к использованию скрипт test.pl приблизительно следующего вида (см. листинг 7.19). Исполнять же его можно по-прежнему, стандартным образом, с помощью команды perl test.pl.
Листинг 7.19
В качестве дополнительного препятствия к анализу исходного текста сценария можно еще отметить метод смысловых значений идентификаторов или source
mangling. Будучи пропущенным через своего рода конвертер, скрипт-сценарий
приобретает практически нечитаемый и невероятно неудобный для анализа вид.
Например, при использовании source mangling текст такого сценария:
#!/usr/bin/perl
# randomize source file
# lines are output in random order
# reads from from stdin or arg0, writes to stdout or arg1
open (STDIN, $ARGV[0]) if (($ARGV[0] ne "") && ($ARGV[0] ne "-"));
open (STDOUT, ">$ARGV[1]") if ($ARGV[1] ne "");
@list = <STDIN>;
while (@list) {
$rand = rand (@list);
print $list[$rand];
plice @list, $rand, 1;
}
превратится в нечто подобное:
#!/usr/bin/perl
open (STDIN,$ARGV[0])if (($ARGV[0] ne "")&&($ARGV[0] ne "-"));open
(STDOUT, ">$ARGV[1]")if ($ARGV[1] ne "");@110202012_as=<STDIN>;
while(@110202012_as){$qewre7_434=rand(@l110202012_as);print
$110202012_as[$qewre7_434];splice@110202012_as,$qewre7_434,1;}
Ранее искажение исходных текстов было весьма популярно — достаточно
вспомнить распространяемые исходные тексты на языке Паскаль к пакетам Turbo
Power для Borland Pascal 7.0. Искаженные подобным образом, они тем не менее

236 Глава 7. Прикладные задачи шифрования
оставались годными к компиляции и линковке. Возможно, что с развитием сценарного программирования source mangling приобретет вторую молодость.
Исследование программного обеспечения,
реализующего криптографические алгоритмы
Одной из наиболее захватывающих тем, касающихся современного программирования и криптографии, является тема исследования программ и особенно реализаций криптографических алгоритмов. Подобный верификационный анализ
необходим для того, чтобы удостовериться в правильности работы реализации
криптографического алгоритма и соответственно отсутствия каких-либо неточностей, которые могут повлиять на криптостойкость используемых методов закрытия информации.
Попробуем провести подобный анализ на примере программы, реализующей
функции шифрованного диска — PGPdisk для Windows 9x/NT/W2K из пакета
программ PGP.
Суть такой программы заключается в создании прозрачного для пользователя
и системы способа шифрования и управления информацией, хранимой на виртуальном диске, содержимое которого зашифровывается и расшифровывается, что
называется, «на лету», то есть непосредственно во время доступа к данным.
Для этого создатели PGPdisk используют стандартные средства и интерфейсы, предоставляемые операционной системой Windows. Схема организации доступа к данным, к примеру, в системе Windows NT представлена на рис. 7.1.
Ðèñ. 7.1

Глава 7. Прикладные задачи шифрования 237
Доступ к виртуальному шифрованному диску реализован через драйвер
устройства ввода-вывода. На каждый пользовательский вызов реагирует драйвер
дискового устройства, входящий в комплект программ PGPdisk (рис. 7.2). Он соответственно запросу расшифровывает или зашифровывает определенные участки
памяти накопителя и передает данные либо от пользователя, либо к пользователю.
Ðèñ. 7.2
Пакет программного обеспечения обычно распространяется без исходных
текстов, однако на сайте производителя (www.pgpi.com) есть возможность скачать их отдельно. Для того чтобы исследовать применяемые в PGPdisk алгоритмы, они нам понадобятся, поэтому рекомендуем их скачать с сайта или взять с
компакт-диска, прилагаемого к данной книге.
Диалоговое окно создания нового диска в данном пакете представлено на рис. 7.3.
Механизм шифрования данных на виртуальном диске основан на использовании алгоритма симметричного шифрования CAST-128 длиной блока 128 битов.
Данный алгоритм основан на архитектуре Файстеля и наследует те же принципы
построения, что и DES и ГОСТ 28147—89. Его реализация приведена в листинге
7.20 (в исходном тексте листинга отсутствуют полные описания узлов замен алгоритма из-за занимаемого ими большого объема).
Листинг 7.20
#include "unidef.h"
#include "memory.h"
, Test examples:
, KEY = 0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78,
, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A;
, TEXT = 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF;
, EXPECT = 0x23, 0x8B, 0x4F, 0xE5, 0x84, 0x7E, 0x44, 0xB2;

238 Глава 7. Прикладные задачи шифрования
Ðèñ. 7.3
typedef struct {
uint32 master[32];
uint32 regs[4];
int empty;
} CAST128_KEY;
void setup_cast128(CAST128_KEY *, uint8 *, int);
void encode_cast128(CAST128_KEY *, uint32 *, uint32 *);
void decode_cast128(CAST128_KEY *, uint32 *, uint32 *);
void encode_cast128_ecb(CAST128_KEY *, uint32 *, int);
void decode_cast128_ecb(CAST128_KEY *, uint32 *, int);
...
#define B0(x) (((x) >> 24) & 255)
#define B1(x) (((x) >> 16) & 255)
#define B2(x) (((x) >> 8) & 255)
#define B3(x) ((x) & 255)
#define x0 B0(ix0)
#define x1 B1(ix0)
#define x2 B2(ix0)
#define x3 B3(ix0)
#define x4 B0(ix1)
#define x5 B1(ix1)
#define x6 B2(ix1)
#define x7 B3(ix1)
#define x8 B0(ix2)

Глава 7. Прикладные задачи шифрования 239
#define x9 B1(ix2)
#define xA B2(ix2)
#define xB B3(ix2)
#define xC B0(ix3)
#define xD B1(ix3)
#define xE B2(ix3)
#define xF B3(ix3)
#define z0 B0(iz0)
#define z1 B1(iz0)
#define z2 B2(iz0)
#define z3 B3(iz0)
#define z4 B0(iz1)
#define z5 B1(iz1)
#define z6 B2(iz1)
#define z7 B3(iz1)
#define z8 B0(iz2)
#define z9 B1(iz2)
#define zA B2(iz2)
#define zB B3(iz2)
#define zC B0(iz3)
#define zD B1(iz3)
#define zE B2(iz3)
#define zF B3(iz3)
#define rcl(x,r) (((x) << (r)) | ((x) >> (32-(r))))
#define f1(x, key, i) (rcl(key[2 * (i)] + (x), key[2*(i)+1]))
#define f2(x, key, i) (rcl(key[2 * (i)] ^ (x), key[2*(i)+1]))
#define f3(x, key, i) (rcl(key[2 * (i)] - (x), key[2*(i)+1]))
#define g1(x) (((S1[B0(x)] ^ S2[B1(x)]) - S3[B2(x)]) + S4[B3(x)])
#define g2(x) (((S1[B0(x)] - S2[B1(x)]) + S3[B2(x)]) ^ S4[B3(x)])
#define g3(x) (((S1[B0(x)] + S2[B1(x)]) ^ S3[B2(x)]) - S4[B3(x)])
uint32 S1[256] = {
...
};
...
uint32 S7[256] = {
...
};
uint32 S8[256] = {
...
};
#define prepare_keys(ix0, ix1, ix2, ix3, iz0, iz1, iz2, iz3) \
do { \
(iz0) = (ix0) ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8]; \
(iz1) = (ix2) ^ S5[z0] ^ S6[z2] ^ S7[z1] ^ S8[z3] ^ S8[xA]; \
(iz2) = (ix3) ^ S5[z7] ^ S6[z6] ^ S7[z5] ^ S8[z4] ^ S5[x9]; \

240 Глава 7. Прикладные задачи шифрования
(iz3) = (ix1) ^ S5[zA] ^ S6[z9] ^ S7[zB] ^ S8[z8] ^ S6[xB]; \
\
(ix0) = (iz2) ^ S5[z5] ^ S6[z7] ^ S7[z4] ^ S8[z6] ^ S7[z0]; \
(ix1) = (iz0) ^ S5[x0] ^ S6[x2] ^ S7[x1] ^ S8[x3] ^ S8[z2]; \
(ix2) = (iz1) ^ S5[x7] ^ S6[x6] ^ S7[x5] ^ S8[x4] ^ S5[z1]; \
(ix3) = (iz3) ^ S5[xA] ^ S6[x9] ^ S7[xB] ^ S8[x8] ^ S6[z3]; \
} while(0)
void setup_cast128(CAST128_KEY *key, uint8 *pass, int passlen)
{
uint8 km[16];
uint32 ix0, ix1, ix2, ix3;
uint32 iz0, iz1, iz2, iz3;
if (passlen > 16) passlen = 16;
memset(&km, 0, sizeof(km));
memcpy(&km, pass, passlen);
ix0 = ((uint32) km[0] << 24) | ((uint32) km[1] << 16) | ((uint32) km[2] << 8) | km[3];
ix1 = ((uint32) km[4] << 24) | ((uint32) km[5] << 16) | ((uint32) km[6] << 8) | km[7];
ix2 = ((uint32) km[8] << 24) | ((uint32) km[9] << 16) | ((uint32) km[10] << 8) | km[11];
ix3 = ((uint32) km[12] << 24)|((uint32) km[13] << 16) | ((uint32) km[14] << 8) | km[15];
prepare_keys(ix0, ix1, ix2, ix3, iz0, iz1, iz2, iz3);
key->master[0] = (S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]);
key->master[2] = (S5[zA] ^ S6[zB] ^ S7[z5] ^ S8[z4] ^ S6[z6]);
key->master[4] = (S5[zC] ^ S6[zD] ^ S7[z3] ^ S8[z2] ^ S7[z9]);
key->master[6] = (S5[zE] ^ S6[zF] ^ S7[z1] ^ S8[z0] ^ S8[zC]);
key->master[8] = (S5[x3] ^ S6[x2] ^ S7[xC] ^ S8[xD] ^ S5[x8]);
key->master[10] = (S5[x1] ^ S6[x0] ^ S7[xE] ^ S8[xF] ^ S6[xD]);
key->master[12] = (S5[x7] ^ S6[x6] ^ S7[x8] ^ S8[x9] ^ S7[x3]);
key->master[14] = (S5[x5] ^ S6[x4] ^ S7[xA] ^ S8[xB] ^ S8[x7]);
prepare_keys(ix0, ix1, ix2, ix3, iz0, iz1, iz2, iz3);
key->master[16] = (S5[z3] ^ S6[z2] ^ S7[zC] ^ S8[zD] ^ S5[z9]);
key->master[18] = (S5[z1] ^ S6[z0] ^ S7[zE] ^ S8[zF] ^ S6[zC]);
key->master[20] = (S5[z7] ^ S6[z6] ^ S7[z8] ^ S8[z9] ^ S7[z2]);
key->master[22] = (S5[z5] ^ S6[z4] ^ S7[zA] ^ S8[zB] ^ S8[z6]);
key->master[24] = (S5[x8] ^ S6[x9] ^ S7[x7] ^ S8[x6] ^ S5[x3]);
key->master[26] = (S5[xA] ^ S6[xB] ^ S7[x5] ^ S8[x4] ^ S6[x7]);
key->master[28] = (S5[xC] ^ S6[xD] ^ S7[x3] ^ S8[x2] ^ S7[x8]);
key->master[30] = (S5[xE] ^ S6[xF] ^ S7[x1] ^ S8[x0] ^ S8[xD]);
prepare_keys(ix0, ix1, ix2, ix3, iz0, iz1, iz2, iz3);
key->master[1] = ((S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2]) & 31);
key->master[3] = ((S5[zA] ^ S6[zB] ^ S7[z5] ^ S8[z4] ^ S6[z6]) & 31);
key->master[5] = ((S5[zC] ^ S6[zD] ^ S7[z3] ^ S8[z2] ^ S7[z9]) & 31);
key->master[7] = ((S5[zE] ^ S6[zF] ^ S7[z1] ^ S8[z0] ^ S8[zC]) & 31);
key->master[9] = ((S5[x3] ^ S6[x2] ^ S7[xC] ^ S8[xD] ^ S5[x8]) & 31);
key->master[11] = ((S5[x1] ^ S6[x0] ^ S7[xE] ^ S8[xF] ^ S6[xD]) & 31);
key->master[13] = ((S5[x7] ^ S6[x6] ^ S7[x8] ^ S8[x9] ^ S7[x3]) & 31);
key->master[15] = ((S5[x5] ^ S6[x4] ^ S7[xA] ^ S8[xB] ^ S8[x7]) & 31);
prepare_keys(ix0, ix1, ix2, ix3, iz0, iz1, iz2, iz3);
key->master[17] = ((S5[z3] ^ S6[z2] ^ S7[zC] ^ S8[zD] ^ S5[z9]) & 31);
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
