Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практическая криптография алгоритмы и их программирование
.pdf
Глава 7. Прикладные задачи шифрования 241
key->master[19] = ((S5[z1] ^ S6[z0] ^ S7[zE] ^ S8[zF] ^ S6[zC]) & 31);
key->master[21] = ((S5[z7] ^ S6[z6] ^ S7[z8] ^ S8[z9] ^ S7[z2]) & 31);
key->master[23] = ((S5[z5] ^ S6[z4] ^ S7[zA] ^ S8[zB] ^ S8[z6]) & 31);
key->master[25] = ((S5[x8] ^ S6[x9] ^ S7[x7] ^ S8[x6] ^ S5[x3]) & 31);
key->master[27] = ((S5[xA] ^ S6[xB] ^ S7[x5] ^ S8[x4] ^ S6[x7]) & 31);
key->master[29] = ((S5[xC] ^ S6[xD] ^ S7[x3] ^ S8[x2] ^ S7[x8]) & 31);
key->master[31] = ((S5[xE] ^ S6[xF] ^ S7[x1] ^ S8[x0] ^ S8[xD]) & 31);
key->regs[0] = key->regs[1] = key->regs[2] = key->regs[3] = 0;
key->empty = 1;
}
#define subencode(key, in, out) \
do { \
L = ((uint32) in[0] << 24) | ((uint32) in[1] << 16) | ((uint32) in[2] << 8) |
in[3]; \
R = ((uint32) in[4] << 24) | ((uint32) in[5] << 16) | ((uint32) in[6] << 8) |
in[7]; \
B = f1(R, key->master, 0); L ^= g1(B); \
B = f2(L, key->master, 1); R ^= g2(B); \
B = f3(R, key->master, 2); L ^= g3(B); \
B = f1(L, key->master, 3); R ^= g1(B); \
B = f2(R, key->master, 4); L ^= g2(B); \
B = f3(L, key->master, 5); R ^= g3(B); \
B = f1(R, key->master, 6); L ^= g1(B); \
B = f2(L, key->master, 7); R ^= g2(B); \
B = f3(R, key->master, 8); L ^= g3(B); \
B = f1(L, key->master, 9); R ^= g1(B); \
B = f2(R, key->master, 10); L ^= g2(B); \
B = f3(L, key->master, 11); R ^= g3(B); \
\
B = f1(R, key->master, 12); L ^= g1(B); \
B = f2(L, key->master, 13); R ^= g2(B); \
B = f3(R, key->master, 14); L ^= g3(B); \
B = f1(L, key->master, 15); R ^= g1(B); \
\
out[0] = (uint8) B0(R); out[1] = (uint8) B1(R); \
out[2] = (uint8) B2(R); out[3] = (uint8) B3(R); \
out[4] = (uint8) B0(L); out[5] = (uint8) B1(L); \
out[6] = (uint8) B2(L); out[7] = (uint8) B3(L); \
} while(0)
#define subdecode(key, in, out) \
do { \
R = ((uint32) in[0] << 24) | ((uint32) in[1] << 16) | ((uint32) in[2] <<
8) | in[3]; \
L = ((uint32) in[4] << 24) | ((uint32) in[5] << 16) | ((uint32) in[6] <<
8) | in[7]; \
B = f1(L, key->master, 15); R ^= g1(B); \
B = f3(R, key->master, 14); L ^= g3(B); \
B = f2(L, key->master, 13); R ^= g2(B); \
B = f1(R, key->master, 12); L ^= g1(B); \
\
\

242 Глава 7. Прикладные задачи шифрования
\
B = f3(L, key->master, 11); R ^= g3(B); \
B = f2(R, key->master, 10); L ^= g2(B); \
B = f1(L, key->master, 9); R ^= g1(B); \
B = f3(R, key->master, 8); L ^= g3(B); \
B = f2(L, key->master, 7); R ^= g2(B); \
B = f1(R, key->master, 6); L ^= g1(B); \
B = f3(L, key->master, 5); R ^= g3(B); \
B = f2(R, key->master, 4); L ^= g2(B); \
B = f1(L, key->master, 3); R ^= g1(B); \
B = f3(R, key->master, 2); L ^= g3(B); \
B = f2(L, key->master, 1); R ^= g2(B); \
B = f1(R, key->master, 0); L ^= g1(B); \
\
out[0] = (uint8) B0(L); out[1] = (uint8) B1(L); \
out[2] = (uint8) B2(L); out[3] = (uint8) B3(L); \
out[4] = (uint8) B0(R); out[5] = (uint8) B1(R); \
out[6] = (uint8) B2(R); out[7] = (uint8) B3(R); \
} while(0)
void encode_cast128(CAST128_KEY *key, uint32 *in, uint32 *out) {
uint32 L, R, B;
subencode(key, ((uint8 *) in), ((uint8 *) out));
}
void decode_cast128(CAST128_KEY *key, uint32 *in, uint32 *out) {
uint32 L, R, B;
subdecode(key, ((uint8 *) in), ((uint8 *) out));
}
void encode_cast128_ecb(CAST128_KEY *key, uint32 *buf, int buflen) {
uint32 L, R, B;
while (buflen--) {
memcpy(key->regs, buf, sizeof(key->regs));
subencode(key, ((uint8 *) &key->regs), ((uint8 *) &key->regs));
*buf++ = key->regs[0];
*buf++ = key->regs[1];
subencode(key, ((uint8 *) (&key->regs + 2)), ((uint8 *) &key->regs));
*buf++ = key->regs[2];
*buf++ = key->regs[3];
}
}
void decode_cast128_ecb(CAST128_KEY *key, uint32 *buf, int buflen) {
uint32 L, R, B;
while (buflen--) {
memcpy(key->regs, buf, sizeof(key->regs));
subdecode(key, ((uint8 *) &key->regs), ((uint8 *) &key->regs));
*buf++ = key->regs[0];
*buf++ = key->regs[1];
subdecode(key, ((uint8 *) (&key->regs + 2)), ((uint8 *) &key->regs));
*buf++ = key->regs[2];
*buf++ = key->regs[3];
}

Глава 7. Прикладные задачи шифрования 243
}
Зашифрование и расшифрование производится блоками по 128 битов в режиме электронной кодовой книги ECB (режим простой замены в соответствии с
ГОСТ 28147-89) с помощью процедур encode_cast128_ecb() и decode_cast128_ecb(). Для задания ключа используется структура CAST128_KEY, содержащая исходную ключевую последовательность в переменной master и текущие значения регистров шифрования в переменной-массиве regs.
Для аутентификации пользователя и выдачи ему прав на доступ к данным дополнительно используется алгоритм криптографического хэширования SHA-1
(Secure Hash Algorithm). Его реализация представлена в листинге 7.21.
Листинг 7.21
, SHA-1
,
, Test examples:
, INPUT = "abc"
, OUTPUT = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
,
, INPUT = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
, OUTPUT = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
,
, INPUT = 1,000,000 of "a" (15625 blocks by 64 bytes)
, OUTPUT = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
typedef struct {
uint32 h0, h1, h2, h3, h4;
uint32 lo, hi;
} SHA_HASH;
#define MOREDATA 0
#define LASTDATA 1
void setup_sha(SHA_HASH *);
void hash_sha(SHA_HASH *, uint8 *, uint32, int);
#define f0(x,y,z) (z ^ (x & (y ^ z)))
#define f1(x,y,z) (x^y^z)
#define f2(x,y,z) ((x & y) | (z & (x | y)))
#define f3(x,y,z) (x^y^z)
#define K0 0x5a827999
#define K1 0x6ed9eba1
#define K2 0x8f1bbcdc
#define K3 0xca62c1d6
#define S(n, X) ((X << n) | (X >> (32 - n)))
#define r0(f, K) \
do { \
tmp = S(5, A) + f(B, C, D)+E+*p0++ + K; \
E=D; \
D=C; \

244 Глава 7. Прикладные задачи шифрования
C = S(30, B); \
B=A; \
A = tmp; \
} while(0)
#define r1(f, K) \
do { \
tmp = *p1++ ^ *p2++ ^ *p3++ ^ *p4++; \
tmp = S(5, A) + f(B, C, D)+E+(*p0++ = S(1, tmp)) + K; \
E=D; \
D=C; \
C = S(30, B); \
B=A; \
A = tmp; \
} while(0)
void setup_sha(SHA_HASH *sha) {
sha->h0 = 0x67452301;
sha->h1 = 0xefcdab89;
sha->h2 = 0x98badcfe;
sha->h3 = 0x10325476;
sha->h4 = 0xc3d2e1f0;
sha->lo = 0;
sha->hi = 0;
}
#include "stdio.h"
void hash_sha(SHA_HASH *sha, uint8 *data, uint32 len, int finish) {
uint16 i, q, padded, nread, nbits;
uint32 *p0, *p1, *p2, *p3, *p4, tmp;
uint32 A, B, C, D, E;
uint8 buf[320], *endbuf = data + len;
uint32 value;
padded = 0;
nread = 64;
for (;;) {
memcpy(buf, data, 64);
if (data + 64 > endbuf) {
if (!finish) break;
if (data < endbuf)
nread = (uint16) (endbuf - data);
else
nread = 0;
nbits = nread << 3;
sha->lo += nbits;
if (sha->lo < nbits) sha->hi++;
if (nread < 64) {
if (!padded) {
buf[nread++] = 0x80;
padded++;
}
for (i = nread; i < 64; i++) buf[i] = 0;
}

Глава 7. Прикладные задачи шифрования 245
if (nread <= 56) {
((uint32 *) buf)[14] = sha->hi;
((uint32 *) buf)[15] = sha->lo;
reorder_bytes(((uint32 *) buf), 14, value, q);
} else
reorder_bytes(((uint32 *) buf), 16, value, q);
} else {
sha->lo += 512;
if (sha->lo < 512) sha->hi++;
reorder_bytes(((uint32 *) buf), 16, value, q);
}
p0 = ((uint32 *) buf);
A = sha->h0;
B = sha->h1;
C = sha->h2;
D = sha->h3;
E = sha->h4;
r0(f0, K0); r0(f0, K0); r0(f0, K0); r0(f0, K0); r0(f0, K0);
r0(f0, K0); r0(f0, K0); r0(f0, K0); r0(f0, K0); r0(f0, K0);
r0(f0, K0); r0(f0, K0); r0(f0, K0); r0(f0, K0); r0(f0, K0);
r0(f0, K0);
p1 = &((uint32 *) buf)[13];
p2 = &((uint32 *) buf)[8];
p3 = &((uint32 *) buf)[2];
p4 = &((uint32 *) buf)[0];
r1(f0, K0); r1(f0, K0); r1(f0, K0); r1(f0, K0);
r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1);
r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1);
r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1);
r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1); r1(f1, K1);
r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2);
r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2);
r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2);
r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2); r1(f2, K2);
r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3);
r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3);
r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3);
r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3); r1(f3, K3);
sha->h0 += A;
sha->h1 += B;
sha->h2 += C;
sha->h3 += D;
sha->h4 += E;
data += 64;
if (nread == 0 && !finish) break;
if (nread <= 56) break;
}

