Saturday, April 11, 2015

Enigma Cipher on Arduino

As a brief follow-up on my previous posting I am posting here a Arduino port of my Enigma cipher engine.  The changes were trivial to deal with the proper include files and to use the Serial class to output the test program results.  But as promised, I am providing the engine class files (enigma.h and enigma.cpp) as well as a test sketch (enigmaTest.ino) that can be copy/pasted and run on the Arduino.

I have chosen to use my new Freetronics Due board, kindly supplied by the good folks down-under at Freetronics for testing.

You are free to use what I post here as you desire, regardless of any copyright notices you may find in my code.  If I posted it in my blog, you are free to use it in any way desired.

Enjoy and don't hesitate to ask if you have any questions or problems.

enigma.h:
// Enigma M3/M4 engine
// Copyright (c) 2015 Jeff Whitlatch - ko7m - All rights reserved.
//

#pragma once

enum EnigmaTypes    { EnigmaTypeM3, EnigmaTypeM4 };

enum RotorPositions { RotorPosG, RotorPosL, RotorPosM, RotorPosR };

enum Rotors         { RotorI, RotorII, RotorIII, RotorIV, RotorV, 
                      RotorVI, RotorVII, RotorVIII, RotorB, RotorG,
                      RotorNone };

enum Reflectors     { ReflectorB, ReflectorC, ReflectorThinB, ReflectorThinG };

enum Letters        { LetterA, LetterB, LetterC, LetterD, LetterE, LetterF, LetterG,
                      LetterH, LetterI, LetterJ, LetterK, LetterL, LetterM, LetterN,
                      LetterO, LetterP, LetterQ, LetterR, LetterS, LetterT, LetterU,
                      LetterV, LetterW, LetterX, LetterY, LetterZ };

enum Direction      { dirRightToLeft, dirLeftToRight };

// Rotor Wiring
//
//             1111111111222222
//   01234567890123456789012345
//   ABCDEFGHIJKLMNOPQRSTUVWXYZ
const static char *rgRotors[10] =
{
    "EKMFLGDQVZNTOWYHXUSPAIBRCJ",   // Rotor I
    "AJDKSIRUXBLHWTMCQGZNPYFVOE",   // Rotor II
    "BDFHJLCPRTXVZNYEIWGAKMUSQO",   // Rotor III
    "ESOVPZJAYQUIRHXLNFTGKDCMWB",   // Rotor IV
    "VZBRGITYUPSDNHLXAWMJQOFECK",   // Rotor V
    "JPGVOUMFYQBENHZRDKASXLICTW",   // Rotor VI
    "NZJHGRCXMYSWBOUFAIVLPEKQDT",   // Rotor VII
    "FKQHTLXOCBJSPDZRAMEWNIUYGV",   // Rotor VIII
    "LEYJVCNIXWPBQMDRTAKZGFUHOS",   // M4 Greek Rotor "b" (beta)
    "FSOKANUERHMBTIYCWLQPZXVGJD"    // M4 Greek Rotor "g" (gama)
};

// Reflectors "B" and "C" (including M4 thin reflectors) wiring
const static char *rgReflectors[4] =
{
    "YRUHQSLDPXNGOKMIEBFZCWVJAT",   // M3 B
    "FVPJIAOYEDRZXWGCTKUQSBNMHL",   // M3 C
    "ENKQAUYWJICOPBLMDXZVFTHRGS",   // M4 thin B (beta)
    "RDOBJNTKVEHMLFCWZAXGYIPSUQ"    // M4 thin G (gamma)
};

// The rotor wheel notch definitions
const static int rgNotches[8][2] =
{
    { LetterQ, LetterQ },       //   Q - one notch  (Rotor I)
    { LetterE, LetterE },       //   E - one notch  (Rotor II)
    { LetterV, LetterV },       //   V - one notch  (Rotor III)
    { LetterJ, LetterJ },       //   J - one notch  (Rotor IV)
    { LetterZ, LetterZ },       //   Z - one notch  (Rotor V)
    { LetterZ, LetterM },       // Z/M - two notches (Rotor VI)
    { LetterZ, LetterM },       // Z/M - two notches (Rotor VII)
    { LetterZ, LetterM }        // Z/M - two notches (Rotor VIII)
};

