HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Brute-force Vigenere Cipher using multiple threads

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
forcethreadsvigenereusingmultiplebrutecipher

Problem

I'm trying to brute force a Vigenere Cipher knowing only that the key length is 5+ characters and that the decrypted text has the word Hello, Andi. So I wrote this small Java program that tries to brute-force the key through a given numbers of threads and nested while loops.

Run.java (main class)

package vignere;

import java.io.IOException;

public class Run {

    public static void main(String[] args) throws IOException {
//number of threads you have and encrypted text file
        Brute brute = new Brute(4, "41259.txt.enc");

        Thread t1 = new Thread() {
            @Override
            public void run() {
                try {
                    brute.bruteF_thread(1);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                try {
                    brute.bruteF_thread(2);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t3 = new Thread() {
            @Override
            public void run() {
                try {
                    brute.bruteF_thread(3);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t4 = new Thread() {
            @Override
            public void run() {
                try {
                    brute.bruteF_thread(4);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };

        t1.start();
        t2.start();
        t3.start();
        t4.start();

        try {
            t1.join();
            t2.join();
            t3.join();
            t4.join();

        } catch (Exception e) {
            e.printStackTrace();

        }

    }
}


And the class that handles the brute-force attack
B

Solution

I'll have a try to decompose the problem into responsibilities:

  • You need a key generator that continiously produces unique keys of a certain key length that are used to shift characters



  • You need a Decoder that will decode an encoded Text with a given key



  • You need an indicator that your decoding was potentially successful.



1

public class KeyGenerator {

    private int currentKeyLength;

    public KeyGenerator(int minimumKeyLength) {
        this.currentKeyLength = minimumKeyLength;
    }

    public synchronized Key next() {

       // determine current key length

        byte[] keyBytes = new byte[currentKeyLength];

        // build next shift sequence

        return new Key(keyBytes);
    }

}


2

public class Decoder {    

    private EncodedText encodedText;
    private Key key;

    public Decoder(EncodedText encodedText, Key key) {
        this.encodedText = encodedText;
        this.key = key;
    }       

    public DecodedText decode() {

        byte[] keyBytes = key.getKeyBytes();
        byte[] decodedTextBytes = new byte[encodedText.getEncodedTextBytes().length];

        // decode encoded text bytes with key bytes

        return new DecodedText(decodedTextBytes);
    }       

}


3

public class DecodeSuccessfulIndicator implements Predicate {

    private String referenceString;

    public DecodeSuccessfulIndicator(String referenceString) {
        this.referenceString = referenceString;
    }  

    @Override
    public boolean test(DecodedText decodedText) {
        return new String(decodedText.getDecodedTextBytes()).indexOf(referenceString) >= 0;
    }

}


  • I introduced some wrapper objects as byte arrays are not that expressive: EncodedText, Key and DecodedText



4

public class EncodedText {

    private byte[] encodedTextBytes;

    public EncodedText(byte[] encodedTextBytes) {
        this.encodedTextBytes = encodedTextBytes;
    }

    public byte[] getEncodedTextBytes() {
        return encodedTextBytes;
    }

}

public class Key {

    private byte[] keyBytes;

    public Key(byte[] keyBytes) {
        this.keyBytes = keyBytes;
    }

    public byte[] getKeyBytes() {
        return keyBytes;
    }

}

public class DecodedText {

    private byte[] decodedTextBytes;

    public DecodedText(byte[] decodedTextBytes) {
        this.decodedTextBytes = decodedTextBytes;
    }

    public byte[] getDecodedTextBytes() {
        return decodedTextBytes;
    }

}


Composing the elements

Now we have all responsibilities together we can couple them. I was really astonished as I found a proper application for streams. This is a JAVA 8 style solution:

First we create a stream of keys:

Stream keyStream = Stream.generate(new KeyGenerator(4)::next);


After that we load the encodedText:

EncodedText encodedText = new EncodedText(Files.readAllBytes(Paths.get("41259.txt.enc")));


Now we can decode the encoded text with every key in the stream and get a stream of decoded texts:

Stream decodedTextStream = keyStream.map(key -> new Decoder(encodedText, key).decode());


After that we create a predicate that defines that we are only interested in potentially successful decoded texts:

DecodeSuccessfulIndicator decodeSuccessfulIndicator = new DecodeSuccessfulIndicator("andi");


Finally we define a filter on the stream and collect our results:

List collect = decodedTextStream.filter(decodeSuccessfulIndicator::test).collect(Collectors.toList());


All together:

public class Run {

    public static void main(String[] args) throws IOException {

        Stream keyStream = Stream.generate(new KeyGenerator(4)::next);

        EncodedText encodedText = new EncodedText(Files.readAllBytes(Paths.get("41259.txt.enc")));

        Stream decodedTextStream = keyStream.map(key -> new Decoder(encodedText, key).decode());

        DecodeSuccessfulIndicator decodeSuccessfulIndicator = new DecodeSuccessfulIndicator("andi");

        List collect = decodedTextStream.filter(decodeSuccessfulIndicator::test).collect(Collectors.toList());

    }

}


Language sugar

One thing I did not adress until now: parallelism. You used threads to increase brute force speed. I want to suggest a different approach. As we are already using the JAVA 8 Streaming API we have only one thing to change to load our CPUs:

Change

Stream decodedTextStream = keyStream.map(key -> new Decoder(encodedText, key).decode());


to

Stream decodedTextStream = keyStream.parallel().map(key -> new Decoder(encodedText, key).decode());


Changing the key stream to a parallel stream will cause concurrency. Noticed the synchronized method in the KeyGenerator to work properly in parallel?

public class KeyGenerator {

    ... 

    public synchronized Key next() {
        ...
    }

}


Finally

I did a little research as I was interested in a solution for limiting streams by a certain condition. I had to implement a mystic interface called "Spl

Code Snippets

public class KeyGenerator {

    private int currentKeyLength;

    public KeyGenerator(int minimumKeyLength) {
        this.currentKeyLength = minimumKeyLength;
    }

    public synchronized Key next() {

       // determine current key length

        byte[] keyBytes = new byte[currentKeyLength];

        // build next shift sequence

        return new Key(keyBytes);
    }

}
public class Decoder {    

    private EncodedText encodedText;
    private Key key;

    public Decoder(EncodedText encodedText, Key key) {
        this.encodedText = encodedText;
        this.key = key;
    }       

    public DecodedText decode() {

        byte[] keyBytes = key.getKeyBytes();
        byte[] decodedTextBytes = new byte[encodedText.getEncodedTextBytes().length];

        // decode encoded text bytes with key bytes

        return new DecodedText(decodedTextBytes);
    }       

}
public class DecodeSuccessfulIndicator implements Predicate<DecodedText> {

    private String referenceString;

    public DecodeSuccessfulIndicator(String referenceString) {
        this.referenceString = referenceString;
    }  

    @Override
    public boolean test(DecodedText decodedText) {
        return new String(decodedText.getDecodedTextBytes()).indexOf(referenceString) >= 0;
    }

}
public class EncodedText {

    private byte[] encodedTextBytes;

    public EncodedText(byte[] encodedTextBytes) {
        this.encodedTextBytes = encodedTextBytes;
    }

    public byte[] getEncodedTextBytes() {
        return encodedTextBytes;
    }

}

public class Key {

    private byte[] keyBytes;

    public Key(byte[] keyBytes) {
        this.keyBytes = keyBytes;
    }

    public byte[] getKeyBytes() {
        return keyBytes;
    }

}

public class DecodedText {

    private byte[] decodedTextBytes;

    public DecodedText(byte[] decodedTextBytes) {
        this.decodedTextBytes = decodedTextBytes;
    }

    public byte[] getDecodedTextBytes() {
        return decodedTextBytes;
    }

}
Stream<Key> keyStream = Stream.generate(new KeyGenerator(4)::next);

Context

StackExchange Code Review Q#154124, answer score: 5

Revisions (0)

No revisions yet.