246 Глава 7. Прикладные задачи шифрования
if (finish) reorder_bytes((uint32 *) sha, 5, value, q);
}
Для того чтобы получить доступ к диску, пользователю необходимо ввести
так называемую парольную фразу произвольной длины (с ограничением, правда,
до 130 символов текста).
Изначально, парольная фраза назначается пользователем при создании диска
(cм. рис. 7.4). Она может (и, вообще говоря, должна!) состоять из нескольких
слов (например, связанного предложения).
Пользователь вводит парольную фразу в специальном окне (см. рис. 7.5), затем она преобразовывается с помощью SHA-1 в фиксированного размера
хэш-сумму. Этим получившимся значением заполняется специальный буфер не-
Ðèñ. 7.4
Ðèñ. 7.5

Глава 7. Прикладные задачи шифрования 247
сколько сотен раз (до 5000). Сколько именно раз происходит заполнение, зависит
от специальной переменной, значение которой устанавливается случайным образом при создании виртуального шифрованного PGP-диска.
После заполнения буфера снова подсчитывается хэш-сумма, но уже от всего
буфера. Полученное хэш-значение подставляется в процедуру разворачивания
ключа CAST-128. Затем с помощью этого шифра расшифровываются контрольные восемь байтов, зашифрованные и записанные при создании диска. Если после
расшифрования они совпадают со значением последнего подсчета хэш-функции
SHA-1, это означает, что парольная фраза была дана верно и пользователь может
подключить диск и получить к нему доступ (см. листинг 7.23).
Листинг 7.22
typedef struct {
uint8 header_magic[4];
uint8 header_type[4];
uint32 header_size;
uint32 header_CRC;
uint64 next_header;
uint32 reserved[2];
} PGPDISK_INFO;
typedef struct {
uint8 encrypted_key[16];
uint8 check_bytes[8];
uint16 hash_reps;
uint16 options;
uint32 reserved;
} PGPDISK_KEY;
typedef struct {
PGPDISK_INFO header_info;
uint8 major_ver;
uint8 minor_ver;
uint16 reserved_a;
uint32 file_blocks;
uint32 header_blocks;
uint32 data_blocks;
uint32 algorithm;
uint8 drive;
bool mounted;
uint64 sessionID;
uint8 reserved_b[52];
uint8 salt[8];
PGPDISK_KEY master_pass;
PGPDISK_KEY other_pass[7];
} PGPDISK;

