Playfair Cipher in Navision

As earlier mentioned I have being reading some books about Cryptology – and now its Playfairs turn to be tested in Navision 🙂

Playfair Cipher (aka. Playfair square) is a symmetric encryption technique and was the first literal digraph substitution cipher. It was invented by Charles Wheatstone and popularized by Lyon Playfair.

The technique encrypts pairs of letters instead of single letters, which means it is a type of digraph cipher.

The Playfair Cipher is significantly harder to break since the frequency analysis used for simple substitution ciphers does not work with it. So its no wonder, that is was used during both the first and second world war.

Lets take a closer look. First we have to choose a key, as example we will use “JULEMANDEN”.
The Playfair cipher uses a 5 by 5 table containing a key word or phrase, where the phrase normally will be the alphabet. So the key “JULEMANDEN” will become:

I U L E M
A N D B C
F G H K O
P Q R S T
V W X Y Z

How did this key be generated? Well first we took JULEMANDEN and removed duplicated letters – after this we where left with JULEMAND.

Next we replaced J with I. J & I are represented by the same letter.

Finally we split it up into a 5 x 5 square, and filled the missing field values with letters from the alphabet.

This is how it could be done in Navision:

//convert j to i
inKey := CONVERTSTR(inKey,'j','i');

//Remove duplicates
FOR i := 1 TO STRLEN(inKey) DO BEGIN
   IF STRPOS(outKey,FORMAT(Key[i])) = 0 THEN
       outKey += FORMAT(inKey[i]);
END;

//Put in fill chars
alphabet := 'abcdefghiiklmnopqrstuvwxyz';
aIndex := 1;
WHILE STRLEN(outKey) < 25 DO BEGIN
   IF STRPOS(outKey, FORMAT(alphabet[aIndex])) = 0 THEN
       outKey += FORMAT(alphabet[aIndex]);
   aIndex += 1;
END;

Now that the key is in place, we will focus on the encryption.

Lets try to encrypt the text: “this is encrypted”. Before the text can be encrypt it has to prepared. The preparation will go through the following steps:

  • Remove spaces
  • Remove unsupported letters. Ex. dot, numbers and localized letters ex. æøå
  • Replace j with i
  • If two letters are the same, then add a X. Ex. “this show” would be “this xshow”
  • If text length is odd then add a X to the end

Finally break the message into digraphs (groups of 2 letters). So before starting the encryption the prepared text would look like this:

Text: this is encrypted
Prepared Text: th is is en cr yp te dx

And as an Navision example:

orgText := LOWERCASE(orgText);

//Remove not supported signs - only letters are supported!
orgText := DELCHR(orgText,'=',' .,0123456789æøå');
orgText := CONVERTSTR(orgText,'j','i');

//If the second letter of a pair is the same as the first letter, then use x.
i := 1;
WHILE i <= STRLEN(orgText) DO BEGIN

   nextChar := FORMAT(orgText[i]);

   IF (i &gt; 1) THEN
       IF orgText[i] = orgText[i-1] THEN
           nextChar := 'x' + FORMAT(orgText[i]);

   preText += nextChar;
   i += 1;
END;

//If string length is odd then add x
IF (STRLEN(preText) MOD 2) = 1 THEN
   preText += 'x';

Now lets do the encryption by looking on the individual groups and their placement in the key.
To encrypt a group we use the following rules:

  • Is both letters in the same Row? If yes then take the next letter in the row
  • Is both letters in the same Column? If yes then take the next letter in the column
  • If none off the above occur, the rule differs. To encrypt the first letter, look along its row until the column containing the second letter is reached – the letter at this intersection replaces the first letter. To encrypt the second letter, look along its row until the column containing the first letter is reached – the letter at this intersection replaces the second letter. Hence, ‘th’ becomes ‘ro’.

The encrypted text will therefor become:

Text: this is encrypted
Encrypted: ro ep ep ub dt vs sm hl

This is how it could be done in Navision:

