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 > 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.
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!
Hi,
Could I be possible to add the types of the variables? Or maybe a working example?
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.
}
}
Very informative post. Thanks for taking the time to share your view with us.
Just looked through the thread! Awesome work.