EnigmAndroid/app/src/main/java/de/vanitasvitae/enigmandroid/Enigma.java

398 lines
12 KiB
Java

package de.vanitasvitae.enigmandroid;
/**
* Enigma-machine
*Copyright (C) 2015 Paul Schaub
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* @author vanitasvitae
*/
public class Enigma
{
private Plugboard plugboard;
//Slots for the rotors
private Rotor r1;
private Rotor r2;
private Rotor r3;
//Slot for the reflector
private Rotor reflector;
private boolean prefAnomaly; //Do you want to simulate the anomaly?
private boolean anomaly; //Is it time to spin twice?
//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};
/**
* 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.
*
* @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);
this.setPlugboard(pbconf);
}
/**
* Encrypt / Decrypt a given String
*
* @param w Text to decrypt/encrypt
* @return encrypted/decrypted string
*/
public String encrypt(String w)
{
//output string
String c = "";
//for each char x in k
for (int i = 0; i < w.length(); i++)
{
char x = w.charAt(i);
//encrypt char
c = c + this.encryptChar(x);
}
//return en-/decrypted string
return c;
}
/**
* 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 && prefAnomaly))
{
r2.incrementCounter();
//Handle Anomaly
this.anomaly = r2.doubleTurnAnomaly();
if (r2.isAtTurnoverPosition())
{
r3.incrementCounter();
}
}
int x = (int) k;
x = x - 65; //Remove Unicode Offset (A=65 in Unicode.)
//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);
return (char) (x + 65); //Add Offset
}
/**
* Prepare String for encryption via enigma
* Replace . , ! ? : with X
* Remove all other chars that are not A..Z
*
* @param word string
* @return prepared string
*/
public static String prepare(String word)
{
String w = word.toUpperCase();
String c = "";
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;
}
/**
* Create Plugboard configuration from String.
* String must be in format XY,AZ and so on.
* X and Y are plugs, that will be switched over.
* Don't use plugs twice such as in AA or AB,CA. This will cause Exceptions.
*
* @param p String
* @return Array containing plugboard configuration
*/
public static char[][] parsePlugs(String p) throws InvalidPlugboardConfigurationFormatException
{
p = p.toUpperCase();
//Check, if empty
if (p.length() == 0)
{
return null;
}
//Ensure uppercase and split string
String[] in = p.toUpperCase().split(",");
//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)
if (in.length != (p.length() + 1) / 3)
{
throw new InvalidPlugboardConfigurationFormatException("Error parsing plugs! Maybe you missed a ','?");
} else
{
//Create new 2 dimensional array for pairs of plugs
char[][] plugs = new char[(p.length() + 1) / 3][2];
//Fill the array
int i = 0;
for (String x : in)
{
//Check, whether string is not representing a pair
if (x.length() != 2)
{
throw new InvalidPlugboardConfigurationFormatException("Error parsing plugs! Maybe you didn't enter a pair somewhere?");
}
//If it does
else
{
char[] pair = x.toCharArray();
//Check, if Plugs are in alphabet
if(pair[0]<65 || pair[1]<65 || pair[0]>90 || pair[1]>90) throw new InvalidPlugboardConfigurationFormatException("Error parsing plugs! Maybe you entered a number or a special character?");
else
{
//add it to the array
plugs[i] = pair;
i++;
}
}
}
return plugs;
}
}
/**
* Set the plugboard to a new created object and set the configuration
*
* @param c configuration
* @throws Plugboard.PlugAlreadyUsedException
*/
public void setPlugboard(char[][] c) throws Plugboard.PlugAlreadyUsedException
{
plugboard = new Plugboard();
if (c != null)
{
//Set each plug pair
for (char[] x : c)
{
plugboard.setPlugPair(x[0], x[1]);
}
}
}
/**
* 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];
//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;
}
}
}
}
/**
* Return the configuration, the enigma machine is in right NOW
*
* @return array containing configuration
*/
public int[] getConfiguration()
{
int[] c = new int[10];
{
c[0] = r1.getType();
c[1] = r2.getType();
c[2] = r3.getType();
c[3] = reflector.getType();
c[4] = r1.getCounter();
c[5] = r2.getCounter();
c[6] = r3.getCounter();
c[7] = r1.getRingsetting();
c[8] = r2.getRingsetting();
c[9] = r3.getRingsetting();
}
return c;
}
public void setPrefAnomaly(boolean b)
{
this.prefAnomaly = b;
}
public static class InvalidPlugboardConfigurationFormatException extends Exception
{
public InvalidPlugboardConfigurationFormatException(String m)
{
super(m);
}
}
}