initial commit

This commit is contained in:
TLINDEN
2015-09-01 22:53:00 +02:00
parent 9f58440588
commit 71ec0f780f
10 changed files with 886 additions and 2 deletions

16
Makefile Normal file
View File

@@ -0,0 +1,16 @@
LDFLAGS = -g
CFLAGS = -g -Wall -Wextra -Werror
DST = twenty4
OBJS = twenty4.o
all: $(DST)
$(DST): $(OBJS)
gcc $(LDFLAGS) $(OBJS) -o $(DST)
%.o: %.c
gcc -c $(CFLAGS) $*.c -o $*.o
clean:
rm -f *.o $(DST)

217
README.md
View File

@@ -1,2 +1,215 @@
# twenty4 ## TWENTY4 - a fun stream cipher
TWENTY4 - fun stream cipher
*THIS IS JUST FOR LEARINING CRYPTO, DO NOT EVER USE THIS FOR ANYTHING*
This is the implementation of the fun stream cipher TWENTY4 by T.v. Dein, 09/2015.
Published under the public domain, Creative Commons Zero License. It works bytewise,
with keys between 1-256 bits in 17 rounds, uses S-Boxes and key output-feedback mode.
The cipher also works with CBC or ECB mode (sample CBC implementation included).
The name TWENTY4 is a reverence to article 20 paragraph 4 of the german constitution
which reads:
> All Germans shall have the right to resist any person seeking to
> abolish this constitutional order, if no other remedy is available.
Also, the cipher uses the contents of the german constitution as the source for its
S-Boxes.
## S-Box generation
TWENTY4 uses the german constitution (called "basic law" in germany) as
the source for S-Boxes. The EPUB version (included in sbox/ subdir)
is encrypted using AES-256-CBC with the passphrase
"grundgesetz\n". The resulting byte stream is used as the source for
S-Boxes.
The following version of the german consitution is used:
Basic Law for the Federal Republic of Germany in the revised version
published in the Federal Law Gazette Part III, classification number
100-1, as last amended by the Act of 23 December 2014
(Federal Law Gazette I p. 2438).
Linux Shell commands to generate the S-Boxes:
curl -o BJNR000010949.epub http://www.gesetze-im-internet.de/gg/BJNR000010949.epub
echo grundgesetz > BJNR000010949.pass
cat BJNR000010949.epub | openssl aes-256-cbc -kfile BJNR000010949.pass | ./gen-static-sbox
'gen-static-sbox' compiled from gen-static-sbox.c in this directory, which has SHA256
checksum: 29bfd8bd6dbca696d4d8b7ca997497e091875d6bf939e9702b1edf669d0742b0.
However, it just prints out bytes which it reads from STDIN, collecting them into an 256
byte array, ignoring possible duplicates, and prints it out as hex.
Both S-Boxes are bijective and have the following properties (calculated using analyze.c):
Char distribution: 100.000000%
Char redundancy: 0.000000%
Char entropy: 8.000000 bits/char
Compression rate: 0.000000%
TWENTY4 uses two S-Box arrays, one for key expansion and one for encryption.
## Key expansion
The input key will be expanded into a 17 byte array. Maximum key size is
17 bytes (136 bit).
IV = KU[0]
for ROUND in 0..16
if KU[ROUND]
K[ROUND] = IV xor KU[ROUND]
else
K[ROUND] = IV yor KBOX[ROUND * 8];
endif
K[ROUND] = KBOX[K[ROUND]]
IV = K[ROUND]
endfor
for KROUND in 0..31
for ROUND in 0..17
K[ROUND] = IV xor (rotateleft-3(K[ROUND]) xor KBOX[rcon(IV)])
IV = K[ROUND]
endfor
endfor
where:
KU: input key
K[17]: initial round key array
ROUND: encryption round 1-17
KROUND: key expansion round 1-32
KBOX[256]: pre computed S-Box for key expansion
## Encryption
for INBYTE in <INSTREAM>
OUTBYTE = INBYTE
for ROUND in 0..17
OUTBYTE = OUTBYTE xor K[ROUND]
OUTBYTE = OUTBYTE xor SBOX[OUTBYTE]
OUTBYTE = rotateleft-ROUND%8(OUTBYTE)
OUTBYTE = rotateright-4(K[ROUND])
endfor
rotatekey(K, OUTBYTE)
OUTBYTE => <OUTSTREAM>
endfor
func rotatekey(K, B)
[rotate K[17] array elementy 1 to the right]
for N in 0..16:
K[N] = KBOX[K[N] xor B]
endfor
endfunc
where:
K[17]: expanded key
ROUND: encryption round 1-17
INBYTE: one input byte
OUTBYTE: encrypted result for output
SBOX[256]: pre computed S-Box for encryption
## Analysis so far
While this stuff only exists for me to play around with
crypto, I tried to test the cipher a little bit. Here are
my results using a couple of statistical measurements:
I encrypted the GPLv3 contents using the key "1". To compare,
I encrypted the same file with AES-256-CBC using the same
passphrase.
### Check using analyze.c
My own measurement, see analyze.c:
File size: 35147 bytes
Char distribution: 100.000000%
Char redundancy: 0.000000%
Char entropy: 7.995333 bits/char
Compression rate: 0.000000% (35147 => 35168 bytes)
For comparision, AES result:
File size: 35168 bytes
Char distribution: 100.000000%
Char redundancy: 0.000000%
Char entropy: 7.994892 bits/char
Compression rate: 0.000000% (35168 => 35189 bytes)
## Check using ent
(ent from http://www.fourmilab.ch/random/):
Entropy = 7.995333 bits per byte.
Optimum compression would reduce the size
of this 35147 byte file by 0 percent.
Chi square distribution for 35147 samples is 229.98, and randomly
would exceed this value 86.79 percent of the times.
Arithmetic mean value of data bytes is 127.6631 (127.5 = random).
Monte Carlo value for Pi is 3.172955438 (error 1.00 percent).
Serial correlation coefficient is -0.004405 (totally uncorrelated = 0.0).
For comparision, AES result:
Entropy = 7.994892 bits per byte.
Optimum compression would reduce the size
of this 35168 byte file by 0 percent.
Chi square distribution for 35168 samples is 250.98, and randomly
would exceed this value 55.94 percent of the times.
Arithmetic mean value of data bytes is 127.8717 (127.5 = random).
Monte Carlo value for Pi is 3.151680601 (error 0.32 percent).
Serial correlation coefficient is 0.002014 (totally uncorrelated = 0.0).
## Check using dieharder
I fed the contents of my primary disk into TWENTY4 and its output
into diehard:
dd if=/dev/sda4 of=/dev/stdout | ./stream 1 e | dieharder -a -g 200
#=============================================================================#
# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
#=============================================================================#
rng_name |rands/second| Seed |
stdin_input_raw| 1.86e+05 |2067533949|
#=============================================================================#
test_name |ntup| tsamples |psamples| p-value |Assessment
#=============================================================================#
diehard_birthdays| 0| 100| 100|0.11286983| PASSED
diehard_operm5| 0| 1000000| 100|0.14228207| PASSED
diehard_rank_32x32| 0| 40000| 100|0.08372938| PASSED
diehard_rank_6x8| 0| 100000| 100|0.47630577| PASSED
diehard_bitstream| 0| 2097152| 100|0.68878582| PASSED
diehard_opso| 0| 2097152| 100|0.36965490| PASSED
diehard_oqso| 0| 2097152| 100|0.85360068| PASSED
diehard_dna| 0| 2097152| 100|0.41389081| PASSED
diehard_count_1s_str| 0| 256000| 100|0.64198483| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.48126427| PASSED
diehard_parking_lot| 0| 12000| 100|0.61281762| PASSED
diehard_2dsphere| 2| 8000| 100|0.98794548| PASSED
diehard_3dsphere| 3| 4000| 100|0.86553337| PASSED
diehard_squeeze| 0| 100000| 100|0.47837267| PASSED
diehard_sums| 0| 100| 100|0.26661852| PASSED
diehard_runs| 0| 100000| 100|0.78455791| PASSED
diehard_runs| 0| 100000| 100|0.56428921| PASSED
diehard_craps| 0| 200000| 100|0.81900152| PASSED
diehard_craps| 0| 200000| 100|0.54592338| PASSED
ctrl-c
(FIXME: I aborted here, I'll repeat that one later)
So, all those checks don't look that bad, but of course this doesn't
say much about TWENTY4. However, not THAT bad for the first cipher :)

16
analyze/Makefile Normal file
View File

@@ -0,0 +1,16 @@
LDFLAGS = -g
CFLAGS = -g -Wall -Wextra -Werror
DST = analyze
OBJS = analyze.o
all: $(DST)
$(DST): $(OBJS)
gcc $(LDFLAGS) $(OBJS) -lm -lz -o $(DST)
%.o: %.c
gcc -c $(CFLAGS) $*.c -o $*.o
clean:
rm -f *.o $(DST)

221
analyze/analyze.c Normal file
View File

@@ -0,0 +1,221 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <assert.h>
#include <zlib.h>
typedef uint8_t byte;
typedef uint32_t word;
typedef uint16_t half;
struct _bytes {
size_t len;
byte *bin;
};
typedef struct _bytes Bytes;
#define CHUNK 16384
Bytes *fetch(char *infile) {
size_t insize, p=0;
FILE *in;
Bytes *out;
if((in = fopen(infile, "rb")) == NULL) {
perror("Could not open infile");
exit(1);
}
fseek(in, 0L, SEEK_END);
insize = ftell(in);
fseek(in, 0L, SEEK_SET);
out = malloc(sizeof(Bytes));
out->bin = malloc(insize * sizeof(byte));
out->len = insize;
memset(out->bin, 0, insize);
while (!ferror(in) && !feof(in)) {
fread(&out->bin[p++], 1, 1, in);
}
fclose(in);
return out;
}
size_t defl(FILE *source, FILE *dest, int level)
{
int ret, flush;
unsigned have;
z_stream strm;
size_t zsize;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
zsize = 0;
/* allocate deflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm, level);
if (ret != Z_OK)
return ret;
/* compress until end of file */
do {
strm.avail_in = fread(in, 1, CHUNK, source);
if (ferror(source)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in;
/* run deflate() on input until output buffer not full, finish
compression if all of source has been read in */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = deflate(&strm, flush); /* no bad return value */
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
have = CHUNK - strm.avail_out;
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
zsize += have;
} while (strm.avail_out == 0);
assert(strm.avail_in == 0); /* all input will be used */
/* done when last data in file processed */
} while (flush != Z_FINISH);
assert(ret == Z_STREAM_END); /* stream will be complete */
/* clean up and return */
(void)deflateEnd(&strm);
return zsize;
}
size_t get_zsize(char *infile) {
FILE *in;
FILE *z;
size_t zsize;
if((in = fopen(infile, "rb")) == NULL) {
perror("Could not open infile");
exit(1);
}
if((z = fopen("/tmp/analyze.z", "wb")) == NULL) {
perror("Could not open zfile");
exit(1);
}
zsize = defl(in, z, 9);
fclose(in);
fclose(z);
unlink("/tmp/analyze.z");
return zsize;
}
double get_entropy(byte *source, size_t len) {
int *hist;
double H;
int wherechar[256];
int i,histlen;
histlen = 0;
H = 0;
hist = (int*)calloc(len, sizeof(int));
for(i=0; i<256; i++)
wherechar[i] = -1;
for(i=0; i<(int)len; i++){
if(wherechar[(int)source[i]] == -1) {
wherechar[(int)source[i]] = histlen;
histlen++;
}
hist[wherechar[(int)source[i]]]++;
}
for(i=0; i<histlen; i++) {
H -= (double)hist[i] / len * log2((double)hist[i] / len);
}
return H;
}
double get_distribution(Bytes *in) {
int i;
byte hash[256] = {0};
double chars = 0;
for (i=0; i<(int)in->len; i++) {
hash[in->bin[i]]++;
}
for (i=0; i<256; i++) {
if(hash[i]) {
chars++;
}
}
return chars;
}
void analyze(char *infile) {
double chars = 0, dist, red, entropy, zrate;
size_t zsize;
Bytes *in = fetch(infile);
entropy = get_entropy(in->bin, in->len);
chars = get_distribution(in);
zsize = get_zsize(infile);
dist = chars / 2.56;
red = 100 - dist;
zrate = zsize > in->len ? 0 : 100 - (zsize / (in->len / 100));
fprintf(stdout,
" File size: %ld bytes\n"
"Char distribution: %lf%%\n"
" Char redundancy: %lf%%\n"
" Char entropy: %lf bits/char\n"
" Compression rate: %lf%% (%ld => %ld bytes)\n",
in->len, dist, red, entropy, zrate, in->len, zsize
);
}
void print_chars_hex(Bytes *in) {
int i;
for (i=0; i<(int)in->len; i++) {
fprintf(stdout, "%02x\n", in->bin[i]);
}
}
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr, "Usage: analyze <file>\n");
return 1;
}
else {
analyze(argv[1]);
}
return 0;
}

