2015-02-04 20:51:31 +01:00
|
|
|
package de.vanitasvitae.enigmandroid;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enigma-machine
|
|
|
|
*
|
2015-02-18 21:51:40 +01:00
|
|
|
* @author vanitasvitae
|
2015-02-04 20:51:31 +01:00
|
|
|
*/
|
2015-02-18 21:51:40 +01:00
|
|
|
public class Enigma
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-18 21:51:40 +01:00
|
|
|
private Plugboard plugboard;
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-11 20:56:43 +01:00
|
|
|
//Slots for the rotors
|
2015-02-18 21:51:40 +01:00
|
|
|
private Rotor r1;
|
|
|
|
private Rotor r2;
|
|
|
|
private Rotor r3;
|
2015-02-11 20:56:43 +01:00
|
|
|
//Slot for the reflector
|
2015-02-18 21:51:40 +01:00
|
|
|
private Rotor reflector;
|
2015-02-17 01:07:33 +01:00
|
|
|
private boolean anomaly;
|
2015-02-18 21:51:40 +01:00
|
|
|
//Standard configuration (rotors 1-3, reflector B, all three rotors set to position 1, rings too)
|
|
|
|
public static final int[] STANDARD_CONFIGURATION = {1, 2, 3, 2, 1, 1, 1, 0, 0, 0};
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
/**
|
2015-02-11 20:56:43 +01:00
|
|
|
* Create new Enigma with given configuration.
|
|
|
|
* If pbconf == null no plugs will be set (no scrambling in the plugboard).
|
|
|
|
* If conf == null the enigma will be set to STANDARD_CONFIGURATION.
|
2015-02-18 21:51:40 +01:00
|
|
|
*
|
|
|
|
* @param pbconf two-dimensional array containing the chars symbolizing plugs that need to be switched over.
|
|
|
|
* @param conf configuration of the enigma (a,b,c,d,e,f,g - a-c rotors, d reflector, e-g positions of the rotors)
|
|
|
|
*/
|
|
|
|
public Enigma(char[][] pbconf, int[] conf) throws Plugboard.PlugAlreadyUsedException
|
|
|
|
{
|
|
|
|
if (conf != null) setConfiguration(conf);
|
|
|
|
else setConfiguration(Enigma.STANDARD_CONFIGURATION);
|
2015-02-04 20:51:31 +01:00
|
|
|
|
|
|
|
this.setPlugboard(pbconf);
|
2015-02-18 21:51:40 +01:00
|
|
|
}
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
/**
|
|
|
|
* Encrypt / Decrypt a given String
|
|
|
|
*
|
|
|
|
* @param w Text to decrypt/encrypt
|
|
|
|
* @return encrypted/decrypted string
|
|
|
|
*/
|
|
|
|
public String encrypt(String w)
|
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
//output string
|
2015-02-18 21:51:40 +01:00
|
|
|
String c = "";
|
|
|
|
//for each char x in k
|
|
|
|
for (int i = 0; i < w.length(); i++)
|
|
|
|
{
|
|
|
|
char x = w.charAt(i);
|
2015-02-11 20:56:43 +01:00
|
|
|
//encrypt char
|
2015-02-18 21:51:40 +01:00
|
|
|
c = c + this.encryptChar(x);
|
|
|
|
}
|
2015-02-11 20:56:43 +01:00
|
|
|
//return en-/decrypted string
|
2015-02-18 21:51:40 +01:00
|
|
|
return c;
|
|
|
|
}
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
/**
|
|
|
|
* Perform crypto on char.
|
|
|
|
* Beforehand rotate rotors. Also implement the rotor anomaly.
|
|
|
|
*
|
|
|
|
* @param k input char
|
|
|
|
* @return output char
|
|
|
|
*/
|
|
|
|
public char encryptChar(char k)
|
|
|
|
{
|
|
|
|
//Rotate rotors
|
|
|
|
r1.incrementCounter();
|
|
|
|
if (r1.isAtTurnoverPosition() || this.anomaly)
|
2015-02-17 01:07:33 +01:00
|
|
|
{
|
2015-02-04 20:51:31 +01:00
|
|
|
r2.incrementCounter();
|
2015-02-17 01:07:33 +01:00
|
|
|
//Handle Anomaly
|
2015-02-18 21:51:40 +01:00
|
|
|
this.anomaly = r2.doubleTurnAnomaly();
|
2015-02-17 01:07:33 +01:00
|
|
|
if (r2.isAtTurnoverPosition())
|
|
|
|
{
|
2015-02-04 20:51:31 +01:00
|
|
|
r3.incrementCounter();
|
|
|
|
}
|
|
|
|
}
|
2015-02-18 21:51:40 +01:00
|
|
|
int x = (int) k;
|
|
|
|
x = x - 65; //Remove Unicode Offset (A=65 in Unicode.)
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
//Encryption
|
|
|
|
//forward direction
|
|
|
|
x = plugboard.encrypt(x);
|
|
|
|
x = (x + r1.getCounter()) % 26;
|
|
|
|
x = r1.encryptForward(x);
|
|
|
|
x = (x + r2.getCounter() - r1.getCounter()) % 26;
|
|
|
|
x = r2.encryptForward(x);
|
|
|
|
x = (x + r3.getCounter() - r2.getCounter()) % 26;
|
|
|
|
x = r3.encryptForward(x);
|
|
|
|
x = (26 + x - r3.getCounter()) % 26;
|
|
|
|
//backward direction
|
|
|
|
x = reflector.encryptForward(x);
|
|
|
|
x = (26 + x + r3.getCounter()) % 26;
|
|
|
|
x = r3.encryptBackward(x);
|
|
|
|
x = (26 + x - r3.getCounter() + r2.getCounter()) % 26;
|
|
|
|
x = r2.encryptBackward(x);
|
|
|
|
x = (26 + x - r2.getCounter() + r1.getCounter()) % 26;
|
|
|
|
x = r1.encryptBackward(x);
|
|
|
|
x = (26 + x - r1.getCounter()) % 26;
|
|
|
|
x = plugboard.encrypt(x);
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
return (char) (x + 65); //Add Offset
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare String for encryption via enigma
|
2015-02-04 20:51:31 +01:00
|
|
|
* Replace . , ! ? : with X
|
|
|
|
* Remove all other chars that are not A..Z
|
2015-02-18 21:51:40 +01:00
|
|
|
*
|
|
|
|
* @param word string
|
|
|
|
* @return prepared string
|
|
|
|
*/
|
|
|
|
public static String prepare(String word)
|
|
|
|
{
|
|
|
|
String w = word.toUpperCase();
|
2015-02-04 20:51:31 +01:00
|
|
|
String c = "";
|
2015-02-18 21:51:40 +01:00
|
|
|
for (int i = 0; i < w.length(); i++)
|
|
|
|
{
|
|
|
|
char x = w.charAt(i);
|
|
|
|
if (x >= 65 && x <= 90) //If x in [A..Z]
|
|
|
|
{
|
|
|
|
c = c + x; //Append to String
|
|
|
|
}
|
|
|
|
//if x is special symbol
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (x == '.' || x == ',' || x == '!' || x == '?' || x == ':')
|
|
|
|
{
|
|
|
|
//replace x with X and encrypt
|
|
|
|
c = c + 'X';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
/**
|
|
|
|
* Create Plugboard configuration from String.
|
2015-02-11 20:56:43 +01:00
|
|
|
* String must be in format XY,AZ and so on.
|
2015-02-04 20:51:31 +01:00
|
|
|
* X and Y are plugs, that will be switched over.
|
2015-02-11 20:56:43 +01:00
|
|
|
* Don't use plugs twice such as in AA or AB,CA. This will cause Exceptions.
|
2015-02-18 21:51:40 +01:00
|
|
|
*
|
|
|
|
* @param p String
|
|
|
|
* @return Array containing plugboard configuration
|
|
|
|
*/
|
|
|
|
public static char[][] parsePlugs(String p) throws InvalidPlugboardConfigurationFormatException
|
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
//Check, if empty
|
2015-02-18 21:51:40 +01:00
|
|
|
if (p.length() == 0)
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
2015-02-11 20:56:43 +01:00
|
|
|
//Ensure uppercase and split string
|
2015-02-04 20:51:31 +01:00
|
|
|
String[] in = p.toUpperCase().split(",");
|
|
|
|
|
2015-02-11 20:56:43 +01:00
|
|
|
//Check, whether input have had a correct length. Length+1 divided by 3 should be exactly how much fields there are in the array.
|
|
|
|
//(2 chars or 2 chars followed by any times a comma and two chars)
|
2015-02-18 21:51:40 +01:00
|
|
|
if (in.length != (p.length() + 1) / 3)
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
throw new InvalidPlugboardConfigurationFormatException("Error parsing plugs! Maybe you missed a ','?");
|
2015-02-18 21:51:40 +01:00
|
|
|
} else
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
//Create new 2 dimensional array for pairs of plugs
|
2015-02-18 21:51:40 +01:00
|
|
|
char[][] plugs = new char[(p.length() + 1) / 3][2];
|
2015-02-11 20:56:43 +01:00
|
|
|
//Fill the array
|
2015-02-18 21:51:40 +01:00
|
|
|
int i = 0;
|
|
|
|
for (String x : in)
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
//Check, whether string is not representing a pair
|
2015-02-18 21:51:40 +01:00
|
|
|
if (x.length() != 2)
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
throw new InvalidPlugboardConfigurationFormatException("Error parsing plugs! Maybe you didn't enter a pair somewhere?");
|
2015-02-04 20:51:31 +01:00
|
|
|
}
|
2015-02-11 20:56:43 +01:00
|
|
|
//If it does
|
2015-02-04 20:51:31 +01:00
|
|
|
else
|
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
//add it to the array
|
2015-02-04 20:51:31 +01:00
|
|
|
plugs[i] = x.toCharArray();
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return plugs;
|
|
|
|
}
|
2015-02-18 21:51:40 +01:00
|
|
|
}
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-11 20:56:43 +01:00
|
|
|
/**
|
|
|
|
* Set the plugboard to a new created object and give it the configuration
|
2015-02-18 21:51:40 +01:00
|
|
|
*
|
2015-02-11 20:56:43 +01:00
|
|
|
* @param c configuration
|
|
|
|
* @throws Plugboard.PlugAlreadyUsedException
|
|
|
|
*/
|
2015-02-04 20:51:31 +01:00
|
|
|
public void setPlugboard(char[][] c) throws Plugboard.PlugAlreadyUsedException
|
|
|
|
{
|
|
|
|
plugboard = new Plugboard();
|
2015-02-18 21:51:40 +01:00
|
|
|
if (c != null)
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-11 20:56:43 +01:00
|
|
|
//Set each plug pair
|
2015-02-18 21:51:40 +01:00
|
|
|
for (char[] x : c)
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-18 21:51:40 +01:00
|
|
|
plugboard.setPlugPair(x[0], x[1]);
|
2015-02-04 20:51:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
/**
|
|
|
|
* Set config of the enigma
|
|
|
|
*
|
|
|
|
* @param conf configuration
|
|
|
|
*/
|
|
|
|
public void setConfiguration(int[] conf)
|
|
|
|
{
|
|
|
|
if (conf.length != 10)
|
|
|
|
{
|
|
|
|
setConfiguration(Enigma.STANDARD_CONFIGURATION);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
int ro1 = conf[0];
|
|
|
|
int ro2 = conf[1];
|
|
|
|
int ro3 = conf[2];
|
|
|
|
int ref = conf[3];
|
|
|
|
int r1rot = 26 + conf[4] - 1;
|
|
|
|
int r2rot = 26 + conf[5] - 1;
|
|
|
|
int r3rot = 26 + conf[6] - 1;
|
|
|
|
int ro1Ring = conf[7];
|
|
|
|
int ro2Ring = conf[8];
|
|
|
|
int ro3Ring = conf[9];
|
2015-02-04 20:51:31 +01:00
|
|
|
|
2015-02-18 21:51:40 +01:00
|
|
|
//Set first rotor
|
|
|
|
switch (ro1)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
r1 = new Rotor('1', (r1rot) % 26, ro1Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
r1 = new Rotor('2', (r1rot) % 26, ro1Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
r1 = new Rotor('3', (r1rot) % 26, ro1Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
r1 = new Rotor('4', (r1rot) % 26, ro1Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 5:
|
|
|
|
{
|
|
|
|
r1 = new Rotor('5', (r1rot) % 26, ro1Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Set second rotor
|
|
|
|
switch (ro2)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
r2 = new Rotor('1', (r2rot) % 26, ro2Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
r2 = new Rotor('2', (r2rot) % 26, ro2Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
r2 = new Rotor('3', (r2rot) % 26, ro2Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
r2 = new Rotor('4', (r2rot) % 26, ro2Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 5:
|
|
|
|
{
|
|
|
|
r2 = new Rotor('5', (r2rot) % 26, ro2Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Set third rotor
|
|
|
|
switch (ro3)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
r3 = new Rotor('1', (r3rot) % 26, ro3Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
r3 = new Rotor('2', (r3rot) % 26, ro3Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
r3 = new Rotor('3', (r3rot) % 26, ro3Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
r3 = new Rotor('4', (r3rot) % 26, ro3Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 5:
|
|
|
|
{
|
|
|
|
r3 = new Rotor('5', (r3rot) % 26, ro3Ring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Set reflector
|
|
|
|
switch (ref)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
reflector = new Rotor('A', 0, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
reflector = new Rotor('B', 0, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
reflector = new Rotor('C', 0, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-11 20:56:43 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the configuration, the enigma machine is in right NOW
|
2015-02-18 21:51:40 +01:00
|
|
|
*
|
2015-02-11 20:56:43 +01:00
|
|
|
* @return array containing configuration
|
|
|
|
*/
|
|
|
|
public int[] getConfiguration()
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
2015-02-18 21:51:40 +01:00
|
|
|
int[] c = new int[10];
|
2015-02-04 20:51:31 +01:00
|
|
|
{
|
|
|
|
c[0] = r1.getType();
|
|
|
|
c[1] = r2.getType();
|
|
|
|
c[2] = r3.getType();
|
2015-02-11 20:56:43 +01:00
|
|
|
c[3] = reflector.getType();
|
2015-02-04 20:51:31 +01:00
|
|
|
c[4] = r1.getCounter();
|
|
|
|
c[5] = r2.getCounter();
|
|
|
|
c[6] = r3.getCounter();
|
2015-02-18 21:51:40 +01:00
|
|
|
c[7] = r1.getRingsetting();
|
|
|
|
c[8] = r2.getRingsetting();
|
|
|
|
c[9] = r3.getRingsetting();
|
2015-02-04 20:51:31 +01:00
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class InvalidPlugboardConfigurationFormatException extends Exception
|
|
|
|
{
|
|
|
|
public InvalidPlugboardConfigurationFormatException(String m)
|
|
|
|
{
|
|
|
|
super(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|