class Enigma
{
public:
    Enigma();
    ~Enigma();

    void rotateDials();
    void doCipher(char *szClear, char *szCipher, int len);
    int swapPlugs(int value);
    int mapLetter(int value, int pos, int dir);
    int reflectLetter(int value);
    int adjustValue(int value);

    void setType(EnigmaTypes T);
    void setRotors(Rotors G, Rotors L, Rotors M, Rotors R);
    void setRotorPos(int G, int L, int M, int R);
    void setRingPos(int G, int L, int M, int R);
    void setPlug(int A, int B);

private:     EnigmaTypes type;       // Type of Enigma machine defaults to M3

    Rotors rgRotorSet[4];   // The set of rotors currently in use
    int rgRotorPos[4];      // The current position of each rotor in use
    int rgRingPos[4];       // Current ring position offset of each rotor in use

    Reflectors reflector;   // The reflector in use

    int plugBoard[26];      // Plugboard (steckerbrett) mapping

};

enigma.cpp:
// Enigma M3/M4 engine
// Copyright (c) 2015 Jeff Whitlatch - ko7m - All rights reserved.
//

#include <string.h>
#include "enigma.h"

// Enigma M3/M4 class constructor initializes the machine to
// M3 with rotors I, II and III, rotor values A A A and rings A A A
// The plugboard is self steckered on all plugs
//
Enigma::Enigma()
{
    // By default, assume M3
    setType(EnigmaTypeM3);

    // By default use rotors I, II, III
    setRotors(RotorNone, RotorI, RotorII, RotorIII);

    // Initial rotor positions are A A A
    setRotorPos(LetterA, LetterA, LetterA, LetterA);

    // Initial ring position A A A
    setRingPos(LetterA, LetterA, LetterA, LetterA);

    // Self stecker each plugboard position
    for (int i = LetterA; i <= LetterZ; i++)
        plugBoard[i] = i;
}

// Class destructor
Enigma::~Enigma()
{
}

// Set the Enigma machine type
void Enigma::setType(EnigmaTypes T)
{
    type = T;
}

// Select the rotor set
void Enigma::setRotors(Rotors G, Rotors L, Rotors M, Rotors R)
{
    rgRotorSet[RotorPosG] = G;
    rgRotorSet[RotorPosL] = L;
    rgRotorSet[RotorPosM] = M;
    rgRotorSet[RotorPosR] = R;
}

// Set the initial rotor positions
void Enigma::setRotorPos(int G, int L, int M, int R)
{
    rgRotorPos[RotorPosG] = G;
    rgRotorPos[RotorPosL] = L;
    rgRotorPos[RotorPosM] = M;
    rgRotorPos[RotorPosR] = R;
}

// Set the initial ring positions
void Enigma::setRingPos(int G, int L, int M, int R)
{
    rgRingPos[RotorPosG] = G;
    rgRingPos[RotorPosL] = L;
    rgRingPos[RotorPosM] = M;
    rgRingPos[RotorPosR] = R;
}

// Set a stecker pair
void Enigma::setPlug(int A, int B)
{
    // Remove any previous steckers of either plug
    for (int i = LetterA; i <= LetterZ; i++)
        if (plugBoard[i] == A || plugBoard[i] == B) plugBoard[i] = i;

    plugBoard[A] = B;
    plugBoard[B] = A;
}

// Adjust the passed value to be LetterA..LetterZ (0..25)
int Enigma::adjustValue(int value)
{
    if (value < LetterA)
    {
        // If negative number, count backwards from Z
        // Emulates wheel rotating backwards
        value += (LetterZ + 1);
    }
    else if (value > LetterZ)
    {
        // If number greater than Z, count forward from A
        // Emulates wheel rotating forwards
        value -= (LetterZ + 1);
    }

    return value;
}


