## 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 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';

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 Responses to “Playfair Cipher in Navision”

1. Gonzalio says:

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. guidorobben says:

Hi,

Could I be possible to add the types of the variables? Or maybe a working example?

3. iby says:

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;

Key@1112800001 : Text;

OrigText@1112800002 : Text;

EncryptText@1112800003 : Text;

prepText@1112800004 : Text;

DecryptText@1112800005 : Text;

PROCEDURE GenerateKeyIndex@1112800001(inKey@1112800000 : Text) outKey : Text;

VAR

i@1112800001 : Integer;

aIndex@1112800003 : Integer;

alphabet@1112800002 : Text;

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) preText : Text;

VAR

i@1112800001 : Integer;

nextChar@1112800002 : Text;

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) crypText : Text;

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) crypText : Text;

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.

}

}

```
4. antivirusqzwzd says:

Very informative post. Thanks for taking the time to share your view with us.

5. aspinnaTeapse says:

Just looked through the thread! Awesome work.