32
analyze/analyze.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
an=./analyze
cr=./stream
mkdir -p t
jot () {
r=$1;
if test -z "$r"; then
echo "jot <rounds>";
else
perl -e "foreach(0..$r){print \"\$_\n\";}";
fi
}
./prepare-analyze
for L in `jot 255`; do
XL=`printf "%02x" $L`
clear="t/$XL.clear"
for K in `jot 255`; do
XK=`printf "%02x" $K`
cipher="t/$XLx$XK.cipher"
cat $clear | $cr 0x$K e > $cipher
echo -n "$XL w/ $XK: "
$an $cipher 1
rm -f $cipher
done
done

BIN
sbox/BJNR000010949.epub Normal file

Binary file not shown.

1
sbox/BJNR000010949.pass Normal file
View File

@@ -0,0 +1 @@
grundgesetz

19
sbox/Makefile Normal file
View File

@@ -0,0 +1,19 @@
LDFLAGS = -g
CFLAGS = -g -Wall -Wextra -Werror
DST = gen-static-sbox
OBJS = gen-static-sbox.o
all: $(DST)
$(DST): $(OBJS)
gcc $(LDFLAGS) $(OBJS) -o $(DST)
%.o: %.c
gcc -c $(CFLAGS) $*.c -o $*.o
clean:
rm -f *.o $(DST)
sboxes:
cat BJNR000010949.epub | openssl aes-256-cbc -kfile BJNR000010949.pass | ./$(DST)

