Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практическая криптография алгоритмы и их программирование
.pdf
Глава 5. Дешифрование современных шифров 161
SetControl(CTRL_BASE ^ CTRL_ADRSEL2); /* let WRB high again */
ioDelay(DELAY_WRB_RECOVER); /* wait */
SetControl(CTRL_BASE); /* let WRB high again */
ioDelay(DELAY_ADRSEL2_SETTLE); /* wait */
}
int GetRegister(int board, int chip, int reg) {
int rval;
if (CURRENT_PORT_CNFG != CNFG_INPUT) /* set IO data lines for input */
ConfigureIO_Port(CNFG_INPUT);
if (CURRENT_BOARD != board) /* make sure board is selected */
selectBoard(board);
if (CURRENT_CHIP != chip) /* make sure chip is selected */
selectChip(chip);
SetAddress(reg); /* select the right address */
SetControl(CTRL_BASE ^ CTRL_ADRSEL2); /* pull adrsel2 low */
ioDelay(max(DELAY_ADDRESS_SETTLE, DELAY_ADRSEL2_SETTLE)); /* wait */
SetControl(CTRL_BASE ^ CTRL_RDB ^ CTRL_ADRSEL2); /* pull RDB low */
ioDelay(DELAY_RDB_HOLD);
rval = GetData();
SetControl(CTRL_BASE ^ CTRL_ADRSEL2); /* let RDB high */
ioDelay(DELAY_RDB_RECOVER);
SetControl(CTRL_BASE); /* let ADRSEL2 high */
ioDelay(DELAY_ADRSEL2_SETTLE);
return (rval);
}
int CheckRegister(int board, int chip, int reg, int value) {
int i;
i = GetRegister(board, chip, reg);
if (i != value)
return (-1);
return (0);
}
А основной модуль, управляющий процессом перебора и заключительной отбраковкой ключей, представляет собой весьма небольшой по размеру исходный
текст (см. листинг 5.3).
Листинг 5.3
#define SOFTWARE_VERSION "1.0"
#define SOFTWARE_DATE "04-21-1998"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <memory.h>
#include <time.h>
#include <string.h>
#include <conio.h>

