Showing posts with label M4. Show all posts
Showing posts with label M4. Show all posts

Saturday, April 11, 2015

Kriegsmarine M4 U-Boat message decryption

To test my M3/M4 Enigma machine code, I have given it the challenge of decoding an actual intercepted M4 message.

The message was intercepted by the British destroyer HMS Hurricane in the North Atlantic on 25 November 1942. This was during the ten months black-out which occurred after the introduction of the four-rotor Enigma M4. During that period, the codebreakers in Bletchley Park were unable to decrypt the Kriegsmarine radio traffic, encrypted with the new Enigma M4.

The original cipher text is show below, stripped of message indicator groups that preceded the message and were repeated at the end:


NCZW VUSX PNYM INHZ XMQX 
SFWX WLKJ AHSH NMCO CCAK 
UQPM KCSM HKSE INJU SBLK 
IOSX CKUB HMLL XCSJ USRR 
DVKO HULX WCCB GVLI YXEO 
AHXR HKKF VDRE WEZL XOBA 
FGYU JQUK GRTV UKAM EURB 
VEKS UHHV OYHA BCJW MAKL 
FKLM YFVN RIZR VVRT KOFD 
ANJM OLBG FFLE OPRG TFLV 
RHOW OPBE KVWM UQFM PWPA 
RMFH AGKX IIBG

The settings for the Enigma machine are as follows:

Enigma model: Kriegsmarine M4
Reflector: B
Rotors: Beta - II - IV - I
Stecker: AT BL DF GJ HM NW OP QY RZ VX
Ring settings: A-A-A-V
Rotor start position: V-J-N-A

I modified my test code to set the machine up with these settings thusly.  I will leave it to the reader to convert this to the Arduino example in my previous post.

int _tmain(int argc, _TCHAR* argv[])
{
// Clear and cipher text
char *szCipherText = 
"NCZW VUSX PNYM INHZ XMQX "
"SFWX WLKJ AHSH NMCO CCAK "
"UQPM KCSM HKSE INJU SBLK "
"IOSX CKUB HMLL XCSJ USRR "
"DVKO HULX WCCB GVLI YXEO "
"AHXR HKKF VDRE WEZL XOBA "
"FGYU JQUK GRTV UKAM EURB "
"VEKS UHHV OYHA BCJW MAKL "
"FKLM YFVN RIZR VVRT KOFD "
"ANJM OLBG FFLE OPRG TFLV "
"RHOW OPBE KVWM UQFM PWPA "
"RMFH AGKX IIBG";

char rgbBuf1[1024];

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

e1.setType(EnigmaTypeM4)
e1.setRotors(RotorB, RotorII, RotorIV, RotorI);
e1.setRingPos(LetterA, LetterA, LetterA, LetterV);
e1.setRotorPos(LetterV, LetterJ, LetterN, LetterA);
e1.setPlug(LetterA, LetterT); 
e1.setPlug(LetterB, LetterL);
e1.setPlug(LetterD, LetterF);
e1.setPlug(LetterG, LetterJ);
e1.setPlug(LetterH, LetterM);
e1.setPlug(LetterN, LetterW);
e1.setPlug(LetterO, LetterP);
e1.setPlug(LetterQ, LetterY);
e1.setPlug(LetterR, LetterZ);
e1.setPlug(LetterV, LetterX);

e1.doCipher(szCipherText, &rgbBuf1[0], 1024);
printf("Cipher: <%s>\n", szCipherText);
printf("Clear:  <%s>\n", &rgbBuf1[0]);
return 0;
}

I modified my Enigma class to output 4 letter code groups rather than the 5 letter groups typical of the M3 if the machine type is set to EnigmaTypeM4.

// Break into 4 or 5 character words\
if (++cchOut % (type == EnigmaTypeM3 ? 5 : 4) == 0) *pchOut++ = ' ';

The program output for this cypher text is as follows:

VONV ONJL OOKS JHFF TTTE 
INSE INSD REIZ WOYY QNNS 
NEUN INHA LTXX BEIA NGRI 
FFUN TERW ASSE RGED RUEC 
KTYW ABOS XLET ZTER GEGN 
ERST ANDN ULAC HTDR EINU 
LUHR MARQ UANT ONJO TANE 
UNAC HTSE YHSD REIY ZWOZ 
WONU LGRA DYAC HTSM YSTO 
SSEN ACHX EKNS VIER MBFA 
ELLT YNNN NNNO OOVI ERYS 
ICHT EINS NULL

When we rearrange the output to insert spaces as appropriate, we get the following:

VON VON JLOOKSJ HFFTTT EINS EINS DREI ZWO YY QNNS NEUN   
 INHALT XX BEI ANGRIFF UNTER WASSER GEDRUECKT Y WABOS X
 LETZTER GEGNERSTAND NUL ACHT DREI NUL UHR
 MARQU ANTON JOTA NEUN ACHT SEYHS DREI Y ZWO ZWO NUL GRAD Y ACHT SM Y STOSSE NACH X  
 EKNS VIER MB FAELLT Y NNN NNN OOO VIER Y SICHT EINS NULL

Now, converting abbreviations, spelled numbers, and so forth, we get the following:

Von Looks: Funktelegramm 1132/19

 Inhalt:

 Bei Angriff unter Wasser gedrueckt, Wasserbomben.  
 Letzter Gegnerstandort 08:30 Uhr,
 Marine Quadrat AJ 9863, 220 Grad, 8 Seemeilen, stosse nach.
 14 Millibar faellt, NNO 4, Sicht 10.
Running this through Google translate, we get the following English:
From Looks: radiogram 1132/19
  content:
  When attacked underwater pressured water bombs.
  Last opponents Location 08:30 clock,
  Marine Square AJ 9863, 220 degrees, 8 nautical miles, according to stumble.
  14 millibar falls, NNO 4, view 10th

Um, yeah...  Automatic translators pretty much suck...  Let's try this version:

From Looks: Radiogram 1132/19
Contents:
Forced to submerge during attack, depth charges,
Last enemy location 08:30h,
Naval Grid AJ 9863, 220 degrees, 8 nautical miles, (I am) following (the enemy).
(Barometer) 1014  Millibar (tendency) falling, North North East 4, visibility 10.

You can read all about this message and its history at this great page.  Have fun! 

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>