diff --git a/README.md b/README.md index 10ea1d7..65edb23 100644 --- a/README.md +++ b/README.md @@ -147,14 +147,16 @@ For each plaintext character P1, do the following: 3. If P1 and P2 are not on the same row and not on the same column, find the opposite corners of the imaginary rectangle on the grid. The ciphertext character C1 will be on the same row as P1, and the ciphertext character C2 - will be on the same row as P2. + will be on the same row as P2. The resulting digraph will be `C1C2`. 4. If P1 and P2 are on the same row, C1 and C2 will be to the immediate right of P1 and P2 respectively. If a character is in the rightmost column, the - resulting character will be on the first one of the same row. + resulting character will be on the first one of the same row. Lastly, you + need to swap the ciphertext characters (`P1P2` => `C2C1`). 5. If P1 and P2 are on the same column, C1 and C2 will be to the immediate down of P1 and P2 respectively. If a character is in the bottommost row, the - resulting character will be on the first one of the same column. -6. Write down C1 and C2 as the ciphertext digraph. + resulting character will be on the first one of the same column. Then you + need to swap the ciphertext characters (`P1P2` => `C2C1`). +6. Write down the resulting ciphertext digraph. After all plaintext characters are processed, add PL random characters in front of the ciphertext, where PL = KL mod 10 + 1, where KL is the initial key phrase @@ -171,10 +173,10 @@ Then, for each ciphertext digraph C1C2, do the following: 2. If C1 and C2 are not on the same row and not on the same column, find the opposite corners of the imaginary rectangle on the grid. The plaintext character P1 will be on the same row as C1. -3. If C1 and C2 are on the same row, P1 will be to the immediate left of C1. - If C1 is in the leftmost column, P1 will be on the last one of the same row. -4. If C1 and C2 are on the same column, P1 will be to the immediate up of C1. - If C1 is in the topmost row, P1 will be on the last one of the same column. +3. If C1 and C2 are on the same row, P1 will be to the immediate left of C2. + If C2 is in the leftmost column, P1 will be on the last one of the same row. +4. If C1 and C2 are on the same column, P1 will be to the immediate up of C2. + If C2 is in the topmost row, P1 will be on the last one of the same column. 5. Write down P1 as the next plaintext character. Design rationale @@ -195,6 +197,12 @@ InterPlay-36 improves over the classic Playfair in several ways: 2. Adds a plaintext interleaving step into the encryption phase (see below). 3. Allows to preserve whitespace in the plaintext while making sure the end ciphertext will never start with whitespace. +4. By reversing the ciphertext digraph in case of the "same row/column" rules, + it eliminates the possibility of homophonic analysis by just discarding + every odd ciphertext character (after removing the prefix). Even letters + still have a statistical bias (25/36 against 10/36) but there's no way to + tell for sure which letter in particular ciphertext digraph is significant + and which one is a decoy. The interleaving step is important to break several negative properties of the Playfair algorithm that make its cryptanalysis easier, like its susceptibility @@ -206,7 +214,8 @@ that no digraph contains a repeated letter, without complicating the logic. InterPlay-36 was designed to be used "as is", however one can easily combine it with other popular encryption methods such as transposition ciphers (using the -key grid, a transposed key grid and/or the keyphrase length as the key sources). +key grid, a transposed key grid and/or the keyphrase length as the key sources) +or a keyed fractionation scheme like Bifid, using the same keyed grid. In any case, it is advised to apply InterPlay-36 first in the chain when you are encrypting the messages, and last when decrypting. The reference implementation of InterPlay-36 in HTML5/JS also contains a flag to apply a DCT (double columnar diff --git a/ip36.py b/ip36.py index 56540a4..de98ad4 100755 --- a/ip36.py +++ b/ip36.py @@ -83,8 +83,9 @@ def ipfencrypt(msg:str, key:str): elif y1 == y2: # same row: get the coords to the right x1 = (x1 + 1) % pfsize x2 = (x2 + 1) % pfsize - enc.append(getgridchar(kg, x1, y1)) + # swap the ciphertext character enc.append(getgridchar(kg, x2, y2)) + enc.append(getgridchar(kg, x1, y1)) # generate a random prefix preflen = (len(key) % 10) + 1 prefix = '' @@ -112,14 +113,14 @@ def ipfdecrypt(enc:str, key:str): msg.append(getgridchar(kg, x2, y1)) else: if x1 == x2: # same column: get the coords above - y1 -= 1 - if y1 < 0: - y1 += pfsize + y2 -= 1 + if y2 < 0: + y2 += pfsize elif y1 == y2: # same row: get the coords to the left - x1 -= 1 - if x1 < 0: - x1 += pfsize - msg.append(getgridchar(kg, x1, y1)) + x2 -= 1 + if x2 < 0: + x2 += pfsize + msg.append(getgridchar(kg, x2, y2)) # finalize the decrypted message return ''.join(msg).strip()