i := 1;
FOR i := 1 TO STRLEN(preText) DO BEGIN

   Index1 := STRPOS(KeyIndex,FORMAT(preText[i]));
   row1 := ROUND(Index1 / 5,1,'>');
   col1 := Index1 MOD 5;

   IF col1 = 0 THEN
       col1 := 5;

   Index2 := STRPOS(KeyIndex,FORMAT(preText[i+1]));
   row2 := ROUND(Index2 / 5,1,'>');
   col2 := Index2 MOD 5;

   IF col2 = 0 THEN 
       col2 := 5;

   i += 1;

   //The letters are in the same Row
   IF row1 = row2 THEN BEGIN
       colpos := NextPos(col1);
       Index1 := ((row1 - 1) * 5) + colpos;

       colpos := NextPos(col2);
       Index2 := ((row2 - 1) * 5) + colpos;

       crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));

       //The Letters are in the same Column
   END ELSE 
       IF col1 = col2 THEN BEGIN
           rowpos := NextPos(row1);
           Index1 := ((rowpos - 1) * 5) + col1;

           rowpos := NextPos(row2);
           Index2 := ((rowpos - 1) * 5) + col2;

           crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));

           //Letters are in a square
       END ELSE BEGIN
           rowpos := NextPos(row1 - 1);
           Index1 := ((rowpos - 1) * 5) + col2;

           rowpos := NextPos(row2 - 1);
           Index2 := ((rowpos - 1) * 5) + col1;

           crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));
       END;

END;

Where NextPos is calculated in this way:

NewPos := (Pos + 1) MOD 5;
IF NewPos = 0 THEN
    NewPos := 5;

To decrypt – just do the inverse 😉

Now you are able to encrypt with Playfair in Navision.

