Реализация Enigma новичком в JAVA

Я хочу изучать Java, поэтому я портировал свою реализацию Enigma на C #. Он получил UnitTests и работает.

Я ищу обзор, рассказывающий мне, где я не знаю лучших практик, где я нарушаю соглашения об именах, где я выгляжу как программист на C #, пишущий на Java 🙂

Спасибо и развлекайся, Гарри

https://github.com/HaraldLeitner/Enigma

BusniessLogic.java

package main;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BusinessLogic {
    private List<Roll> Rolls;
    private List<Roll> RollsReverse;

    public BusinessLogic(List<Roll> rolls) {

        Rolls = rolls;

        RollsReverse = new ArrayList<Roll>(rolls);
        Collections.reverse(RollsReverse);
    }

    public void TransformFile(String inputFfilename, String outputFilename, Enums.Mode mode) throws IOException {
        final int buffersize = 65536;

        FileInputStream fileInStream = new FileInputStream(inputFfilename);
        FileOutputStream fileOutStream = new FileOutputStream(outputFilename);

        byte[] buffer = new byte[buffersize];
        int readCount = 0;

        while ((readCount = fileInStream.read(buffer, 0, buffersize)) > 0) {
            TransformByteArray(buffer, mode);
            fileOutStream.write(buffer, 0, readCount);
        }

        fileInStream.close();
        fileOutStream.close();
    }

    public void TransformByteArray(byte[] input, Enums.Mode mode) {

        if (mode == Enums.Mode.Encode) {

            for (int i = 0; i < input.length; i++) {
                for (Roll roll : Rolls)
                    input[i] = roll.Encrypt(input[i]);

                RollOn();
            }
        }

        if (mode == Enums.Mode.Decode) {
            for (int i = 0; i < input.length; i++) {
                for (Roll roll : RollsReverse)
                    input[i] = roll.Decrypt(input[i]);

                RollOn();
            }
        }
    }

    private void RollOn() {
        for (Roll roll : Rolls) {
            if (!roll.RollOn())
                break;
        }
    }
}

Enigma.Properties

TransitionCount = 53

Enums.java

package main;

public class Enums 
{
    public enum Mode 
    {
        Encode, 
        Decode
    };

}

Program.java

package main;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;

public class Program {
    private static List<Roll> Rolls;
    private static Enums.Mode Mode;
    private static String KeyFilename;
    private static String InputFileName;
    private static int TransitionCount;

    public static void main(String[] args) throws Exception {
        if (args.length != 3) {
            System.out.println("Generate key with 'keygen x key.file' where x > 3 is the number of rolls.");
            System.out.println("Encrypt a file with 'enc a.txt key.file'");
            System.out.println("Decrypt a file with 'dec a.txt key.file'");

            return;
        }

        ReadProperties();

        KeyFilename = args[2];

        if (args[0].compareToIgnoreCase("keygen") == 0) {
            Keygen(Integer.parseInt(args[1]));
            return;
        }

        InputFileName = args[1];
        CreateRolls();

        if (args[0].compareToIgnoreCase("enc") == 0)
            Mode = Enums.Mode.Encode;
        else if (args[0].compareToIgnoreCase("dec") == 0)
            Mode = Enums.Mode.Decode;
        else
            throw new Exception("Undefined Encryption Mode.");

        BusinessLogic businessLogic = new BusinessLogic(Rolls);

        businessLogic.TransformFile(InputFileName, InputFileName + "." + Mode, Mode);
    }

    private static void ReadProperties() throws FileNotFoundException, IOException {
        Properties prop = new Properties();
        
        prop.load(new FileInputStream("Enigma.properties"));

        TransitionCount = Integer.parseInt(prop.getProperty("TransitionCount"));
    }

    private static void CreateRolls() throws Exception {
        Rolls = new ArrayList<Roll>();

        int rollKeylength = 256 + TransitionCount;

        byte[] definition = new byte[(int) new File(KeyFilename).length()];
        FileInputStream fileInputStream = new FileInputStream(KeyFilename);
        fileInputStream.read(definition);
        fileInputStream.close();

        if (definition.length % rollKeylength > 0)
            throw new Exception("Invalid Keysize");

        int rollCount = definition.length / rollKeylength;

        for (int rollNumber = 0; rollNumber < rollCount; rollNumber++) {
            List<Integer> transitions = new ArrayList<Integer>();

            for (int index = 0; index < TransitionCount; index++)
                transitions.add((int) definition[rollNumber * rollKeylength + 256 + index]); 

            byte[] singleRoll = new byte[256];
            for(int index = 0; index < 256; index ++)
                singleRoll[index] = definition[rollNumber * rollKeylength + index];

            Rolls.add(new Roll(singleRoll, transitions));
        }

        for (Roll roll : Rolls)
            roll.CheckInput(TransitionCount);
    }