162 Глава 5. Дешифрование современных шифров
#include "search.h"
#include "chipio.h"
#include "keyblock.h"
#include "des.h"
/*
* SEARCH_CHIP STRUCTURE: Contains status information about each chip.
*
* board: The board this chip is on (1 byte).
* chip: The ID of this chip on the board (1 byte).
* initialized: 0=uninitialized, 1=initialized, -1=defective.
* region[]: Specifies the top 24 bits of the key being searched by each
* search unit. A value of -1 means the search unit is idle
* (idle), and a value of -2 means the search unit is not used.
* overFlow[]: Specifies the value at which the low 32 bits of the
* key (the key counter) will have gone through all 2^32
* possibilities. Note: this only has the top 24 bits of the
* counter, which corresponds to key bytes: .. .. .. XX XX XX.. (LSB)
* lastSeen[]: The value last seen in the low 32 bits of the key.
* This has the same encoding as overFlow.
*/
typedef struct CHIP_CTX {
unsigned char board, chip;
int initialized;
long region[SEARCH_UNITS_PER_CHIP];
long overFlow[SEARCH_UNITS_PER_CHIP];
long lastDone[SEARCH_UNITS_PER_CHIP];
struct CHIP_CTX *nextChip;
} CHIP_CTX;
/*
* GLOBAL VARIABLES
*/
CHIP_CTX *CHIP_ARRAY = NULL;
SEARCH_CTX CTX;
static int QUIET = 0;
static int VERBOSE = 0;
static FILE *FP_LOG = NULL;
/*
* FUNCTION PROTOTYPES & MINI FUNCTIONS & MACROS
*/
static void EXIT_ERR(char *s) { fprintf(stderr, s); exit(1); }
long ReadConfig(char *configFilespec);
void RunSearch(FILE *ctxFile);
void InitializeChip(CHIP_CTX *cp, SEARCH_CTX *ctx);
void ServiceChip(CHIP_CTX *cp, SEARCH_CTX *ctx, FILE *ctxFile);
long GetUnitKeyCounter(int board, int chip, int unit);
void CheckAndPrintKey(CHIP_CTX *cp, SEARCH_CTX *ctx, int unit);
int ServiceKeyboard(SEARCH_CTX *ctx);
int CheckKey(unsigned char key[56], SEARCH_CTX *ctx);
void main(int argc, char **argv) {
FILE *ctxFile;

Глава 5. Дешифрование современных шифров 163
int i;
time_t t;
CHIP_CTX *cp;
printf("\nDES Search Engine Controller (Ver %s, %s). May be export "
"controlled.\nWritten 1998 by Cryptography Research "
"(http://www.cryptography.com) for EFF.\n"
"This is unsupported "
"free software: Use and distribute at your own risk.\n"
"-------------------------------------------"
"---------------------------------\n\n\n",
SOFTWARE_VERSION, SOFTWARE_DATE);
if (argc < 3) {
fprintf(stderr,
"Usage: search configFile contextFile [logfile] [-v] [-q]\n"
" configFile: Search array configuration from autoconf\n"
" contextFile: Search context (from init)\n"
" logfile: Output file with detailed reporting info\n"
" -v: verbose output to logfile\n"
" -q: quiet mode (less output to the screen)\n"
" (Note: paramaters must be in the order above.)\n");
exit(1);
}
for (i = 3; i < argc; i++) {
if (i == 3 && argv[i][0] != '-') {
FP_LOG = fopen(argv[3], "w");
if (FP_LOG == NULL)
EXIT_ERR("Error opening log file.");
} else if (stricmp(argv[i], "-v") == 0)
VERBOSE = 1;
else if (stricmp(argv[i], "-q") == 0)
QUIET = 1;
else {
fprintf(stderr, "Unknown parameter \"%s\"\n", argv[i]);
exit(1);
}
}
/* READ CONFIGURATION FILE SPECIFYING BASE PORT AND SEARCH UNITS */
CTX.totalUnits = ReadConfig(argv[1]);
/* RESET THE SEARCH ARRAY */
if (!QUIET) printf("Resetting the search array.\n");
i = -1;
for (cp = CHIP_ARRAY; cp != NULL; cp = cp->nextChip) {
if (i != cp->board) {
i = cp->board;
ResetBoard(i);
}
}
t = time(NULL);
/* READ SEARCH FILE SPECIFYING SEARCH INFO & REMAINING KEY BLOCKS */
ctxFile = fopen(argv[2], "r+b");

164 Глава 5. Дешифрование современных шифров
if (ctxFile == NULL) {
fprintf(stderr, "Error opening search context file \"%s\"\n", argv[2]);
exit(1);
}
/* MAKE SURE RESET HAD AT LEAST 1 SECOND TO SETTLE. */
if (!QUIET) printf("Waiting for reset to settle.\n");
while(t+1>=time(NULL)) {}
/* RUN THE SEARCH! */
RunSearch(ctxFile);
fclose(ctxFile);
if (!QUIET) printf("Exiting.\n");
}
/*
* Run the search. Uses the search parameters in the
* global linked list CHIP_ARRAY and keeps its context info
* in the global CTX.
*/
void RunSearch(FILE *ctxFile) {
CHIP_CTX *cp;
SEARCH_CTX *ctx = &CTX;
int halt = 0;
time_t startTime, lastReportTime, t;
long loopCount = 0;
char buffer[128];
if (!QUIET) printf("Loading search context file...\n");
OpenSearchContext(ctxFile, ctx);
printf("Initialization Successful - Beginning search.\n");
if (QUIET) printf("Quiet mode: Press ? for help during search.\n");
if (FP_LOG && VERBOSE) fprintf(FP_LOG, "--- Beginning search ---\n");
for (cp = CHIP_ARRAY; cp != NULL; cp = cp->nextChip)
InitializeChip(cp, ctx);
startTime = time(NULL);
lastReportTime = 0;
while (halt == 0) {
t = time(NULL); /* report every 5 seconds */
if (t/5 != lastReportTime/5) {
sprintf(buffer, "%7ld blocks done, %7ld left, %4ld running (time=%7ld).",
ctx->totalFinishedKeyBlocks, ctx->totalUnstartedKeyBlocks +
ctx->totalPendingKeyBlocks, ctx->totalPendingKeyBlocks,
(long)(t - startTime));
if (!QUIET) printf(">%s ('?'=help)\n", buffer);
if (FP_LOG && VERBOSE) fprintf(FP_LOG, "Report: %s\n", buffer);
lastReportTime = t;
}
for (cp = CHIP_ARRAY; cp != NULL && halt == 0; cp = cp->nextChip) {
ServiceChip(cp, ctx, ctxFile);
if (ServiceKeyboard(ctx) < 0)
halt = 1;
}

Глава 5. Дешифрование современных шифров 165
if (ctx->totalFinishedKeyBlocks == (1L<<24))
halt = 1;
GetRegister(255, 255, 255);
loopCount++;
}
}
/* InitializeChip(cp, ctx): Initialize a chip whose chip context is
* at cp, using the search parameters at ctx.
*/
void InitializeChip(CHIP_CTX *cp, SEARCH_CTX *ctx) {
int i,j;
if (!QUIET) printf("Initializing board 0x%02X, chip 0x%02X\n",
cp->board, cp->chip);
if (FP_LOG && VERBOSE) fprintf(FP_LOG,
"Initializing board 0x%02X, chip 0x%02X\n", cp->board, cp->chip);
SetRegister(cp->board, cp->chip, REG_PTXT_BYTE_MASK, 0xFF); /* halt chip */
for (i = 0; i < 32; i++)
SetRegister(cp->board, cp->chip, REG_PTXT_VECTOR+i,
ctx->plaintextVector[i]);
for(i=0;i<8;i++)
SetRegister(cp->board, cp->chip,REG_PTXT_XOR_MASK+i,
ctx->plaintextXorMask[i]);
for(i=0;i<8;i++)
SetRegister(cp->board, cp->chip, REG_CIPHERTEXT0+i, ctx->ciphertext0[i]);
for(i=0;i<8;i++)
SetRegister(cp->board, cp->chip, REG_CIPHERTEXT1+i, ctx->ciphertext1[i]);
SetRegister(cp->board, cp->chip, REG_PTXT_BYTE_MASK, ctx->plaintextByteMask);
SetRegister(cp->board, cp->chip, REG_SEARCHINFO, ctx->searchInfo);
/* TO BE SAFE, VERIFY THAT ALL REGISTERS WERE WRITTEN PROPERLY */
/* (Each chip only gets initialized once, so this is quick.) */
j=0;
for (i = 0; i < 32; i++)
j += CheckRegister(cp->board, cp->chip, REG_PTXT_VECTOR+i,
ctx->plaintextVector[i]);
for(i=0;i<8;i++) {
j += CheckRegister(cp->board, cp->chip, REG_PTXT_XOR_MASK+i,
ctx->plaintextXorMask[i]);
j += CheckRegister(cp->board, cp->chip, REG_CIPHERTEXT0+i,
ctx->ciphertext0[i]);
j += CheckRegister(cp->board, cp->chip, REG_CIPHERTEXT1+i,
ctx->ciphertext1[i]);
}
j += CheckRegister(cp->board, cp->chip, REG_PTXT_BYTE_MASK,
ctx->plaintextByteMask);
j += CheckRegister(cp->board, cp->chip, REG_SEARCHINFO, ctx->searchInfo);
if (j != 0) {
printf("Bad register on board 0x%02X, chip 0x%02X. Chip disabled.\n",
cp->board, cp->chip);
if (FP_LOG) fprintf(FP_LOG, "Bad register on board 0x%02X, chip 0x%02X.%s",
cp->board, cp->chip, " Chip disabled.\n");
}

166 Глава 5. Дешифрование современных шифров
/* UPDATE THE CHIP CONTEXT */
cp->initialized = (j == 0)?1:-1; /*initialized or defective */
}
/*
* Service a chip by doing the following:
* - Check if it has halted
* - Check to see if it has finished its region
* - Restart if it is idle
*/
void ServiceChip(CHIP_CTX *cp, SEARCH_CTX *ctx, FILE *ctxFile) {
int unit;
long k;
if (cp->initialized < 0)
return;
/*
* READ KEYS & RESTART ANY HALTED UNITS
*/
for (unit = 0; unit < SEARCH_UNITS_PER_CHIP; unit++) {
if (cp->region[unit] >= 0) { /* if currently running */
if (!(GetRegister(cp->board, cp->chip, REG_SEARCH_STATUS(unit)) & 1)) {
CheckAndPrintKey(cp, ctx, unit);
SetRegister(cp->board, cp->chip, REG_SEARCH_STATUS(unit), 1);
}
}
}
/*
* See if any units have completed their search regions
* Note: If I/O bandwidth was a problem and the clock rate of the
* search system was fixed, we could predict when the keycounter
* would flip and avoid this check.
*/
for (unit = 0; unit < SEARCH_UNITS_PER_CHIP; unit++) {
if (cp->region[unit] < 0)
continue;
k = GetUnitKeyCounter(cp->board, cp->chip, unit);
k -= cp->overFlow[unit];
if (k < 0)
k += (1L << 24);
if (VERBOSE && FP_LOG) fprintf(FP_LOG,
"Board 0x%02X chip 0x%02X unit 0x%02X is at 0x%06lX "
"(lastDone=0x%06lX, overFlow=%06lX)\n",
cp->board, cp->chip, unit, k,
cp->lastDone[unit], cp->overFlow[unit]);
if (k < cp->lastDone[unit]) {
if (!QUIET) printf("Board 0x%02X chip 0x%02X unit 0x%02X finished block "
"0x%06lX (lastDone=0x%06lX, got 0x%06lX, overFlow=%06lX)\n",
cp->board, cp->chip, unit, cp->region[unit],
cp->lastDone[unit], k, cp->overFlow[unit]);
if (FP_LOG) fprintf(FP_LOG, "Unit 0x%02X 0x%02X 0x%02X finished "

Глава 5. Дешифрование современных шифров 167
"0x%06lX (last=%06lX, got %06lX, oFlow=%06lX)\n",
cp->board, cp->chip, unit, cp->region[unit],
cp->lastDone[unit], k, cp->overFlow[unit]);
FinishKeyRegion(ctxFile, ctx, cp->region[unit]); /* region is done */
cp->region[unit] = -1; /* unit is now idle */
} else {
cp->lastDone[unit] = k;
}
}
/*
* Start any units that are currently stalled
*/
for (unit = 0; unit < SEARCH_UNITS_PER_CHIP; unit++) {
if (cp->region[unit] == -1) {
k = ReserveKeyRegion(ctxFile, ctx);
if (k < 0)
break; /* no more regions... */
if (!QUIET) printf("Starting board 0x%02X, chip 0x%02X, unit 0x%02X... ",
cp->board, cp->chip, unit);
if (FP_LOG) fprintf(FP_LOG, "Starting unit 0x%02X 0x%02X 0x%02X... ",
cp->board, cp->chip, unit);
cp->region[unit] = k;
/* LOAD UP THE KEY REGION AND LET 'ER RIP... */
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+6,
(unsigned char)((k >> 16) & 0xFF));
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+5,
(unsigned char)((k >> 8) & 0xFF));
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+4,
(unsigned char)(k & 0xFF));
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+3, 0);
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+2, 0);
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+1, 0);
SetRegister(cp->board, cp->chip, REG_SEARCH_KEY(unit)+0, 0);
SetRegister(cp->board, cp->chip, REG_SEARCH_STATUS(unit), 1); /* GO! */
/* READ OUT THE KEY COUNTER (3 BYTES) FOR OVERFLOW SENSING */
k = GetUnitKeyCounter(cp->board, cp->chip, unit);
cp->overFlow[unit] = k;
cp->lastDone[unit] = k;
if (!QUIET) printf("Region=0x%06lX, overFlow=0x%06lX\n",
cp->region[unit], k);
if (FP_LOG) fprintf(FP_LOG, "Region=0x%06lX, overFlow=0x%06lX\n",
cp->region[unit], k);
}
}
}
/*
* Read the value of a rapidly-incrementing key counter register.
* The function reads the register twice, finds the most-significant
* bit that changed during the operation, and returns the later
* (higher) value with all bits to the right of the one that changed

168 Глава 5. Дешифрование современных шифров
* set to zero.
* The return value is the top 24 bits of the low 32 bits of the
* key counter -- i.e., key bytes (MSB).. .. .. XX XX XX ..(LSB)
*/
long GetUnitKeyCounter(int board, int chip, int unit) {
long v1, v2, m;
do {
v1 = ((long)GetRegister(board, chip, REG_SEARCH_KEY(unit)+3)) << 16;
v1 |= ((long)GetRegister(board, chip, REG_SEARCH_KEY(unit)+2)) << 8;
v1 |= ((long)GetRegister(board, chip, REG_SEARCH_KEY(unit)+1));
v2 = ((long)GetRegister(board, chip, REG_SEARCH_KEY(unit)+3)) << 16;
v2 |= ((long)GetRegister(board, chip, REG_SEARCH_KEY(unit)+2)) << 8;
v2 |= ((long)GetRegister(board, chip, REG_SEARCH_KEY(unit)+1));
} while (v1 > v2);
for (m = 0x800000L; m != 0; m >>= 1) {
if ((v1 & m) != (v2 & m)) {
v2 = (v2 & (0xFFFFFFL-m+1));
break;
}
}
return (v2);
}
/*
* Get the key out of a halted unit and print it to the screen/logs
*/
void CheckAndPrintKey(CHIP_CTX *cp, SEARCH_CTX *ctx, int unit) {
unsigned char key[7];
unsigned char binKey[56];
char buf[128];
int i,j, goodKey;
for(i=0;i<7;i++)
key[i] = (unsigned char)GetRegister(cp->board, cp->chip,
REG_SEARCH_KEY(unit)+i);
if (--(key[0]) == 0xFF) /* Decrement key */
if (--(key[1]) == 0xFF)
if (--(key[2]) == 0xFF)
--key[3];
for (i = 0; i < 56; i++)
binKey[i] = (key[i/8] >> (i&7)) & 1;
for (i = 7; i >= 0; i--) {
j = binKey[i*7]*2 + binKey[i*7+1]*4 + binKey[i*7+2]*8 + binKey[i*7+3]*16 +
binKey[i*7+4]*32 + binKey[i*7+5]*64 + binKey[i*7+6]*128;
sprintf(buf+14-2*i, "%02X", j);
}
if (QUIET)
printf("Halt in %02X.%02X.%02X, K=%s P=", cp->board, cp->chip, unit, buf);
else {
printf("BOARD 0x%02X, CHIP 0x%02X, UNIT 0x%02X HALTED!\n K56 = ",
cp->board, cp->chip, unit);

Глава 5. Дешифрование современных шифров 169
for (i = 6; i >= 0; i--) printf("%02X", key[i]);
printf("\n K64 = %s\n", buf);
}
if (FP_LOG) {
fprintf(FP_LOG, "Halt@ %02X.%02X.%02X, K=",
cp->board, cp->chip, unit);
for (i = 6; i >= 0; i--) fprintf(FP_LOG, "%02X", key[i]);
if (VERBOSE) fprintf(FP_LOG, ", K64=%s", buf);
}
goodKey = CheckKey(binKey, ctx); /* prints plaintexts */
if (QUIET) printf(goodKey ? " (OK!)\n" : " (BAD)\n");
else printf(" ***** KEY IS %s *****\n", goodKey ? " OKAY " : "BAD");
if (FP_LOG) fprintf(FP_LOG, goodKey ? " (=OK!)\n" : " (=BAD)\n");
fflush(stdout);
if (FP_LOG) fflush(FP_LOG);
}
/*
* If needed, this function can be used to decide whether keys are
* actually good or not to reject false positives.
* Returns 1 if the key is not bad, zero if it is wrong.
*/
int CheckKey(unsigned char key[56], SEARCH_CTX *ctx) {
bool ctxt[64],ptxt0[64],ptxt1[64];
unsigned char p0[8],p1[8];
int i,c;
/* Compute the plaintext and try to print it to the screen */
for (i = 0; i < 64; i++)
ctxt[i] = (ctx->ciphertext0[i/8] >> (i&7)) & 1;
DecryptDES((bool*)key, ptxt0, ctxt, 0);
for(i=0;i<8;i++) {
p0[i] = (unsigned char)(ptxt0[i*8+0]+ptxt0[i*8+1]*2+ptxt0[i*8+2]*4+
ptxt0[i*8+3]*8+ptxt0[i*8+4]*16+ptxt0[i*8+5]*32+ptxt0[i*8+6]*64+
ptxt0[i*8+7]*128);
}
for(i=0;i<8;i++)
p0[i] ^= ctx->plaintextXorMask[i];
if (!QUIET) {
printf(" Plaintext0 =");
for (i = 7; i>=0; i--) printf(" %02X", p0[i]);
printf(" (\"");
for (i = 7; i>=0; i--)
printf("%c", (p0[i] < 32) ? '?' : p0[i]);
printf("\")\n");
}
if (QUIET) for (i = 7; i>=0; i--) printf("%02X", p0[i]);
if (FP_LOG) fprintf(FP_LOG, ", ptxt=");
if (FP_LOG) for (i = 7; i>=0; i--) fprintf(FP_LOG, "%02X", p0[i]);
for (i = 0; i < 64; i++)
ctxt[i] = (ctx->ciphertext1[i/8] >> (i&7)) & 1;
DecryptDES((bool*)key, ptxt1, ctxt, 0);

170 Глава 5. Дешифрование современных шифров
for(i=0;i<8;i++) {
p1[i] = (unsigned char)(ptxt1[i*8+0]+ptxt1[i*8+1]*2+ptxt1[i*8+2]*4+
ptxt1[i*8+3]*8+ptxt1[i*8+4]*16+ptxt1[i*8+5]*32+ptxt1[i*8+6]*64+
ptxt1[i*8+7]*128);
}
if (ctx->searchInfo & 1) { /* if CBC mode, XOR w/ 1st ctxt */
for(i=0;i<8;i++)
p1[i] ^= ctx->ciphertext0[i];
}
if (!QUIET) printf(" Plaintext1 =");
if (QUIET) printf("/");
if (FP_LOG) fprintf(FP_LOG, "/");
if (!QUIET) for (i = 7; i>=0; i--) printf(" %02X", p1[i]);
if (QUIET) for (i = 7; i>=0; i--) printf("%02X", p1[i]);
if (FP_LOG) for (i = 7; i>=0; i--) fprintf(FP_LOG, "%02X", p1[i]);
if (!QUIET) {
printf(" (\"");
for (i = 7; i>=0; i--)
printf("%c", (p1[i] < 32) ? '?' : p1[i]);
printf("\")\n");
}
/* Reject key if doesn't contain good characters */
for(i = 0; i < 8;i++) {
if (((ctx->plaintextByteMask) >> i) & 1)
continue;
c = p0[i];
if (((ctx->plaintextVector[c/8] >> (c & 7)) & 1) == 0)
return (0);
c = p1[i];
if (((ctx->plaintextVector[c/8] >> (c & 7)) & 1) == 0)
return (0);
}
return (1);
}
Кроме метода определения, подходит ключ для дальнейшего исследования или
нет, интересной особенностью чипа является также то, как он выбирает следующий ключ для опробования. Вместо простого инкрементирования на единицу текущего значения ключа для ускорения работы аппаратного сумматора используется
не 48-битовое сложение, а 32-битовое. Таким образом, старшие 24 бита в чипе всегда остаются постоянными. При ротации двух с половиной миллионов ключей в секунду чип переберет весь этот сектор пространства подключей за 1717 секунд (что
приблизительно составляет полчаса времени). После этого главный компьютер
остановит чип, отведет для него новый сектор перебора и снова запустит.
Для этого чип предоставляет упрощенный аппаратный интерфейс, позволяющий остановить его, опросить его или передать ему данные, а также снова запустить. Плата с 24 чипами способна перебрать 60 миллионов ключей в секунду. Перебор всего пространства для одной такой платы займет около 40 лет. Поэтому в
комплексе их было использовано 64 штуки. Все вместе они и осуществили тотальное опробование всего за трое суток.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