// Perform rotation of the encoding rotors taking into account single and double stepping
// If we are simulating an M4 machine, the greek wheel (RotorG) does not rotate once installed.
//
void Enigma::rotateDials()
{
    // Check if the right rotor is at a notch position
    if (rgRotorPos[RotorPosR] == rgNotches[rgRotorSet[RotorPosR]][0] || 
        rgRotorPos[RotorPosR] == rgNotches[rgRotorSet[RotorPosR]][1])
    {
        // If the notch on the right wheel is reached rotate middle wheel
        // But first check if it too is a notch
        if (rgRotorPos[RotorPosM] == rgNotches[rgRotorSet[RotorPosM]][0] || 
            rgRotorPos[RotorPosM] == rgNotches[rgRotorSet[RotorPosM]][1])
        {
            // If the notch on the middle wheel is reached rotate left wheel
            rgRotorPos[RotorPosL]++;
        }
        rgRotorPos[RotorPosM]++;
    }
    else 
    {
        if (rgRotorPos[RotorPosM] == rgNotches[rgRotorSet[RotorPosM]][0] || 
            rgRotorPos[RotorPosM] == rgNotches[rgRotorSet[RotorPosM]][1])
        {
            // If the notch on the middle wheel is reached rotate left AND middle wheels
            // (the double stepping mechanism)
            rgRotorPos[RotorPosL]++;
            rgRotorPos[RotorPosM]++;
        }
    }

    // Rotate right wheel (this wheel is always rotated).
    rgRotorPos[RotorPosR]++;

    // All rotor positions are modulo 26
    rgRotorPos[RotorPosL] %= 26;
    rgRotorPos[RotorPosM] %= 26;
    rgRotorPos[RotorPosR] %= 26;
}

// Swap plugboard characters.  Assumes initialized to self steckered
//
int Enigma::swapPlugs(int value)
{
    return plugBoard[value];
}

// Map a letter through a particular rotor in either direction
//
int Enigma::mapLetter(int value, int rp, int direction)
{
    char chIn = value + 'A';

    // Wheels rotation is anti-clockwise when viewed from the right
    value = adjustValue(value - rgRingPos[rp]);     // Adjust character by ring position
    value = adjustValue(value + rgRotorPos[rp]);    // and by amount of rotation

    // Map letter either right to left or left to right according to direction
    if (direction == dirRightToLeft)
    {
        value = *(rgRotors[rgRotorSet[rp]] + value) - 'A';
    }
    else
    {
        const char *pch = strchr(rgRotors[rgRotorSet[rp]], value + 'A');
        value = pch - rgRotors[rgRotorSet[rp]];
    }

    value = adjustValue(value - rgRotorPos[rp]);
    value = adjustValue(value + rgRingPos[rp]);
    return value;
}

// Reflect a letter through the current reflector
//
int Enigma::reflectLetter(int value)
{
    const char *pch = strchr(rgReflectors[reflector], value + 'A');
    value = pch - rgReflectors[reflector];
    return value;
}

// Perform the Enigma cypher
// Caller must provide sufficient buffer space for ciphertext output
//
void Enigma::doCipher(char *szClear, char *szCipher, int len)
{
    char *pch = szClear;
    char *pchOut = szCipher;
    char ch;
    unsigned cchOut = 0;

    memset(szCipher, 0, len);           // Clear the cipher text

    // If using M4 Enigma, make sure the thin reflectors are in use.
    if (type == EnigmaTypeM4 && reflector != ReflectorThinB && reflector != ReflectorThinG)
    {
        // If we don't have a thin reflector, force thin reflector B
        reflector = ReflectorThinB;
    }
    else 
    {
        // If we don't have a thick reflector, force reflector B
        if (reflector != ReflectorB && reflector != ReflectorC)
            reflector = ReflectorB;
    }

    // Walk through each character to be encrypted or decrypted and perform the transformation
    while (ch = *pch++)
    {
        // Convert to upper case as a convenience
        if (ch >= 'a' && ch <= 'z')
        {
            ch -= 'a';
            ch += 'A';
        }

        // Skip anything that is not A-Z
        if (ch < 'A' || ch > 'Z')
            continue;

        // Rotors always turn before performing the encryption of each letter
        //
        rotateDials();

        // Convert input character to LetterA..LetterZ (0..25)
        int value = (ch - 'A'); 

        // Run the value through the plugboard and perform any swaps and route to ETW
        value = swapPlugs(value);

        // From ETW, value goes to right-most rotor
        value = mapLetter(value, RotorPosR, dirRightToLeft);

        // Then to the middle rotor
        value = mapLetter(value, RotorPosM, dirRightToLeft);

        // And then the left rotor
        value = mapLetter(value, RotorPosL, dirRightToLeft);

        // Now, if simulating M4 Enigma, use the greek wheel
        if (type == EnigmaTypeM4)
            value = mapLetter(value, RotorPosG, dirRightToLeft);

        // Next is the reflector
        value = reflectLetter(value);

        // Pass back through rotors and ETW
        if (type == EnigmaTypeM4)
            value = mapLetter(value, RotorPosG, dirLeftToRight);

        value = mapLetter(value, RotorPosL, dirLeftToRight);
        value = mapLetter(value, RotorPosM, dirLeftToRight);
        value = mapLetter(value, RotorPosR, dirLeftToRight);

        // And again back through the plugboard
        value = swapPlugs(value);

        // Convert the value to text and insert in output buffer
        *pchOut++ = value + 'A';

        // Break into 5 character words
        if (++cchOut % 5 == 0) *pchOut++ = ' ';

        // Terminate the string after each character is inserted
        *pchOut = '\0';
    }

}