    private static void Keygen(int rollCount) throws Exception {
        
        if (rollCount < 4)
            throw new Exception("Not enough rolls.");

        Random random = new Random();

        if((new File(KeyFilename)).exists())
            Files.delete(Paths.get(KeyFilename));

        byte[] key = new byte[(256 + TransitionCount) * rollCount] ;

        for (int i = 0; i < rollCount; i++) {
            byte[] transform = new byte[256];
            for (int j = 0; j <= 255; j++)
                transform[j] = (byte) j;

            while (!IsTwisted(transform)) {
                for (int j = 0; j < 256 * 2; j++) {
                    int rand1 = random.nextInt(256);
                    int rand2 = random.nextInt(256);

                    byte temp = transform[rand1];
                    transform[rand1] = transform[rand2];
                    transform[rand2] = temp;
                }
            }

            for (int index = 0; index < 256; index++)
                key[(256 + TransitionCount) * i + index] = transform[index];

            List<Integer> transitions = new ArrayList<Integer>();

            while (transitions.size() < TransitionCount) 
            {
                int rand = random.nextInt(256);
                if (!transitions.contains(rand))
                    transitions.add(rand);
            }

            for (int index = 0; index < TransitionCount; index++)
                key[(256 + TransitionCount) * i + 256 + index] = (byte)(int) transitions.get(index);            
        }

        FileOutputStream fileOutputStream = new FileOutputStream(KeyFilename);
        fileOutputStream.write(key);
        fileOutputStream.close();

        System.out.println("Keys generated.");
        Thread.sleep(1000);
    }

    private static boolean IsTwisted(byte[] trans) {
        for (int i = 0; i <= 255; i++)
            if (trans[i] == i)
                return false;

        return true;
    }
}

Roll.java

package main;

import java.util.Collections;
import java.util.List;


public class Roll
 {
    private int Position;          //This is the actual position of this roll starting at 0
    private byte[] Transitions;     //This is the wiring of the roll: if Transitions[0] = 0x04 the value 0x00 will be mapped to 0x04
    private List<Integer> TurnOverIndices;  //While rolling after each char encryption the next roll will also rotate, if these indices contain Position 
    private byte[] ReTransitions;   //Reverted transitionlist for decryption

    public Roll(byte[] transitions, List<Integer> turnOverIndices) {
        Transitions = transitions;
        TurnOverIndices = turnOverIndices;
        Position = 0;

        ReTransitions = new byte[256];
        for (int i = -128; i < 128; i++)
            ReTransitions[Transitions[i + 128] + 128] = (byte) i;
    }

    public void CheckInput(int transitionCount) throws Exception {
        if (Transitions.length != 256)
            throw new Exception("Wrong Transition length ");

        for (int i = -128; i < 128; i++) {
            boolean found = false;
            for (int j = 0; j < 256; j++)
            {
                if (Transitions[j] == i)
                {
                    found = true;   
                    break;
                }
            }
            if (!found)
                throw new Exception("Transitions not 1-1 complete. Problem at " + i);
        }

        if (TurnOverIndices.size() != transitionCount)
            throw new Exception("Wrong TurnOverIndices length ");

        Collections.sort(TurnOverIndices);

        for (int i = 0; i < TurnOverIndices.size() - 1; i++)
            if (TurnOverIndices.get(i) == TurnOverIndices.get(i + 1))
                throw new Exception("Turnoverindizes has doubles");
    }

    public byte Encrypt(byte input) {
        return Transitions[((input + Position + 128)%256)];
    }

    public byte Decrypt(byte input) {
        return (byte) (ReTransitions[input + 128] - Position);
    }

    public boolean RollOn() {
        Position = (Position + 1) % 256;
        return TurnOverIndices.contains(Position);
    }
}

UnitTest.java

package test;

import static org.junit.jupiter.api.Assertions.*;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.imageio.stream.FileImageInputStream;
import javax.xml.stream.events.EntityDeclaration;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import main.*;

class UnitTest {

    byte[] transLinear = new byte[256]; // here every char is mapped to itself
    byte[] transLinearInvert = new byte[256]; // match the first to the last etc
    byte[] transShift1 = new byte[256]; // 'a' is mapped to 'b' etc
    byte[] transShift2 = new byte[256]; // 'a' is mapped to 'c' etc

    private BusinessLogic BusinessLogicEncode;
    private BusinessLogic BusinessLogicDecode;

    byte[] encryptedMsg;
    byte[] decryptedMsg;

    void Crypt(byte[] msg) {
        encryptedMsg = msg.clone();
        BusinessLogicEncode.TransformByteArray(encryptedMsg, Enums.Mode.Enc);
        decryptedMsg = encryptedMsg.clone();
        BusinessLogicDecode.TransformByteArray(decryptedMsg, Enums.Mode.Dec);
    }

