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

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

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
Глава 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);
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]