248 Глава 7. Прикладные задачи шифрования
Каждый PGP-диск физически располагается в одном (обычно большом) файле на одном из жестких дисков компьютера. Первые несколько сотен байтов занимают специальные заголовки, содержащие сведения, необходимые для аутентификации пользователя и шифрования данных (см. листинг 7.22). Подструктура
PGPDISK_INFO в структуре PGPDISK отвечает за информацию, касающуюся физической целостности содержимого виртуального диска и его маркировки.
Наиболее интересной ñ ýòîé точки зрения является подструктура
PGPDISK_KEY, содержащая ключевую информацию. Область encrypted_key содержит зашифрованный ключ для алгоритма CAST128, который после процедуры
аутентификации пользователя используется для шифрования данных на диске.
Область check_bytes представляет описанные ранее контрольные восемь байтов,
а hash_reps — не что иное, как количество повторений хэш-значений парольной
фразы в специальном буфере перед снятием второй хэш-суммы.
Учитывая приведенный выше алгоритм, был разработан небольшой програм-
Ðèñ. 7.6
мный модуль, реализующий проверку парольной фразы и тестированием потенциальной скорости тотального опробования парольных фраз (рис. 7.6).
Программа «PGP check password» работает в трех режимах. Режим «count
speed» позволяет провести подсчет скорости опробования единичной парольной
фразы. Режим «check passphrase» позволяет проверить ее верность, а режим «test
ciphers» проверяет работу функции криптографического хэширования и шифрования. Полные исходные тексты читатель может найти на прилагаемом к книге компакт-диске.
Листинг 7.23
,
#include <vcl\vcl.h>
#pragma hdrstop
#include "main.h"
,