    @BeforeEach
    public void Init() {
        for (int i = -0; i < 256; i++)
            transLinear[i] = (byte) (i - 128);

        for (int i = 0; i < 256; i++)
            transLinearInvert[i] = (byte) (255 - i - 128);

        for (int i = 0; i < 256; i++)
            transShift1[i] = (byte) ((i + 1 - 128) % 256);

        for (int i = 0; i < 256; i++)
            transShift2[i] = (byte) ((i + 2 - 128) % 256);
    }

    void InitBusinessLogic(ArrayList<byte[]> transitions, ArrayList<ArrayList<Integer>> turnovers) {
        if (transitions.size() != turnovers.size())
            assertFalse(true, "There must be as much transitions as roll defs!");

        List<Roll> rollsEncrypt = new ArrayList<Roll>();
        List<Roll> rollsDecrypt = new ArrayList<Roll>();

        for (int i = 0; i < transitions.size(); i++) {
            rollsEncrypt.add(new Roll(transitions.get(i), turnovers.get(i)));
            rollsDecrypt.add(new Roll(transitions.get(i), turnovers.get(i)));

        }

        BusinessLogicEncode = new BusinessLogic(rollsEncrypt);
        BusinessLogicDecode = new BusinessLogic(rollsDecrypt); // need a second, because the enc BusinessLogic has
                                                                // turned over rolls
    }