58
sbox/gen-static-sbox.c Normal file
View File

@@ -0,0 +1,58 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
typedef uint8_t byte;
typedef uint32_t word;
typedef uint16_t half;
#define MAX 4096
void dump256(byte *hash) {
int i, b = 1;
for(i=0; i<256; i++) {
fprintf(stderr, "0x%02x, ", hash[i]);
if(b == 16) {
fprintf(stderr, "\n");
b = 1;
}
else
b++;
}
fprintf(stderr, "\n");
}
int main() {
byte raw[MAX];
byte hash[256] = {0}, reg[256] = {0}, out;
int i, b=0, has=0;
fread(raw, MAX, 1, stdin);
memset(hash, 0, 256);
for (i=0; i<MAX; i++) {
out = raw[i];
if(reg[out] == 0) {
reg[out]++;
hash[b++] = out;
has++;
}
if(has == 256) {
dump256(hash);
has = b = 0;
memset(hash, 0, 256);
memset(reg, 0, 256);
}
}
//fwrite(hash, 256, 1, stdout);
fprintf(stderr, "done\n");
return 0;
}

308
twenty4.c Normal file
View File

@@ -0,0 +1,308 @@
/*
******* THIS IS JUST FOR LEARINING CRYPTO, DO NOT EVER USE THIS FOR ANYTHING *******
This is the implementation of the fun stream cipher TWENTY4 by Thomas von Dein, 09/2015.
Published under the public domain, Creative Commons Zero License.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
typedef uint8_t byte;
typedef uint32_t word;
typedef uint16_t half;
const byte kbox[] = {
0x53, 0x61, 0x6c, 0x74, 0x65, 0x64, 0x5f, 0xdf, 0x40, 0xc1, 0x9d, 0x46, 0x33, 0x45, 0x92, 0x95,
0xd8, 0x24, 0xf5, 0x1c, 0xe0, 0x29, 0xff, 0xa3, 0x71, 0x6f, 0x35, 0x2e, 0x4b, 0x0d, 0xa7, 0x5d,
0x97, 0xe1, 0x98, 0x58, 0x2b, 0xc4, 0xae, 0xe3, 0xec, 0xb8, 0x38, 0xee, 0x91, 0x2c, 0xb4, 0xa0,
0xc6, 0x34, 0x1f, 0x57, 0x0e, 0xc3, 0x4f, 0xb9, 0x80, 0x21, 0x5b, 0x06, 0xf6, 0x87, 0xfa, 0x5e,
0xe7, 0xda, 0xce, 0xdd, 0x23, 0xe9, 0x03, 0x39, 0xa5, 0x8e, 0xb6, 0xca, 0x3c, 0x7a, 0x44, 0x2d,
0x07, 0xcf, 0x1b, 0xd0, 0x94, 0x85, 0xc5, 0x20, 0xaa, 0x81, 0xc9, 0xb7, 0x2f, 0xfb, 0xb2, 0x50,
0x54, 0xf0, 0x14, 0xd9, 0x00, 0x67, 0x15, 0x9f, 0xa2, 0x02, 0x93, 0xcc, 0xdb, 0x8d, 0x30, 0x78,
0xb1, 0x7b, 0x19, 0xc0, 0x43, 0x6b, 0xbb, 0x2a, 0x3b, 0x4d, 0xe4, 0x08, 0x12, 0x90, 0x32, 0xef,
0xe8, 0x5a, 0xac, 0xf4, 0x8c, 0xe2, 0x4e, 0x6d, 0xaf, 0x66, 0xf8, 0xbc, 0x36, 0x72, 0x01, 0x1e,
0x68, 0x37, 0x59, 0x51, 0xa6, 0x7c, 0xbe, 0x86, 0x8a, 0x8b, 0xfe, 0x0a, 0x05, 0x52, 0x76, 0x27,
0x69, 0x18, 0x22, 0x63, 0x42, 0x4a, 0xad, 0x10, 0xe5, 0xa1, 0xc8, 0xeb, 0xb0, 0x09, 0x6a, 0x4c,
0x16, 0xf7, 0xde, 0xfc, 0x7f, 0x7d, 0xdc, 0x99, 0xbd, 0x7e, 0x26, 0xcd, 0xba, 0xc2, 0xa8, 0x04,
0x0f, 0x3e, 0x82, 0x1d, 0x89, 0xb5, 0x31, 0xb3, 0x47, 0x6e, 0xf3, 0x0b, 0xd3, 0x84, 0x49, 0x0c,
0x3d, 0xd5, 0x9a, 0xd6, 0x9e, 0xd7, 0x8f, 0xa9, 0x79, 0xd4, 0x48, 0x9b, 0x55, 0x56, 0xcb, 0x3a,
0xf9, 0xfd, 0xd2, 0xe6, 0x75, 0x1a, 0x11, 0xf2, 0xa4, 0x5c, 0x96, 0x13, 0xea, 0xd1, 0xbf, 0x60,
0x28, 0xab, 0x9c, 0x77, 0x83, 0x62, 0x17, 0x41, 0x70, 0x25, 0xf1, 0x3f, 0x88, 0x73, 0xc7, 0xed,
};
const byte sbox[] = {
0x61, 0x2d, 0x19, 0xf3, 0xe5, 0xd9, 0xde, 0x5f, 0x41, 0x31, 0xa7, 0xc2, 0x48, 0x02, 0xef, 0x98,
0x67, 0xcb, 0x6e, 0x4c, 0xf4, 0x11, 0xfa, 0x87, 0x0f, 0x6f, 0x0a, 0x3b, 0x71, 0x09, 0x1a, 0xb8,
0x3c, 0x44, 0xd8, 0xd4, 0xc8, 0x91, 0x6d, 0x8c, 0x2f, 0xce, 0x85, 0x22, 0xd5, 0x08, 0xa6, 0x97,
0x68, 0xbc, 0x3a, 0xa0, 0xbf, 0xa5, 0x47, 0x94, 0x83, 0xd1, 0x18, 0x29, 0x03, 0xb2, 0xa4, 0xfe,
0xe4, 0x4d, 0xdf, 0x21, 0xc0, 0x70, 0x4f, 0x90, 0x04, 0x40, 0x0b, 0x49, 0xe0, 0x25, 0xd7, 0xda,
0xf8, 0x1f, 0x9e, 0x76, 0xbb, 0xaa, 0xc5, 0x2e, 0x72, 0x64, 0xd6, 0x74, 0x10, 0x78, 0xfd, 0x45,
0x80, 0x4e, 0x7f, 0x12, 0xb7, 0xc6, 0xea, 0xb3, 0x37, 0x5a, 0xf2, 0xc3, 0xb6, 0x5b, 0x81, 0x95,
0xbd, 0xb0, 0xae, 0x8f, 0xd2, 0xcf, 0x1e, 0xc7, 0xee, 0xa1, 0x7a, 0xb9, 0x06, 0xa8, 0xb1, 0x93,
0x30, 0xad, 0x33, 0x77, 0x3d, 0x7c, 0xb4, 0x36, 0x92, 0x15, 0x89, 0x7e, 0xe9, 0x17, 0x07, 0x8a,
0x9f, 0x32, 0x2c, 0xf9, 0xb5, 0x7d, 0xeb, 0x23, 0xdc, 0x2b, 0x63, 0x88, 0x56, 0x42, 0x84, 0x4b,
0x0e, 0xec, 0x8d, 0x7b, 0x05, 0xed, 0xca, 0xe8, 0xe6, 0xba, 0x01, 0x5d, 0x26, 0x28, 0x13, 0x9d,
0x54, 0x59, 0xfb, 0xf0, 0xd3, 0xf7, 0xdb, 0xe7, 0xbe, 0x58, 0x5e, 0x99, 0x65, 0x8b, 0x20, 0xa3,
0xc1, 0x1c, 0xaf, 0xac, 0x55, 0xe3, 0xdd, 0x62, 0x2a, 0xcc, 0xd0, 0xe2, 0x0c, 0x66, 0x96, 0x8e,
0xab, 0xfc, 0xc4, 0x1d, 0x6a, 0x6c, 0x3f, 0x9b, 0x9a, 0x51, 0xa2, 0x86, 0x52, 0x4a, 0x43, 0x14,
0x75, 0xff, 0xf5, 0xcd, 0x1b, 0x0d, 0x35, 0x24, 0x9c, 0xe1, 0x60, 0x73, 0x3e, 0x39, 0x53, 0x16,
0x50, 0x6b, 0xc9, 0x46, 0x57, 0x5c, 0x69, 0x79, 0x82, 0xf1, 0x27, 0x38, 0x34, 0xf6, 0x00, 0xa9,
};
byte revsbox[256];
#define K_HASH_ROUNDS 32
#define S_BOX_ROUNDS 17
byte rot8left(byte in, int rot) {
return (in >> (8-rot)) | (in << rot);
}
byte rot8right(byte in, int rot) {
return (in << (8-rot)) | (in >> rot);
}
void printbits(byte v) {
int i;
for(i = 7; i >= 0; i--) fprintf(stderr, "%c", '0' + ((v >> i) & 1));
}
void dump8(char *n, byte d) {
fprintf(stderr, "%s: %02x ", n, d);
printbits(d);
fprintf(stderr, "\n");
}
void dumpN(char *n, byte *d, size_t s) {
int l = strlen(n) + 9;
fprintf(stderr, "%s (%04ld): ", n, s);
size_t i;
int c;
for (i=0; i<s; ++i) {
fprintf(stderr, "%02x ", d[i]);
if(i % 8 == 7 && i > 0) {
fprintf(stderr, "\n");
for(c=0; c<l; ++c)
fprintf(stderr, " ");
}
}
fprintf(stderr, "\n");
}
/* for decryption */
void reverse_sbox() {
int i;
for(i=0; i<256; i++)
revsbox[sbox[i]] = i;
}
byte getiv() {
FILE *RAND;
byte rand;
if((RAND = fopen("/dev/urandom", "rb")) == NULL) {
perror("Could not open /dev/urandom");
exit(1);
}
if(fread(&rand, 1, 1, RAND) != 1) {
perror("Could not read from /dev/urandom");
exit(1);
}
fclose(RAND);
return rand;
}
byte rcon(byte in) {
byte c=1;
if(in == 0)
return 0;
while(in != 1) {
byte b;
b = c & 0x80;
c <<= 1;
if(b == 0x80) {
c ^= 0x1b;
}
in--;
}
return c;
}
/* we use rounds * 8bit sub keys expanded from
given password */
void keyhash(char *pw, byte *hash) {
byte iv;
int i, round;
unsigned int HEX;
size_t pwlen;
if(strncmp(pw, "0x", 2) == 0) {
/* hex pw */
sscanf(pw, "0x%02x", &HEX);
pw[0] = (byte)HEX;
pwlen = 1;
}
else {
pwlen = strlen(pw);
}
iv = kbox[(byte)pw[0]];
/* stretch pw */
for(i=0; i<S_BOX_ROUNDS; i++) {
if((size_t)i < pwlen)
hash[i] = iv ^ pw[i];
else
hash[i] = iv ^ kbox[i*8];
hash[i] = kbox[hash[i]];
iv = hash[i];
}
/* diffuse and confuse hash */
for(round=0; round<K_HASH_ROUNDS; round++) {
for(i=0; i<S_BOX_ROUNDS; i++) {
hash[i] = iv ^ (rot8left(hash[i], 3) ^ kbox[rcon(iv)]);
iv = hash[i];
}
}
}
void reverse(byte a[], int sz) {
int i, j;
for (i = 0, j = sz; i < j; i++, j--) {
byte tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
void rotate(byte array[], int size, int amt) {
if (amt < 0)
amt = size + amt;
reverse(array, size-amt-1);
reverse(array+size-amt, amt-1);
reverse(array, size-1);
}
void rotatekey(byte *key, byte feedback) {
rotate(key, S_BOX_ROUNDS, 1);
int i;
for(i=0; i<S_BOX_ROUNDS; i++) {
key[i] = kbox[key[i] ^ feedback];
}
}
/* actual stream cipher:
- xor with round key
- apply sbox
- rotate left by (round mod 8) bits
- xor with (round key rotated left by 4 bits [halfes reversed])
*/
byte bytebox(byte in, byte *key, int encrypt) {
int i;
byte out = in;
if(encrypt) {
for(i=0; i<S_BOX_ROUNDS; i++) {
out ^= key[i];
out = sbox[out];
out = rot8left(out, i%8);
out ^= rot8right(key[i], 4);
}
rotatekey(key, out);
}
else {
for(i=S_BOX_ROUNDS-1; i>= 0; i--) {
out ^= rot8left(key[i], 4);
out = rot8right(out, i%8);
out = revsbox[out];
out ^= key[i];
}
rotatekey(key, in);
}
return out;
}
/* work on stdin and stdout */
int handleio(byte *key, int encrypt) {
byte in, out;
while (fread(&in, 1, 1, stdin) == 1) {
out = bytebox(in, key, encrypt);
fwrite(&out, 1, 1, stdout);
}
return 0;
}
/* work on stdin and stdout, in CBC 8bit mode */
int cbc_handleio(byte *key, int encrypt) {
byte in, out, iv;
if(encrypt) {
iv = getiv();
fwrite(&iv, 1, 1, stdout);
}
else {
fread(&iv, 1, 1, stdin);
}
while (fread(&in, 1, 1, stdin) == 1) {
if(encrypt) {
out = bytebox(iv ^ in, key, encrypt);
iv = out;
}
else {
out = iv ^ bytebox(in, key, encrypt);
iv = in;
}
fwrite(&out, 1, 1, stdout);
}
return 0;
}
int main(int argc, char **argv) {
byte key[S_BOX_ROUNDS];
int encrypt;
if(argc != 3) {
fprintf(stderr, "Usage: stream <passwd> <e|n>\ne=encrypt, n=decrypt\n");
return 1;
}
else {
encrypt = 0;
if(strcmp(argv[2], "e") == 0)
encrypt = 1;
reverse_sbox();
keyhash(argv[1], key);
return handleio(key, encrypt);
}
}