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