mirror of
https://codeberg.org/scip/twenty4.git
synced 2025-12-16 11:30:57 +01:00
initial commit
This commit is contained in:
16
Makefile
Normal file
16
Makefile
Normal 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
217
README.md
@@ -1,2 +1,215 @@
|
||||
# twenty4
|
||||
TWENTY4 - fun stream cipher
|
||||
## TWENTY4 - a 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
16
analyze/Makefile
Normal 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
221
analyze/analyze.c
Normal 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
32
analyze/analyze.sh
Executable 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
BIN
sbox/BJNR000010949.epub
Normal file
Binary file not shown.
1
sbox/BJNR000010949.pass
Normal file
1
sbox/BJNR000010949.pass
Normal file
@@ -0,0 +1 @@
|
||||
grundgesetz
|
||||
19
sbox/Makefile
Normal file
19
sbox/Makefile
Normal 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
58
sbox/gen-static-sbox.c
Normal 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
308
twenty4.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user