enigmaTest.ino:
// Enigma M3/M4 test code
//

#include "enigma.h"

// Clear and cipher text buffers
char *szClearText = "Now is the time for all good men to come to the aid of thier country.";
char rgbBuf1[512];
char rgbBuf2[512];

// Create a couple of instances to easily reset to default state 
Enigma e1;      // Encode instance of engine
Enigma e2;      // Decode instance of engine

void setup()
{
  Serial.begin(115200);

  e1.setRotors(RotorNone, RotorI, RotorIV, RotorV);
  e2.setRotors(RotorNone, RotorI, RotorIV, RotorV);

  e1.setRingPos(LetterA, LetterT, LetterF, LetterS);
  e2.setRingPos(LetterA, LetterT, LetterF, LetterS);

  e1.setPlug(LetterA, LetterT); 
  e1.setPlug(LetterB, LetterJ);
  e1.setPlug(LetterD, LetterL);
  e1.setPlug(LetterF, LetterP);
  e1.setPlug(LetterG, LetterI);
  e1.setPlug(LetterH, LetterY);
  e1.setPlug(LetterK, LetterZ);
  e1.setPlug(LetterM, LetterR);
  e1.setPlug(LetterN, LetterW);
  e1.setPlug(LetterQ, LetterX);

  e2.setPlug(LetterA, LetterT);
  e2.setPlug(LetterB, LetterJ);
  e2.setPlug(LetterD, LetterL);
  e2.setPlug(LetterF, LetterP);
  e2.setPlug(LetterG, LetterI);
  e2.setPlug(LetterH, LetterY);
  e2.setPlug(LetterK, LetterZ);
  e2.setPlug(LetterM, LetterR);
  e2.setPlug(LetterN, LetterW);
  e2.setPlug(LetterQ, LetterX);

  e1.doCipher(szClearText, &rgbBuf1[0], 512);
  Serial.print("Clear:  <");
  Serial.print(szClearText);
  Serial.println(">");
  Serial.print("Cipher: <");
  Serial.print(&rgbBuf1[0]);
  Serial.println(">");  
  
  e2.doCipher(&rgbBuf1[0], &rgbBuf2[0], 512);
  Serial.print("Cipher: <");
  Serial.print(&rgbBuf1[0]);
  Serial.println(">");
  Serial.print("Clear:  <");
  Serial.print(&rgbBuf2[0]);
  Serial.println(">"); 
}

void loop()
{
   // Nothing to do

}

Here is the output from this sketch on the serial monitor:
Clear:  <Now is the time for all good men to come to the aid of thier country.>
Cipher: <ODLKP BMQHG CGAHP XNXYY TIRLS KWPGG ZVTYG GQBWY NWREK EZTOS WZM>
Cipher: <ODLKP BMQHG CGAHP XNXYY TIRLS KWPGG ZVTYG GQBWY NWREK EZTOS WZM>
Clear:  <NOWIS THETI MEFOR ALLGO ODMEN TOCOM ETOTH EAIDO FTHIE RCOUN TRY>

No comments:

Post a Comment