    @Test
    public void OneByte1RollLinear() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transLinear);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i });

            assertEquals(i, encryptedMsg[0]);
            assertEquals(i, decryptedMsg[0]);
        }
    }

    @Test
    public void OneByte1RollShift1() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transShift1);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i });
            assertEquals((byte) (i + 1), encryptedMsg[0]);
            assertEquals(i, decryptedMsg[0]);
        }
    }

    @Test
    public void OneByte1RollShift2() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transShift2);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i });
            assertEquals((byte) (i + 2), encryptedMsg[0]);
            assertEquals(i, decryptedMsg[0]);
        }
    }

    @Test
    public void TwoByte1RollLinear() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transLinear);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) ((i + 1)) });

            assertEquals(i, encryptedMsg[0]);
            assertEquals((byte) (i + 2), encryptedMsg[1]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals((byte) (i + 1), decryptedMsg[1]);
        }
    }

    @Test
    public void TwoByte1RollShift() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transShift1);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) ((i + 1)) });

            assertEquals((byte) (i + 1), encryptedMsg[0]);
            assertEquals((byte) (i + 3), encryptedMsg[1]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals((byte) (i + 1), decryptedMsg[1]);
        }
    }

    @Test
    public void TwoByte1RollInvert() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transLinearInvert);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) ((i)) });

            assertEquals((byte) (-i - 1), encryptedMsg[0]);
            assertEquals((byte) (-i - 2), encryptedMsg[1]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals(i, decryptedMsg[1]);
        }
    }

    @Test
    public void TwoByte2RollLinearInvert() {

        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transLinearInvert);
            transformation.add(transLinearInvert);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) ((i + 1)) });
            assertEquals(i, encryptedMsg[0]);
            assertEquals((byte) (i + 2), encryptedMsg[1]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals((byte) (i + 1), decryptedMsg[1]);
        }
    }

    @Test
    public void TwoByte2RollShift() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transShift1);
            transformation.add(transShift1);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) ((i + 1)) });
            assertEquals((byte) (i + 2), encryptedMsg[0]);
            assertEquals((byte) (i + 4), encryptedMsg[1]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals((byte) (i + 1), decryptedMsg[1]);
        }
    }

    @Test
    public void TwoByte2RollShift2() {
        ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
        ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>(0);
        turnovers.add(turnover);
        turnovers.add(turnover);

        ArrayList<byte[]> transformation = new ArrayList<byte[]>();
        transformation.add(transShift2);
        transformation.add(transShift2);

        InitBusinessLogic(transformation, turnovers);

        Crypt(new byte[] { 7, 107 });

        assertEquals(11, encryptedMsg[0]);
        assertEquals(112, encryptedMsg[1]);

        assertEquals(7, decryptedMsg[0]);
        assertEquals(107, decryptedMsg[1]);
    }

    @Test
    public void TwoByte2RollInvert() {
        for (int i = -128; i < 128; i++) {
            ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0));
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(turnover);
            turnovers.add(turnover);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transLinearInvert);
            transformation.add(transLinearInvert);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) ((i + 1)) });
            assertEquals((byte) (i), encryptedMsg[0]);
            assertEquals((byte) (i + 2), encryptedMsg[1]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals((byte) (i + 1), decryptedMsg[1]);
        }
    }

    @Test
    public void ThreeByte2RollTransit() {
        ArrayList<Integer> always = new ArrayList<>();
        for (int j = 0; j < 256; j++)
            always.add(j);

        for (int i = -128; i < 128; i++) {
            ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
            turnovers.add(always);
            turnovers.add(always);

            ArrayList<byte[]> transformation = new ArrayList<byte[]>();
            transformation.add(transLinear);
            transformation.add(transLinear);

            InitBusinessLogic(transformation, turnovers);

            Crypt(new byte[] { (byte) i, (byte) (i + 1), (byte) (i + 2) });

            assertEquals((byte) (i), encryptedMsg[0]);
            assertEquals((byte) (i + 3), encryptedMsg[1]);
            assertEquals((byte) (i + 6), encryptedMsg[2]);

            assertEquals(i, decryptedMsg[0]);
            assertEquals((byte) (i + 1), decryptedMsg[1]);
            assertEquals((byte) (i + 2), decryptedMsg[2]);
        }
    }

    @Test
    public void TwoByte2DifferentRollsTransit() {
        ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4));
        ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
        turnovers.add(turnover);
        turnovers.add(turnover);

        ArrayList<byte[]> transformation = new ArrayList<byte[]>();
        transformation.add(transLinear);
        transformation.add(transShift1);

        InitBusinessLogic(transformation, turnovers);

        Crypt(new byte[] { 7, 107 });

        assertEquals(8, encryptedMsg[0]);
        assertEquals(110, encryptedMsg[1]);

        assertEquals(7, decryptedMsg[0]);
        assertEquals(107, decryptedMsg[1]);
    }

    @Test
    public void TwoByte2DifferentRollsTransit3() {
        ArrayList<Integer> turnover = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4));
        ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
        turnovers.add(turnover);
        turnovers.add(turnover);

        ArrayList<byte[]> transformation = new ArrayList<byte[]>();
        transformation.add(transLinear);
        transformation.add(transLinearInvert);

        InitBusinessLogic(transformation, turnovers);

        Crypt(new byte[] { 7, 107 });

        assertEquals(-8, encryptedMsg[0]);
        assertEquals(-110, encryptedMsg[1]);

        assertEquals(7, decryptedMsg[0]);
        assertEquals(107, decryptedMsg[1]);
    }

    @Test
    public void RealLive()
    {
        int msgSize = 5 * 65536;

        byte[] msg = new byte[msgSize];
        for (int i = 0; i < msgSize; i++)
            msg[i] = (byte)(i);

        ArrayList<Integer> turnover1 = new ArrayList<>(Arrays.asList(0, 22, 44, 100));
        ArrayList<Integer> turnover2 = new ArrayList<>(Arrays.asList(11, 44, 122, 200));
        ArrayList<Integer> turnover3 = new ArrayList<>(Arrays.asList(33, 77, 99, 222));
        ArrayList<Integer> turnover4 = new ArrayList<>(Arrays.asList(55, 67, 79, 240));
        ArrayList<ArrayList<Integer>> turnovers = new ArrayList<ArrayList<Integer>>();
        turnovers.add(turnover1);
        turnovers.add(turnover2);
        turnovers.add(turnover3);
        turnovers.add(turnover4);

        ArrayList<byte[]> transformation = new ArrayList<byte[]>();
        transformation.add(transLinear);
        transformation.add(transLinearInvert);
        transformation.add(transShift1);
        transformation.add(transShift2);

        InitBusinessLogic(transformation, turnovers);

        Crypt(msg);

        assertEquals(msg.length, decryptedMsg.length);
        
        Crypt(msg);

        for (int i = 0; i < msgSize; i++)
            assertEquals(msg[i], decryptedMsg[i]);
    }

    @Test
    public void Integrationtest() throws Exception
    {
        int msgSize = 5 * 65536;    //bigger than buffersize:-)
        String keyname = "any.key";
        String msgFileName = "msg.file";

        byte[] msg = new byte[msgSize];
        for (int i = 0; i < msgSize; i++)
            msg[i] = (byte)(i % 256);

        FileOutputStream fileOutputStream = new FileOutputStream(msgFileName);
        fileOutputStream.write(msg);
        fileOutputStream.close();

        Program.main(new String[] { "keygen", "4", keyname});
        Program.main(new String[] { "enc", msgFileName, keyname });
        Program.main(new String[] { "dec", msgFileName + ".Enc", keyname });
 
        byte[] encdec = new byte[msgSize];
        FileInputStream fileInputStream = new FileInputStream(msgFileName + ".Enc.Dec");
        fileInputStream.read(encdec);
        fileInputStream.close();

        for(int i = 0; i < msg.length; i++)
            assertEquals(msg[i], encdec[i]);

        assertEquals(msg.length, encdec.length);
    }
}
```

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *