There's something wrong with:
@k = unpack( 'C*', pack( 'H*', shift ) );
I tried with key "ciao" and @k ends up with only two elements instead of 4 as in the C version, which operates on all 4 characters. I really don't know anything about pack/unpack, so I cannot solve the problem without studying it, and it's friday afternoon :)
Update: sorry, fried in friday, I completely missed the pre-calculations over the input key, ikegami answer forced me to take a deeper look. I swear I'll re-read it, in the meantime "--" me!
Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')
Don't fool yourself.
| [reply] [d/l] [select] |
The Perl version is expecting the key to be represented as hex digits. Try '6369616F' instead of 'ciao' for the Perl version (making @k = ('c', 'i', 'a', 'o')). I'm too lazy to figure out whatI don't know what the C version expects.
| [reply] |
zentara, as promised I returned on the analysis of the
codes to get a solution for this interworking. I don't know if multiple replies to a post is frowned, I make a new reply just to allow people "--" me on the other sloppy response :)
The initial key's state shuffling is different in the two programs, mainly
due to the unpack/pack trick (at least in this I did not make a mistake). Quoting from perldoc -f pack:
The "h" and "H" fields pack a string that
many nybbles (4-bit groups, representable
as hexadecimal digits, 0-9a-f) long.
Each byte of the input field of pack()
generates 4 bits of the result. For non-
alphabetical bytes the result is based on
the 4 least-significant bits of the input
byte, i.e., on "ord($byte)%16". In par-
ticular, bytes "0" and "1" generate nyb-
bles 0 and 1, as do bytes "\0" and "\1".
For bytes "a".."f" and "A".."F" the result
is compatible with the usual hexadecimal
digits, so that "a" and "A" both generate
the nybble "0xa==10". The result for
bytes "g".."z" and "G".."Z" is not
well-defined.
Which I read as: "never, never, never count on it for evaluating portable shuffling keys in ARCFOUR implementations!".
The initial shuffling in the C program is even more akward, if
possible: trying to interpret characters out of [0-9a-fA-F] as
hexadecimal can lead to a situation in which the input key
is virtually ignored (for example, key "zentara!" leads to
"AAAAAAAA" in hex, much like "pppppppp"...).
To make both programs use the same shuffling data, I've simply
used the input key - how good this system is I leave to the
experts. This is straightforward in C, and requires a bit
of map mangling in Perl (C has to be better for something. :-)
Interworking has been tested on a very little test file, I leave further
analysis to the interested ones.
The Perl modified code is the following. I've added strict and
warnings just to be sure that there wasn't anything spoiling the
output under the hood. The shuffling array @k is
evaluated from the input key applying ord() to each
input char. Yes, I could use unpack() instead of
split().
#!/usr/bin/perl -0777 --
# $0 key infile > outfile
# symmetric works both ways
use strict;
use warnings;
my @k = map { ord($_) } split //, shift;
my (@s, $x, $y);
$y = 0;
for ( my @t = @s = 0 .. 255 ) {
$y = ( $k[ $_ % @k ] + $s[ $x = $_ ] + $y ) % 256;
&S;
}
$x = $y = 0;
for ( unpack( 'C*', <> ) ) {
$x++;
$y = ( $s[ $x %= 256 ] + $y ) % 256;
&S;
print pack( 'C', $_ ^= $s[ ( $s[$x] + $s[$y] ) % 256 ] );
}
sub S { @s[ $x, $y ] = @s[ $y, $x ] }
The C code is virtually unmodified (for being a C code, of course). I
added a couple of includes and removed some unneeded variables to
shut the warnings off (-Wall was added for the same reasons I used
strict and warnings). Of course, I eliminated the initial key mangling to make the program use it directly for state shuffling inside key.
/* gcc -o rc4 rc4.c -Wall */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define buf_size 1024
typedef struct rc4_key
{
unsigned char state[256];
unsigned char x;
unsigned char y;
} rc4_key;
#define swap_byte(x,y) t = *(x); *(x) = *(y); *(y) = t
void dumphex(unsigned char* data, unsigned long size) {
unsigned long i;
printf("%lu elementi\n\t", size);
for (i = 0; i < size; ) {
printf("%02X ", data[i]);
if (! (++i % 16)) {
printf("\n\t");
}
}
printf("\n");
}
void prepare_key(unsigned char *key_data_ptr, int key_data_len, rc4_ke
+y *key)
{
unsigned char t;
unsigned char index1;
unsigned char index2;
unsigned char* state;
short counter;
state = &key->state[0];
for(counter = 0; counter < 256; counter++)
state[counter] = counter;
key->x = 0;
key->y = 0;
index1 = 0;
index2 = 0;
for(counter = 0; counter < 256; counter++)
{
index2 = (key_data_ptr[index1] + state[counter] + index2) % 256;
swap_byte(&state[counter], &state[index2]);
index1 = (index1 + 1) % key_data_len;
}
}
void rc4(unsigned char *buffer_ptr, int buffer_len, rc4_key *key)
{
unsigned char t;
unsigned char x;
unsigned char y;
unsigned char* state;
unsigned char xorIndex;
short counter;
x = key->x;
y = key->y;
state = &key->state[0];
for(counter = 0; counter < buffer_len; counter++)
{
x = (x + 1) % 256;
y = (state[x] + y) % 256;
swap_byte(&state[x], &state[y]);
xorIndex = (state[x] + state[y]) % 256;
buffer_ptr[counter] ^= state[xorIndex];
}
key->x = x;
key->y = y;
}
int main(int argc, char* argv[])
{
char seed[256];
char buf[buf_size];
int rd;
rc4_key key;
if (argc < 2)
{
fprintf(stderr,"%s key <in >out\n",argv[0]);
exit(1);
}
strcpy(seed,argv[1]);
prepare_key(seed, strlen(seed), &key);
rd = fread(buf,1,buf_size,stdin);
while (rd>0)
{
rc4(buf,rd,&key);
fwrite(buf,1,rd,stdout);
rd = fread(buf,1,buf_size,stdin);
}
return 0;
}
As a final note, it has to be observed that the C approach seems
more robust for large input files: read a chunk of data, encrypt and
write. In the Perl version, slurp mode in input (according to "-0777")
makes the process read the entire file in memory before analysing it.
Transforming the encription cycle in the Perl source is left as an
exercise for the memory impaired Monk.
Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')
Don't fool yourself.
| [reply] [d/l] [select] |
Wow, thanks frodo72, I wish there was a way to give you 100 points for figuring that out.
I'm not really a human, but I play one on earth.
flash japh
| [reply] |