I've tested Twofish (256 bit) as follows. A lengthy pseudorandom sequence was generated and used as a key. Plaintext phrases 000000000..00
, 00000..01
, 0000...FFFFF
, were generated and encoded using Twofish with the key. Ciphertexts were split in 32 bit numbers (40 million numbers total).
In theory, the cipher should pass the Dieharder tests. In practice, it appears to fail a number of them.
Pseudorandom number generator passed all tests, so this isn't a bug with Dieharder.
Does this mean that Twofish is not as secure as claimed?
Edited:
TwoFish.java comes from http://www.sethi.org/tmp/ssh/src/com/mindbright/security/cipher/Twofish.java
FishRunner.java
is my wrapper that generates "sequential plain text blocks as ASCII characters". Each block is ciphered using Twofish with byte[] defaultKey = {-24,29,83,38,-77,-92,-46,25,-117,-71,42,-44,-52,97,58,-114,50,-27,49,47,12,52,-76,-88,-26,17,18,84,30,-95,80,13,32};
. From each plaintext block I get 256 bits of ciphered text. These 256 bits are split in chunks of 32 bit each and saved to "/tmp/FromFish/fish.txt" with an appropriate header.
first several lines of fish.txt
#==================================================================
# generator mt19937 seed = 316179543
#==================================================================
type: d
count: 40000000
numbit: 32
4205098517
1950644630
217236044
4021751533
1670623305
4216006007
1982760947
2956150679
2523872870
1802016715
546984380
3195151793
478820823
3432394711
Once all 40000000 records were created (440Mb of data) Dieharder was launched with dieharder -a -g 202 -f fish.txt >>res.txt
dieharder version 3.31.1 Copyright 2003 Robert G. Brown #
##
rng_name | filename |rands/second|
file_input| fish.txt| 7.26e+06 |
#
test_name |ntup| tsamples |psamples| p-value |Assessment
# b #
diehard_birthdays| 0| 100| 100|0.65344498| PASSED
diehard_operm5| 0| 1000000| 100|0.11726095| PASSED
diehard_rank_32x32| 0| 40000| 100|0.84203088| PASSED
diehard_rank_6x8| 0| 100000| 100|0.73796762| PASSED
diehard_bitstream| 0| 2097152| 100|0.38051493| PASSED
diehard_opso| 0| 2097152| 100|0.04995365| PASSED
diehard_oqso| 0| 2097152| 100|0.59990831| PASSED
diehard_dna| 0| 2097152| 100|0.62220146| PASSED
diehard_count_1s_str| 0| 256000| 100|0.86262588| PASSED
diehard_count_1s_byt| 0| 256000| 100|0.12560712| PASSED
diehard_parking_lot| 0| 12000| 100|0.28041223| PASSED
diehard_2dsphere| 2| 8000| 100|0.76183470| PASSED
diehard_3dsphere| 3| 4000| 100|0.17846568| PASSED
diehard_squeeze| 0| 100000| 100|0.00384865| WEAK
diehard_sums| 0| 100| 100|0.43470008| PASSED
diehard_runs| 0| 100000| 100|0.70560801| PASSED
diehard_runs| 0| 100000| 100|0.98961342| PASSED
diehard_craps| 0| 200000| 100|0.18043071| PASSED
diehard_craps| 0| 200000| 100|0.89290364| PASSED
marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED
marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED
sts_monobit| 1| 100000| 100|0.72613955| PASSED
sts_runs| 2| 100000| 100|0.26712378| PASSED
sts_serial| 1| 100000| 100|0.94935591| PASSED
sts_serial| 2| 100000| 100|0.93198856| PASSED
sts_serial| 3| 100000| 100|0.99554380| WEAK
sts_serial| 3| 100000| 100|0.72444970| PASSED
sts_serial| 4| 100000| 100|0.19981717| PASSED
sts_serial| 4| 100000| 100|0.06124493| PASSED
sts_serial| 5| 100000| 100|0.87811046| PASSED
sts_serial| 5| 100000| 100|0.09618787| PASSED
sts_serial| 6| 100000| 100|0.87317579| PASSED
sts_serial| 6| 100000| 100|0.99688012| WEAK
sts_serial| 7| 100000| 100|0.50043383| PASSED
sts_serial| 7| 100000| 100|0.61746527| PASSED
sts_serial| 8| 100000| 100|0.97376805| PASSED
sts_serial| 8| 100000| 100|0.94872352| PASSED
sts_serial| 9| 100000| 100|0.35958803| PASSED
sts_serial| 9| 100000| 100|0.39252366| PASSED
sts_serial| 10| 100000| 100|0.40682407| PASSED
sts_serial| 10| 100000| 100|0.69846273| PASSED
sts_serial| 11| 100000| 100|0.14757958| PASSED
sts_serial| 11| 100000| 100|0.85142983| PASSED
sts_serial| 12| 100000| 100|0.33628714| PASSED
sts_serial| 12| 100000| 100|0.80400201| PASSED
sts_serial| 13| 100000| 100|0.50635238| PASSED
sts_serial| 13| 100000| 100|0.04113439| PASSED
sts_serial| 14| 100000| 100|0.48030593| PASSED
sts_serial| 14| 100000| 100|0.83615004| PASSED
sts_serial| 15| 100000| 100|0.85634237| PASSED
sts_serial| 15| 100000| 100|0.86413582| PASSED
sts_serial| 16| 100000| 100|0.81247787| PASSED
sts_serial| 16| 100000| 100|0.62279344| PASSED
rgb_bitdist| 1| 100000| 100|0.10521730| PASSED
rgb_bitdist| 2| 100000| 100|0.36419006| PASSED
rgb_bitdist| 3| 100000| 100|0.50848488| PASSED
rgb_bitdist| 4| 100000| 100|0.91911028| PASSED
rgb_bitdist| 5| 100000| 100|0.68355135| PASSED
rgb_bitdist| 6| 100000| 100|0.07298074| PASSED
rgb_bitdist| 7| 100000| 100|0.99703911| WEAK
rgb_bitdist| 8| 100000| 100|0.50843903| PASSED
rgb_bitdist| 9| 100000| 100|0.51151893| PASSED
rgb_bitdist| 10| 100000| 100|0.40558627| PASSED
rgb_bitdist| 11| 100000| 100|0.27640943| PASSED
rgb_bitdist| 12| 100000| 100|0.64415540| PASSED
rgb_minimum_distance| 2| 10000| 1000|0.77930849| PASSED
rgb_minimum_distance| 3| 10000| 1000|0.89974521| PASSED
rgb_minimum_distance| 4| 10000| 1000|0.52655953| PASSED
rgb_minimum_distance| 5| 10000| 1000|0.01029230| PASSED
rgb_permutations| 2| 100000| 100|0.49947588| PASSED
rgb_permutations| 3| 100000| 100|0.48976917| PASSED
rgb_permutations| 4| 100000| 100|0.99435735| PASSED
rgb_permutations| 5| 100000| 100|0.18313717| PASSED
rgb_lagged_sum| 0| 1000000| 100|0.11846988| PASSED
rgb_lagged_sum| 1| 1000000| 100|0.20793259| PASSED
rgb_lagged_sum| 2| 1000000| 100|0.08406645| PASSED
rgb_lagged_sum| 3| 1000000| 100|0.00002903| WEAK
rgb_lagged_sum| 4| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 5| 1000000| 100|0.00156593| WEAK
rgb_lagged_sum| 6| 1000000| 100|0.13398055| PASSED
rgb_lagged_sum| 7| 1000000| 100|0.00000071| FAILED
rgb_lagged_sum| 8| 1000000| 100|0.07282751| PASSED
rgb_lagged_sum| 9| 1000000| 100|0.00000653| WEAK
rgb_lagged_sum| 10| 1000000| 100|0.24179580| PASSED
rgb_lagged_sum| 11| 1000000| 100|0.00620851| PASSED
rgb_lagged_sum| 12| 1000000| 100|0.24310357| PASSED
rgb_lagged_sum| 13| 1000000| 100|0.15323532| PASSED
rgb_lagged_sum| 14| 1000000| 100|0.00000174| WEAK
rgb_lagged_sum| 15| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 16| 1000000| 100|0.39385345| PASSED
rgb_lagged_sum| 17| 1000000| 100|0.11381934| PASSED
rgb_lagged_sum| 18| 1000000| 100|0.78715021| PASSED
rgb_lagged_sum| 19| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 20| 1000000| 100|0.52371093| PASSED
rgb_lagged_sum| 21| 1000000| 100|0.00531931| PASSED
rgb_lagged_sum| 22| 1000000| 100|0.19857791| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 24| 1000000| 100|0.00000431| WEAK
rgb_lagged_sum| 25| 1000000| 100|0.02471464| PASSED
rgb_lagged_sum| 26| 1000000| 100|0.07546643| PASSED
rgb_lagged_sum| 27| 1000000| 100|0.01786362| PASSED
rgb_lagged_sum| 28| 1000000| 100|0.28778610| PASSED
rgb_lagged_sum| 23| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 30| 1000000| 100|0.13632978| PASSED
rgb_lagged_sum| 31| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 32| 1000000| 100|0.11176539| PASSED
rgb_kstest_test| 0| 10000| 1000|0.29113864| PASSED
dab_bytedistrib| 0| 51200000| 1|0.00000000| FAILED
dab_dct| 256| 50000| 1|0.93719430| PASSED
Preparing to run test 207. ntuple = 0
dab_filltree| 32| 15000000| 1|0.60930301| PASSED
dab_filltree| 32| 15000000| 1|0.29252232| PASSED
Preparing to run test 208. ntuple = 0
dab_filltree2| 0| 5000000| 1|0.64246455| PASSED
dab_filltree2| 1| 5000000| 1|0.20829118| PASSED
Preparing to run test 209. ntuple = 0
dab_monobit2| 12| 65000000| 1|1.00000000| FAILED
Here's a summary of the tests that failed:
test_name |ntup| tsamples |psamples| p-value |Assessment
marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED
marsaglia_tsang_gcd| 0| 10000000| 100|0.00000000| FAILED
rgb_lagged_sum| 7| 1000000| 100|0.00000071| FAILED
rgb_lagged_sum| 15| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 19| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 23| 1000000| 100|0.00000000| FAILED
rgb_lagged_sum| 31| 1000000| 100|0.00000000| FAILED
dab_bytedistrib| 0| 51200000| 1|0.00000000| FAILED
dab_monobit2| 12| 65000000| 1|1.00000000| FAILED
Twofish algo is saved here: https://pastebin.com/THGegxKY
Class below is my wrapper to generate ciphers from consequential plain text messages
package models;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Random;
public class FishRunner {
private String filePath = "~/Dev/CleanFish/res/";
private String valuesFolder = "values/in_";
private String keysFolder = "keys/k_";
private String resultsFolder = "results/dh_";
private int keySize = 32;
private byte[] defaultKey = {-24,29,83,38,-77,-92,-46,25,-117,-71,42,-44,-52,97,58,-114,50,-27,49,47,12,52,-76,-88,-26,17,18,84,30,-95,80,13};
public static void main(String[] args) {
System.out.println("l: " + fS(toUnsignedLong(-1)) +", 2xMaxInt "+ (2* ((long) Integer.MAX_VALUE)) +"\n\n\n");
FishRunner fr = new FishRunner();
}
private FishRunner() {
int[] numberOfReps = {6180339, 9227465, 10000000, 14930352};
char[] codesN = {'a', 'b', 'c','d'};
byte[] defPlainTextByteValue = {0,1,2,8,15};
char[] codesP = {'0','1','2','8','F'};
byte[] defaultOffsetStartingPosition = {0,3,7,12};
String[] codesOff = {"00","03","07","12"};
for(int i=0; i< numberOfReps.length; i++) {
for(int j=0; j< defPlainTextByteValue.length; j++) {
for(int k=0; k< defaultOffsetStartingPosition.length; k++) {
String fName = codesN[i]+ "_"+codesP[j]+ "_"+codesOff[k]+".txt";
try {
testAlgo(fName, numberOfReps[i], 0, defPlainTextByteValue[j], defaultOffsetStartingPosition[k]) ;
System.out.println("Done: "+fName);
} catch (Exception e) { e.printStackTrace(); }
}
}
}
System.out.println("Done.");
}
private void testAlgo(String filename, int numOfRepetitions, int initPlainTextValue, byte backgroundValue, byte defaultOffsetStartingPosition) throws Exception {
writeDHCommand(filename);
byte[] keyChars = defaultKey;
Object key = TwoFish.makeKey(keyChars);
int lineCount = numOfRepetitions*4;
writeKeyToFile(filename,keyChars);
PrintWriter writer = new PrintWriter(filePath + valuesFolder + filename, "UTF-8");
writer.println("#==================================================================");
writer.println("# generator mt19937 seed = 316179543");
writer.println("#==================================================================");
writer.println("type: d");
writer.println("count: "+lineCount+"");
writer.println("numbit: 32");
for(int i = 0; i < numOfRepetitions; i++) {
int intOffset = i + initPlainTextValue;
byte[] text = generateText(intOffset,backgroundValue,defaultOffsetStartingPosition);
byte[] ct = TwoFish.blockEncrypt(text, 0, key);
for (int x = 0; x < ct.length ; x=x+4) {
writer.println(fS(Long.toString(toUnsignedLong(toInt(ct, x))),10));
}
}
writer.close();
}
private byte[] generateText(int stepNumber, byte defValue, byte defaultOffsetStartingPosition) {
byte[] text = new byte[32];
Arrays.fill(text, defValue);
text[(defaultOffsetStartingPosition)% 32] = (byte) (stepNumber );
text[(defaultOffsetStartingPosition+1)% 32] = (byte) (stepNumber >>> 8);
text[(defaultOffsetStartingPosition+2)% 32] = (byte) (stepNumber >>> 16);
text[(defaultOffsetStartingPosition+3)% 32] = (byte) (stepNumber >>> 24);
return text;
}
private byte[] initKey() {
Random r = new Random(0);
byte[] key = new byte[keySize];
for (int i = 0; i < keySize; i++) {
key[i] = (byte)(-128+r.nextInt(256)) ;
}
System.out.println("Key: "+Arrays.toString(key));
return key;
}
public static int toInt(byte[] bytes, int offset) {
int ret = 0;
for (int i=0; i<4 && i+offset<bytes.length; i++) {
ret <<= 8;
ret |= (int)bytes[i+offset] & 0xFF;
}
return ret;
}
public static long toUnsignedLong(int x) {
return x & 0x00000000ffffffffL;
}
private static final char[] HEX_DIGITS = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
private static String toString (byte[] ba, int offset, int length) {
char[] buf = new char[length * 2];
for (int i = offset, j = 0, k; i < offset+length; ) {
k = ba[i++];
buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
buf[j++] = HEX_DIGITS[ k & 0x0F];
}
return new String(buf);
}
private void writeDHCommand(String fileName) throws Exception {
String formattedFileName = valuesFolder + fileName;
String dhFileName = resultsFolder + fileName;
PrintWriter writerDHCommand = new PrintWriter(new FileOutputStream(new File(filePath+"dh_commands.txt"), true));
String dhCmdLine = "dieharder -a -g 202 -f "+ formattedFileName+ " >>"+ dhFileName;
writerDHCommand.println(dhCmdLine);
writerDHCommand.close();
}
private void writeKeyToFile(String filename, byte[] keyChars) throws Exception {
PrintWriter keyWritter = new PrintWriter(filePath + keysFolder + filename, "UTF-8");
keyWritter.println(Arrays.toString(keyChars));
keyWritter.close();
}
// INNER METHODS
private static final int longSpaces = 35;
private static final int intSpaces = 15;
public static String fS(long text) {
return fS(Long.toString(text), longSpaces);
}
public static String fS(int text) {
return fS(Integer.toString(text), intSpaces);
}
public static String fS(String text, int charTotal) {
return String.format("%1$" + charTotal + "s",text + "" );
}
private static String toString (byte[] ba) {
return toString(ba, 0, ba.length);
}
}