Глава 7. Прикладные задачи шифрования 249
#pragma resource "*.dfm"
TMainForm *MainForm;
#include <stdio.h>
#include "unidef.h"
#include "cast128.h"
#include "sha.h"
#define MAX_PASSPHRASE_LEN 130
#define PGPDISK_CAST_ENCRYPTED 3
typedef struct {
uint8 header_magic[4];
uint8 header_type[4];
uint32 header_size;
uint32 header_CRC;
uint64 next_header;
uint32 reserved[2];
} PGPDISK_INFO;
typedef struct {
uint8 encrypted_key[16];
uint8 check_bytes[8];
uint16 hash_reps;
uint16 options;
uint32 reserved;
} PGPDISK_KEY;
typedef struct {
PGPDISK_INFO header_info;
uint8 major_ver;
uint8 minor_ver;
uint16 reserved_a;
uint32 file_blocks;
uint32 header_blocks;
uint32 data_blocks;
uint32 algorithm;
uint8 drive;
bool mounted;
uint64 sessionID;
uint8 reserved_b[52];
uint8 salt[8];
PGPDISK_KEY master_pass;
PGPDISK_KEY other_pass[7];
} PGPDISK;
PGPDISK *open_pgpdisk(char *diskname) {
FILE *f;
PGPDISK *disk = (PGPDISK *) malloc(sizeof(PGPDISK));
if (!disk) return NULL;
if ((f = fopen(diskname, "rb")) == NULL) {
free(disk);

250 Глава 7. Прикладные задачи шифрования
return NULL;
}
if ((fread(disk, 1, sizeof(PGPDISK), f) != sizeof(PGPDISK))
|| memcmp(disk->header_info.header_magic, "PGPd", 4)) {
free(disk);
fclose(f);
return NULL;
}
fclose(f);
return disk;
}
uint8 pass_block[300*1024]; /* (hash_of_encrypted_key + 1)*16000 times */
uint8 enc_key[16], key[16], check_bytes[8], j, *block;
uint16 i, *hash_reps;
CAST128_KEY cast;
SHA_HASH hash;
int check_passphrase(PGPDISK *disk, char *passphrase) {
if (!disk || !passphrase) return 0;
hash_reps = (uint16 *) &disk->master_pass.hash_reps;
setup_sha(&hash);
hash_sha(&hash, passphrase, strlen(passphrase), 1);
memcpy(&key, &hash, 16);
memcpy(&pass_block, &disk->salt, 8);
block = (uint8 *) &pass_block[8];
j=0;
for (i = 0; i < *hash_reps; i++) {
memcpy(block, &key, 16);
block += 16;
*block++ = j++;
}
setup_sha(&hash);
hash_sha(&hash, (uint8 *) &pass_block, (uint32) *hash_reps*17 + 8, 1);
setup_cast128(&cast, (uint8 *) &hash, 16);
memcpy(&enc_key, &disk->master_pass.encrypted_key, 16);
memcpy(&check_bytes, &disk->master_pass.check_bytes, 8);
decode_cast128(&cast, (uint32 *) &enc_key, (uint32 *) &enc_key);
decode_cast128(&cast, (uint32 *) &enc_key[8], (uint32 *) &enc_key[8]);
decode_cast128(&cast, (uint32 *) &check_bytes, (uint32 *) &check_bytes);
return (memcmp(&check_bytes, &hash, 8)?0:1);
}
,
void __fastcall TMainForm::Button1Click(TObject *Sender)
{
PGPDISK *disk;
disk = open_pgpdisk(FileEdit->Text.c_str());
if (!disk)
ShowMessage("Disk damaged!");
else {
if (check_passphrase(disk, PassEdit->Text.c_str()))
ShowMessage("RIGHT PASSPHRASE");
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
