From: Andy Brown <asb@nexor.co.uk>
To: cypherpunks@toad.com
Message Hash: 90d91d10e50c795df018314c4c88e7452a93aa49b942a5b69d2bae325cad1409
Message ID: <Pine.SOL.3.91.950712102618.28272C-100000@eagle.nexor.co.uk>
Reply To: N/A
UTC Datetime: 1995-07-12 09:55:39 UTC
Raw Date: Wed, 12 Jul 95 02:55:39 PDT
From: Andy Brown <asb@nexor.co.uk>
Date: Wed, 12 Jul 95 02:55:39 PDT
To: cypherpunks@toad.com
Subject: general RC4 key searcher: optimisations anyone?
Message-ID: <Pine.SOL.3.91.950712102618.28272C-100000@eagle.nexor.co.uk>
MIME-Version: 1.0
Content-Type: text/plain
Hi,
The following program is the part of my RC4 key search program that
actually does the searching, adapted into a small speed test. It is
designed to handle any size key, with any number of unknown bits, in any
position within the key. There are, of course, problems with it in its
current form:
1. It's too slow. I get 50% of the performance of the bruterc4.c on
utopia.hacktic.nl (~9500/sec on a 60Mhz Pentium and ~12000/sec on a
Sparc 20)
2. It can only handle bit offsets of 0 (i.e. the lower n bits of the key
are unknown). I'm unsure of a really fast way of generalising this
to any (contiguous) n bits.
3. There are probably bugs.
The code is included below. Does anyone have any comments?
- Andy
--------------------------- begin code fragment ------------------------
/* RC4 Brute Force Key Searcher, by Andy Brown 1995
This part of the package is meant to be portable between most systems
so that Unix users can take part in the searching. After all, the
kind of really high powered systems that can make a large dent in the
key space are not running Windows NT. You will, however, require
an ANSII compiler */
#include <stdio.h>
#include <time.h>
#include <ctype.h>
/* function declarations */
int main(void);
char *search_range(char *,unsigned long,unsigned long,char *,int,
unsigned char *,unsigned char *,int);
static void hex_to_bytes(char *,unsigned char *);
#define SwapByte(a,b) ((a)^=(b),(b)^=(a),(a)^=(b))
#define hexdigit(a) ((a)<10 ? (a)+'0' : (a)-10+'A')
#define decdigit(a) (isdigit(a) ? (a)-'0' : toupper(a)-'A'+10)
/*****************************/
/* Main function: test speed */
/*****************************/
int main(void)
{
/* The key has 20 "unknown" bits */
unsigned char *keyhex="0102030405060708090A0B0C0D000000";
unsigned char *first="0";
unsigned char ciphertext[11]=
{ 0xF2,0xA2,0xA0,0xF6,0x0F,0xBD,0x69,0x98,0xC0,0xFF,0x4C };
char *retval;
time_t before,diff;
before=time(NULL);
retval=search_range(first,0xFFFFF,0,keyhex,0,"hello world",ciphertext,11);
diff=time(NULL)-before;
if(retval==NULL)
fprintf(stderr,"Key not found, bug in key search code\n");
else
fprintf(stderr,"Key is: %s\n%ld keys/sec\n",retval,0xFFFFFL/(long)diff);
return 0;
}
/***********************************/
/* Search a region of the keyspace */
/***********************************
Arguments:
start_str: ASCII hex representation of the first "search key"
testsl: low order 32 bits of the number of keys to test
testsh: high order 32 bits of the number of keys to test
keyhex: ASCII hex representation of the key "skeleton"
Zeros appear in the key throughout the search range
firstbit: zero based index of the first unknown bit
plaintext: known plaintext
ciphertext: corresponding ciphertext
textsize: number of bytes of plain/ciphertext
NB: A "search key" is an offset into the searchable keyspace, not
a full key in itself. It may vary from 0..(2^numbits)-1
Returns:
NULL if the key is not found in the search range, otherwise an ASCII
hex representation of the key is returned. This pointer must be
dynamically allocated with malloc
*/
char *search_range(char *start_str,unsigned long testsl,unsigned long testsh,
char *keyhex,int firstbit,
unsigned char *plaintext,unsigned char *ciphertext,
int textsize)
{
unsigned char *start,*key,*skeleton,state[256],index1,index2;
char *retval;
int keybytes,startbytes,x,y,counter,i,found=0;
unsigned long lowcounter,highcounter;
/* allocate space for the key bytes and our starting value */
keybytes=strlen(keyhex)/2;
if(strlen(keyhex)&1)
keybytes++;
startbytes=strlen(start_str)/2;
if(strlen(start_str)&1)
startbytes++;
start=(unsigned char *)malloc(keybytes);
memset(start,'\0',keybytes);
skeleton=(unsigned char *)malloc(keybytes);
key=(unsigned char *)malloc(keybytes);
/* convert the hex strings to bytes */
hex_to_bytes(start_str,start+keybytes-startbytes);
hex_to_bytes(keyhex,skeleton);
/* OK, now things get time-critical. We are about to drop into a loop
that prepares and tests each candidate key */
for(highcounter=0;highcounter<=testsh;highcounter++)
{
for(lowcounter=0;lowcounter<testsl;lowcounter++)
{
/* construct the key from the skeleton and our part */
if(!firstbit)
{
memcpy(key,skeleton,keybytes);
for(i=keybytes-1;i>startbytes;i--)
key[i]|=start[i];
}
/* prepare the key */
for(counter=0;counter<256;counter++)
state[counter]=(unsigned char)counter;
x=y=0;
index1=index2=0;
for(counter=0;counter<256;counter++)
{
index2=(key[index1]+state[counter]+index2) & 0xFF;
SwapByte(state[counter],state[index2]);
if(++index1==keybytes)
index1=0;
}
/* do two RC4 operations as a preliminary test. If this fails then test
the next one, then the rest. This should result in a lot of rejections
before the rest of the loop is entered */
x=(x+1) & 0xFF;
y=(state[x]+y) & 0xFF;
SwapByte(state[x],state[y]);
if(plaintext[0]==(ciphertext[0]^state[(state[x]+state[y]) & 0xFF]))
{
x=(x+1) & 0xFF;
y=(state[x]+y) & 0xFF;
SwapByte(state[x],state[y]);
if(plaintext[1]==(ciphertext[1]^state[(state[x]+state[y]) & 0xFF]))
{
/* rest of the loop. This will only be entered, on average once
every 65536 tests */
for(i=2;i<textsize;i++)
{
x=(x+1) & 0xFF;
y=(state[x]+y) & 0xFF;
SwapByte(state[x],state[y]);
if(plaintext[i]!=(ciphertext[i]^state[(state[x]+state[y]) & 0xFF]))
break;
}
/* if we got to the end of the loop then that's it. We have won */
if(i==textsize)
{
found=1;
goto endloops;
}
}
}
/* increment our key segment */
i=keybytes-1;
do
{
start[i]++;
} while(!start[i--]);
}
}
/* free memory */
endloops:
free(start);
free(skeleton);
if(found)
{
retval=(char *)malloc((keybytes*2)+2);
i=0;
for(i=0;i<keybytes;i++)
{
retval[i*2]=hexdigit((key[i]&0xF0)>>4);
retval[(i*2)+1]=hexdigit(key[i]&0xF);
}
retval[i*2]='\0';
return retval;
}
else
return NULL;
}
/*******************************/
/* convert hex string to bytes */
/*******************************
eg. "05FC9" would become 0x00,0x5F,0xC9 */
static void hex_to_bytes(char *str,unsigned char *bytes)
{
int i,firstzero=(strlen(str)&1) ? 1 : 0;
unsigned char b;
i=0;
while(i<(int)strlen(str))
{
if(firstzero)
firstzero=0;
else
{
b=(decdigit(str[i]))<<4;
i++;
}
b|=decdigit(str[i]);
*bytes++=b;
i++;
}
}
-------------------------- end code fragment -----------------------
Return to July 1995
Return to “Andy Brown <asb@nexor.co.uk>”
1995-07-12 (Wed, 12 Jul 95 02:55:39 PDT) - general RC4 key searcher: optimisations anyone? - Andy Brown <asb@nexor.co.uk>