5 Comments

  1. An еxcеllеnt sitе! And not badly madе. The main thing that thе information on it useful, I will go into free timе … Good luck to you all!

  2. Hi,

    I had to do some digging… but finally I found my sample code…
    So here you are:


    OBJECT Codeunit 50300 Playfair Cipher
    {
    OBJECT-PROPERTIES
    {
    Date=16-11-08;
    Time=19:49:42;
    Modified=Yes;
    Version List=;
    }
    PROPERTIES
    {
    OnRun=BEGIN

    Key := 'julemanden';
    KeyIndex := GenerateKeyIndex(Key);

    OrigText := 'This text should be encrypted';
    prepText := PrepareText(OrigText);
    EncryptText := Encrypt(prepText);
    DecryptText := Decrypt(EncryptText);
    MESSAGE('origtext: ' + OrigText + 'Prep: ' + prepText + 'Encrypted: ' + EncryptText + 'Decrypt: ' + DecryptText);
    END;

    }
    CODE
    {
    VAR
    KeyIndex@1112800000 : Text[25];
    Key@1112800001 : Text[30];
    OrigText@1112800002 : Text[250];
    EncryptText@1112800003 : Text[250];
    prepText@1112800004 : Text[250];
    DecryptText@1112800005 : Text[250];

    PROCEDURE GenerateKeyIndex@1112800001(inKey@1112800000 : Text[25]) outKey : Text[25];
    VAR
    i@1112800001 : Integer;
    aIndex@1112800003 : Integer;
    alphabet@1112800002 : Text[30];
    BEGIN
    inKey := CONVERTSTR(inKey,'j','i');

    //Remove duplicates
    FOR i := 1 TO STRLEN(inKey) DO BEGIN
    IF STRPOS(outKey,FORMAT(Key[i])) = 0 THEN
    outKey += FORMAT(inKey[i]);
    END;

    //Put in fill chars
    alphabet := 'abcdefghiiklmnopqrstuvwxyz';
    aIndex := 1;
    WHILE STRLEN(outKey) < 25 DO BEGIN IF STRPOS(outKey, FORMAT(alphabet[aIndex])) = 0 THEN outKey += FORMAT(alphabet[aIndex]); aIndex += 1; END; END; PROCEDURE PrepareText@1112800000(orgText@1112800000 : Text[250]) preText : Text[250]; VAR i@1112800001 : Integer; nextChar@1112800002 : Text[2]; BEGIN orgText := LOWERCASE(orgText); //Remove not supported signs - only letters are supported! orgText := DELCHR(orgText,'=',' .,0123456789‘›†'); orgText := CONVERTSTR(orgText,'j','i'); //If the seond letter of a pair is the same as the first letter, then use x. i := 1; WHILE i <= STRLEN(orgText) DO BEGIN nextChar := FORMAT(orgText[i]); IF (i > 1) THEN
    IF orgText[i] = orgText[i-1] THEN
    nextChar := 'x' + FORMAT(orgText[i]);

    preText += nextChar;
    i += 1;
    END;

    //If string length is odd then add x
    IF (STRLEN(preText) MOD 2) = 1 THEN
    preText += 'x';
    END;

    PROCEDURE Encrypt@1112800002(VAR preText@1112800002 : Text[250]) crypText : Text[250];
    VAR
    i@1112800001 : Integer;
    row1@1112800003 : Integer;
    row2@1112800000 : Integer;
    col1@1112800004 : Integer;
    col2@1112800005 : Integer;
    Index1@1112800008 : Integer;
    Index2@1112800009 : Integer;
    colpos@1112800012 : Integer;
    rowpos@1112800013 : Integer;
    BEGIN

    i := 1;
    FOR i := 1 TO STRLEN(preText) DO BEGIN

    Index1 := STRPOS(KeyIndex,FORMAT(preText[i]));
    row1 := ROUND(Index1 / 5,1,'>');
    col1 := Index1 MOD 5;
    IF col1 = 0 THEN col1 := 5;

    Index2 := STRPOS(KeyIndex,FORMAT(preText[i+1]));
    row2 := ROUND(Index2 / 5,1,'>');
    col2 := Index2 MOD 5;
    IF col2 = 0 THEN col2 := 5;

    i += 1;

    //The letters are in the same Row
    IF row1 = row2 THEN BEGIN
    colpos := NextPos(col1);
    Index1 := ((row1 - 1) * 5) + colpos;

    colpos := NextPos(col2);
    Index2 := ((row2 - 1) * 5) + colpos;

    crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));

    //The Letters are in the same Column
    END ELSE IF col1 = col2 THEN BEGIN
    rowpos := NextPos(row1);
    Index1 := ((rowpos - 1) * 5) + col1;

    rowpos := NextPos(row2);
    Index2 := ((rowpos - 1) * 5) + col2;

    crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));

    //Letters are in a square
    END ELSE BEGIN
    rowpos := NextPos(row1 - 1);
    Index1 := ((rowpos - 1) * 5) + col2;

    rowpos := NextPos(row2 - 1);
    Index2 := ((rowpos - 1) * 5) + col1;

    crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));
    END;

    END;
    END;

    PROCEDURE Decrypt@1112800003(VAR preText@1112800002 : Text[250]) crypText : Text[250];
    VAR
    i@1112800001 : Integer;
    row1@1112800003 : Integer;
    row2@1112800000 : Integer;
    col1@1112800004 : Integer;
    col2@1112800005 : Integer;
    Index1@1112800008 : Integer;
    Index2@1112800009 : Integer;
    colpos@1112800012 : Integer;
    rowpos@1112800013 : Integer;
    BEGIN

    i := 1;
    FOR i := 1 TO STRLEN(preText) DO BEGIN

    Index1 := STRPOS(KeyIndex,FORMAT(preText[i]));
    row1 := ROUND(Index1 / 5,1,'>');
    col1 := Index1 MOD 5;
    IF col1 = 0 THEN col1 := 5;

    Index2 := STRPOS(KeyIndex,FORMAT(preText[i+1]));
    row2 := ROUND(Index2 / 5,1,'>');
    col2 := Index2 MOD 5;
    IF col2 = 0 THEN col2 := 5;

    i += 1;

    //The letters are in the same Row
    IF row1 = row2 THEN BEGIN
    colpos := NextPos2(col1);
    Index1 := ((row1 - 1) * 5) + colpos;

    colpos := NextPos2(col2);
    Index2 := ((row2 - 1) * 5) + colpos;

    crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));

    //The Letters are in the same Column
    END ELSE IF col1 = col2 THEN BEGIN
    rowpos := NextPos(row1);
    Index1 := (rowpos * 5) + col1;

    rowpos := NextPos(row2);
    Index2 := (rowpos * 5) + col2;

    crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));

    //Letters are in a square
    END ELSE BEGIN
    rowpos := NextPos(row2 - 1);
    Index2 := ((rowpos - 1) * 5) + col1;

    rowpos := NextPos(row1 - 1);
    Index1 := ((rowpos - 1) * 5) + col2;

    crypText += (FORMAT(KeyIndex[Index1]) + FORMAT(KeyIndex[Index2]));
    END;

    END;
    END;

    PROCEDURE "**** Help functions *****"@1112800004();
    BEGIN
    END;

    PROCEDURE NextPos@1112800005(Pos@1112800000 : Integer) NewPos : Integer;
    BEGIN

    NewPos := (Pos + 1) MOD 5;

    IF NewPos = 0 THEN
    NewPos := 5;
    END;

    PROCEDURE NextPos2@1112800006(Pos@1112800000 : Integer) NewPos : Integer;
    BEGIN

    NewPos := (Pos - 1) MOD 5;

    IF NewPos = 0 THEN
    NewPos := 5;
    END;

    BEGIN
    END.
    }
    }

Leave a Reply

Your email address will not be published.


*